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

Feature/member management Add Members Directly #107

7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@
"redux": "^4.0.5",
"redux-logger": "^3.0.6",
"redux-promise-middleware": "^6.1.2",
"redux-thunk": "^2.3.0"
"redux-thunk": "^2.3.0",
"tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.4"
},
"browserslist": [
"last 1 version",
Expand Down
13 changes: 11 additions & 2 deletions src/components/BaseModal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,22 @@ const containerStyle = {
padding: "10px",
};

function BaseModal({ open, onClose, children, title, button, disabled }) {
function BaseModal({
open,
onClose,
children,
title,
button,
disabled,
extraModalStyle,
}) {
return (
<Modal
open={open}
onClose={onClose}
closeIcon={<IconCross width="15px" height="15px" />}
styles={{
modal: modalStyle,
modal: { ...modalStyle, ...extraModalStyle },
modalContainer: containerStyle,
}}
center={true}
Expand Down Expand Up @@ -63,6 +71,7 @@ BaseModal.propTypes = {
title: PT.string,
button: PT.element,
disabled: PT.bool,
extraModalStyle: PT.object,
};

export default BaseModal;
2 changes: 1 addition & 1 deletion src/components/DateInput/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ DateInput.propTypes = {
placeholder: PT.string,
onBlur: PT.func,
onFocus: PT.func,
className: PT.string
className: PT.string,
};

export default DateInput;
3 changes: 1 addition & 2 deletions src/components/DateInput/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
width: 100%;
}
&.error {
input{
input {
border-color: #fe665d;
}

}
}

Expand Down
8 changes: 4 additions & 4 deletions src/components/FormField/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const FormField = ({ field }) => {
<Field name={field.name}>
{({ input, meta }) => (
<div>
{ !field.readonly && (
{!field.readonly && (
<label
styleName={
(input.value != "undefined" &&
Expand Down Expand Up @@ -89,9 +89,9 @@ const FormField = ({ field }) => {
onFocus={input.onFocus}
/>
)}
{(field.isRequired || field.customValidator) && meta.error && meta.touched && (
<div styleName="field-error">{meta.error}</div>
)}
{(field.isRequired || field.customValidator) &&
meta.error &&
meta.touched && <div styleName="field-error">{meta.error}</div>}
</div>
)}
</Field>
Expand Down
45 changes: 33 additions & 12 deletions src/components/ReactSelect/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import React from "react";
import PT from "prop-types";
import Select from "react-select";
import CreatableSelect from "react-select/creatable";
import "./styles.module.scss";

const ReactSelect = (props) => {
Expand Down Expand Up @@ -69,18 +70,36 @@ const ReactSelect = (props) => {

return (
<div styleName="select-wrapper">
<Select
value={props.value}
styles={customStyles}
onChange={props.onChange}
options={props.options}
styleName={props.error ? "error" : ""}
isMulti={props.isMulti}
onBlur={props.onBlur}
onFocus={props.onFocus}
placeholder={props.placeholder}
onInputChange={props.onInputChange}
/>
{props.isCreatable ? (
<CreatableSelect
value={props.value}
styles={customStyles}
onChange={props.onChange}
options={props.options}
styleName={props.error ? "error" : ""}
isMulti={props.isMulti}
onBlur={props.onBlur}
onFocus={props.onFocus}
placeholder={props.placeholder}
onInputChange={props.onInputChange}
noOptionsMessage={() => props.noOptionsText}
createOptionPosition="first"
/>
) : (
<Select
value={props.value}
styles={customStyles}
onChange={props.onChange}
options={props.options}
styleName={props.error ? "error" : ""}
isMulti={props.isMulti}
onBlur={props.onBlur}
onFocus={props.onFocus}
placeholder={props.placeholder}
onInputChange={props.onInputChange}
noOptionsMessage={() => props.noOptionsText}
/>
)}
</div>
);
};
Expand All @@ -100,6 +119,8 @@ ReactSelect.propTypes = {
label: PT.string.isRequired,
}).isRequired
),
isCreatable: PT.bool,
noOptionsText: PT.string,
};

export default ReactSelect;
4 changes: 1 addition & 3 deletions src/components/TCForm/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ const TCForm = ({
<div styleName="field-group">
{row.fields.map((field) => (
<div styleName="field-group-field" key={field.name}>
<FormField
field={fields[field]}
/>
<FormField field={fields[field]} />
</div>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/TCForm/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}
}

.job-form-fields-wrapper{
.job-form-fields-wrapper {
width: 100%;
max-width: 640px;
margin: 0 auto;
Expand Down
2 changes: 1 addition & 1 deletion src/components/TextInput/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import "./styles.module.scss";
function TextInput(props) {
return (
<input
styleName={cn("TextInput", props.className, {"readonly": props.readonly})}
styleName={cn("TextInput", props.className, { readonly: props.readonly })}
maxLength={props.maxLength}
min={props.minValue}
onChange={(event) => {
Expand Down
27 changes: 27 additions & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,30 @@ export const STATUS_OPTIONS = [
{ value: "closed", label: "closed" },
{ value: "cancelled", label: "cancelled" },
];

/*
* TopCoder user roles
*/
export const ROLE_TOPCODER_USER = "Topcoder User";
export const ROLE_CONNECT_COPILOT = "Connect Copilot";
export const ROLE_CONNECT_MANAGER = "Connect Manager";
export const ROLE_CONNECT_ACCOUNT_MANAGER = "Connect Account Manager";
export const ROLE_CONNECT_ADMIN = "Connect Admin";
export const ROLE_ADMINISTRATOR = "administrator";
export const ROLE_CONNECT_COPILOT_MANAGER = "Connect Copilot Manager";
export const ROLE_BUSINESS_DEVELOPMENT_REPRESENTATIVE =
"Business Development Representative";
export const ROLE_PRESALES = "Presales";
export const ROLE_ACCOUNT_EXECUTIVE = "Account Executive";
export const ROLE_PROGRAM_MANAGER = "Program Manager";
export const ROLE_SOLUTION_ARCHITECT = "Solution Architect";
export const ROLE_PROJECT_MANAGER = "Project Manager";

// User roles that can see suggestions when adding new members to project
export const SEE_SUGGESTION_ROLES = [
ROLE_ADMINISTRATOR,
ROLE_CONNECT_ADMIN,
ROLE_CONNECT_MANAGER,
ROLE_CONNECT_ACCOUNT_MANAGER,
ROLE_CONNECT_COPILOT_MANAGER,
];
11 changes: 8 additions & 3 deletions src/routes/JobDetails/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ const JobDetails = ({ teamId, jobId }) => {
const skill = _.find(skills, { id: skillId });

if (!skill) {
console.warn(`Couldn't find name for skill id "${skillId}" of the job "${job.id}".`)
return null
console.warn(
`Couldn't find name for skill id "${skillId}" of the job "${job.id}".`
);
return null;
}

return skill.name;
Expand Down Expand Up @@ -79,7 +81,10 @@ const JobDetails = ({ teamId, jobId }) => {
<DataItem title="Resource Type" icon={<IconDescription />}>
{job.resourceType}
</DataItem>
<DataItem title="Resource Rate Frequency" icon={<IconDescription />}>
<DataItem
title="Resource Rate Frequency"
icon={<IconDescription />}
>
{job.rateType}
</DataItem>
<DataItem title="Workload" icon={<IconDescription />}>
Expand Down
29 changes: 15 additions & 14 deletions src/routes/JobForm/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,21 @@ const JobForm = ({ teamId, jobId }) => {

// as we are using `PUT` method (not `PATCH`) we have send ALL the fields
// fields which we don't send would become `null` otherwise
const getRequestData = (values) => _.pick(values, [
'projectId',
'externalId',
'description',
'title',
'startDate',
'duration',
'numPositions',
'resourceType',
'rateType',
'workload',
'skills',
'status',
]);
const getRequestData = (values) =>
_.pick(values, [
"projectId",
"externalId",
"description",
"title",
"startDate",
"duration",
"numPositions",
"resourceType",
"rateType",
"workload",
"skills",
"status",
]);

useEffect(() => {
if (skills && job && !options) {
Expand Down
14 changes: 10 additions & 4 deletions src/routes/PositionDetails/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import "./styles.module.scss";

const PositionDetails = ({ teamId, positionId }) => {
// be dafault show "Interested" tab
const [candidateStatus, setCandidateStatus] = useState(CANDIDATE_STATUS.SHORTLIST);
const [candidateStatus, setCandidateStatus] = useState(
CANDIDATE_STATUS.SHORTLIST
);
const {
state: { position, error },
updateCandidate,
Expand All @@ -32,10 +34,14 @@ const PositionDetails = ({ teamId, positionId }) => {

// if there are some candidates to review, then show "To Review" tab by default
useEffect(() => {
if (position && _.filter(position.candidates, { status: CANDIDATE_STATUS.OPEN }).length > 0) {
setCandidateStatus(CANDIDATE_STATUS.OPEN)
if (
position &&
_.filter(position.candidates, { status: CANDIDATE_STATUS.OPEN }).length >
0
) {
setCandidateStatus(CANDIDATE_STATUS.OPEN);
}
}, [position])
}, [position]);

return (
<Page title="Job Details">
Expand Down
56 changes: 30 additions & 26 deletions src/routes/ResourceBookingDetails/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,30 @@ import ResourceDetails from "./ResourceDetails";
import "./styles.module.scss";

const ResourceBookingDetails = ({ teamId, resourceBookingId }) => {
const [resource, loadingError] = useData(getReourceBookingById, resourceBookingId);
const [resource, loadingError] = useData(
getReourceBookingById,
resourceBookingId
);
const [team, loadingTeamError] = useData(getTeamById, teamId);
const [jobTitle, setJobTitle] = useState("")
const [member, setMember] = useState("")
const [jobTitle, setJobTitle] = useState("");
const [member, setMember] = useState("");

useEffect(() => {
if (team) {
const resourceWithMemberDetails = _.find(
team.resources,
{ id: resourceBookingId }
);
const resourceWithMemberDetails = _.find(team.resources, {
id: resourceBookingId,
});

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

if (resourceWithMemberDetails.jobId) {
const job = _.find(team.jobs, { id: resourceWithMemberDetails.jobId });
setJobTitle(_.get(job, "title", `<Not Found> ${resourceWithMemberDetails.jobId}`));
setJobTitle(
_.get(job, "title", `<Not Found> ${resourceWithMemberDetails.jobId}`)
);
} else {
setJobTitle("<Not Assigned>")
setJobTitle("<Not Assigned>");
}
}
}, [team, resourceBookingId]);
Expand All @@ -49,25 +53,25 @@ const ResourceBookingDetails = ({ teamId, resourceBookingId }) => {
{!(member && resource) ? (
<LoadingIndicator error={loadingError || loadingTeamError} />
) : (
<>
<PageHeader
title="Member Details"
backTo={`/taas/myteams/${teamId}`}
/>
<div styleName="content-wrapper">
<ResourceSummary member={member} />
<ResourceDetails resource={resource} jobTitle={jobTitle} />
<div styleName="actions">
<Button
size="medium"
routeTo={`/taas/myteams/${teamId}/rb/${resource.id}/edit`}
>
Edit Member Details
<>
<PageHeader
title="Member Details"
backTo={`/taas/myteams/${teamId}`}
/>
<div styleName="content-wrapper">
<ResourceSummary member={member} />
<ResourceDetails resource={resource} jobTitle={jobTitle} />
<div styleName="actions">
<Button
size="medium"
routeTo={`/taas/myteams/${teamId}/rb/${resource.id}/edit`}
>
Edit Member Details
</Button>
</div>
</div>
</>
)}
</div>
</>
)}
</Page>
);
};
Expand Down
Loading