Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 295aecb

Browse files
authoredFeb 26, 2021
Merge pull request #129 from mbaghel/dev
fix: issue #124
2 parents 3fb3593 + 04643b6 commit 295aecb

File tree

17 files changed

+207
-125
lines changed

17 files changed

+207
-125
lines changed
 

‎config/dev.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ module.exports = {
99
*/
1010
CONNECT_WEBSITE_URL: "https://connect.topcoder-dev.com",
1111

12-
/**
13-
* Email to request extension
14-
*/
15-
EMAIL_REQUEST_EXTENSION: "customersuccess@topcoder-dev.com",
16-
1712
API: {
1813
V5: "https://api.topcoder-dev.com/v5",
1914
V3: "https://api.topcoder-dev.com/v3",

‎config/prod.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ module.exports = {
99
*/
1010
CONNECT_WEBSITE_URL: "https://connect.topcoder.com",
1111

12-
/**
13-
* Email to request extension
14-
*/
15-
EMAIL_REQUEST_EXTENSION: "customersuccess@topcoder.com",
16-
1712
API: {
1813
V5: "https://api.topcoder.com/v5",
1914
V3: "https://api.topcoder.com/v3",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Report popup actions
3+
*/
4+
import { ACTION_TYPE } from "constants";
5+
6+
/**
7+
* Action to create an email popup and open it
8+
* @param {Object} popupOptions Options to customize popup appearance and behaviour
9+
* @param {Object} data Data to send to server with email text
10+
*/
11+
export const openEmailPopup = (popupOptions, data) => ({
12+
type: ACTION_TYPE.OPEN_EMAIL_POPUP,
13+
payload: { popupOptions, data },
14+
});
15+
16+
/**
17+
* Action to close an email popup
18+
*/
19+
export const closeEmailPopup = () => ({
20+
type: ACTION_TYPE.CLOSE_EMAIL_POPUP,
21+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Use email popup hook
3+
*/
4+
5+
import { useDispatch } from "react-redux";
6+
import { openEmailPopup } from "../actions";
7+
8+
/**
9+
* Hook to allow email popup to be opened by any other component
10+
* (as long as it is mounted somewhere in the tree)
11+
*
12+
* @returns func A wrapper around the open report dispatch
13+
*/
14+
export const useEmailPopup = () => {
15+
const dispatch = useDispatch();
16+
17+
return (popupOptions, data) => {
18+
dispatch(openEmailPopup(popupOptions, data));
19+
};
20+
};
Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,51 @@
11
/**
2-
* A report popup used to report issues with teams or team members
2+
* An email popup used to format and send emails for reporting issues
3+
* and requesting extensions
34
*/
45

56
import React, { useCallback, useState } from "react";
7+
import _ from "lodash";
68
import { useSelector, useDispatch } from "react-redux";
79
import { toastr } from "react-redux-toastr";
8-
import { closeReport } from "./actions";
10+
import { closeEmailPopup } from "./actions";
911
import BaseModal from "components/BaseModal";
1012
import TextArea from "components/TextArea";
1113
import Button from "../Button";
12-
import { postReport } from "services/teams";
14+
import { postEmail } from "services/teams";
1315
import CenteredSpinner from "components/CenteredSpinner";
1416

15-
function ReportPopup() {
16-
const { isOpen, teamName, teamId, memberHandle } = useSelector(
17-
(state) => state.reportPopup
17+
function EmailPopup() {
18+
const { isOpen, popupOptions, data } = useSelector(
19+
(state) => state.emailPopup
1820
);
1921

2022
const dispatch = useDispatch();
2123
const [textVal, setTextVal] = useState("");
2224
const [isLoading, setIsLoading] = useState(false);
2325

24-
const submitReport = () => {
26+
const submitEmail = () => {
2527
setIsLoading(true);
2628

27-
postReport(teamName, teamId, textVal, memberHandle)
29+
const postData = _.merge({}, data);
30+
31+
_.set(postData, popupOptions.textDataField, textVal);
32+
33+
postEmail(postData)
2834
.then(() => {
2935
setIsLoading(false);
3036
closeModal();
31-
toastr.success("Report submitted successfully");
37+
toastr.success(popupOptions.successTitle);
3238
})
3339
.catch((err) => {
3440
setIsLoading(false);
3541

36-
toastr.error("Report failed", err.message);
42+
toastr.error(popupOptions.errorTitle, err.message);
3743
});
3844
};
3945

4046
const button = (
4147
<Button
42-
onClick={() => submitReport()}
48+
onClick={submitEmail}
4349
size="medium"
4450
isSubmit
4551
disabled={textVal.trim().length < 1 || isLoading}
@@ -49,17 +55,15 @@ function ReportPopup() {
4955
);
5056

5157
const closeModal = useCallback(() => {
52-
dispatch(closeReport());
58+
dispatch(closeEmailPopup());
5359
setTextVal("");
5460
}, [dispatch]);
5561

5662
return (
5763
<BaseModal
5864
open={isOpen}
5965
onClose={closeModal}
60-
title={`Issue Report - ${teamName}${
61-
memberHandle ? " - " + memberHandle : ""
62-
}`}
66+
title={popupOptions.title}
6367
button={button}
6468
disabled={isLoading}
6569
>
@@ -69,11 +73,11 @@ function ReportPopup() {
6973
<TextArea
7074
value={textVal}
7175
onChange={setTextVal}
72-
placeholder="Describe your issue"
76+
placeholder={popupOptions.textPlaceholder}
7377
/>
7478
)}
7579
</BaseModal>
7680
);
7781
}
7882

79-
export default ReportPopup;
83+
export default EmailPopup;

‎src/components/ReportPopup/reducers/index.js renamed to ‎src/components/EmailPopup/reducers/index.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
/**
2-
* Reducer for Report popup
2+
* Reducer for Email popup
33
*/
44

55
import { ACTION_TYPE } from "constants";
66

77
const initialState = {
8-
teamName: undefined,
9-
teamId: undefined,
10-
memberHandle: undefined,
118
isOpen: false,
9+
popupOptions: {
10+
title: "",
11+
textPlaceholder: "",
12+
textDataField: "",
13+
},
14+
data: null,
1215
};
1316

1417
const reducer = (state = initialState, action) => {
1518
switch (action.type) {
16-
case ACTION_TYPE.OPEN_REPORT:
19+
case ACTION_TYPE.OPEN_EMAIL_POPUP:
1720
return {
1821
...state,
1922
...action.payload,
2023
isOpen: true,
2124
};
2225

23-
case ACTION_TYPE.CLOSE_REPORT:
26+
case ACTION_TYPE.CLOSE_EMAIL_POPUP:
2427
return {
2528
...state,
2629
isOpen: false,

‎src/components/ReportPopup/actions/index.js

Lines changed: 0 additions & 22 deletions
This file was deleted.

‎src/components/ReportPopup/hooks/useReportPopup.js

Lines changed: 0 additions & 20 deletions
This file was deleted.

‎src/constants/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,10 @@ export const ACTION_TYPE = {
183183
AUTH_CLEAR_TEAM_MEMBERS: "AUTH_CLEAR_TEAM_MEMBERS",
184184

185185
/*
186-
Report Popup
186+
Email Popup
187187
*/
188-
OPEN_REPORT: "OPEN_REPORT",
189-
CLOSE_REPORT: "CLOSE_REPORT",
188+
OPEN_EMAIL_POPUP: "OPEN_EMAIL_POPUP",
189+
CLOSE_EMAIL_POPUP: "CLOSE_EMAIL_POPUP",
190190

191191
/*
192192
Team (project) Members

‎src/reducers/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import { combineReducers } from "redux";
55
import { reducer as toastrReducer } from "react-redux-toastr";
66
import positionDetailsReducer from "../routes/PositionDetails/reducers";
77
import teamMembersReducer from "../routes/TeamAccess/reducers";
8-
import reportPopupReducer from "../components/ReportPopup/reducers";
8+
import emailPopupReducer from "../components/EmailPopup/reducers";
99
import authUserReducer from "../hoc/withAuthentication/reducers";
1010

1111
const rootReducer = combineReducers({
1212
toastr: toastrReducer,
1313
positionDetails: positionDetailsReducer,
1414
teamMembers: teamMembersReducer,
15-
reportPopup: reportPopupReducer,
15+
emailPopup: emailPopupReducer,
1616
authUser: authUserReducer,
1717
});
1818

‎src/routes/MyTeamsDetails/components/TeamMembers/index.jsx

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@ import { TEAM_MEMBERS_PER_PAGE } from "constants";
1919
import {
2020
formatDateRange,
2121
formatMoney,
22-
formatRequestExtensionUrl,
22+
formatReportData,
23+
formatReportPopup,
24+
formatExtensionData,
25+
formatExtensionPopup,
2326
} from "utils/format";
2427
import Input from "components/Input";
2528
import { skillShape } from "components/SkillsList";
26-
import { useReportPopup } from "components/ReportPopup/hooks/useReportPopup";
29+
import { useEmailPopup } from "components/EmailPopup/hooks/useEmailPopup";
2730
import { hasPermission } from "utils/permissions";
2831
import { PERMISSIONS } from "constants/permissions";
2932

3033
const TeamMembers = ({ team }) => {
3134
const { resources, jobs } = team;
3235
const [filter, setFilter] = useState("");
3336

34-
const showReportPopup = useReportPopup();
37+
const showEmailPopup = useEmailPopup();
3538

3639
const filteredMembers = useMemo(
3740
() =>
@@ -158,24 +161,38 @@ const TeamMembers = ({ team }) => {
158161
`/taas/myteams/${team.id}/rb/${member.id}/edit`
159162
);
160163
},
161-
hidden: !hasPermission(PERMISSIONS.UPDATE_RESOURCE_BOOKING),
164+
hidden: !hasPermission(
165+
PERMISSIONS.UPDATE_RESOURCE_BOOKING
166+
),
162167
},
163168
{
164169
separator: true,
165-
hidden: !hasPermission(PERMISSIONS.UPDATE_RESOURCE_BOOKING),
170+
hidden: !hasPermission(
171+
PERMISSIONS.UPDATE_RESOURCE_BOOKING
172+
),
166173
},
167174
{
168175
label: "Report an Issue",
169176
action: () => {
170-
showReportPopup(team.name, team.id, member.handle);
177+
showEmailPopup(
178+
formatReportPopup(team.name, member.handle),
179+
formatReportData(
180+
team.name,
181+
team.id,
182+
member.handle
183+
)
184+
);
171185
},
172186
},
173187
{
174188
label: "Request an Extension",
175189
action: () => {
176-
window.open(
177-
formatRequestExtensionUrl(
178-
`Request extension for ${member.handle} on ${team.name}`
190+
showEmailPopup(
191+
formatExtensionPopup(team.name, member.handle),
192+
formatExtensionData(
193+
team.name,
194+
team.id,
195+
member.handle
179196
)
180197
);
181198
},

‎src/routes/MyTeamsDetails/components/TeamSummary/index.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@ import {
1010
formatConnectProjectUrl,
1111
formatMoney,
1212
formatRemainingTimeForTeam,
13+
formatReportPopup,
14+
formatReportData,
1315
} from "utils/format";
1416
import IconClock from "../../../../assets/images/icon-clock.svg";
1517
import IconMoney from "../../../../assets/images/icon-money.svg";
1618
// import IconRating from "../../../../assets/images/icon-rating.svg";
1719
import Button from "components/Button";
18-
import { useReportPopup } from "components/ReportPopup/hooks/useReportPopup";
20+
import { useEmailPopup } from "components/EmailPopup/hooks/useEmailPopup";
1921
// import Rating from "components/Rating";
2022
import "./styles.module.scss";
2123

2224
const TeamSummary = ({ team }) => {
23-
const showReportPopup = useReportPopup();
25+
const showReportPopup = useEmailPopup();
2426

2527
return (
2628
<div styleName="team-summary">
@@ -61,7 +63,10 @@ const TeamSummary = ({ team }) => {
6163
type="warning"
6264
size="medium"
6365
onClick={() => {
64-
showReportPopup(team.name, team.id);
66+
showReportPopup(
67+
formatReportPopup(team.name),
68+
formatReportData(team.name, team.id)
69+
);
6570
}}
6671
target="_blank"
6772
>

‎src/routes/MyTeamsDetails/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import TeamSummary from "./components/TeamSummary";
1515
import TeamMembers from "./components/TeamMembers";
1616
import TeamPositions from "./components/TeamPositions";
1717
import withAuthentication from "../../hoc/withAuthentication";
18-
import ReportPopup from "components/ReportPopup";
18+
import EmailPopup from "components/EmailPopup";
1919

2020
const MyTeamsDetails = ({ teamId }) => {
2121
const [team, loadingError] = useData(getTeamById, teamId);
@@ -36,7 +36,7 @@ const MyTeamsDetails = ({ teamId }) => {
3636
/>
3737
</>
3838
)}
39-
<ReportPopup />
39+
<EmailPopup />
4040
</Page>
4141
);
4242
};

‎src/routes/MyTeamsList/components/TeamCard/index.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ import {
1616
formatMoney,
1717
formatRemainingTimeForTeam,
1818
formatConnectProjectUrl,
19+
formatReportPopup,
20+
formatReportData,
1921
} from "utils/format";
2022
import AvatarGroup from "components/AvatarGroup";
2123
import ThreeDotsMenu from "components/ThreeDotsMenu";
22-
import { useReportPopup } from "components/ReportPopup/hooks/useReportPopup";
24+
import { useEmailPopup } from "components/EmailPopup/hooks/useEmailPopup";
2325

2426
const TeamCard = ({ team }) => {
25-
const showReportPopup = useReportPopup();
27+
const showReportPopup = useEmailPopup();
2628

2729
return (
2830
<div styleName="team-card">
@@ -50,7 +52,10 @@ const TeamCard = ({ team }) => {
5052
{
5153
label: "Report an Issue",
5254
action: () => {
53-
showReportPopup(team.name, team.id);
55+
showReportPopup(
56+
formatReportPopup(team.name),
57+
formatReportData(team.name, team.id)
58+
);
5459
},
5560
},
5661
]}

‎src/routes/MyTeamsList/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { useDebounce } from "react-use";
1818
import { TEAMS_PER_PAGE } from "constants";
1919
import "./styles.module.scss";
2020
import { INPUT_DEBOUNCE_DELAY } from "constants/";
21-
import ReportPopup from "components/ReportPopup";
21+
import EmailPopup from "components/EmailPopup";
2222

2323
const MyTeamsList = () => {
2424
let [myTeams, setMyTeams] = useState(null);
@@ -96,7 +96,7 @@ const MyTeamsList = () => {
9696
)}
9797
</>
9898
)}
99-
<ReportPopup />
99+
<EmailPopup />
100100
</Page>
101101
);
102102
};

‎src/services/teams.js

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -119,29 +119,14 @@ export const getMemberSuggestions = (fragment) => {
119119
};
120120

121121
/**
122-
* Post an issue report
122+
* Post an email
123123
*
124-
* @param {string} teamName team name
125-
* @param {string|number} teamId team id
126-
* @param {string} memberHandle member handle
124+
* @param {Object} data Body object containing template name and email data
127125
*/
128-
export const postReport = (teamName, teamId, reportText, memberHandle) => {
126+
export const postEmail = (data) => {
129127
const url = `${config.API.V5}/taas-teams/email`;
130-
const bodyObj = {
131-
template: "team-issue-report",
132-
data: {
133-
projectName: teamName,
134-
projectId: teamId,
135-
reportText,
136-
},
137-
};
138-
139-
if (memberHandle) {
140-
(bodyObj.template = "member-issue-report"),
141-
(bodyObj.data.userHandle = memberHandle);
142-
}
143128

144-
return axios.post(url, bodyObj);
129+
return axios.post(url, data);
145130
};
146131

147132
/**

‎src/utils/format.js

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,16 +124,90 @@ export const formatFullName = (firstName, lastName) => {
124124
};
125125

126126
/**
127-
* Format Request an Extension URL (mailto:)
127+
* Formats popup options for reports
128+
* @param {string} teamName Team name
129+
* @param {string} memberHandle Member Handle
128130
*
129-
* @param {string} subject email subject
131+
* @returns {Object} Popup Options
132+
*/
133+
export const formatReportPopup = (teamName, memberHandle) => {
134+
return {
135+
title: `Issue Report - ${teamName}${
136+
!!memberHandle ? " - " + memberHandle : ""
137+
}`,
138+
textPlaceholder: "Describe your issue",
139+
textDataField: "data.reportText",
140+
successTitle: "Report submitted successfully",
141+
errorTitle: "Report failed",
142+
};
143+
};
144+
145+
/**
146+
* Formats popup options for extension requests
147+
* @param {string} teamName Team name
148+
* @param {string} memberHandle Member Handle
149+
*
150+
* @returns {Object} Popup Options
151+
*/
152+
export const formatExtensionPopup = (teamName, memberHandle) => {
153+
return {
154+
title: `Extension Request - ${teamName}${
155+
memberHandle ? " - " + memberHandle : ""
156+
}`,
157+
textPlaceholder: "Add any comments...",
158+
textDataField: "data.text",
159+
successTitle: "Extension request submitted successfully",
160+
errorTitle: "Extension request failed",
161+
};
162+
};
163+
164+
/**
165+
* Formats data for sending email reports
166+
* @param {string} teamName Team name
167+
* @param {string|number} teamId Team ID
168+
* @param {string} memberHandle Member handle
169+
*
170+
* @returns {Object} Data object for report
171+
*/
172+
export const formatReportData = (teamName, teamId, memberHandle) => {
173+
const data = {
174+
template: "team-issue-report",
175+
data: {
176+
projectName: teamName,
177+
projectId: teamId,
178+
},
179+
};
180+
181+
if (!!memberHandle) {
182+
_.set(data, "template", "member-issue-report");
183+
_.set(data, "data.userHandle", memberHandle);
184+
}
185+
186+
return data;
187+
};
188+
189+
/**
190+
* Formats data for sending email extension requests
191+
* @param {string} teamName Team name
192+
* @param {string|number} teamId Team ID
193+
* @param {string} memberHandle Member handle
130194
*
131-
* @returns {string} request an extension URL
195+
* @returns {Object} Data object for extension request
132196
*/
133-
export const formatRequestExtensionUrl = (subject) => {
134-
return `mailto:${EMAIL_REQUEST_EXTENSION}?subject=${encodeURIComponent(
135-
subject
136-
)}`;
197+
export const formatExtensionData = (teamName, teamId, memberHandle) => {
198+
const data = {
199+
template: "extension-request",
200+
data: {
201+
projectName: teamName,
202+
projectId: teamId,
203+
},
204+
};
205+
206+
if (!!memberHandle) {
207+
_.set(data, "data.userHandle", memberHandle);
208+
}
209+
210+
return data;
137211
};
138212

139213
/**

0 commit comments

Comments
 (0)
This repository has been archived.