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

Feature/role jd #289

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -8,8 +8,8 @@ import React from "react";
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 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";

Original file line number Diff line number Diff line change
@@ -10,11 +10,15 @@ import cn from "classnames";
import PT from "prop-types";
import CompleteProgress from "../CompleteProgress";
import "./styles.module.scss";
import IconListQuill from "../../../../assets/images/icon-list-quill.svg";
import IconListQuill from "../../assets/images/icon-list-quill.svg";

function Completeness({ isDisabled, onClick, buttonLabel, stage }) {
function Completeness({ title, backgroundIcon, isDisabled, backgroundImage, onClick, buttonLabel, stage }) {
return (
<div styleName="completeness">
<div styleName="completeness"
style={{
backgroundImage,
}}
>
<CompleteProgress
percentDone={stage === 1 ? 26 : stage === 2 ? 52 : 98}
/>
@@ -26,7 +30,7 @@ function Completeness({ isDisabled, onClick, buttonLabel, stage }) {
{ done: stage > 1 }
)}
>
Input Skills
Input {title}
</li>
<li
styleName={cn(
@@ -49,7 +53,7 @@ function Completeness({ isDisabled, onClick, buttonLabel, stage }) {
>
{buttonLabel}
</Button>
<IconListQuill styleName="transparent-icon" />
{backgroundIcon}
</div>
);
}
@@ -59,6 +63,9 @@ Completeness.propTypes = {
onClick: PT.func,
buttonLabel: PT.string,
stage: PT.number,
title: PT.string,
backgroundImage: PT.string,
backgroundIcon: PT.string
};

export default Completeness;
Original file line number Diff line number Diff line change
@@ -4,9 +4,17 @@
@include rounded-card;
padding: 12px;
position: relative;
background-image: linear-gradient(221.5deg, #2c95d7 0%, #9d41c9 100%);
width: 250px;
color: #fff;

> svg {
position: absolute;
right: -50px;
top: 85px;
opacity: 10%;
width: 144px;
height: 144px;
}
}

.list {
@@ -47,12 +55,3 @@
}
}
}

.transparent-icon {
position: absolute;
right: -50px;
top: 85px;
opacity: 10%;
width: 144px;
height: 144px;
}
Original file line number Diff line number Diff line change
@@ -6,11 +6,11 @@
*/
import React, { useState } from "react";
import "./styles.module.scss";
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 Curve from "../../../../assets/images/curve.svg";
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 Curve from "../../assets/images/curve.svg";
import Button from "components/Button";

function ResultCard() {
Original file line number Diff line number Diff line change
@@ -5,10 +5,10 @@
*/
import React, { useEffect, useState } from "react";
import "./styles.module.scss";
import IconEarthSearch from "../../../../assets/images/icon-earth-search.svg";
import WorldMapDotted from "../../../../assets/images/world-map-dotted.svg";
import WorldMapSearch1 from "../../../../assets/images/world-map-search1.svg";
import WorldMapSearch2 from "../../../../assets/images/world-map-search2.svg";
import IconEarthSearch from "../../assets/images/icon-earth-search.svg";
import WorldMapDotted from "../../assets/images/world-map-dotted.svg";
import WorldMapSearch1 from "../../assets/images/world-map-search1.svg";
import WorldMapSearch2 from "../../assets/images/world-map-search2.svg";

function SearchCard() {
const [searchState, setSearchState] = useState(null);
2 changes: 2 additions & 0 deletions src/root.component.jsx
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ import JobForm from "./routes/JobForm";
import TeamAccess from "./routes/TeamAccess";
import CreateNewTeam from "./routes/CreateNewTeam";
import InputSkills from "./routes/InputSkills";
import InputJobDescription from "./routes/InputJobDescription";
import ReduxToastr from "react-redux-toastr";
import store from "./store";
import "./styles/main.vendor.scss";
@@ -33,6 +34,7 @@ export default function Root() {
<ResourceBookingForm path="/taas/myteams/:teamId/rb/:resourceBookingId/edit" />
<PositionDetails path="/taas/myteams/:teamId/positions/:positionId/candidates" />
<TeamAccess path="/taas/myteams/:teamId/access" />
<InputJobDescription path="/taas/myteams/createnewteam/jd" />
<InputSkills path="/taas/myteams/createnewteam/:projectId/skills" />
<SelectRole path="/taas/myteams/createnewteam/:projectId/role" />
</Router>
6 changes: 5 additions & 1 deletion src/routes/CreateNewTeam/index.jsx
Original file line number Diff line number Diff line change
@@ -30,6 +30,10 @@ function CreateNewTeam() {
});
};

const goToJobDescription = ()=> {
navigate(`/taas/myteams/createnewteam/jd`);
}

return (
<Page title="Create New Team">
<PageHeader title="Create New Team" />
@@ -55,7 +59,7 @@ function CreateNewTeam() {
description="You would like to use a description to explain what you need."
icon={<IconOfficeFileText />}
backgroundImage="linear-gradient(135deg, #2984BD 0%, #0AB88A 100%)"
isDisabled
onClick={goToJobDescription}
/>
</Page>
);
80 changes: 80 additions & 0 deletions src/routes/InputJobDescription/components/Popup/index.jsx
Original file line number Diff line number Diff line change
@@ -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 Popup({ open, skills, onClose, isLoading, onContinueClick }) {
return (
<Modal
open={open}
center
onClose={onClose}
closeIcon={
<IconCrossLight height="18px" width="18px" styleName="cross" />
}
styles={{
modal: modalStyle,
modalContainer: containerStyle,
}}
>
<div styleName="modal-body">
{isLoading ? (
<>
<CenteredSpinner />
<h5>loading skills</h5>
</>
) : (
<>
<IconSingleManAdd />
<h5>skills</h5>
{_.map(skills, (s) => {
return <div>{s.tag}</div>;
})}
</>
)}
</div>
<div styleName="button-group">
<Button
type="primary"
size="medium"
disabled={isLoading}
onClick={onContinueClick}
>
Continue
</Button>
</div>
</Modal>
);
}

Popup.propTypes = {
open: PT.bool,
onClose: PT.func,
isLoading: PT.bool,
onContinueClick: PT.func,
skills: PT.arrayOf(PT.shape()),
};

export default Popup;
48 changes: 48 additions & 0 deletions src/routes/InputJobDescription/components/Popup/styles.module.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
145 changes: 145 additions & 0 deletions src/routes/InputJobDescription/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* Input Job Description page
*
*/
import React, { useCallback, useEffect, useState } from "react";
import { useData } from "hooks/useData";
import { navigate } from "@reach/router";
import { toastr } from "react-redux-toastr";
import MarkdownEditor from "../../components/MarkdownEditor";
import { getSkillsByJobDescription } from "../../services/teams";
import Page from "components/Page";
import PageHeader from "components/PageHeader";
import PT from "prop-types";
import Completeness from "components/Completeness";
import { getSkills } from "services/skills";
import LoadingIndicator from "components/LoadingIndicator";
import SearchCard from "components/SearchCard";
import ResultCard from "components/ResultCard";
import AddAnotherModal from "components/AddAnotherModal";
import SkillListPopup from "./components/Popup";
import "./styles.module.scss";
import withAuthentication from "../../hoc/withAuthentication";
import IconOfficeFileText from "../../assets/images/icon-office-file-text.svg";

function InputJobDescription() {
const [jdString, setJdString] = useState("");
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");
setTimeout(() => {
setSearchState("done");
}, 3000);
}, []);

const addAnother = useCallback(() => {
// navigate(`/taas/myteams/createnewteam/${projectId}/role`);
}, []);

const submitJob = () => {
setSubmitDone(false);
setModalOpen(true);
setTimeout(() => {
setSubmitDone(true);
}, 3000);
};

const onEditChange = useCallback((value) => {
setJdString(value);
}, []);

return (
<div>
{!searchState ? (
<div styleName="page">
<div styleName='edit-container'>
<PageHeader
title="Input Job Description"
backTo="/taas/myteams/createnewteam"
/>
<MarkdownEditor
height='482px'
placeholder="input job description"
onChange={onEditChange}
/>
</div>
<Completeness
title="Job Desccription"
isDisabled={jdString.length < 10}
backgroundImage="linear-gradient(135deg, #2984BD 0%, #0AB88A 100%)"
backgroundIcon={<IconOfficeFileText />}
onClick={onSearch}
buttonLabel="Search"
stage={1}
/>
<SkillListPopup
open={skillModalOpen}
skills={skills}
onClose={() => setSkillModalOpen(false)}
isLoading={isLoadingSkills}
onContinueClick={onConfirationClick}
/>
</div>
) : searchState === "searching" ? (
<div styleName="page">
<SearchCard />
<Completeness
title="Job Desccription"
backgroundImage="linear-gradient(135deg, #2984BD 0%, #0AB88A 100%)"
backgroundIcon={<IconOfficeFileText />}
isDisabled
buttonLabel="Submit Request"
stage={2}
/>
</div>
) : (
<div styleName="page">
<ResultCard />
<Completeness
title="Job Desccription"
backgroundImage="linear-gradient(135deg, #2984BD 0%, #0AB88A 100%)"
backgroundIcon={<IconOfficeFileText />}
buttonLabel="Submit Request"
stage={3}
onClick={submitJob}
/>
<AddAnotherModal
open={modalOpen}
onClose={() => setModalOpen(false)}
submitDone={submitDone}
addAnother={addAnother}
/>
</div>
)}
</div>
);
}

InputJobDescription.propTypes = {
projectId: PT.string,
};

export default withAuthentication(InputJobDescription);
17 changes: 17 additions & 0 deletions src/routes/InputJobDescription/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.page {
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
margin: 42px 35px;

.edit-container {
background-color: #ffffff;
border-radius: 8px;
max-width: 746px;
position: relative;
margin-right: 30px;
padding: 0 30px 30px;
flex: 1;
}
}
25 changes: 19 additions & 6 deletions src/routes/InputSkills/index.jsx
Original file line number Diff line number Diff line change
@@ -12,16 +12,17 @@ import { useData } from "hooks/useData";
import { navigate } from "@reach/router";
import { toastr } from "react-redux-toastr";
import PT from "prop-types";
import SkillsList from "./components/SkillsList";
import Completeness from "./components/Completeness";
import Completeness from "components/Completeness";
import "./styles.module.scss";
import { getSkills } from "services/skills";
import LoadingIndicator from "components/LoadingIndicator";
import SearchCard from "./components/SearchCard";
import ResultCard from "./components/ResultCard";
import SearchCard from "components/SearchCard";
import ResultCard from "components/ResultCard";
import { createJob } from "services/jobs";
import AddAnotherModal from "./components/AddAnotherModal";
import AddAnotherModal from "components/AddAnotherModal";
import SkillsList from "./components/SkillsList";
import withAuthentication from "../../hoc/withAuthentication";
import IconListQuill from "../../assets/images/icon-list-quill.svg";

function InputSkills({ projectId }) {
const [selectedSkills, setSelectedSkills] = useState([]);
@@ -91,6 +92,9 @@ function InputSkills({ projectId }) {
toggleSkill={toggleSkill}
/>
<Completeness
title='Skills'
backgroundImage='linear-gradient(221.5deg, #2c95d7 0%, #9d41c9 100%)'
backgroundIcon={<IconListQuill/>}
isDisabled={selectedSkills.length < 1}
onClick={search}
buttonLabel="Search"
@@ -100,12 +104,21 @@ function InputSkills({ projectId }) {
) : searchState === "searching" ? (
<div styleName="page">
<SearchCard />
<Completeness isDisabled buttonLabel="Submit Request" stage={2} />
<Completeness
title='Skills'
backgroundImage='linear-gradient(221.5deg, #2c95d7 0%, #9d41c9 100%)'
backgroundIcon={<IconListQuill/>}
isDisabled
buttonLabel="Submit Request"
stage={2} />
</div>
) : (
<div styleName="page">
<ResultCard />
<Completeness
title='Skills'
backgroundImage='linear-gradient(221.5deg, #2c95d7 0%, #9d41c9 100%)'
backgroundIcon={<IconListQuill/>}
buttonLabel="Submit Request"
stage={3}
onClick={submitJob}
11 changes: 11 additions & 0 deletions src/services/teams.js
Original file line number Diff line number Diff line change
@@ -31,6 +31,17 @@ export const getV5UserProfile = () => {
return axios.get(`${config.API.V5}/taas-teams/me`);
};

/**
* Get skills by job description
* @param {string} description
* @returns {Promise<{}>} skills list
*/
export const getSkillsByJobDescription = (description) => {
return axios.post(`${config.API.V5}/taas-teams/getSkillsByJobDescription`, {
description,
});
};

/**
* Get team by id.
*