diff --git a/config/dev.js b/config/dev.js index 2daf4829..dfe168fd 100644 --- a/config/dev.js +++ b/config/dev.js @@ -13,7 +13,4 @@ module.exports = { V5: "https://api.topcoder-dev.com/v5", //"http://localhost:3030/api/v5" V3: "https://api.topcoder-dev.com/v3", }, - - // matching rate for search role result page - MATCHING_RATE: process.env.MATCHING_RATE || 80, }; diff --git a/config/prod.js b/config/prod.js index 5416e540..21cdc373 100644 --- a/config/prod.js +++ b/config/prod.js @@ -13,7 +13,4 @@ module.exports = { V5: "https://api.topcoder.com/v5", V3: "https://api.topcoder.com/v3", }, - - // matching rate for search role result page - MATCHING_RATE: process.env.MATCHING_RATE || 80, }; diff --git a/src/root.component.jsx b/src/root.component.jsx index 97b8bdcb..797270ff 100644 --- a/src/root.component.jsx +++ b/src/root.component.jsx @@ -35,8 +35,8 @@ export default function Root() { - - + + {/* Global config for Toastr popups */} diff --git a/src/routes/CreateNewTeam/components/Completeness/styles.module.scss b/src/routes/CreateNewTeam/components/Completeness/styles.module.scss index 8e488b15..3227642c 100644 --- a/src/routes/CreateNewTeam/components/Completeness/styles.module.scss +++ b/src/routes/CreateNewTeam/components/Completeness/styles.module.scss @@ -6,7 +6,6 @@ position: relative; width: 250px; color: #fff; - overflow: hidden; button { border: none; } diff --git a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx index 2938cccf..0bde6659 100644 --- a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx +++ b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx @@ -3,17 +3,13 @@ * Card that appears when there are no matching profiles after searching. */ import React from "react"; -import PT from "prop-types"; import { navigate } from "@reach/router"; -import { - formatMoney, -} from "utils/format"; import "./styles.module.scss"; import IconEarthX from "../../../../assets/images/icon-earth-x.svg"; import Curve from "../../../../assets/images/curve.svg"; import Button from "components/Button"; -function NoMatchingProfilesResultCard({rates = [{global: 0}], onSearch}) { +function NoMatchingProfilesResultCard() { return (
@@ -29,11 +25,11 @@ function NoMatchingProfilesResultCard({rates = [{global: 0}], onSearch}) {

Niche Rate

-

{formatMoney(rates[0].global)}

+

$1,200

/Week

- {showRates && !isExternalMember && ( + {showRates && showSpecialRates && (

Hi {userHandle}, we have special rates for you as a Xeno User! @@ -79,23 +72,23 @@ function ResultCard({

(40h / week)

-

Global Rate

+

Senior Member

-

{formatMoney(rates[0].global)}

+

$2,000

/Week

-

In-Conutry Rate

+

Standard Member

-

{formatMoney(rates[0].inCountry)}

+

$1,500

/Week

-

Offshore Rate

+

Junior Member

-

{formatMoney(rates[0].offShore)}

+

$1,000

/Week

@@ -106,23 +99,23 @@ function ResultCard({

(30h / week)

-

Global Rate

+

Senior Member

-

{formatMoney(rates[0].rate30Global)}

+

$1,800

/Week

-

In-Conutry Rate

+

Standard Member

-

{formatMoney(rates[0].rate30InCountry)}

+

$1,300

/Week

-

Offshore Rate

+

Junior Member

-

{formatMoney(rates[0].rate30OffShore)}

+

$800

/Week

@@ -133,23 +126,23 @@ function ResultCard({

(20h / week)

-

Global Rate

+

Senior Member

-

{formatMoney(rates[0].rate20Global)}

+

$1,600

/Week

-

In-Conutry Rate

+

Standard Member

-

{formatMoney(rates[0].rate20InCountry)}

+

$1,100

/Week

-

Offshore Rate

+

Junior Member

-

{formatMoney(rates[0].rate20OffShore)}

+

$600

/Week

@@ -157,7 +150,7 @@ function ResultCard({ )} - {showRates && isExternalMember && ( + {showRates && !showSpecialRates && (
@@ -166,7 +159,7 @@ function ResultCard({

(40h / week)

-
{formatMoney(rates[0].global)}
+
$1,800

/Week

@@ -176,7 +169,7 @@ function ResultCard({

(30h / week)

-
{formatMoney(rates[0].inCountry)}
+
$1,250

/Week

@@ -186,7 +179,7 @@ function ResultCard({

(20h / week)

-
{formatMoney(rates[0].offShore)}
+
$800

/Week

@@ -197,14 +190,14 @@ function ResultCard({

Qualified candidates within

-
{timeToCandidate}h
+
24h

Interviews can start within

-
{timeToInterview}h
+
48h
@@ -216,11 +209,11 @@ function ResultCard({
-

{config.MATCHING_RATE}%

+

80%

Matching rate

} @@ -229,7 +222,7 @@ function ResultCard({
-

{numberOfMembersAvailable}+

+

300+

Members matched

@@ -253,12 +246,4 @@ function ResultCard({ ); } -ResultCard.propTypes = { - numberOfMembersAvailable:PT.number, - numberOfMembers: PT.string, - timeToCandidate: PT.number, - timeToInterview: PT.number, - isExternalMember: PT.bool, - rates: PT.array, -}; export default ResultCard; diff --git a/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss b/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss index cba4f0a9..1b4e7347 100644 --- a/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss +++ b/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss @@ -3,7 +3,6 @@ .result-card { @include rounded-card; max-width: 746px; - overflow: hidden; width: 50vw; margin-right: 30px; } @@ -24,6 +23,7 @@ position: relative; text-align: center; border-radius: 8px 8px 0 0; + cursor: pointer; svg { margin-bottom: 8px; @@ -72,12 +72,9 @@ margin: 0 40px; } .progressbar-child { + margin-top: 5px; width: 90px; height: 90px; - overflow: hidden; - > h4 { - margin-top: 10px; - } } } @@ -157,9 +154,6 @@ &:not(:first-child) { margin-left: 40px; } - &:not(:last-child) { - border-right: 1px solid #E9E9E9; - } .rate-heading { display: flex; diff --git a/src/routes/CreateNewTeam/components/RoleDetailsModal/index.jsx b/src/routes/CreateNewTeam/components/RoleDetailsModal/index.jsx index 179619c3..17039e66 100644 --- a/src/routes/CreateNewTeam/components/RoleDetailsModal/index.jsx +++ b/src/routes/CreateNewTeam/components/RoleDetailsModal/index.jsx @@ -6,18 +6,17 @@ import React, { useState, useEffect } from "react"; import PT from "prop-types"; import Modal from "react-responsive-modal"; import Button from "components/Button"; -import MarkdownEditorViewer from "../../../../components/MarkdownEditorViewer"; import IconCrossLight from "../../../../assets/images/icon-cross-light.svg"; import FallbackIcon from "../../../../assets/images/icon-role-fallback.svg"; import "./styles.module.scss"; import CenteredSpinner from "components/CenteredSpinner"; import { getRoleById } from "services/roles"; + const modalStyle = { - borderRadius: "10px", - padding: "70px 80px 60px", - maxWidth: "680px", + borderRadius: "8px", + padding: "32px 32px 22px 32px", + maxWidth: "460px", width: "100%", - height: "650px", margin: 0, "overflow-x": "hidden", }; @@ -45,7 +44,9 @@ function RoleDetailsModal({ roleId, open, onClose }) { open={open} center onClose={onClose} - showCloseIcon={false} + closeIcon={ + + } styles={{ modal: modalStyle, modalContainer: containerStyle, @@ -55,7 +56,7 @@ function RoleDetailsModal({ roleId, open, onClose }) { {isLoading ? ( <> -
Loading...
+
Loading...
) : ( <> @@ -69,7 +70,6 @@ function RoleDetailsModal({ roleId, open, onClose }) { ) : ( )} -
{role?.name}
- {!showSkills &&
} - {showSkills &&

{_.map(role?.listOfSkills, (s)=> - {s} - )}

} +
{role?.name}
+

{role?.description}

)} diff --git a/src/routes/CreateNewTeam/components/RoleDetailsModal/styles.module.scss b/src/routes/CreateNewTeam/components/RoleDetailsModal/styles.module.scss index 3f964c9a..81daa623 100644 --- a/src/routes/CreateNewTeam/components/RoleDetailsModal/styles.module.scss +++ b/src/routes/CreateNewTeam/components/RoleDetailsModal/styles.module.scss @@ -1,39 +1,5 @@ @import "styles/include"; - -.loading { - margin-bottom: 100px; - margin-top: 200px; -} - -.skill-tag-list { - display: flex; - height: 260px; - flex-wrap: wrap; -} - -.desc-container { - width: 100%; - height: 260px; - overflow: scroll; -} - -.skill-tag { - @include font-roboto; - - background-color: #E9E9E9; - border-radius: 5px; - padding: 6px 10px; - margin-right: 6px; - margin-bottom: 10px; - color: #2A2A2A; - font-size: 12px; - letter-spacing: 0.25px; - line-height: 16px; - height: 26px; - display: inline-block; -} - .button-group { display: flex; flex-direction: row; @@ -49,8 +15,7 @@ flex-direction: row; align-items: center; justify-content: center; - margin-top: 14px; - margin-bottom: 30px; + margin-bottom: 42px; } .modal-body { @@ -58,19 +23,18 @@ flex-direction: column; justify-content: flex-start; align-items: center; - // text-align: center; - margin-bottom: 40px; + text-align: center; + margin-bottom: 80px; .role-icon { width: 42px; height: 42px; - margin-bottom: 18px; } h5 { @include font-barlow-condensed; font-size: 34px; - color: #000; + color: #1e94a3; text-transform: uppercase; font-weight: 500; margin-bottom: 10px; diff --git a/src/routes/CreateNewTeam/components/SearchableList/index.jsx b/src/routes/CreateNewTeam/components/SearchableList/index.jsx deleted file mode 100644 index 88ab6624..00000000 --- a/src/routes/CreateNewTeam/components/SearchableList/index.jsx +++ /dev/null @@ -1,88 +0,0 @@ -/** - * SearchableList - * wrap roles list and skills list - * Allows selecting and filtering by name. - */ -import React, { useEffect, useState } from "react"; -import { useDebounce } from "react-use"; -import PT from "prop-types"; -import Input from "components/Input"; -import PageHeader from "components/PageHeader"; -import "./styles.module.scss"; -import { INPUT_DEBOUNCE_DELAY } from "constants/"; - -function SearchableList({title, inputPlaceholder, itemList, selectedRoleId, onDescriptionClick, toggleRole, renderItem, countElement}) { - const [filteredItems, setFilteredItems] = useState(itemList); - const [filter, setFilter] = useState(""); - const [debouncedFilter, setDebouncedFilter] = useState(""); - - const onFilterChange = (e) => { - setFilter(e.target.value); - }; - - useDebounce( - () => { - setDebouncedFilter(filter); - }, - INPUT_DEBOUNCE_DELAY, - [filter] - ); - - useEffect(() => { - if (debouncedFilter.length > 0) { - const filterText = debouncedFilter.toLowerCase(); - setFilteredItems( - itemList.filter((item) => item.name.toLowerCase().includes(filterText)) - ); - } else { - setFilteredItems(itemList); - } - }, [debouncedFilter, itemList]); - - return ( -
- - - {filter && ( - setFilter("")} - > - X - - )} -
- } - /> - {countElement} -
- {filteredItems.map((item) => ( - renderItem(item) - ))} -
- - ); -} - -SearchableList.propTypes = { - itemList: PT.array, - title: PT.string, - inputPlaceholder: PT.string, - renderItem: PT.func, - countElement: PT.element, - toggleRole: PT.func, -}; - -export default SearchableList; diff --git a/src/routes/CreateNewTeam/components/SearchableList/styles.module.scss b/src/routes/CreateNewTeam/components/SearchableList/styles.module.scss deleted file mode 100644 index e17803f5..00000000 --- a/src/routes/CreateNewTeam/components/SearchableList/styles.module.scss +++ /dev/null @@ -1,73 +0,0 @@ -@import "styles/include"; - - -.input-container { - position: relative; -} - -.searchable-list { - @include rounded-card; - max-width: 746px; - margin-right: 20px; - position: relative; - flex: 1; - - > header { - padding: 16px 24px; - } -} - -.role-count { - position: absolute; - font-size: 12px; - top: 72px; - left: 73px; -} - -// adding "input:not([type="checkbox"])" to make sure that we override reset styles -input:not([type="checkbox"]).filter-input { - display: inline-block; - position: relative; - width: 300px; - background-color: #ffffff; - border: 1px solid #aaaaaa; - border-radius: 6px; - box-sizing: border-box; - color: #2a2a2a; - font-size: 14px; - height: 40px; - line-height: 38px; - outline: none; - padding: 0 15px; - - &:not(:focus) { - background-image: url("../../../../assets/images/icon-search.svg"); - background-repeat: no-repeat; - background-position: 10px center; - text-indent: 20px; - } - - &::placeholder { - color: #aaaaaa; - } -} - -.clear-input-button { - position: absolute; - right: 15px; - font-size: 14px; - font-weight: 700; - line-height: 38px; - cursor: pointer; - &:hover { - color: rgb(216, 24, 24); - } -} - -.searchable-container { - display: flex; - flex-direction: row; - justify-content: flex-start; - flex-wrap: wrap; - margin-right: 24px; -} diff --git a/src/routes/CreateNewTeam/index.jsx b/src/routes/CreateNewTeam/index.jsx index 763791cf..62fd1826 100644 --- a/src/routes/CreateNewTeam/index.jsx +++ b/src/routes/CreateNewTeam/index.jsx @@ -14,10 +14,24 @@ import LandingBox from "./components/LandingBox"; 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 { postProject } from "services/teams"; +import withAuthentication from "../../hoc/withAuthentication"; function CreateNewTeam() { - const onSearchClick = (page) => { - navigate(`/taas/myteams/createnewteam/${page}`); + const createProjectAndNavigate = async (navigateTo) => { + postProject() + .then((res) => { + const id = _.get(res, "data.id"); + navigate(`/taas/myteams/createnewteam/${id}/${navigateTo}`); + }) + .catch((err) => { + toastr.warning("Error", "Failed to create a new team."); + console.error(err); + }); + }; + + const goToJobDescription = () => { + navigate(`/taas/myteams/createnewteam/jd`); }; return ( @@ -31,24 +45,24 @@ function CreateNewTeam() { description="You know you want a front end developer, or a full stack developer, mobile one or others." icon={} backgroundImage="linear-gradient(101.95deg, #8B41B0 0%, #EF476F 100%)" - onClick={() => onSearchClick("role")} + onClick={() => createProjectAndNavigate("role")} /> } backgroundImage="linear-gradient(221.5deg, #2C95D7 0%, #9D41C9 100%)" - onClick={() => onSearchClick("skills")} + onClick={() => createProjectAndNavigate("skills")} /> } backgroundImage="linear-gradient(135deg, #2984BD 0%, #0AB88A 100%)" - onClick={() => onSearchClick("jd")} + onClick={goToJobDescription} /> ); } -export default CreateNewTeam; +export default withAuthentication(CreateNewTeam); diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/index.jsx b/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/index.jsx new file mode 100644 index 00000000..60317253 --- /dev/null +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/index.jsx @@ -0,0 +1,80 @@ +/** + * Temporary Popup for skill list + * show skill list + */ +import React from "react"; +import _ from "lodash"; +import PT from "prop-types"; +import Modal from "react-responsive-modal"; +import Button from "components/Button"; +import IconCrossLight from "../../../../../../assets/images/icon-cross-light.svg"; +import IconSingleManAdd from "../../../../../../assets/images/icon-single-man-add.svg"; +import "./styles.module.scss"; +import CenteredSpinner from "components/CenteredSpinner"; + +const modalStyle = { + borderRadius: "8px", + padding: "32px 32px 22px 32px", + maxWidth: "460px", + width: "100%", + margin: 0, + "overflow-x": "hidden", +}; + +const containerStyle = { + padding: "10px", +}; + +function SkillListPopup({ open, skills, onClose, isLoading, onContinueClick }) { + return ( + + } + styles={{ + modal: modalStyle, + modalContainer: containerStyle, + }} + > +
+ {isLoading ? ( + <> + +
loading skills
+ + ) : ( + <> + +
skills
+ {_.map(skills, (s) => { + return
{s.tag}
; + })} + + )} +
+
+ +
+
+ ); +} + +SkillListPopup.propTypes = { + open: PT.bool, + onClose: PT.func, + isLoading: PT.bool, + onContinueClick: PT.func, + skills: PT.arrayOf(PT.shape()), +}; + +export default SkillListPopup; diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/styles.module.scss b/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/styles.module.scss new file mode 100644 index 00000000..b0270326 --- /dev/null +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/components/SkillListPopup/styles.module.scss @@ -0,0 +1,48 @@ +@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; + 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; + } +} diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx b/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx index 8020ce43..f6bbc2de 100644 --- a/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/index.jsx @@ -15,13 +15,12 @@ import MarkdownEditor from "../../../../components/MarkdownEditor"; import { getSkillsByJobDescription } from "../../../../services/teams"; import Completeness from "../../components/Completeness"; import { getSkills } from "services/skills"; -import { sendRoleSearchRequest } from "services/teams"; import SearchCard from "../../components/SearchCard"; import ResultCard from "../../components/ResultCard"; -import NoMatchingProfilesResultCard from "../../components/NoMatchingProfilesResultCard"; import AddAnotherModal from "../../components/AddAnotherModal"; +import SkillListPopup from "./components/SkillListPopup"; import "./styles.module.scss"; -import AddedRolesAccordion from "../../components/AddedRolesAccordion"; +import withAuthentication from "../../../../hoc/withAuthentication"; import IconOfficeFileText from "../../../../assets/images/icon-office-file-text.svg"; function InputJobDescription() { @@ -30,18 +29,39 @@ function InputJobDescription() { { name: "Search Member" }, { name: "Overview of the Results" }, ]); - const [roleSearchResult, setRoleSearchResult] = useState(null); - const [addedRoles, setAddedRoles] = useState([]); - const [ - previousRoleSearchRequestId, - setPreviousRoleSearchRequestId, - ] = useState(null); const [jdString, setJdString] = useState(""); - const [matchingProfiles, setMatchingProfiles] = useState(null); const [searchState, setSearchState] = useState(null); const [modalOpen, setModalOpen] = useState(false); + const [skillModalOpen, setSkillModalOpen] = useState(false); const [submitDone, setSubmitDone] = useState(false); const [skills, setSkills] = useState([]); + const [isLoadingSkills, setIsLoadingSkills] = useState(false); + + const onSearch = useCallback( + (value) => { + setSkillModalOpen(true); + setIsLoadingSkills(true); + getSkillsByJobDescription(jdString) + .then((response) => { + setSkills(response.data); + setIsLoadingSkills(false); + setSkillModalOpen(true); + }) + .catch(() => { + setIsLoadingSkills(false); + }); + }, + [jdString] + ); + + const onConfirationClick = useCallback(() => { + setSearchState("searching"); + setCurrentStage(1, stages, setStages); + setTimeout(() => { + setCurrentStage(2, stages, setStages); + setSearchState("done"); + }, 3000); + }, []); const addAnother = useCallback(() => { // navigate(`/taas/myteams/createnewteam/${projectId}/role`); @@ -55,36 +75,6 @@ function InputJobDescription() { }, 3000); }; - const search = useCallback(() => { - setCurrentStage(1, stages, setStages); - setSearchState("searching"); - sendRoleSearchRequest({ - jobDescription: jdString, - previousRoleSearchRequestId, - }) - .then(({ data }) => { - setRoleSearchResult(data); - setPreviousRoleSearchRequestId(data.roleSearchRequestId); - setCurrentStage(2, stages, setStages); - setMatchingProfiles(null); // display no matching profiles screen for a while - if (data.name && data.name.toLowerCase() !== "niche") { - setMatchingProfiles(true); - setAddedRoles((addedRoles) => [ - ...addedRoles, - { id: data.id, name: data.name }, - ]); - } else { - setMatchingProfiles(false); - } - setSearchState("done"); - }) - .catch((e) => { - setCurrentStage(2, stages, setStages); - setMatchingProfiles(false); // display no matching profiles screen for a while - setSearchState("done"); - }); - }, [jdString]); - const onEditChange = useCallback((value) => { setJdString(value); }, []); @@ -104,20 +94,21 @@ function InputJobDescription() { onChange={onEditChange} /> - -
- {addedRoles.length > 0 && ( - - )} -
+ setSkillModalOpen(false)} + isLoading={isLoadingSkills} + onContinueClick={onConfirationClick} + /> ) : searchState === "searching" ? (
@@ -132,19 +123,7 @@ function InputJobDescription() {
) : (
- {matchingProfiles ? ( - - ) : ( - - )} - -
- {addedRoles.length > 0 && ( - - )} +
-
)} ); @@ -169,4 +147,4 @@ InputJobDescription.propTypes = { projectId: PT.string, }; -export default InputJobDescription; +export default withAuthentication(InputJobDescription); diff --git a/src/routes/CreateNewTeam/pages/InputJobDescription/styles.module.scss b/src/routes/CreateNewTeam/pages/InputJobDescription/styles.module.scss index a1b3188b..31e3ca4b 100644 --- a/src/routes/CreateNewTeam/pages/InputJobDescription/styles.module.scss +++ b/src/routes/CreateNewTeam/pages/InputJobDescription/styles.module.scss @@ -14,11 +14,4 @@ padding: 0 30px 30px; flex: 1; } - .right-side { - display: flex; - flex-direction: column; - & > div:not(:first-child) { - margin-top: 16px; - } - } } diff --git a/src/routes/CreateNewTeam/pages/InputSkills/index.jsx b/src/routes/CreateNewTeam/pages/InputSkills/index.jsx index e513ac5c..9341e86b 100644 --- a/src/routes/CreateNewTeam/pages/InputSkills/index.jsx +++ b/src/routes/CreateNewTeam/pages/InputSkills/index.jsx @@ -12,20 +12,17 @@ import { useData } from "hooks/useData"; import { navigate } from "@reach/router"; import { toastr } from "react-redux-toastr"; import PT from "prop-types"; -import SearchableList from "../../components/SearchableList"; -import SkillItem from "./components/SkillItem"; +import SkillsList from "./components/SkillsList"; import Completeness from "../../components/Completeness"; import "./styles.module.scss"; import { getSkills } from "services/skills"; -import { sendRoleSearchRequest } from "services/teams"; import { setCurrentStage } from "utils/helpers"; import LoadingIndicator from "components/LoadingIndicator"; import SearchCard from "../../components/SearchCard"; import ResultCard from "../../components/ResultCard"; -import NoMatchingProfilesResultCard from "../../components/NoMatchingProfilesResultCard"; import { createJob } from "services/jobs"; import AddAnotherModal from "../../components/AddAnotherModal"; -import AddedRolesAccordion from "../../components/AddedRolesAccordion"; +import withAuthentication from "../../../../hoc/withAuthentication"; function InputSkills({ projectId }) { const [stages, setStages] = useState([ @@ -33,17 +30,10 @@ function InputSkills({ projectId }) { { name: "Search Member" }, { name: "Overview of the Results" }, ]); - const [ - previousRoleSearchRequestId, - setPreviousRoleSearchRequestId, - ] = useState(null); - const [roleSearchResult, setRoleSearchResult] = useState(null); const [selectedSkills, setSelectedSkills] = useState([]); - const [matchingProfiles, setMatchingProfiles] = useState(null); const [searchState, setSearchState] = useState(null); const [modalOpen, setModalOpen] = useState(false); const [submitDone, setSubmitDone] = useState(false); - const [addedRoles, setAddedRoles] = useState([]); const [skills, loadingError] = useData(getSkills); @@ -76,7 +66,6 @@ function InputSkills({ projectId }) { const toggleSkill = useCallback( (id) => { - setPreviousRoleSearchRequestId(null); if (selectedSkills.includes(id)) { setSelectedSkills(selectedSkills.filter((skill) => skill !== id)); } else { @@ -88,64 +77,27 @@ function InputSkills({ projectId }) { [selectedSkills] ); - const search = useCallback(() => { - setCurrentStage(1, stages, setStages); + // mocked search for users with given skills + const search = () => { setSearchState("searching"); - sendRoleSearchRequest({ - skills: selectedSkills, - previousRoleSearchRequestId, - }) - .then(({ data }) => { - setRoleSearchResult(data); - setPreviousRoleSearchRequestId(data.roleSearchRequestId); - setCurrentStage(2, stages, setStages); - setMatchingProfiles(null); // display no matching profiles screen for a while - if (data.name && data.name.toLowerCase() !== "niche") { - setAddedRoles((addedRoles) => [ - ...addedRoles, - { id: data.id, name: data.name }, - ]); - setMatchingProfiles(true); - } else { - setMatchingProfiles(false); - } - setSearchState("done"); - }) - .catch((e) => { - setCurrentStage(2, stages, setStages); - setMatchingProfiles(false); // display no matching profiles screen for a while - setSearchState("done"); - }); - }, [selectedSkills]); + setCurrentStage(1, stages, setStages); + searchTimer = setTimeout(() => { + setSearchState("done"); + setCurrentStage(2, stages, setStages); + }, 3000); + }; + + useEffect(() => clearTimeout(searchTimer)); return !skills ? ( ) : !searchState ? (
- 0 && ( -

- {selectedSkills.length} skills selected -

- ) - } - renderItem={({ id, name }) => { - return ( - - ); - }} + - {addedRoles.length > 0 && } ) : (
- {matchingProfiles ? ( - - ) : ( - - )} -
- {addedRoles.length > 0 && ( - - )} +
-
); } @@ -199,4 +142,4 @@ InputSkills.propTypes = { projectId: PT.string, }; -export default InputSkills; +export default withAuthentication(InputSkills); diff --git a/src/routes/CreateNewTeam/pages/InputSkills/styles.module.scss b/src/routes/CreateNewTeam/pages/InputSkills/styles.module.scss index a6c5fff9..b47da072 100644 --- a/src/routes/CreateNewTeam/pages/InputSkills/styles.module.scss +++ b/src/routes/CreateNewTeam/pages/InputSkills/styles.module.scss @@ -4,20 +4,4 @@ justify-content: center; align-items: flex-start; margin: 42px 35px; - - .right-side { - display: flex; - flex-direction: column; - & > div:not(:first-child) { - margin-top: 16px; - } - } -} - - -.skill-count { - position: absolute; - font-size: 12px; - top: 72px; - left: 73px; } diff --git a/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx b/src/routes/CreateNewTeam/pages/SelectRole/components/AddedRolesAccordion/index.jsx similarity index 100% rename from src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx rename to src/routes/CreateNewTeam/pages/SelectRole/components/AddedRolesAccordion/index.jsx diff --git a/src/routes/CreateNewTeam/components/AddedRolesAccordion/styles.module.scss b/src/routes/CreateNewTeam/pages/SelectRole/components/AddedRolesAccordion/styles.module.scss similarity index 100% rename from src/routes/CreateNewTeam/components/AddedRolesAccordion/styles.module.scss rename to src/routes/CreateNewTeam/pages/SelectRole/components/AddedRolesAccordion/styles.module.scss diff --git a/src/routes/CreateNewTeam/pages/SelectRole/components/RoleItem/styles.module.scss b/src/routes/CreateNewTeam/pages/SelectRole/components/RoleItem/styles.module.scss index a1d5560d..3f5b7652 100644 --- a/src/routes/CreateNewTeam/pages/SelectRole/components/RoleItem/styles.module.scss +++ b/src/routes/CreateNewTeam/pages/SelectRole/components/RoleItem/styles.module.scss @@ -33,7 +33,6 @@ } .button { - width: 75px; font-size: 14px; line-height: 22px; padding: 0; diff --git a/src/routes/CreateNewTeam/pages/SelectRole/index.jsx b/src/routes/CreateNewTeam/pages/SelectRole/index.jsx index 9e15b3b7..a8137e94 100644 --- a/src/routes/CreateNewTeam/pages/SelectRole/index.jsx +++ b/src/routes/CreateNewTeam/pages/SelectRole/index.jsx @@ -11,8 +11,7 @@ import { useData } from "hooks/useData"; import { navigate } from "@reach/router"; import { toastr } from "react-redux-toastr"; import PT from "prop-types"; -import RoleItem from "./components/RoleItem"; -import SearchableList from '../../components/SearchableList' +import RolesList from "./components/RolesList"; import Completeness from "../../components/Completeness"; import "./styles.module.scss"; import { getRoles } from "services/roles"; @@ -22,11 +21,10 @@ import SearchCard from "../../components/SearchCard"; import ResultCard from "../../components/ResultCard"; import NoMatchingProfilesResultCard from "../../components/NoMatchingProfilesResultCard"; import { createJob } from "services/jobs"; -import { sendRoleSearchRequest } from "services/teams"; import AddAnotherModal from "../../components/AddAnotherModal"; import RoleDetailsModal from "../../components/RoleDetailsModal"; -import AddedRolesAccordion from "../../components/AddedRolesAccordion"; - +import withAuthentication from "../../../../hoc/withAuthentication"; +import AddedRolesAccordion from "./components/AddedRolesAccordion"; function SelectRole({ projectId }) { const [stages, setStages] = useState([ @@ -34,8 +32,6 @@ function SelectRole({ projectId }) { { name: "Search Member" }, { name: "Overview of the Results" }, ]); - const [roleSearchResult, setRoleSearchResult] = useState(null); - const [previousRoleSearchRequestId, setPreviousRoleSearchRequestId] = useState(null); const [addedRoles, setAddedRoles] = useState([]); const [selectedRoleId, setSelectedRoleId] = useState(null); const [searchState, setSearchState] = useState(null); @@ -80,7 +76,6 @@ function SelectRole({ projectId }) { const toggleRole = useCallback( (id) => { - setPreviousRoleSearchRequestId(null) setSelectedRoleId((selectedRoleId) => id === selectedRoleId ? null : id ); @@ -93,27 +88,22 @@ function SelectRole({ projectId }) { setRoleDetailsModalOpen(true); }, []); - const search = useCallback(() => { + // mocked search for users with given roles + const search = () => { setCurrentStage(1, stages, setStages); setSearchState("searching"); - sendRoleSearchRequest({roleId: selectedRoleId, previousRoleSearchRequestId}).then(({data})=> { - setRoleSearchResult(data) - setCurrentStage(2, stages, setStages); - setMatchingProfiles(null); // display no matching profiles screen for a while - setPreviousRoleSearchRequestId(data.roleSearchRequestId) - if (data.name && data.name.toLowerCase() !== 'niche' ) { - setAddedRoles((addedRoles) => [...addedRoles, { id: data.id, name: data.name }]); - setMatchingProfiles(true) - } else { - setMatchingProfiles(false) - } - setSearchState("done"); - }).catch(e=> { - setCurrentStage(2, stages, setStages); - setMatchingProfiles(false); // display no matching profiles screen for a while - setSearchState("done"); - }) - }, [selectedRoleId, previousRoleSearchRequestId]); + searchTimer = setTimeout(() => { + setCurrentStage(2, stages, setStages); + setMatchingProfiles(null); // display no matching profiles screen for a while + setSearchState("done"); + setTimeout(() => setMatchingProfiles(true), 2000); + // add selected role + const { id, name } = roles.find((r) => r.id === selectedRoleId); + setAddedRoles((addedRoles) => [...addedRoles, { id, name }]); + }, 3000); + }; + + useEffect(() => clearTimeout(searchTimer)); if (!roles) { return ; @@ -122,23 +112,11 @@ function SelectRole({ projectId }) { if (roles && !searchState) { return (
- { - return ( - - ) - }} +
{addedRoles.length > 0 && ( @@ -152,15 +130,11 @@ function SelectRole({ projectId }) { stages={stages} percentage="26" /> - { - roleDetailsModalOpen && ( - setRoleDetailsModalOpen(false)} - /> - ) - } + setRoleDetailsModalOpen(false)} + />
); @@ -184,7 +158,7 @@ function SelectRole({ projectId }) { if (searchState === "done") { return (
- {matchingProfiles ? : } + {matchingProfiles ? : }
{matchingProfiles && } { return axios.get(`${config.API.V5}/taas-teams?${query}`); }; -/** - * search role detail. - * - * @param {object} data search param, roleId | jobDescription | skills - * @returns {Promise<{}>} role detail - */ -export const sendRoleSearchRequest = (data) => { - // mock data - delete data.previousRoleSearchRequestId - return axios.post(`${config.API.V5}/taas-teams/sendRoleSearchRequest`, data); -}; - /** * Get v5 user profile. * @@ -202,3 +190,19 @@ export const postMembers = (teamId, handles, emails) => { return axios.post(url, bodyObj); }; + +/** + * Post new project + * + * @returns {Promise} project object + */ +export const postProject = () => { + const url = `${config.API.V5}/taas-teams/createTeamRequest`; + + const bodyObj = { + name: `project-${Date()}`, + type: "talent-as-a-service", + }; + + return axios.post(url, bodyObj); +};