From 8597adb05f4b0c8304edab02cf0d6e532f1f9914 Mon Sep 17 00:00:00 2001 From: yoution Date: Wed, 14 Jul 2021 10:18:52 +0800 Subject: [PATCH 01/22] fix: issue #373 --- .../components/SearchAndSubmit/index.jsx | 12 ++++++++++-- .../CreateNewTeam/components/SearchCard/index.jsx | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx index bfefd418..5e3ae76e 100644 --- a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx +++ b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx @@ -14,6 +14,8 @@ import InputContainer from "../InputContainer"; import SearchContainer from "../SearchContainer"; import SubmitContainer from "../SubmitContainer"; +const SEARCHINGTIME = 1600; + function SearchAndSubmit(props) { const { stages, setStages, searchObject, onClick, page } = props; @@ -48,6 +50,7 @@ function SearchAndSubmit(props) { if (previousSearchId) { searchObjectCopy.previousRoleSearchRequestId = previousSearchId; } + const searchingBegin = Date.now(); searchRoles(searchObjectCopy) .then((res) => { const name = _.get(res, "data.name"); @@ -63,8 +66,13 @@ function SearchAndSubmit(props) { console.error(err); }) .finally(() => { - setCurrentStage(2, stages, setStages); - setSearchState("done"); + _.delay( + () => { + setCurrentStage(2, stages, setStages); + setSearchState("done"); + }, + Date.now() - searchingBegin > SEARCHINGTIME ? 0 : 1500 + ); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [dispatch, previousSearchId, searchObject]); diff --git a/src/routes/CreateNewTeam/components/SearchCard/index.jsx b/src/routes/CreateNewTeam/components/SearchCard/index.jsx index 487f9ceb..dfd365ea 100644 --- a/src/routes/CreateNewTeam/components/SearchCard/index.jsx +++ b/src/routes/CreateNewTeam/components/SearchCard/index.jsx @@ -19,8 +19,8 @@ function SearchCard() { setSearchState("state1"); timer2 = setTimeout(() => { setSearchState("state2"); - }, 800); - }, 800); + }, 500); + }, 500); return () => { clearTimeout(timer1); From 9c63cc2984ba38e1e978387aece5cdb4b2bf703d Mon Sep 17 00:00:00 2001 From: Michael Baghel Date: Wed, 14 Jul 2021 11:36:48 +0400 Subject: [PATCH 02/22] Label updates and simple UI style fixes Resolves: #363, #366, #367, #368, #369, #371, #372 --- .../NoMatchingProfilesResultCard/index.jsx | 9 ++-- .../styles.module.scss | 2 +- .../components/ResultCard/index.jsx | 15 +----- .../components/SearchContainer/index.jsx | 2 +- .../components/SubmitContainer/index.jsx | 2 +- src/routes/CreateNewTeam/index.jsx | 8 +-- .../components/SkillListPopup/index.jsx | 10 ++-- .../SkillListPopup/styles.module.scss | 51 +++---------------- .../pages/InputJobDescription/index.jsx | 7 +++ .../InputJobDescription/styles.module.scss | 7 +++ .../SelectRole/components/RoleItem/index.jsx | 2 +- 11 files changed, 40 insertions(+), 75 deletions(-) diff --git a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx index 2085ab7c..7606b4b8 100644 --- a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx +++ b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx @@ -42,7 +42,7 @@ function NoMatchingProfilesResultCard({ role }) {
-

No Matching Profiles

+

Additional Evaluation Needed

@@ -53,8 +53,11 @@ function NoMatchingProfilesResultCard({ role }) { : "Custom Role"}

- We will be looking internally for members matching your requirements - and be back at them in about 2 weeks. + We did not get a perfect match to your requirements on the first pass, + but we are confident they are out there. We'd like to dig a little + deeper into our community to find someone who can fit your needs. This + may take up to two weeks. Please continue to submit your request, and + a Topcoder representative will reach out to you soon with next steps.

{role.rates && role.name ? (
diff --git a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/styles.module.scss b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/styles.module.scss index b39ab76b..d60db1e5 100644 --- a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/styles.module.scss +++ b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/styles.module.scss @@ -56,7 +56,7 @@ @include font-roboto; font-size: 14px; line-height: 22px; - width: 357px; + width: 80%; text-align: center; } .niche-rate-box { diff --git a/src/routes/CreateNewTeam/components/ResultCard/index.jsx b/src/routes/CreateNewTeam/components/ResultCard/index.jsx index 07f21c13..87b8e469 100644 --- a/src/routes/CreateNewTeam/components/ResultCard/index.jsx +++ b/src/routes/CreateNewTeam/components/ResultCard/index.jsx @@ -245,20 +245,7 @@ function ResultCard({ role }) {

Members matched

-
-

- 60% of members are available 20 hours / week (part - time) -

-

- 20% of members are available 30 hours / week (part - time) -

-

- 10% of members are available 40 hours / week (full - time) -

-
+
)} diff --git a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx index 33f1a580..2a02501d 100644 --- a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx +++ b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx @@ -59,7 +59,7 @@ function SearchContainer({ } onClick={() => setAddAnotherOpen(true)} extraStyleName={completenessStyle} - buttonLabel="Submit Request" + buttonLabel="Continue" stages={stages} percentage={getPercentage()} /> diff --git a/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx b/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx index b7bfdc40..96e52738 100644 --- a/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx +++ b/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx @@ -119,7 +119,7 @@ function SubmitContainer({ setAddAnotherOpen(true)} extraStyleName={completenessStyle} - buttonLabel="Submit Request" + buttonLabel="Continue" stages={stages} percentage="98" /> diff --git a/src/routes/CreateNewTeam/index.jsx b/src/routes/CreateNewTeam/index.jsx index 7140a739..570bd41a 100644 --- a/src/routes/CreateNewTeam/index.jsx +++ b/src/routes/CreateNewTeam/index.jsx @@ -32,22 +32,22 @@ function CreateNewTeam() { Please select how you want to find members that match your requirements.

} backgroundImage="linear-gradient(101.95deg, #8B41B0 0%, #EF476F 100%)" onClick={() => goToRoute("/taas/createnewteam/role")} /> } backgroundImage="linear-gradient(221.5deg, #2C95D7 0%, #9D41C9 100%)" onClick={() => goToRoute("/taas/createnewteam/skills")} /> } backgroundImage="linear-gradient(135deg, #2984BD 0%, #0AB88A 100%)" onClick={() => goToRoute("/taas/createnewteam/jd")} diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/index.jsx b/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/index.jsx index 6d095ca5..0597ba98 100644 --- a/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/index.jsx +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/index.jsx @@ -32,10 +32,10 @@ function SkillListPopup({ open, skills, isLoading, onClose, onContinueClick }) { open={open} onClose={onClose} headerIcon={} - title="Skills" + title="Identified Skills" subtitle={ skills.length - ? "These skills are found in your Job Description" + ? "Topcoder has identified the following skills referenced in your Job Description." : "No skills are found in your Job Description" } isLoading={isLoading} @@ -43,11 +43,11 @@ function SkillListPopup({ open, skills, isLoading, onClose, onContinueClick }) { maxWidth="460px" buttons={Buttons} > -
+
    {_.map(skills, (s) => { - return
    {s.tag}
    ; + return
  • {s.tag}
  • ; })} -
+ ); } diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/styles.module.scss b/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/styles.module.scss index b0270326..ee0c59e2 100644 --- a/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/styles.module.scss +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/styles.module.scss @@ -1,48 +1,9 @@ @import "styles/include"; -.button-group { - display: flex; - flex-direction: row; - justify-content: center; - align-items: flex-end; - :first-child { - margin-right: 8px; - } -} - -.modal-body { - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: center; +.list { + @include font-roboto; + font-size: 18px; + line-height: normal; + color: #2a2a2a; text-align: center; - margin-bottom: 80px; - - svg { - width: 48px; - height: 48px; - margin-bottom: 16px; - } - - h5 { - @include font-barlow-condensed; - font-size: 34px; - color: #1e94a3; - text-transform: uppercase; - font-weight: 500; - margin-bottom: 10px; - } - - p { - @include font-roboto; - font-size: 16px; - color: #555555; - line-height: 26px; - } -} - -.cross { - g { - stroke: #000; - } -} +} \ No newline at end of file diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx b/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx index 10e1809b..1fbfee71 100644 --- a/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx @@ -67,6 +67,13 @@ function InputJobDescription() { title={
Input Job Description
} backTo="/taas/createnewteam" /> +

+ Input a Job Description for your opening and the Topcoder Platform + will identify the skills required to perform the job duties and find + the best matching freelancers for your job opening. After inputting + the Job Description click on the "Search" button to see the skills + identified. +

- Description + View Description & Skills
); From 81ab3ee6ddfb952290a492ff39cd9c128ae615c1 Mon Sep 17 00:00:00 2001 From: Michael Baghel Date: Wed, 14 Jul 2021 13:47:12 +0400 Subject: [PATCH 03/22] feat: Integrate Customer Scroll - Add Customer Scroll to all pages under createnewteam route - Refactor Create New Team Landing Page to its own component - Change createnewteam routes to nested routes to facilitate code sharing - Change height on ItemList and Job Description input components to avoid overlapping with Customer Scroll Resolves: #355 --- src/assets/images/customer-logos.svg | 139 ++++++++++++++++++ src/components/CustomerScroll/index.jsx | 13 ++ .../CustomerScroll/styles.module.scss | 45 ++++++ src/root.component.jsx | 11 +- .../components/ItemList/styles.module.scss | 2 +- src/routes/CreateNewTeam/index.jsx | 62 ++------ .../pages/CreateTeamLanding/index.jsx | 58 ++++++++ .../CreateTeamLanding/styles.module.scss | 3 + .../pages/InputJobDescription/index.jsx | 2 +- src/routes/CreateNewTeam/styles.module.scss | 20 ++- 10 files changed, 296 insertions(+), 59 deletions(-) create mode 100644 src/assets/images/customer-logos.svg create mode 100644 src/components/CustomerScroll/index.jsx create mode 100644 src/components/CustomerScroll/styles.module.scss create mode 100644 src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx create mode 100644 src/routes/CreateNewTeam/pages/CreateTeamLanding/styles.module.scss diff --git a/src/assets/images/customer-logos.svg b/src/assets/images/customer-logos.svg new file mode 100644 index 00000000..0719e61e --- /dev/null +++ b/src/assets/images/customer-logos.svg @@ -0,0 +1,139 @@ + + + Homepage-Logos + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/CustomerScroll/index.jsx b/src/components/CustomerScroll/index.jsx new file mode 100644 index 00000000..bc89cecf --- /dev/null +++ b/src/components/CustomerScroll/index.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import "./styles.module.scss"; + +function CustomerScroll() { + return ( +
+
Trusted By
+
+
+ ); +} + +export default CustomerScroll; diff --git a/src/components/CustomerScroll/styles.module.scss b/src/components/CustomerScroll/styles.module.scss new file mode 100644 index 00000000..35c004fb --- /dev/null +++ b/src/components/CustomerScroll/styles.module.scss @@ -0,0 +1,45 @@ +@import "styles/include"; + +.title { + @include font-barlow; + font-weight: 600; + font-size: 22px; + color: #7f7f7f; + text-align: center; + text-transform: uppercase; + margin-bottom: 30px; +} + +@keyframes scroll { + from {background-position: 0 0;} + to {background-position: -7701px 0;} +} + +.scrolling-logos { + background-image: url("../../assets/images/customer-logos.svg"); + height: 60px; + width: 100%; + animation: scroll 300s linear infinite; + position: relative; + + &:before { + background: linear-gradient(to right, #F4F5F6 0%, rgba(255, 255, 255, 0) 100%); + content: ''; + height: 60px; + left: 0; + position: absolute; + top: 0; + width: 60px; + z-index: 2; + } + &:after { + background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, #F4F5F6 100%); + content: ''; + height: 60px; + position: absolute; + right: 0; + top: 0; + width: 60px; + z-index: 2; + } +} \ No newline at end of file diff --git a/src/root.component.jsx b/src/root.component.jsx index bdd3b79b..4392f546 100644 --- a/src/root.component.jsx +++ b/src/root.component.jsx @@ -10,6 +10,7 @@ import JobDetails from "./routes/JobDetails"; import JobForm from "./routes/JobForm"; import TeamAccess from "./routes/TeamAccess"; import CreateNewTeam from "./routes/CreateNewTeam"; +import CreateTeamLanding from "./routes/CreateNewTeam/pages/CreateTeamLanding"; import InputSkills from "./routes/CreateNewTeam/pages/InputSkills"; import InputJobDescription from "./routes/CreateNewTeam/pages/InputJobDescription"; import SelectRole from "./routes/CreateNewTeam/pages/SelectRole"; @@ -25,7 +26,6 @@ export default function Root() { - @@ -34,9 +34,12 @@ export default function Root() { - - - + + + + + + {/* Global config for Toastr popups */} diff --git a/src/routes/CreateNewTeam/components/ItemList/styles.module.scss b/src/routes/CreateNewTeam/components/ItemList/styles.module.scss index cf201197..3ee04ddc 100644 --- a/src/routes/CreateNewTeam/components/ItemList/styles.module.scss +++ b/src/routes/CreateNewTeam/components/ItemList/styles.module.scss @@ -6,7 +6,7 @@ width: 100%; margin-right: 20px; position: relative; - height: 80vh; + height: 70vh; overflow-y: auto; .title { diff --git a/src/routes/CreateNewTeam/index.jsx b/src/routes/CreateNewTeam/index.jsx index 570bd41a..44c07e6c 100644 --- a/src/routes/CreateNewTeam/index.jsx +++ b/src/routes/CreateNewTeam/index.jsx @@ -1,59 +1,19 @@ /** * Create New Team * - * Landing page for creating new teams - * by selecting a role, inputting skills, - * or inputting a job description + * Container for Create New Team subroutes */ -import React, { useEffect } from "react"; -import { useDispatch } from "react-redux"; -import { navigate } from "@reach/router"; -import _ from "lodash"; -import Page from "components/Page"; -import PageHeader from "components/PageHeader"; -import LandingBox from "./components/LandingBox"; -import { clearMatchingRole } from "./actions"; -import IconMultipleActionsCheck from "../../assets/images/icon-multiple-actions-check-2.svg"; -import IconListQuill from "../../assets/images/icon-list-quill.svg"; -import IconOfficeFileText from "../../assets/images/icon-office-file-text.svg"; +import React from "react"; +import CustomerScroll from "components/CustomerScroll"; import "./styles.module.scss"; -function CreateNewTeam() { - const dispatch = useDispatch(); - const goToRoute = (path) => { - dispatch(clearMatchingRole()); - navigate(path); - }; - - return ( - - Create New Team
} /> -

