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

Commit 2aca828

Browse files
authored
Merge pull request #107 from mbaghel/feature/member-management
Feature/member management Add Members Directly
2 parents 1be375a + e3a73cb commit 2aca828

File tree

26 files changed

+552
-347
lines changed

26 files changed

+552
-347
lines changed

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
"redux": "^4.0.5",
8282
"redux-logger": "^3.0.6",
8383
"redux-promise-middleware": "^6.1.2",
84-
"redux-thunk": "^2.3.0"
84+
"redux-thunk": "^2.3.0",
85+
"tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.4"
8586
},
8687
"browserslist": [
8788
"last 1 version",

src/components/BaseModal/index.jsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,22 @@ const containerStyle = {
2727
padding: "10px",
2828
};
2929

30-
function BaseModal({ open, onClose, children, title, button, disabled }) {
30+
function BaseModal({
31+
open,
32+
onClose,
33+
children,
34+
title,
35+
button,
36+
disabled,
37+
extraModalStyle,
38+
}) {
3139
return (
3240
<Modal
3341
open={open}
3442
onClose={onClose}
3543
closeIcon={<IconCross width="15px" height="15px" />}
3644
styles={{
37-
modal: modalStyle,
45+
modal: { ...modalStyle, ...extraModalStyle },
3846
modalContainer: containerStyle,
3947
}}
4048
center={true}
@@ -63,6 +71,7 @@ BaseModal.propTypes = {
6371
title: PT.string,
6472
button: PT.element,
6573
disabled: PT.bool,
74+
extraModalStyle: PT.object,
6675
};
6776

6877
export default BaseModal;

src/components/DateInput/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ DateInput.propTypes = {
3131
placeholder: PT.string,
3232
onBlur: PT.func,
3333
onFocus: PT.func,
34-
className: PT.string
34+
className: PT.string,
3535
};
3636

3737
export default DateInput;

src/components/DateInput/styles.module.scss

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
width: 100%;
66
}
77
&.error {
8-
input{
8+
input {
99
border-color: #fe665d;
1010
}
11-
1211
}
1312
}
1413

src/components/FormField/index.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const FormField = ({ field }) => {
1818
<Field name={field.name}>
1919
{({ input, meta }) => (
2020
<div>
21-
{ !field.readonly && (
21+
{!field.readonly && (
2222
<label
2323
styleName={
2424
(input.value != "undefined" &&
@@ -89,9 +89,9 @@ const FormField = ({ field }) => {
8989
onFocus={input.onFocus}
9090
/>
9191
)}
92-
{(field.isRequired || field.customValidator) && meta.error && meta.touched && (
93-
<div styleName="field-error">{meta.error}</div>
94-
)}
92+
{(field.isRequired || field.customValidator) &&
93+
meta.error &&
94+
meta.touched && <div styleName="field-error">{meta.error}</div>}
9595
</div>
9696
)}
9797
</Field>

src/components/ReactSelect/index.jsx

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import React from "react";
77
import PT from "prop-types";
88
import Select from "react-select";
9+
import CreatableSelect from "react-select/creatable";
910
import "./styles.module.scss";
1011

1112
const ReactSelect = (props) => {
@@ -69,18 +70,36 @@ const ReactSelect = (props) => {
6970

7071
return (
7172
<div styleName="select-wrapper">
72-
<Select
73-
value={props.value}
74-
styles={customStyles}
75-
onChange={props.onChange}
76-
options={props.options}
77-
styleName={props.error ? "error" : ""}
78-
isMulti={props.isMulti}
79-
onBlur={props.onBlur}
80-
onFocus={props.onFocus}
81-
placeholder={props.placeholder}
82-
onInputChange={props.onInputChange}
83-
/>
73+
{props.isCreatable ? (
74+
<CreatableSelect
75+
value={props.value}
76+
styles={customStyles}
77+
onChange={props.onChange}
78+
options={props.options}
79+
styleName={props.error ? "error" : ""}
80+
isMulti={props.isMulti}
81+
onBlur={props.onBlur}
82+
onFocus={props.onFocus}
83+
placeholder={props.placeholder}
84+
onInputChange={props.onInputChange}
85+
noOptionsMessage={() => props.noOptionsText}
86+
createOptionPosition="first"
87+
/>
88+
) : (
89+
<Select
90+
value={props.value}
91+
styles={customStyles}
92+
onChange={props.onChange}
93+
options={props.options}
94+
styleName={props.error ? "error" : ""}
95+
isMulti={props.isMulti}
96+
onBlur={props.onBlur}
97+
onFocus={props.onFocus}
98+
placeholder={props.placeholder}
99+
onInputChange={props.onInputChange}
100+
noOptionsMessage={() => props.noOptionsText}
101+
/>
102+
)}
84103
</div>
85104
);
86105
};
@@ -100,6 +119,8 @@ ReactSelect.propTypes = {
100119
label: PT.string.isRequired,
101120
}).isRequired
102121
),
122+
isCreatable: PT.bool,
123+
noOptionsText: PT.string,
103124
};
104125

105126
export default ReactSelect;

src/components/TCForm/index.jsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ const TCForm = ({
5858
<div styleName="field-group">
5959
{row.fields.map((field) => (
6060
<div styleName="field-group-field" key={field.name}>
61-
<FormField
62-
field={fields[field]}
63-
/>
61+
<FormField field={fields[field]} />
6462
</div>
6563
))}
6664
</div>

src/components/TCForm/styles.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
}
1515
}
1616

17-
.job-form-fields-wrapper{
17+
.job-form-fields-wrapper {
1818
width: 100%;
1919
max-width: 640px;
2020
margin: 0 auto;

src/components/TextInput/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import "./styles.module.scss";
1111
function TextInput(props) {
1212
return (
1313
<input
14-
styleName={cn("TextInput", props.className, {"readonly": props.readonly})}
14+
styleName={cn("TextInput", props.className, { readonly: props.readonly })}
1515
maxLength={props.maxLength}
1616
min={props.minValue}
1717
onChange={(event) => {

src/constants/index.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,30 @@ export const STATUS_OPTIONS = [
222222
{ value: "closed", label: "closed" },
223223
{ value: "cancelled", label: "cancelled" },
224224
];
225+
226+
/*
227+
* TopCoder user roles
228+
*/
229+
export const ROLE_TOPCODER_USER = "Topcoder User";
230+
export const ROLE_CONNECT_COPILOT = "Connect Copilot";
231+
export const ROLE_CONNECT_MANAGER = "Connect Manager";
232+
export const ROLE_CONNECT_ACCOUNT_MANAGER = "Connect Account Manager";
233+
export const ROLE_CONNECT_ADMIN = "Connect Admin";
234+
export const ROLE_ADMINISTRATOR = "administrator";
235+
export const ROLE_CONNECT_COPILOT_MANAGER = "Connect Copilot Manager";
236+
export const ROLE_BUSINESS_DEVELOPMENT_REPRESENTATIVE =
237+
"Business Development Representative";
238+
export const ROLE_PRESALES = "Presales";
239+
export const ROLE_ACCOUNT_EXECUTIVE = "Account Executive";
240+
export const ROLE_PROGRAM_MANAGER = "Program Manager";
241+
export const ROLE_SOLUTION_ARCHITECT = "Solution Architect";
242+
export const ROLE_PROJECT_MANAGER = "Project Manager";
243+
244+
// User roles that can see suggestions when adding new members to project
245+
export const SEE_SUGGESTION_ROLES = [
246+
ROLE_ADMINISTRATOR,
247+
ROLE_CONNECT_ADMIN,
248+
ROLE_CONNECT_MANAGER,
249+
ROLE_CONNECT_ACCOUNT_MANAGER,
250+
ROLE_CONNECT_COPILOT_MANAGER,
251+
];

src/routes/JobDetails/index.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ const JobDetails = ({ teamId, jobId }) => {
3636
const skill = _.find(skills, { id: skillId });
3737

3838
if (!skill) {
39-
console.warn(`Couldn't find name for skill id "${skillId}" of the job "${job.id}".`)
40-
return null
39+
console.warn(
40+
`Couldn't find name for skill id "${skillId}" of the job "${job.id}".`
41+
);
42+
return null;
4143
}
4244

4345
return skill.name;
@@ -79,7 +81,10 @@ const JobDetails = ({ teamId, jobId }) => {
7981
<DataItem title="Resource Type" icon={<IconDescription />}>
8082
{job.resourceType}
8183
</DataItem>
82-
<DataItem title="Resource Rate Frequency" icon={<IconDescription />}>
84+
<DataItem
85+
title="Resource Rate Frequency"
86+
icon={<IconDescription />}
87+
>
8388
{job.rateType}
8489
</DataItem>
8590
<DataItem title="Workload" icon={<IconDescription />}>

src/routes/JobForm/index.jsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,21 @@ const JobForm = ({ teamId, jobId }) => {
6161

6262
// as we are using `PUT` method (not `PATCH`) we have send ALL the fields
6363
// fields which we don't send would become `null` otherwise
64-
const getRequestData = (values) => _.pick(values, [
65-
'projectId',
66-
'externalId',
67-
'description',
68-
'title',
69-
'startDate',
70-
'duration',
71-
'numPositions',
72-
'resourceType',
73-
'rateType',
74-
'workload',
75-
'skills',
76-
'status',
77-
]);
64+
const getRequestData = (values) =>
65+
_.pick(values, [
66+
"projectId",
67+
"externalId",
68+
"description",
69+
"title",
70+
"startDate",
71+
"duration",
72+
"numPositions",
73+
"resourceType",
74+
"rateType",
75+
"workload",
76+
"skills",
77+
"status",
78+
]);
7879

7980
useEffect(() => {
8081
if (skills && job && !options) {

src/routes/PositionDetails/index.jsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import "./styles.module.scss";
1717

1818
const PositionDetails = ({ teamId, positionId }) => {
1919
// be dafault show "Interested" tab
20-
const [candidateStatus, setCandidateStatus] = useState(CANDIDATE_STATUS.SHORTLIST);
20+
const [candidateStatus, setCandidateStatus] = useState(
21+
CANDIDATE_STATUS.SHORTLIST
22+
);
2123
const {
2224
state: { position, error },
2325
updateCandidate,
@@ -32,10 +34,14 @@ const PositionDetails = ({ teamId, positionId }) => {
3234

3335
// if there are some candidates to review, then show "To Review" tab by default
3436
useEffect(() => {
35-
if (position && _.filter(position.candidates, { status: CANDIDATE_STATUS.OPEN }).length > 0) {
36-
setCandidateStatus(CANDIDATE_STATUS.OPEN)
37+
if (
38+
position &&
39+
_.filter(position.candidates, { status: CANDIDATE_STATUS.OPEN }).length >
40+
0
41+
) {
42+
setCandidateStatus(CANDIDATE_STATUS.OPEN);
3743
}
38-
}, [position])
44+
}, [position]);
3945

4046
return (
4147
<Page title="Job Details">

src/routes/ResourceBookingDetails/index.jsx

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,30 @@ import ResourceDetails from "./ResourceDetails";
2020
import "./styles.module.scss";
2121

2222
const ResourceBookingDetails = ({ teamId, resourceBookingId }) => {
23-
const [resource, loadingError] = useData(getReourceBookingById, resourceBookingId);
23+
const [resource, loadingError] = useData(
24+
getReourceBookingById,
25+
resourceBookingId
26+
);
2427
const [team, loadingTeamError] = useData(getTeamById, teamId);
25-
const [jobTitle, setJobTitle] = useState("")
26-
const [member, setMember] = useState("")
28+
const [jobTitle, setJobTitle] = useState("");
29+
const [member, setMember] = useState("");
2730

2831
useEffect(() => {
2932
if (team) {
30-
const resourceWithMemberDetails = _.find(
31-
team.resources,
32-
{ id: resourceBookingId }
33-
);
33+
const resourceWithMemberDetails = _.find(team.resources, {
34+
id: resourceBookingId,
35+
});
3436

3537
// resource inside Team object has all the member details we need
3638
setMember(resourceWithMemberDetails);
3739

3840
if (resourceWithMemberDetails.jobId) {
3941
const job = _.find(team.jobs, { id: resourceWithMemberDetails.jobId });
40-
setJobTitle(_.get(job, "title", `<Not Found> ${resourceWithMemberDetails.jobId}`));
42+
setJobTitle(
43+
_.get(job, "title", `<Not Found> ${resourceWithMemberDetails.jobId}`)
44+
);
4145
} else {
42-
setJobTitle("<Not Assigned>")
46+
setJobTitle("<Not Assigned>");
4347
}
4448
}
4549
}, [team, resourceBookingId]);
@@ -49,25 +53,25 @@ const ResourceBookingDetails = ({ teamId, resourceBookingId }) => {
4953
{!(member && resource) ? (
5054
<LoadingIndicator error={loadingError || loadingTeamError} />
5155
) : (
52-
<>
53-
<PageHeader
54-
title="Member Details"
55-
backTo={`/taas/myteams/${teamId}`}
56-
/>
57-
<div styleName="content-wrapper">
58-
<ResourceSummary member={member} />
59-
<ResourceDetails resource={resource} jobTitle={jobTitle} />
60-
<div styleName="actions">
61-
<Button
62-
size="medium"
63-
routeTo={`/taas/myteams/${teamId}/rb/${resource.id}/edit`}
64-
>
65-
Edit Member Details
56+
<>
57+
<PageHeader
58+
title="Member Details"
59+
backTo={`/taas/myteams/${teamId}`}
60+
/>
61+
<div styleName="content-wrapper">
62+
<ResourceSummary member={member} />
63+
<ResourceDetails resource={resource} jobTitle={jobTitle} />
64+
<div styleName="actions">
65+
<Button
66+
size="medium"
67+
routeTo={`/taas/myteams/${teamId}/rb/${resource.id}/edit`}
68+
>
69+
Edit Member Details
6670
</Button>
67-
</div>
6871
</div>
69-
</>
70-
)}
72+
</div>
73+
</>
74+
)}
7175
</Page>
7276
);
7377
};

0 commit comments

Comments
 (0)