- Please select how you want to find members that match your requirements. -

- } - backgroundImage="linear-gradient(101.95deg, #8B41B0 0%, #EF476F 100%)" - onClick={() => goToRoute("/taas/createnewteam/role")} - /> - } - backgroundImage="linear-gradient(221.5deg, #2C95D7 0%, #9D41C9 100%)" - onClick={() => goToRoute("/taas/createnewteam/skills")} - /> - } - backgroundImage="linear-gradient(135deg, #2984BD 0%, #0AB88A 100%)" - onClick={() => goToRoute("/taas/createnewteam/jd")} - /> - - ); -} +const CreateNewTeam = (props) => ( +
+ {props.children} +
+ +
+
+); export default CreateNewTeam; diff --git a/src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx b/src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx new file mode 100644 index 00000000..cae9e49a --- /dev/null +++ b/src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx @@ -0,0 +1,58 @@ +/** + * Create Team Landing + * + * Landing page for creating new teams + * by selecting a role, inputting skills, + * or inputting a job description + */ +import React from "react"; +import { useDispatch } from "react-redux"; +import { navigate } from "@reach/router"; +import Page from "components/Page"; +import PageHeader from "components/PageHeader"; +import LandingBox from "../../components/LandingBox"; +import { clearMatchingRole } from "../../actions"; +import IconMultipleActionsCheck from "../../../../assets/images/icon-multiple-actions-check-2.svg"; +import IconListQuill from "../../../../assets/images/icon-list-quill.svg"; +import IconOfficeFileText from "../../../../assets/images/icon-office-file-text.svg"; +import "./styles.module.scss"; + +function CreateNewTeam() { + const dispatch = useDispatch(); + const goToRoute = (path) => { + dispatch(clearMatchingRole()); + navigate(path); + }; + + return ( + + Create New Team} /> +

+ Please select how you want to find members that match your requirements. +

+ } + backgroundImage="linear-gradient(101.95deg, #8B41B0 0%, #EF476F 100%)" + onClick={() => goToRoute("/taas/createnewteam/role")} + /> + } + backgroundImage="linear-gradient(221.5deg, #2C95D7 0%, #9D41C9 100%)" + onClick={() => goToRoute("/taas/createnewteam/skills")} + /> + } + backgroundImage="linear-gradient(135deg, #2984BD 0%, #0AB88A 100%)" + onClick={() => goToRoute("/taas/createnewteam/jd")} + /> +
+ ); +} + +export default CreateNewTeam; diff --git a/src/routes/CreateNewTeam/pages/CreateTeamLanding/styles.module.scss b/src/routes/CreateNewTeam/pages/CreateTeamLanding/styles.module.scss new file mode 100644 index 00000000..e1903c90 --- /dev/null +++ b/src/routes/CreateNewTeam/pages/CreateTeamLanding/styles.module.scss @@ -0,0 +1,3 @@ +.title { + font-weight: 500; +} \ No newline at end of file diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx b/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx index 1fbfee71..1216aaa1 100644 --- a/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx @@ -84,7 +84,7 @@ function InputJobDescription() { /> Date: Wed, 14 Jul 2021 10:51:43 +0800 Subject: [PATCH 04/22] fix: issue #364 --- .../components/ResultCard/index.jsx | 38 +++++++++++++++---- .../components/ResultCard/styles.module.scss | 12 ++++-- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/routes/CreateNewTeam/components/ResultCard/index.jsx b/src/routes/CreateNewTeam/components/ResultCard/index.jsx index 07f21c13..cb56b8ee 100644 --- a/src/routes/CreateNewTeam/components/ResultCard/index.jsx +++ b/src/routes/CreateNewTeam/components/ResultCard/index.jsx @@ -78,10 +78,10 @@ function ResultCard({ role }) { {showRates && !isExternalMember && ( -
+
{userHandle && (

- Hi {userHandle}, we have special rates for you as a Xeno User! + Hi {userHandle}, we have special rates for you as a Wipro User!

)}
@@ -97,8 +97,15 @@ function ResultCard({ role }) {

/Week

-
-

In-Country Rate

+
+

Global Niche Rate

+
+

{formatRate(rates.niche)}

+

/Week

+
+
+
+

Offshore Niche Rate

{formatRate(rates.inCountry)}

/Week

@@ -124,8 +131,15 @@ function ResultCard({ role }) {

/Week

-
-

In-Country Rate

+
+

Global Niche Rate

+
+

{formatRate(rates.rate30Niche)}

+

/Week

+
+
+
+

Offshore Niche Rate

{formatRate(rates.rate30InCountry)}

/Week

@@ -151,8 +165,16 @@ function ResultCard({ role }) {

/Week

-
-

In-Country Rate

+ +
+

Global Niche Rate

+
+

{formatRate(rates.rate20Niche)}

+

/Week

+
+
+
+

Offshore Niche Rate

{formatRate(rates.rate20InCountry)}

/Week

diff --git a/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss b/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss index 6babd0a1..150884c0 100644 --- a/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss +++ b/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss @@ -138,7 +138,7 @@ padding-bottom: 50px; } -.xeno-rates { +.wipro-rates { display: flex; flex-direction: column; padding: 0 25px 50px 52px; @@ -180,7 +180,8 @@ } } .global, - .in-country, + .global-niche, + .offshore-niche, .offshore { display: flex; flex-direction: column; @@ -225,7 +226,12 @@ .global::before { background-color: #c99014; } - .in-country::before { + + .global-niche::before { + background-color: #0ab88a; + } + + .offshore-niche::before { background-color: #716d67; } .offshore::before { From 2090bc8dc2fa5d84c28e3f35f8a6e36f736842f9 Mon Sep 17 00:00:00 2001 From: yoution Date: Thu, 15 Jul 2021 11:24:50 +0800 Subject: [PATCH 05/22] fix: issue #356 --- src/constants/index.js | 1 + src/routes/CreateNewTeam/actions/index.js | 10 ++ .../components/EditRoleModal/index.jsx | 165 ++++++++++++++++++ .../EditRoleModal/styles.module.scss | 98 +++++++++++ .../EditRoleModal/utils/validator.js | 56 ++++++ .../components/SearchAndSubmit/index.jsx | 6 +- .../components/SearchContainer/index.jsx | 32 +++- .../components/TeamDetailsModal/index.jsx | 10 +- src/routes/CreateNewTeam/reducers/index.js | 16 ++ 9 files changed, 387 insertions(+), 7 deletions(-) create mode 100644 src/routes/CreateNewTeam/components/EditRoleModal/index.jsx create mode 100644 src/routes/CreateNewTeam/components/EditRoleModal/styles.module.scss create mode 100644 src/routes/CreateNewTeam/components/EditRoleModal/utils/validator.js diff --git a/src/constants/index.js b/src/constants/index.js index f1c5b67c..f36d2868 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -264,6 +264,7 @@ export const ACTION_TYPE = { */ ADD_MATCHING_ROLE: "ADD_MATCHING_ROLE", DELETE_MATCHING_ROLE: "DELETE_MATCHING_ROLE", + EDIT_MATCHING_ROLE: "EDIT_MATCHING_ROLE", }; /** diff --git a/src/routes/CreateNewTeam/actions/index.js b/src/routes/CreateNewTeam/actions/index.js index 0a224017..62d6e04a 100644 --- a/src/routes/CreateNewTeam/actions/index.js +++ b/src/routes/CreateNewTeam/actions/index.js @@ -36,6 +36,11 @@ const deleteMatchingRole = () => ({ type: ACTION_TYPE.DELETE_MATCHING_ROLE, }); +const editMatchingRole = (role) => ({ + type: ACTION_TYPE.EDIT_MATCHING_ROLE, + payload: role, +}); + export const clearSearchedRoles = () => (dispatch, getState) => { dispatch(clearRoles()); updateLocalStorage(getState().searchedRoles); @@ -51,6 +56,11 @@ export const addRoleSearchId = (id) => (dispatch, getState) => { updateLocalStorage(getState().searchedRoles); }; +export const editRoleAction = (role) => (dispatch, getState) => { + dispatch(editMatchingRole(role)); + updateLocalStorage(getState().searchedRoles); +}; + export const deleteSearchedRole = (id) => (dispatch, getState) => { dispatch(deleteRole(id)); updateLocalStorage(getState().searchedRoles); diff --git a/src/routes/CreateNewTeam/components/EditRoleModal/index.jsx b/src/routes/CreateNewTeam/components/EditRoleModal/index.jsx new file mode 100644 index 00000000..fa8bdedb --- /dev/null +++ b/src/routes/CreateNewTeam/components/EditRoleModal/index.jsx @@ -0,0 +1,165 @@ +/** + * Edit Role Modal + * Popup form to enter details about current role + */ +import React, { useEffect, useState } from "react"; +import PT from "prop-types"; +import { Form, Field, useField } from "react-final-form"; +import FormField from "components/FormField"; +import BaseCreateModal from "../BaseCreateModal"; +import Button from "components/Button"; +import MonthPicker from "components/MonthPicker"; +import InformationTooltip from "components/InformationTooltip"; +import IconCrossLight from "../../../../assets/images/icon-cross-light.svg"; +import "./styles.module.scss"; +import NumberInput from "components/NumberInput"; +import { validator, validateExists, validateMin, composeValidators } from "./utils/validator"; + +const Error = ({ name }) => { + const { + meta: { dirty, error }, + } = useField(name, { subscription: { dirty: true, error: true } }); + return dirty && error ? {error} : null; +}; + +function EditRoleModal({ open, onClose, submitForm, role }) { + const [startMonthVisible, setStartMonthVisible] = useState(false); + + return ( +
{ + changeValue(state, fieldName, () => undefined); + }, + }} + validate={validator} + > + {({ + handleSubmit, + hasValidationErrors, + form: { + mutators: { clearField }, + getState, + }, + }) => { + return ( + + Submit + + } + disableFocusTrap + > +
+ + + + + + + + + + + +
# of resourcesDuration (weeks)Start month
+ + {({ input, meta }) => ( + + )} + + + + + {({ input, meta }) => ( + + )} + + + + {startMonthVisible ? ( + <> + + {(props) => ( + + )} + + + + ) : ( +
+ + +
+ )} +
+
+
+ ); + }} +
+ ); +} + +EditRoleModal.propTypes = { + open: PT.bool, + onClose: PT.func, + submitForm: PT.func, + role: PT.object, +}; + +export default EditRoleModal; diff --git a/src/routes/CreateNewTeam/components/EditRoleModal/styles.module.scss b/src/routes/CreateNewTeam/components/EditRoleModal/styles.module.scss new file mode 100644 index 00000000..4d303148 --- /dev/null +++ b/src/routes/CreateNewTeam/components/EditRoleModal/styles.module.scss @@ -0,0 +1,98 @@ +@import "styles/include"; + +.toggle-button { + @include font-roboto; + outline: none; + border: none; + background: none; + font-size: 12px; + font-weight: 500; + color: #137D60; + padding: 1px 6px 0 6px; + + &.toggle-description { + margin-top: 12px; + > span { + font-size: 18px; + vertical-align: middle; + } + } +} + +.table { + margin-top: 40px; + width: 100%; + th { + @include font-roboto; + font-size: 12px; + color: #555; + padding-bottom: 7px; + border-bottom: 1px solid #d4d4d4; + + &.bold { + font-weight: 700; + } + } + + .role-row { + td { + padding: 18px 18px 18px 0; + vertical-align: top; + @include font-barlow; + font-weight: 600; + font-size: 16px; + color: #2a2a2a; + border-bottom: 1px solid #e9e9e9; + + &:last-child { + padding-right: 0; + } + + input { + @include font-roboto; + font-size: 14px; + line-height: normal; + height: 34px; + &[type="number"] { + width: 98px; + } + } + } + } +} + +.flex-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + width: 118px; + margin-top: 13px; +} + +.error { + font-size: 14px; + font-weight: 400; + color: red; + display: block; +} + +.delete-role { + border: none; + background: none; + + margin-top: 13px; + + &:hover { + g { + stroke: red; + } + } +} + +.modal-body { + overflow-x: auto; + textarea { + height: 95px; + } +} diff --git a/src/routes/CreateNewTeam/components/EditRoleModal/utils/validator.js b/src/routes/CreateNewTeam/components/EditRoleModal/utils/validator.js new file mode 100644 index 00000000..60cb16a8 --- /dev/null +++ b/src/routes/CreateNewTeam/components/EditRoleModal/utils/validator.js @@ -0,0 +1,56 @@ +const composeValidators = (...validators) => (value) => + validators.reduce((error, validator) => error || validator(value), undefined); + +const validateMin = (min) => (value) => + isNaN(value) || value >= min ? undefined : `Should be greater than ${min}`; + +const validateName = (name) => { + if (!name || name.trim().length === 0) { + return "Please enter a team name."; + } + return undefined; +}; + +const validateNumber = (number) => { + const converted = Number(number); + + if ( + Number.isNaN(converted) || + converted !== Math.floor(converted) || + converted < 1 + ) { + return "Please enter a positive integer"; + } + return undefined; +}; + +const validateMonth = (monthString) => { + const then = new Date(monthString); + const now = new Date(); + const thenYear = then.getFullYear(); + const nowYear = now.getFullYear(); + const thenMonth = then.getMonth(); + const nowMonth = now.getMonth(); + + if (thenYear < nowYear || (thenYear === nowYear && thenMonth < nowMonth)) { + return "Start month may not be before current month"; + } + return undefined; +}; + +const validator = (role) => { + const roleErrors = {}; + roleErrors.numberOfResources = validateNumber(role.numberOfResources); + roleErrors.durationWeeks = validateNumber(role.durationWeeks); + if (role.startMonth) { + roleErrors.startMonth = validateMonth(role.startMonth); + } + + return roleErrors; +}; + +const validateExists = (value) => { + return value === undefined ? "Please enter a positive integer" : undefined; +}; + +export { validator, validateExists, validateMin, composeValidators }; diff --git a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx index 5e3ae76e..e24ed940 100644 --- a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx +++ b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx @@ -20,6 +20,7 @@ function SearchAndSubmit(props) { const { stages, setStages, searchObject, onClick, page } = props; const [searchState, setSearchState] = useState(null); + const [isNewRole, setIsNewRole] = useState(false); const { matchingRole } = useSelector((state) => state.searchedRoles); @@ -56,7 +57,8 @@ function SearchAndSubmit(props) { const name = _.get(res, "data.name"); const searchId = _.get(res, "data.roleSearchRequestId"); if (name && !isCustomRole({ name })) { - dispatch(addSearchedRole({ searchId, name })); + dispatch(addSearchedRole({ searchId, name, numberOfResources: 1, durationWeeks: 4 })); + setIsNewRole(true) } else if (searchId) { dispatch(addRoleSearchId(searchId)); } @@ -88,9 +90,11 @@ function SearchAndSubmit(props) { /> { + return _.find(addedRoles, { searchId: previousSearchId }); + }, [addedRoles, previousSearchId]); + + useEffect(() => { + if (isNewRole) { + setShowEditModal(true) + } + }, [isNewRole]); + + const onSubmitEditRole = useCallback((role) => { + setShowEditModal(false) + dispatch(editRoleAction({...role, searchId: previousSearchId})) + }, [addedRoles, previousSearchId]); const onSubmit = useCallback(() => { setAddAnotherOpen(false); @@ -64,6 +86,12 @@ function SearchContainer({ percentage={getPercentage()} />
+ {showEditModal && setShowEditModal(false)} + submitForm={onSubmitEditRole} + />} setAddAnotherOpen(false)} @@ -76,8 +104,10 @@ function SearchContainer({ } SearchContainer.propTypes = { + isNewRole: PT.bool, stages: PT.array, completenessStyle: PT.string, + previousSearchId: PT.string, navigate: PT.func, addedRoles: PT.array, searchState: PT.string, diff --git a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx index 30d89b08..0f8743a1 100644 --- a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx +++ b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx @@ -133,14 +133,14 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) { Start month - {addedRoles.map(({ searchId: id, name }) => ( + {addedRoles.map(({ searchId: id, name, numberOfResources, durationWeeks, startMonth }) => ( {name} {({ input, meta }) => ( {({ input, meta }) => ( - {startMonthVisible[id] ? ( + {startMonth || startMonthVisible[id] ? ( <> {(props) => ( { ...state, matchingRole: action.payload, }; + case ACTION_TYPE.DELETE_MATCHING_ROLE: return { ...state, matchingRole: null, }; + + case ACTION_TYPE.EDIT_MATCHING_ROLE: + const index = _.findIndex(state.addedRoles, { + searchId: action.payload.searchId, + }); + state.addedRoles[index] = _.extend( + {}, + state.addedRoles[index], + _.omit(action.payload, "searchId") + ); + return { + ...state, + addedRoles: [...state.addedRoles], + }; + case ACTION_TYPE.ADD_SEARCHED_ROLE: return { ...state, From 2270993b045c8bd07659839d88bbdc0c59faa77b Mon Sep 17 00:00:00 2001 From: Michael Baghel <31278895+mbaghel@users.noreply.github.com> Date: Thu, 15 Jul 2021 13:44:12 +0400 Subject: [PATCH 06/22] fix: Increase job description length, Change minimum job duration - Validate job descriptions are no longer than 100,000 characters - Validate duration is 4 or greater in Team Details Modal - Change width of Customer scroll Resolves: #362, #374 --- .../components/TeamDetailsModal/index.jsx | 194 +++++++++--------- .../TeamDetailsModal/utils/validator.js | 11 +- .../pages/InputJobDescription/index.jsx | 6 +- src/routes/CreateNewTeam/styles.module.scss | 5 +- 4 files changed, 116 insertions(+), 100 deletions(-) diff --git a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx index 0f8743a1..7f717007 100644 --- a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx +++ b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx @@ -133,100 +133,108 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) { Start month - {addedRoles.map(({ searchId: id, name, numberOfResources, durationWeeks, startMonth }) => ( - - {name} - - - {({ input, meta }) => ( - + {addedRoles.map( + ({ + searchId: id, + name, + numberOfResources, + durationWeeks, + startMonth, + }) => ( + + {name} + + + {({ input, meta }) => ( + + )} + + + + + + {({ input, meta }) => ( + + )} + + + + + {startMonth || startMonthVisible[id] ? ( + <> + + {(props) => ( + + )} + + + + ) : ( +
+ + +
)} -
- - - - - {({ input, meta }) => ( - - )} - - - - - {startMonth || startMonthVisible[id] ? ( - <> - - {(props) => ( - - )} - - - - ) : ( -
- - -
- )} - - - - - - ))} + + + + + + ) + )}
diff --git a/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js b/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js index 9e1a9caf..e10139cb 100644 --- a/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js +++ b/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js @@ -18,6 +18,15 @@ const validateNumber = (number) => { return undefined; }; +const validateGreaterThan = (number, min) => { + const isInvalidNum = validateNumber(number); + if (isInvalidNum) return isInvalidNum; + + return number < min + ? "Talent as a Service engagements have a 4 week minimum commitment." + : undefined; +}; + const validateMonth = (monthString) => { const then = new Date(monthString); const now = new Date(); @@ -35,7 +44,7 @@ const validateMonth = (monthString) => { const validateRole = (role) => { const roleErrors = {}; roleErrors.numberOfResources = validateNumber(role.numberOfResources); - roleErrors.durationWeeks = validateNumber(role.durationWeeks); + roleErrors.durationWeeks = validateGreaterThan(role.durationWeeks, 4); if (role.startMonth) { roleErrors.startMonth = validateMonth(role.startMonth); } diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx b/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx index 1216aaa1..c212a93f 100644 --- a/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx @@ -56,7 +56,7 @@ function InputJobDescription() { 2000} + isCompletenessDisabled={jdString.length < 10 || jdString.length > 100000} completenessStyle="input-job-description" searchObject={searchObject} page="jd" @@ -88,8 +88,8 @@ function InputJobDescription() { placeholder="input job description" onChange={onEditChange} errorMessage={ - jdString.length > 2000 - ? "Maximum of 2000 characters. Please reduce job description length." + jdString.length > 100000 + ? "Maximum of 100,000 characters. Please reduce job description length." : "" } /> diff --git a/src/routes/CreateNewTeam/styles.module.scss b/src/routes/CreateNewTeam/styles.module.scss index c24b9708..045668bd 100644 --- a/src/routes/CreateNewTeam/styles.module.scss +++ b/src/routes/CreateNewTeam/styles.module.scss @@ -1,14 +1,13 @@ .logos { position: fixed; bottom: 15px; - width: calc(100vw - 340px); - margin-left: 35px; + width: calc(100vw - 270px); z-index: -1; } @media only screen and (max-width: 1023px) { .logos { - width: calc(100vw - 70px) + width: 100vw; } } From 0cbe16ed68a298637021f89de6ac97b6c711f0e4 Mon Sep 17 00:00:00 2001 From: Michael Baghel <31278895+mbaghel@users.noreply.github.com> Date: Thu, 15 Jul 2021 15:10:46 +0400 Subject: [PATCH 07/22] feat: Update Team Details Labels & add additional options under "Advanced" Resolves: #365 --- package-lock.json | 29 ++++++++-- package.json | 3 +- .../components/SubmitContainer/index.jsx | 10 +++- .../components/TeamDetailsModal/index.jsx | 55 ++++++++++++------- .../TeamDetailsModal/styles.module.scss | 2 +- .../TeamDetailsModal/utils/validator.js | 4 +- src/utils/helpers.js | 9 +++ 7 files changed, 81 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index da18caae..b524184a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14995,6 +14995,12 @@ "psl": "^1.1.28", "punycode": "^2.1.1" } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, @@ -15873,6 +15879,14 @@ "faye-websocket": "^0.10.0", "uuid": "^3.4.0", "websocket-driver": "0.6.5" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "sockjs-client": { @@ -17104,10 +17118,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.1.1", @@ -17755,6 +17768,14 @@ "requires": { "ansi-colors": "^3.0.0", "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "webpack-merge": { diff --git a/package.json b/package.json index 61cd6967..a95fce45 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,8 @@ "redux-logger": "^3.0.6", "redux-promise-middleware": "^6.1.2", "redux-thunk": "^2.3.0", - "tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.4" + "tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.4", + "uuid": "^8.3.2" }, "browserslist": [ "last 1 version", diff --git a/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx b/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx index 96e52738..56d1f1b1 100644 --- a/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx +++ b/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx @@ -23,7 +23,7 @@ import TeamDetailsModal from "../TeamDetailsModal"; import ConfirmationModal from "../ConfirmationModal"; import withAuthentication from "../../../../hoc/withAuthentication"; import "./styles.module.scss"; -import { isCustomRole, setCurrentStage } from "utils/helpers"; +import { isCustomRole, isUuid, setCurrentStage } from "utils/helpers"; import { clearSearchedRoles } from "../../actions"; import { postTeamRequest } from "services/teams"; import NoMatchingProfilesResultCard from "../NoMatchingProfilesResultCard"; @@ -65,11 +65,15 @@ function SubmitContainer({ }; const assembleTeam = (formData) => { - const teamObject = _.pick(formData, ["teamName", "teamDescription"]); + const teamObject = _.pick(formData, [ + "teamName", + "teamDescription", + "refCode", + ]); const positions = []; for (let key of Object.keys(formData)) { - if (key === "teamName" || key === "teamDescription") { + if (!isUuid(key)) { continue; } const position = _.mapValues(formData[key], (val, key) => diff --git a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx index 7f717007..fde71b11 100644 --- a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx +++ b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx @@ -11,6 +11,7 @@ import FormField from "components/FormField"; import BaseCreateModal from "../BaseCreateModal"; import { FORM_FIELD_TYPE } from "constants/"; import { formatPlural } from "utils/format"; +import { isUuid } from "utils/helpers"; import Button from "components/Button"; import MonthPicker from "components/MonthPicker"; import InformationTooltip from "components/InformationTooltip"; @@ -28,7 +29,7 @@ const Error = ({ name }) => { }; function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) { - const [showDescription, setShowDescription] = useState(false); + const [showAdvanced, setShowAdvanced] = useState(false); const [startMonthVisible, setStartMonthVisible] = useState({}); // Ensure role is removed from form state when it is removed from redux store @@ -37,7 +38,7 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) { useEffect(() => { const values = getFormState().values; for (let fieldName of Object.keys(values)) { - if (fieldName === "teamName" || fieldName === "teamDescription") { + if (!isUuid(fieldName)) { continue; } if (addedRoles.findIndex((role) => role.searchId === fieldName) === -1) { @@ -49,8 +50,8 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) { const dispatch = useDispatch(); - const toggleDescription = () => { - setShowDescription((prevState) => !prevState); + const toggleAdvanced = () => { + setShowAdvanced((prevState) => !prevState); }; return ( @@ -78,7 +79,7 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) { open={open} onClose={onClose} title="Team Details" - subtitle="Please provide your team details before submitting a request." + subtitle="Please provide a name for your Team. This could be the name of the project they will work on, the name of the team they are joining, or whatever else will make this talent request meaningful for you." buttons={ @@ -147,7 +160,7 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) { {({ input, meta }) => ( {({ input, meta }) => ( span { font-size: 18px; diff --git a/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js b/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js index e10139cb..d5290486 100644 --- a/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js +++ b/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js @@ -1,3 +1,5 @@ +import { isUuid } from "utils/helpers"; + const validateName = (name) => { if (!name || name.trim().length === 0) { return "Please enter a team name."; @@ -58,7 +60,7 @@ const validator = (values) => { errors.teamName = validateName(values.teamName); for (const key of Object.keys(values)) { - if (key === "teamDescription" || key === "teamName") continue; + if (!isUuid(key)) continue; errors[key] = validateRole(values[key]); } diff --git a/src/utils/helpers.js b/src/utils/helpers.js index c54cbe5f..60eea1d5 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -5,8 +5,17 @@ * If there are multiple methods which could be grouped into a separate file by their meaning they should be extracted from here to not make this file too big. */ import _ from "lodash"; +import { validate } from "uuid"; + import { CUSTOM_ROLE_NAMES } from "constants/"; +/** + * @param {String} string a possible uuid + * + * @returns {Boolean} true if uuid, false if not + */ +export const isUuid = validate; + /** * Delay code for some milliseconds using promise. * From a06e0650d95344b87114d354b1c0835138ed45cf Mon Sep 17 00:00:00 2001 From: yoution Date: Fri, 16 Jul 2021 10:25:22 +0800 Subject: [PATCH 08/22] fix: issue #356 --- .../components/EditRoleForm/index.jsx | 153 ++++++++++++++++ .../styles.module.scss | 3 + .../utils/validator.js | 0 .../components/EditRoleModal/index.jsx | 165 ------------------ .../components/ResultCard/index.jsx | 22 +-- .../components/SearchContainer/index.jsx | 12 +- 6 files changed, 167 insertions(+), 188 deletions(-) create mode 100644 src/routes/CreateNewTeam/components/EditRoleForm/index.jsx rename src/routes/CreateNewTeam/components/{EditRoleModal => EditRoleForm}/styles.module.scss (97%) rename src/routes/CreateNewTeam/components/{EditRoleModal => EditRoleForm}/utils/validator.js (100%) delete mode 100644 src/routes/CreateNewTeam/components/EditRoleModal/index.jsx diff --git a/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx b/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx new file mode 100644 index 00000000..64d9a3de --- /dev/null +++ b/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx @@ -0,0 +1,153 @@ +/** + * Edit Role Form + * form for eidting details about current role + */ +import React, { useEffect, useState } from "react"; +import PT from "prop-types"; +import { Form, Field, useField } from "react-final-form"; +import FormField from "components/FormField"; +import BaseCreateModal from "../BaseCreateModal"; +import Button from "components/Button"; +import MonthPicker from "components/MonthPicker"; +import InformationTooltip from "components/InformationTooltip"; +import IconCrossLight from "../../../../assets/images/icon-cross-light.svg"; +import "./styles.module.scss"; +import NumberInput from "components/NumberInput"; +import { + validator, + validateExists, + validateMin, + composeValidators, +} from "./utils/validator"; + +const Error = ({ name }) => { + const { + meta: { dirty, error }, + } = useField(name, { subscription: { dirty: true, error: true } }); + return dirty && error ? {error} : null; +}; + +function EditRoleForm({ submitForm, role }) { + const [startMonthVisible, setStartMonthVisible] = useState(false); + + return ( +
{ + changeValue(state, fieldName, () => undefined); + }, + }} + validate={validator} + > + {({ + handleSubmit, + hasValidationErrors, + form: { + mutators: { clearField }, + getState, + }, + }) => { + return ( +
+
+ + + + + + + + + + +
# of resourcesDuration (weeks)Start month
+ + {({ input, meta }) => ( + + )} + + + + + {({ input, meta }) => ( + + )} + + + + {startMonthVisible ? ( + <> + + {(props) => ( + + )} + + + + ) : ( +
+ + +
+ )} +
+ +
+ ); + }} + + ); +} + +EditRoleForm.propTypes = { + submitForm: PT.func, + role: PT.object, +}; + +export default EditRoleForm; diff --git a/src/routes/CreateNewTeam/components/EditRoleModal/styles.module.scss b/src/routes/CreateNewTeam/components/EditRoleForm/styles.module.scss similarity index 97% rename from src/routes/CreateNewTeam/components/EditRoleModal/styles.module.scss rename to src/routes/CreateNewTeam/components/EditRoleForm/styles.module.scss index 4d303148..1c47d84f 100644 --- a/src/routes/CreateNewTeam/components/EditRoleModal/styles.module.scss +++ b/src/routes/CreateNewTeam/components/EditRoleForm/styles.module.scss @@ -91,6 +91,9 @@ } .modal-body { + > button { + margin: 0 auto; + } overflow-x: auto; textarea { height: 95px; diff --git a/src/routes/CreateNewTeam/components/EditRoleModal/utils/validator.js b/src/routes/CreateNewTeam/components/EditRoleForm/utils/validator.js similarity index 100% rename from src/routes/CreateNewTeam/components/EditRoleModal/utils/validator.js rename to src/routes/CreateNewTeam/components/EditRoleForm/utils/validator.js diff --git a/src/routes/CreateNewTeam/components/EditRoleModal/index.jsx b/src/routes/CreateNewTeam/components/EditRoleModal/index.jsx deleted file mode 100644 index fa8bdedb..00000000 --- a/src/routes/CreateNewTeam/components/EditRoleModal/index.jsx +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Edit Role Modal - * Popup form to enter details about current role - */ -import React, { useEffect, useState } from "react"; -import PT from "prop-types"; -import { Form, Field, useField } from "react-final-form"; -import FormField from "components/FormField"; -import BaseCreateModal from "../BaseCreateModal"; -import Button from "components/Button"; -import MonthPicker from "components/MonthPicker"; -import InformationTooltip from "components/InformationTooltip"; -import IconCrossLight from "../../../../assets/images/icon-cross-light.svg"; -import "./styles.module.scss"; -import NumberInput from "components/NumberInput"; -import { validator, validateExists, validateMin, composeValidators } from "./utils/validator"; - -const Error = ({ name }) => { - const { - meta: { dirty, error }, - } = useField(name, { subscription: { dirty: true, error: true } }); - return dirty && error ? {error} : null; -}; - -function EditRoleModal({ open, onClose, submitForm, role }) { - const [startMonthVisible, setStartMonthVisible] = useState(false); - - return ( -
{ - changeValue(state, fieldName, () => undefined); - }, - }} - validate={validator} - > - {({ - handleSubmit, - hasValidationErrors, - form: { - mutators: { clearField }, - getState, - }, - }) => { - return ( - - Submit - - } - disableFocusTrap - > -
- - - - - - - - - - - -
# of resourcesDuration (weeks)Start month
- - {({ input, meta }) => ( - - )} - - - - - {({ input, meta }) => ( - - )} - - - - {startMonthVisible ? ( - <> - - {(props) => ( - - )} - - - - ) : ( -
- - -
- )} -
-
-
- ); - }} -
- ); -} - -EditRoleModal.propTypes = { - open: PT.bool, - onClose: PT.func, - submitForm: PT.func, - role: PT.object, -}; - -export default EditRoleModal; diff --git a/src/routes/CreateNewTeam/components/ResultCard/index.jsx b/src/routes/CreateNewTeam/components/ResultCard/index.jsx index cb56b8ee..38bd0690 100644 --- a/src/routes/CreateNewTeam/components/ResultCard/index.jsx +++ b/src/routes/CreateNewTeam/components/ResultCard/index.jsx @@ -12,6 +12,7 @@ import IconEarthCheck from "../../../../assets/images/icon-earth-check.svg"; import IconMultipleUsers from "../../../../assets/images/icon-multiple-users.svg"; import IconMultipleActionsCheck from "../../../../assets/images/icon-multiple-actions-check-2.svg"; import IconTeamMeetingChat from "../../../../assets/images/icon-team-meeting-chat.svg"; +import EditRoleForm from "../EditRoleForm"; import Curve from "../../../../assets/images/curve.svg"; import CircularProgressBar from "../CircularProgressBar"; import Button from "components/Button"; @@ -26,7 +27,7 @@ function formatPercent(value) { return `${Math.round(value * 100)}%`; } -function ResultCard({ role }) { +function ResultCard({ role, currentRole, onSubmitEditRole }) { const { numberOfMembersAvailable, isExternalMember, @@ -267,20 +268,9 @@ function ResultCard({ role }) {

Members matched

-
-

- 60% of members are available 20 hours / week (part - time) -

-

- 20% of members are available 30 hours / week (part - time) -

-

- 10% of members are available 40 hours / week (full - time) -

-
+ {currentRole && ( + + )}
)} @@ -289,6 +279,8 @@ function ResultCard({ role }) { ResultCard.propTypes = { role: PT.object, + currentRole: PT.object, + onSubmitEditRole: PT.func, }; export default ResultCard; diff --git a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx index 7b94720c..b9ee59da 100644 --- a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx +++ b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx @@ -13,7 +13,6 @@ import AddedRolesAccordion from "../AddedRolesAccordion"; import Completeness from "../Completeness"; import SearchCard from "../SearchCard"; import ResultCard from "../ResultCard"; -import EditRoleModal from '../EditRoleModal' import NoMatchingProfilesResultCard from "../NoMatchingProfilesResultCard"; import { isCustomRole } from "utils/helpers"; import AddAnotherModal from "../AddAnotherModal"; @@ -59,7 +58,10 @@ function SearchContainer({ const renderLeftSide = () => { if (searchState === "searching") return ; - if (!isCustomRole(matchingRole)) return ; + if (!isCustomRole(matchingRole)) return ; return ; }; @@ -86,12 +88,6 @@ function SearchContainer({ percentage={getPercentage()} /> - {showEditModal && setShowEditModal(false)} - submitForm={onSubmitEditRole} - />} setAddAnotherOpen(false)} From 628f5774e9dbb05ffbe509fd79f88f943e82ab04 Mon Sep 17 00:00:00 2001 From: yoution Date: Fri, 16 Jul 2021 18:10:04 +0800 Subject: [PATCH 09/22] fix: issue #356 --- .../components/EditRoleForm/index.jsx | 42 +++++++++++-------- .../EditRoleForm/styles.module.scss | 11 ++++- .../EditRoleForm/utils/validator.js | 4 +- .../components/ResultCard/index.jsx | 6 +-- .../components/SearchAndSubmit/index.jsx | 11 ++++- .../components/SearchContainer/index.jsx | 30 +++++++++---- 6 files changed, 68 insertions(+), 36 deletions(-) diff --git a/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx b/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx index 64d9a3de..c2c651dd 100644 --- a/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx +++ b/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx @@ -27,12 +27,19 @@ const Error = ({ name }) => { return dirty && error ? {error} : null; }; -function EditRoleForm({ submitForm, role }) { +function EditRoleForm({ onChange, role }) { const [startMonthVisible, setStartMonthVisible] = useState(false); + const onRoleChange = (state) => { + if (state.hasValidationErrors) { + onChange(false); + }else { + onChange(true, state.values); + } + }; return (
{}} mutators={{ clearField: ([fieldName], state, { changeValue }) => { changeValue(state, fieldName, () => undefined); @@ -41,15 +48,13 @@ function EditRoleForm({ submitForm, role }) { validate={validator} > {({ - handleSubmit, - hasValidationErrors, form: { mutators: { clearField }, getState, }, }) => { return ( -
+
@@ -59,7 +64,7 @@ function EditRoleForm({ submitForm, role }) {
# of resources
@@ -67,7 +72,10 @@ function EditRoleForm({ submitForm, role }) { { + input.onChange(v); + onRoleChange(getState()); + }} onBlur={input.onBlur} onFocus={input.onFocus} min="1" @@ -79,7 +87,7 @@ function EditRoleForm({ submitForm, role }) { @@ -87,7 +95,10 @@ function EditRoleForm({ submitForm, role }) { { + input.onChange(v); + onRoleChange(getState()); + }} onBlur={input.onBlur} onFocus={input.onFocus} min="4" @@ -105,7 +116,10 @@ function EditRoleForm({ submitForm, role }) { { + props.input.onChange(v); + onRoleChange(getState()); + }} onBlur={props.input.onBlur} onFocus={props.input.onFocus} /> @@ -130,14 +144,6 @@ function EditRoleForm({ submitForm, role }) {
-
); }} diff --git a/src/routes/CreateNewTeam/components/EditRoleForm/styles.module.scss b/src/routes/CreateNewTeam/components/EditRoleForm/styles.module.scss index 1c47d84f..d9f635f9 100644 --- a/src/routes/CreateNewTeam/components/EditRoleForm/styles.module.scss +++ b/src/routes/CreateNewTeam/components/EditRoleForm/styles.module.scss @@ -36,7 +36,13 @@ .role-row { td { + > div { + margin-left: auto; + margin-right: auto; + } + padding: 18px 18px 18px 0; + width: 30%; vertical-align: top; @include font-barlow; font-weight: 600; @@ -90,11 +96,12 @@ } } -.modal-body { +.table-container { > button { margin: 0 auto; } - overflow-x: auto; + padding: 0 30px; + width: 80%; textarea { height: 95px; } diff --git a/src/routes/CreateNewTeam/components/EditRoleForm/utils/validator.js b/src/routes/CreateNewTeam/components/EditRoleForm/utils/validator.js index 60cb16a8..704f1099 100644 --- a/src/routes/CreateNewTeam/components/EditRoleForm/utils/validator.js +++ b/src/routes/CreateNewTeam/components/EditRoleForm/utils/validator.js @@ -1,8 +1,8 @@ const composeValidators = (...validators) => (value) => validators.reduce((error, validator) => error || validator(value), undefined); -const validateMin = (min) => (value) => - isNaN(value) || value >= min ? undefined : `Should be greater than ${min}`; +const validateMin = (min, message) => (value) => + isNaN(value) || value >= min ? undefined : message; const validateName = (name) => { if (!name || name.trim().length === 0) { diff --git a/src/routes/CreateNewTeam/components/ResultCard/index.jsx b/src/routes/CreateNewTeam/components/ResultCard/index.jsx index 38bd0690..8aa4fa42 100644 --- a/src/routes/CreateNewTeam/components/ResultCard/index.jsx +++ b/src/routes/CreateNewTeam/components/ResultCard/index.jsx @@ -27,7 +27,7 @@ function formatPercent(value) { return `${Math.round(value * 100)}%`; } -function ResultCard({ role, currentRole, onSubmitEditRole }) { +function ResultCard({ role, currentRole, onSaveEditRole }) { const { numberOfMembersAvailable, isExternalMember, @@ -269,7 +269,7 @@ function ResultCard({ role, currentRole, onSubmitEditRole }) {
{currentRole && ( - + )} )} @@ -280,7 +280,7 @@ function ResultCard({ role, currentRole, onSubmitEditRole }) { ResultCard.propTypes = { role: PT.object, currentRole: PT.object, - onSubmitEditRole: PT.func, + onSaveEditRole: PT.func, }; export default ResultCard; diff --git a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx index e24ed940..cd55604d 100644 --- a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx +++ b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx @@ -57,8 +57,15 @@ function SearchAndSubmit(props) { const name = _.get(res, "data.name"); const searchId = _.get(res, "data.roleSearchRequestId"); if (name && !isCustomRole({ name })) { - dispatch(addSearchedRole({ searchId, name, numberOfResources: 1, durationWeeks: 4 })); - setIsNewRole(true) + dispatch( + addSearchedRole({ + searchId, + name, + numberOfResources: 1, + durationWeeks: 4, + }) + ); + setIsNewRole(true); } else if (searchId) { dispatch(addRoleSearchId(searchId)); } diff --git a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx index 4529b9c2..2c35d8eb 100644 --- a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx +++ b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx @@ -7,6 +7,7 @@ */ import React, { useCallback, useState, useMemo, useEffect } from "react"; import PT from "prop-types"; +import _ from "lodash"; import { useDispatch } from "react-redux"; import { editRoleAction } from "../../actions"; import AddedRolesAccordion from "../AddedRolesAccordion"; @@ -30,6 +31,7 @@ function SearchContainer({ }) { const [addAnotherOpen, setAddAnotherOpen] = useState(false); const [showEditModal, setShowEditModal] = useState(false); + const [buttonClickable, setButtonClickable] = useState(true); const dispatch = useDispatch(); const currentRole = useMemo(() => { @@ -38,14 +40,19 @@ function SearchContainer({ useEffect(() => { if (isNewRole) { - setShowEditModal(true) + setShowEditModal(true); } }, [isNewRole]); - const onSubmitEditRole = useCallback((role) => { - setShowEditModal(false) - dispatch(editRoleAction({...role, searchId: previousSearchId})) - }, [addedRoles, previousSearchId]); + const onSaveEditRole = useCallback( + (isValid, role) => { + setButtonClickable(isValid) + if (isValid) { + dispatch(editRoleAction({ ...role, searchId: previousSearchId })); + } + }, + [addedRoles, previousSearchId] + ); const onSubmit = useCallback(() => { setAddAnotherOpen(false); @@ -58,10 +65,14 @@ function SearchContainer({ const renderLeftSide = () => { if (searchState === "searching") return ; - if (!isCustomRole(matchingRole)) return ; + if (!isCustomRole(matchingRole)) + return ( + + ); return ; }; @@ -78,6 +89,7 @@ function SearchContainer({ Date: Sun, 18 Jul 2021 20:04:45 +0800 Subject: [PATCH 10/22] fix: issue #375 --- .../components/InputContainer/index.jsx | 16 ++++++++-------- .../index.jsx | 12 ++++++------ .../styles.module.scss | 0 .../components/SearchContainer/index.jsx | 10 +++++----- .../components/SubmitContainer/index.jsx | 10 +++++----- .../{Completeness => progress}/index.jsx | 18 +++++++++--------- .../styles.module.scss | 2 +- .../pages/InputJobDescription/index.jsx | 4 ++-- .../CreateNewTeam/pages/InputSkills/index.jsx | 4 ++-- .../CreateNewTeam/pages/SelectRole/index.jsx | 4 ++-- 10 files changed, 40 insertions(+), 40 deletions(-) rename src/routes/CreateNewTeam/components/{CompleteProgress => ProgressBar}/index.jsx (73%) rename src/routes/CreateNewTeam/components/{CompleteProgress => ProgressBar}/styles.module.scss (100%) rename src/routes/CreateNewTeam/components/{Completeness => progress}/index.jsx (82%) rename src/routes/CreateNewTeam/components/{Completeness => progress}/styles.module.scss (98%) diff --git a/src/routes/CreateNewTeam/components/InputContainer/index.jsx b/src/routes/CreateNewTeam/components/InputContainer/index.jsx index cebc1551..f87a671c 100644 --- a/src/routes/CreateNewTeam/components/InputContainer/index.jsx +++ b/src/routes/CreateNewTeam/components/InputContainer/index.jsx @@ -8,16 +8,16 @@ import React from "react"; import PT from "prop-types"; import AddedRolesAccordion from "../AddedRolesAccordion"; -import Completeness from "../Completeness"; +import Progress from "../Progress"; import "./styles.module.scss"; function InputContainer({ stages, - isCompletenessDisabled, + isProgressDisabled, toRender, search, onClick, - completenessStyle, + progressStyle, addedRoles, }) { return ( @@ -25,10 +25,10 @@ function InputContainer({ {toRender(search)}
- 100) { @@ -17,7 +17,7 @@ function CompleteProgress({ percentDone }) { return (
-

Completeness

+

Progress

{percentDone}%
@@ -30,8 +30,8 @@ function CompleteProgress({ percentDone }) { ); } -CompleteProgress.propTypes = { +ProgressBar.propTypes = { percentDone: PT.number, }; -export default CompleteProgress; +export default ProgressBar; diff --git a/src/routes/CreateNewTeam/components/CompleteProgress/styles.module.scss b/src/routes/CreateNewTeam/components/ProgressBar/styles.module.scss similarity index 100% rename from src/routes/CreateNewTeam/components/CompleteProgress/styles.module.scss rename to src/routes/CreateNewTeam/components/ProgressBar/styles.module.scss diff --git a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx index 2c35d8eb..9b177e0b 100644 --- a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx +++ b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx @@ -11,7 +11,7 @@ import _ from "lodash"; import { useDispatch } from "react-redux"; import { editRoleAction } from "../../actions"; import AddedRolesAccordion from "../AddedRolesAccordion"; -import Completeness from "../Completeness"; +import Progress from "../Progress"; import SearchCard from "../SearchCard"; import ResultCard from "../ResultCard"; import NoMatchingProfilesResultCard from "../NoMatchingProfilesResultCard"; @@ -22,7 +22,7 @@ import "./styles.module.scss"; function SearchContainer({ isNewRole, stages, - completenessStyle, + progressStyle, navigate, addedRoles, searchState, @@ -87,14 +87,14 @@ function SearchContainer({ {renderLeftSide()}
- setAddAnotherOpen(true)} - extraStyleName={completenessStyle} + extraStyleName={progressStyle} buttonLabel="Continue" stages={stages} percentage={getPercentage()} @@ -114,7 +114,7 @@ function SearchContainer({ SearchContainer.propTypes = { isNewRole: PT.bool, stages: PT.array, - completenessStyle: PT.string, + progressStyle: PT.string, previousSearchId: PT.string, navigate: PT.func, addedRoles: PT.array, diff --git a/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx b/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx index 56d1f1b1..5a523cfd 100644 --- a/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx +++ b/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx @@ -17,7 +17,7 @@ import { toastr } from "react-redux-toastr"; import { navigate } from "@reach/router"; import ResultCard from "../ResultCard"; import AddedRolesAccordion from "../AddedRolesAccordion"; -import Completeness from "../Completeness"; +import Progress from "../Progress"; import AddAnotherModal from "../AddAnotherModal"; import TeamDetailsModal from "../TeamDetailsModal"; import ConfirmationModal from "../ConfirmationModal"; @@ -31,7 +31,7 @@ import NoMatchingProfilesResultCard from "../NoMatchingProfilesResultCard"; function SubmitContainer({ stages, setStages, - completenessStyle, + progressStyle, matchingRole, addedRoles, }) { @@ -120,9 +120,9 @@ function SubmitContainer({ )}
- setAddAnotherOpen(true)} - extraStyleName={completenessStyle} + extraStyleName={progressStyle} buttonLabel="Continue" stages={stages} percentage="98" @@ -154,7 +154,7 @@ function SubmitContainer({ SubmitContainer.propTypes = { stages: PT.array, setStages: PT.func, - completenessStyle: PT.string, + progressStyle: PT.string, addedRoles: PT.array, matchingRole: PT.object, }; diff --git a/src/routes/CreateNewTeam/components/Completeness/index.jsx b/src/routes/CreateNewTeam/components/progress/index.jsx similarity index 82% rename from src/routes/CreateNewTeam/components/Completeness/index.jsx rename to src/routes/CreateNewTeam/components/progress/index.jsx index df29ba1d..06c1cebe 100644 --- a/src/routes/CreateNewTeam/components/Completeness/index.jsx +++ b/src/routes/CreateNewTeam/components/progress/index.jsx @@ -1,6 +1,6 @@ /** - * Completeness Sidebar - * Shows level of completeness through skill + * Progress Sidebar + * Shows level of progress through skill * input process and contains a button for * searching for users or submitting the job. */ @@ -8,13 +8,13 @@ import Button from "components/Button"; import React from "react"; import cn from "classnames"; import PT from "prop-types"; -import CompleteProgress from "../CompleteProgress"; +import ProgressBar from "../ProgressBar"; import "./styles.module.scss"; import IconMultipleActionsCheck from "../../../../assets/images/icon-multiple-actions-check-2.svg"; import IconListQuill from "../../../../assets/images/icon-list-quill.svg"; import IconOfficeFileText from "../../../../assets/images/icon-office-file-text.svg"; -function Completeness({ +function Progress({ extraStyleName, isDisabled, onClick, @@ -23,7 +23,7 @@ function Completeness({ percentage, }) { - let backgroundIcon + let backgroundIcon if (extraStyleName === "input-skills") { backgroundIcon= } else if (extraStyleName === "input-job-description") { @@ -33,8 +33,8 @@ function Completeness({ } return ( -
- +
+
    {stages.map((stage) => (
  • 100000} - completenessStyle="input-job-description" + isProgressDisabled={jdString.length < 10 || jdString.length > 100000} + progressStyle="input-job-description" searchObject={searchObject} page="jd" onClick={onClick} diff --git a/src/routes/CreateNewTeam/pages/InputSkills/index.jsx b/src/routes/CreateNewTeam/pages/InputSkills/index.jsx index 5e520cb2..52a2b6cf 100644 --- a/src/routes/CreateNewTeam/pages/InputSkills/index.jsx +++ b/src/routes/CreateNewTeam/pages/InputSkills/index.jsx @@ -48,10 +48,10 @@ function InputSkills() { ( ( <> Date: Sun, 18 Jul 2021 20:19:15 +0800 Subject: [PATCH 11/22] fix: issue #376 --- .../pages/InputSkills/components/SkillsList/index.jsx | 6 ++++++ .../InputSkills/components/SkillsList/styles.module.scss | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 src/routes/CreateNewTeam/pages/InputSkills/components/SkillsList/styles.module.scss diff --git a/src/routes/CreateNewTeam/pages/InputSkills/components/SkillsList/index.jsx b/src/routes/CreateNewTeam/pages/InputSkills/components/SkillsList/index.jsx index effefd34..dbccd509 100644 --- a/src/routes/CreateNewTeam/pages/InputSkills/components/SkillsList/index.jsx +++ b/src/routes/CreateNewTeam/pages/InputSkills/components/SkillsList/index.jsx @@ -8,6 +8,7 @@ import React, { useCallback, useState } from "react"; import PT from "prop-types"; import SkillItem from "../SkillItem"; import ItemList from "../../../../components/ItemList"; +import "./styles.module.scss"; import { formatPlural } from "utils/format"; function SkillsList({ skills, selectedSkills, toggleSkill }) { @@ -38,6 +39,11 @@ function SkillsList({ skills, selectedSkills, toggleSkill }) { : null } > +

    + Please select one or more essential skills you require your talent to + have. Topcoder will match to profiles which contain most or all of these + skills. +

    {filteredSkills.map(({ id, name }) => ( Date: Mon, 19 Jul 2021 16:14:35 +0800 Subject: [PATCH 12/22] fix: issue #375 deploy error --- .../components/Progress/index.jsx | 72 +++++++++++++++++ .../components/Progress/styles.module.scss | 81 +++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/routes/CreateNewTeam/components/Progress/index.jsx create mode 100644 src/routes/CreateNewTeam/components/Progress/styles.module.scss diff --git a/src/routes/CreateNewTeam/components/Progress/index.jsx b/src/routes/CreateNewTeam/components/Progress/index.jsx new file mode 100644 index 00000000..06c1cebe --- /dev/null +++ b/src/routes/CreateNewTeam/components/Progress/index.jsx @@ -0,0 +1,72 @@ +/** + * Progress Sidebar + * Shows level of progress through skill + * input process and contains a button for + * searching for users or submitting the job. + */ +import Button from "components/Button"; +import React from "react"; +import cn from "classnames"; +import PT from "prop-types"; +import ProgressBar from "../ProgressBar"; +import "./styles.module.scss"; +import IconMultipleActionsCheck from "../../../../assets/images/icon-multiple-actions-check-2.svg"; +import IconListQuill from "../../../../assets/images/icon-list-quill.svg"; +import IconOfficeFileText from "../../../../assets/images/icon-office-file-text.svg"; + +function Progress({ + extraStyleName, + isDisabled, + onClick, + buttonLabel, + stages, + percentage, +}) { + + let backgroundIcon + if (extraStyleName === "input-skills") { + backgroundIcon= + } else if (extraStyleName === "input-job-description") { + backgroundIcon= + } else { + backgroundIcon= + } + + return ( +
    + +
      + {stages.map((stage) => ( +
    • + {stage.name} +
    • + ))} +
    + + {backgroundIcon} +
    + ); +} + +Progress.propTypes = { + extraStyleName: PT.string, + isDisabled: PT.bool, + onClick: PT.func, + buttonLabel: PT.string, + currentStageIdx: PT.number, + stages: PT.arrayOf(PT.string), +}; + +export default Progress; diff --git a/src/routes/CreateNewTeam/components/Progress/styles.module.scss b/src/routes/CreateNewTeam/components/Progress/styles.module.scss new file mode 100644 index 00000000..529e15e6 --- /dev/null +++ b/src/routes/CreateNewTeam/components/Progress/styles.module.scss @@ -0,0 +1,81 @@ +@import "styles/include"; + +.progress { + @include rounded-card; + overflow: hidden; + padding: 12px; + position: relative; + width: 250px; + color: #fff; + button { + border: none; + } +} + +.input-job-description { + background-image: linear-gradient(135deg, #2984BD 0%, #0AB88A 100%); +} + +.input-skills { + background-image: linear-gradient(221.5deg, #646CD0 0%, #9d41c9 100%); +} + +.role-selection { + background-image: linear-gradient(45deg, #8B41B0 0%, #EF476F 100%); +} + +.list { + margin-bottom: 55px; + & + button[disabled] { + background-color: #E9E9E9; + color: #FFF; + opacity: 1; + filter: none; + } +} + +.list-item { + margin-bottom: 14px; + font-size: 14px; + line-height: 16px; + font-weight: 400; + + &:before { + content: ""; + color: #fff; + border: 1px solid #ffffff; + border-radius: 100%; + width: 16px; + height: 16px; + margin-right: 10px; + display: block; + float: left; + } + + &.active { + font-weight: 500; + &:before { + background-color: #fff; + } + } + + &.done { + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + &:before { + content: "✓"; + font-size: 9px; + line-height: 14px; + padding-left: 2px; + } + } +} + +.transparent-icon { + position: absolute; + right: -50px; + top: 85px; + opacity: 10%; + width: 144px; + height: 144px; +} From 012c0ecb760c32c00423e61ef1c61547afecd313 Mon Sep 17 00:00:00 2001 From: Michael Baghel <31278895+mbaghel@users.noreply.github.com> Date: Mon, 19 Jul 2021 20:47:20 +0400 Subject: [PATCH 13/22] fix: #383 Restrict the duration field on Jobs to a 4 week minimum --- src/constants/index.js | 2 ++ .../CreateNewTeam/components/EditRoleForm/index.jsx | 3 ++- .../components/TeamDetailsModal/utils/validator.js | 5 +++-- src/routes/JobForm/utils.js | 13 +++++++++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/constants/index.js b/src/constants/index.js index f36d2868..8e18b13d 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -367,3 +367,5 @@ export const MAX_ALLOWED_INTERVIEWS = 3; * Custom role names to remove from RoleList component */ export const CUSTOM_ROLE_NAMES = ["custom", "niche"]; + +export const MIN_DURATION = 4; diff --git a/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx b/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx index c2c651dd..959df7d6 100644 --- a/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx +++ b/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx @@ -19,6 +19,7 @@ import { validateMin, composeValidators, } from "./utils/validator"; +import { MIN_DURATION } from "constants"; const Error = ({ name }) => { const { @@ -87,7 +88,7 @@ function EditRoleForm({ onChange, role }) { diff --git a/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js b/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js index d5290486..059605e8 100644 --- a/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js +++ b/src/routes/CreateNewTeam/components/TeamDetailsModal/utils/validator.js @@ -1,4 +1,5 @@ import { isUuid } from "utils/helpers"; +import { MIN_DURATION } from "constants"; const validateName = (name) => { if (!name || name.trim().length === 0) { @@ -25,7 +26,7 @@ const validateGreaterThan = (number, min) => { if (isInvalidNum) return isInvalidNum; return number < min - ? "Talent as a Service engagements have a 4 week minimum commitment." + ? `Talent as a Service engagements have a ${MIN_DURATION} week minimum commitment.` : undefined; }; @@ -46,7 +47,7 @@ const validateMonth = (monthString) => { const validateRole = (role) => { const roleErrors = {}; roleErrors.numberOfResources = validateNumber(role.numberOfResources); - roleErrors.durationWeeks = validateGreaterThan(role.durationWeeks, 4); + roleErrors.durationWeeks = validateGreaterThan(role.durationWeeks, MIN_DURATION); if (role.startMonth) { roleErrors.startMonth = validateMonth(role.startMonth); } diff --git a/src/routes/JobForm/utils.js b/src/routes/JobForm/utils.js index 0ee34141..095e0475 100644 --- a/src/routes/JobForm/utils.js +++ b/src/routes/JobForm/utils.js @@ -11,6 +11,7 @@ import { RESOURCE_TYPE_OPTIONS, FORM_ROW_TYPE, FORM_FIELD_TYPE, + MIN_DURATION, } from "../../constants"; const EDIT_JOB_ROWS = [ @@ -25,6 +26,17 @@ const EDIT_JOB_ROWS = [ { type: FORM_ROW_TYPE.SINGLE, fields: ["status"] }, ]; +const validateDuration = (x, y, {duration}) => { + if (duration === undefined) return undefined; + const converted = Number(duration); + + if (isNaN(converted) || converted !== Math.floor(converted) || converted < MIN_DURATION) { + return `Talent as a Service engagements have a ${MIN_DURATION} week minimum commitment.`; + } + + return undefined; +} + /** * return edit job configuration * @param {any} skillOptions skill options @@ -92,6 +104,7 @@ export const getEditJobConfig = ( placeholder: "Duration", disabled: onlyEnableStatus, step: 1, + customValidator: validateDuration, }, { label: "Resource Type", From 66fecb987b74277ba3ad8971d9ef42d55306ff1b Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Tue, 20 Jul 2021 11:08:21 +0300 Subject: [PATCH 14/22] docs: add constant comment ref issue #383 --- src/constants/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/constants/index.js b/src/constants/index.js index 8e18b13d..a94ca818 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -368,4 +368,7 @@ export const MAX_ALLOWED_INTERVIEWS = 3; */ export const CUSTOM_ROLE_NAMES = ["custom", "niche"]; +/** + * Minimal Resource Booking duration (weeks) + */ export const MIN_DURATION = 4; From 755f424921f2c6b419ccee6c719c2a3ff30802f4 Mon Sep 17 00:00:00 2001 From: Michael Baghel <31278895+mbaghel@users.noreply.github.com> Date: Tue, 20 Jul 2021 14:46:56 +0400 Subject: [PATCH 15/22] fix: Min duration message showing when value is float. --- src/routes/JobForm/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/JobForm/utils.js b/src/routes/JobForm/utils.js index 095e0475..9f2876ec 100644 --- a/src/routes/JobForm/utils.js +++ b/src/routes/JobForm/utils.js @@ -30,7 +30,7 @@ const validateDuration = (x, y, {duration}) => { if (duration === undefined) return undefined; const converted = Number(duration); - if (isNaN(converted) || converted !== Math.floor(converted) || converted < MIN_DURATION) { + if (isNaN(converted) || converted < MIN_DURATION) { return `Talent as a Service engagements have a ${MIN_DURATION} week minimum commitment.`; } From d1b5aa689c5922b02f6a64e57e7f6b6fdec13804 Mon Sep 17 00:00:00 2001 From: Michael Baghel <31278895+mbaghel@users.noreply.github.com> Date: Wed, 21 Jul 2021 01:01:47 +0400 Subject: [PATCH 16/22] fix: Typos: #368 & #390 --- src/routes/CreateNewTeam/components/EditRoleForm/index.jsx | 2 +- src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx b/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx index 959df7d6..e816f5aa 100644 --- a/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx +++ b/src/routes/CreateNewTeam/components/EditRoleForm/index.jsx @@ -65,7 +65,7 @@ function EditRoleForm({ onChange, role }) { diff --git a/src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx b/src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx index cae9e49a..a00bf427 100644 --- a/src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx +++ b/src/routes/CreateNewTeam/pages/CreateTeamLanding/index.jsx @@ -39,7 +39,7 @@ function CreateNewTeam() { /> } backgroundImage="linear-gradient(221.5deg, #2C95D7 0%, #9D41C9 100%)" onClick={() => goToRoute("/taas/createnewteam/skills")} From 5acd01a1a936524d0cf7fb313a4a29bd1136b562 Mon Sep 17 00:00:00 2001 From: yoution Date: Wed, 21 Jul 2021 11:02:50 +0800 Subject: [PATCH 17/22] fix: issue #355 --- src/routes/CreateNewTeam/styles.module.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/CreateNewTeam/styles.module.scss b/src/routes/CreateNewTeam/styles.module.scss index 045668bd..bf5fb5aa 100644 --- a/src/routes/CreateNewTeam/styles.module.scss +++ b/src/routes/CreateNewTeam/styles.module.scss @@ -11,8 +11,8 @@ } } -@media only screen and (max-height: 859px) { +@media only screen and (max-height: 820px) { .logos { display: none; } -} \ No newline at end of file +} From 9e4fd3734b9cd23a1a79866bb02fa7765b8ed17b Mon Sep 17 00:00:00 2001 From: Michael Baghel <31278895+mbaghel@users.noreply.github.com> Date: Thu, 22 Jul 2021 13:39:05 +0400 Subject: [PATCH 18/22] feat: Support direct links to the Job Candidates tabs. resolves #391 --- src/constants/index.js | 4 ++++ src/root.component.jsx | 2 +- src/routes/PositionDetails/index.jsx | 24 ++++++++++++++++-------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/constants/index.js b/src/constants/index.js index a94ca818..c8612cf2 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -133,6 +133,7 @@ export const CANDIDATE_STATUS_FILTERS = [ title: "Candidates to Review", noCandidateMessage: "No Candidates To Review", statuses: [CANDIDATE_STATUS.OPEN], + urlParam: "to-review", }, { key: CANDIDATE_STATUS_FILTER_KEY.INTERESTED, @@ -140,6 +141,7 @@ export const CANDIDATE_STATUS_FILTERS = [ title: "Interviews", noCandidateMessage: "No Interviews", statuses: [CANDIDATE_STATUS.INTERVIEW], + urlParam: "interviews", }, { key: CANDIDATE_STATUS_FILTER_KEY.SELECTED, @@ -147,6 +149,7 @@ export const CANDIDATE_STATUS_FILTERS = [ title: "Selected", noCandidateMessage: "No Selected Candidates", statuses: [CANDIDATE_STATUS.SELECTED, CANDIDATE_STATUS.OFFERED], + urlParam: "selected", }, { key: CANDIDATE_STATUS_FILTER_KEY.NOT_INTERESTED, @@ -160,6 +163,7 @@ export const CANDIDATE_STATUS_FILTERS = [ CANDIDATE_STATUS.TOPCODER_REJECTED, CANDIDATE_STATUS.JOB_CLOSED, ], + urlParam: "declined", }, ]; diff --git a/src/root.component.jsx b/src/root.component.jsx index 4392f546..2f5d06a9 100644 --- a/src/root.component.jsx +++ b/src/root.component.jsx @@ -32,7 +32,7 @@ export default function Root() { - + diff --git a/src/routes/PositionDetails/index.jsx b/src/routes/PositionDetails/index.jsx index b18df067..584727aa 100644 --- a/src/routes/PositionDetails/index.jsx +++ b/src/routes/PositionDetails/index.jsx @@ -22,7 +22,12 @@ const inReviewStatusFilter = _.find(CANDIDATE_STATUS_FILTERS, { key: CANDIDATE_STATUS_FILTER_KEY.TO_REVIEW, }); -const PositionDetails = ({ teamId, positionId }) => { +const getKeyFromParam = (urlParam) => { + const filter = _.find(CANDIDATE_STATUS_FILTERS, { urlParam }); + return filter?.key || undefined; +} + +const PositionDetails = ({ teamId, positionId, candidateStatus }) => { // by default show "interested" tab const [candidateStatusFilterKey, setCandidateStatusFilterKey] = useState( CANDIDATE_STATUS_FILTER_KEY.INTERESTED @@ -41,15 +46,18 @@ const PositionDetails = ({ teamId, positionId }) => { // if there are some candidates to review, then show "To Review" tab by default useEffect(() => { - if ( - position && - _.filter(position.candidates, (candidate) => + if (position) { + const key = getKeyFromParam(candidateStatus); + if (key) { + setCandidateStatusFilterKey(key); + } else if (_.filter(position.candidates, (candidate) => inReviewStatusFilter.statuses.includes(candidate.status) - ).length > 0 - ) { - setCandidateStatusFilterKey(CANDIDATE_STATUS_FILTER_KEY.TO_REVIEW); + ).length > 0 + ) { + setCandidateStatusFilterKey(CANDIDATE_STATUS_FILTER_KEY.TO_REVIEW); + } } - }, [position]); + }, [position, candidateStatus]); return ( From 2370ddb296f51292ae367a51fa23e0e0c2ef70c8 Mon Sep 17 00:00:00 2001 From: dengjun Date: Thu, 22 Jul 2021 17:45:52 +0800 Subject: [PATCH 19/22] add withdrawn in decline --- src/constants/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/constants/index.js b/src/constants/index.js index a94ca818..3b811240 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -111,6 +111,7 @@ export const CANDIDATE_STATUS = { TOPCODER_REJECTED: "topcoder-rejected", JOB_CLOSED: "job-closed", OFFERED: "offered", + WITHDRAWN: "withdrawn", }; /** @@ -159,6 +160,7 @@ export const CANDIDATE_STATUS_FILTERS = [ CANDIDATE_STATUS.REJECTED_OTHER, CANDIDATE_STATUS.TOPCODER_REJECTED, CANDIDATE_STATUS.JOB_CLOSED, + CANDIDATE_STATUS.WITHDRAWN, ], }, ]; From 4718302485e6c3904d510db0aa68e666d21ac1ec Mon Sep 17 00:00:00 2001 From: Michael Baghel <31278895+mbaghel@users.noreply.github.com> Date: Thu, 22 Jul 2021 14:39:23 +0400 Subject: [PATCH 20/22] fix: Customer scroll (small screen height fix). issue #355 --- src/components/CustomerScroll/styles.module.scss | 13 ++++++++++++- src/routes/CreateNewTeam/styles.module.scss | 11 +++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/components/CustomerScroll/styles.module.scss b/src/components/CustomerScroll/styles.module.scss index 35c004fb..60cfdf55 100644 --- a/src/components/CustomerScroll/styles.module.scss +++ b/src/components/CustomerScroll/styles.module.scss @@ -17,6 +17,7 @@ .scrolling-logos { background-image: url("../../assets/images/customer-logos.svg"); + background-size: cover; height: 60px; width: 100%; animation: scroll 300s linear infinite; @@ -42,4 +43,14 @@ width: 60px; z-index: 2; } -} \ No newline at end of file +} + +@media only screen and (max-height: 859px) { + .scrolling-logos { + height: 30px; + } + .title { + font-size: 16px; + margin-bottom: 15px; + } +} diff --git a/src/routes/CreateNewTeam/styles.module.scss b/src/routes/CreateNewTeam/styles.module.scss index bf5fb5aa..ba56c3ed 100644 --- a/src/routes/CreateNewTeam/styles.module.scss +++ b/src/routes/CreateNewTeam/styles.module.scss @@ -11,8 +11,15 @@ } } -@media only screen and (max-height: 820px) { +@media only screen and (max-height: 859px) { .logos { - display: none; + position: relative; + width: 100vw; + } +} + +@media only screen and (max-height: 859px) and (min-width: 1024px) { + .logos { + left: -270px; } } From 50ff6bcdb6b95383934dcde6322093c19d86a673 Mon Sep 17 00:00:00 2001 From: Michael Baghel Date: Sat, 24 Jul 2021 04:21:51 +0400 Subject: [PATCH 21/22] Update utils.js --- src/routes/JobForm/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/JobForm/utils.js b/src/routes/JobForm/utils.js index 9f2876ec..2b4c0fe5 100644 --- a/src/routes/JobForm/utils.js +++ b/src/routes/JobForm/utils.js @@ -27,7 +27,7 @@ const EDIT_JOB_ROWS = [ ]; const validateDuration = (x, y, {duration}) => { - if (duration === undefined) return undefined; + if (!duration) return undefined; const converted = Number(duration); if (isNaN(converted) || converted < MIN_DURATION) { From 6780ad7af7cfc7e5c40d227771b2fed37dc13dd2 Mon Sep 17 00:00:00 2001 From: Michael Baghel Date: Sat, 24 Jul 2021 04:54:29 +0400 Subject: [PATCH 22/22] Update index.jsx --- src/routes/PositionDetails/index.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/routes/PositionDetails/index.jsx b/src/routes/PositionDetails/index.jsx index 584727aa..361f1dc4 100644 --- a/src/routes/PositionDetails/index.jsx +++ b/src/routes/PositionDetails/index.jsx @@ -5,6 +5,7 @@ */ import React, { useCallback, useEffect, useState } from "react"; import PT from "prop-types"; +import { navigate } from "@reach/router"; import Page from "components/Page"; import LoadingIndicator from "components/LoadingIndicator"; import PageHeader from "components/PageHeader"; @@ -39,9 +40,9 @@ const PositionDetails = ({ teamId, positionId, candidateStatus }) => { const onCandidateStatusChange = useCallback( (statusFilter) => { - setCandidateStatusFilterKey(statusFilter.key); + navigate(`/taas/myteams/${teamId}/positions/${positionId}/candidates/${statusFilter.urlParam}`); }, - [setCandidateStatusFilterKey] + [teamId, positionId] ); // if there are some candidates to review, then show "To Review" tab by default