diff --git a/src/routes/CreateNewTeam/actions/index.js b/src/routes/CreateNewTeam/actions/index.js
index b90aae4e..0a224017 100644
--- a/src/routes/CreateNewTeam/actions/index.js
+++ b/src/routes/CreateNewTeam/actions/index.js
@@ -32,7 +32,7 @@ const addMatchingRole = (matchingRole) => ({
payload: matchingRole,
});
-const deleteMatchingRole = (matchingRole) => ({
+const deleteMatchingRole = () => ({
type: ACTION_TYPE.DELETE_MATCHING_ROLE,
});
@@ -61,7 +61,7 @@ export const saveMatchingRole = (matchingRole) => (dispatch, getState) => {
updateLocalStorage(getState().searchedRoles);
};
-export const clearMatchingRole = (matchingRole) => (dispatch, getState) => {
+export const clearMatchingRole = () => (dispatch, getState) => {
dispatch(deleteMatchingRole());
updateLocalStorage(getState().searchedRoles);
};
diff --git a/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx b/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx
index ece5e080..1618b2b3 100644
--- a/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx
+++ b/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx
@@ -8,11 +8,16 @@
import React, { useState } from "react";
import PT from "prop-types";
import cn from "classnames";
+import { useDispatch } from "react-redux";
+import { deleteSearchedRole } from "../../actions";
import "./styles.module.scss";
+import IconCrossLight from "../../../../assets/images/icon-cross-light.svg";
function AddedRolesAccordion({ addedRoles }) {
const [isOpen, setIsOpen] = useState(false);
+ const dispatch = useDispatch();
+
return addedRoles.length ? (
setIsOpen(!isOpen)} styleName="button">
@@ -27,8 +32,13 @@ function AddedRolesAccordion({ addedRoles }) {
{isOpen && (
- {addedRoles.map(({ name }) => (
-
{name}
+ {addedRoles.map(({ name, searchId: id }) => (
+
+ {name}
+ dispatch(deleteSearchedRole(id))}>
+
+
+
))}
)}
diff --git a/src/routes/CreateNewTeam/components/AddedRolesAccordion/styles.module.scss b/src/routes/CreateNewTeam/components/AddedRolesAccordion/styles.module.scss
index 6478f1f0..67f7c05e 100644
--- a/src/routes/CreateNewTeam/components/AddedRolesAccordion/styles.module.scss
+++ b/src/routes/CreateNewTeam/components/AddedRolesAccordion/styles.module.scss
@@ -55,11 +55,12 @@
.panel {
padding: 12px 18px 14px 10px;
.role-name {
- height: 40px;
+ position: relative;
width: 100%;
background-color: #F4F4F4;
border-radius: 6px;
padding: 10px;
+ padding-right: 30px;
@include font-barlow;
font-size: 16px;
line-height: 20px;
@@ -68,5 +69,19 @@
&:not(:first-child) {
margin-top: 5px;
}
+
+ >button {
+ outline: none;
+ border: none;
+ background: none;
+ position: absolute;
+ top: 12px;
+ right: 4px;
+ &:hover {
+ g {
+ stroke: red;
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/routes/CreateNewTeam/components/BaseCreateModal/index.jsx b/src/routes/CreateNewTeam/components/BaseCreateModal/index.jsx
index d7fcecbb..17528b3e 100644
--- a/src/routes/CreateNewTeam/components/BaseCreateModal/index.jsx
+++ b/src/routes/CreateNewTeam/components/BaseCreateModal/index.jsx
@@ -35,6 +35,7 @@ function BaseCreateModal({
loadingMessage,
maxWidth = "680px",
darkHeader,
+ disableFocusTrap,
children,
}) {
return (
@@ -51,8 +52,9 @@ function BaseCreateModal({
modalContainer: containerStyle,
closeButton: closeButtonStyle,
}}
+ focusTrapped={!disableFocusTrap}
>
-
+
{isLoading ? (
@@ -86,6 +88,7 @@ BaseCreateModal.propTypes = {
loadingMessage: PT.string,
maxWidth: PT.string,
darkHeader: PT.bool,
+ disableFocusTrap: PT.bool,
children: PT.node,
};
diff --git a/src/routes/CreateNewTeam/components/InputContainer/index.jsx b/src/routes/CreateNewTeam/components/InputContainer/index.jsx
index f5d69e62..cebc1551 100644
--- a/src/routes/CreateNewTeam/components/InputContainer/index.jsx
+++ b/src/routes/CreateNewTeam/components/InputContainer/index.jsx
@@ -5,14 +5,10 @@
* input pages. Contains logic and supporting
* components for selecting for roles.
*/
-import React, { useCallback } from "react";
+import React from "react";
import PT from "prop-types";
import AddedRolesAccordion from "../AddedRolesAccordion";
import Completeness from "../Completeness";
-import SearchCard from "../SearchCard";
-import ResultCard from "../ResultCard";
-import NoMatchingProfilesResultCard from "../NoMatchingProfilesResultCard";
-import { isCustomRole } from "utils/helpers";
import "./styles.module.scss";
function InputContainer({
@@ -33,7 +29,7 @@ function InputContainer({
isDisabled={isCompletenessDisabled}
onClick={onClick ? onClick: search}
extraStyleName={completenessStyle}
- buttonLabel={"Search"}
+ buttonLabel="Search"
stages={stages}
percentage="26"
/>
diff --git a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx
index 4daee929..2085ab7c 100644
--- a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx
+++ b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx
@@ -2,9 +2,11 @@
* No Matching Profiles Result Card
* Card that appears when there are no matching profiles after searching.
*/
-import React from "react";
+import React, { useCallback, useMemo } from "react";
import { Link } from "@reach/router";
import PT from "prop-types";
+import { useDispatch, useSelector } from "react-redux";
+import { addSearchedRole } from "../../actions";
import "./styles.module.scss";
import IconEarthX from "../../../../assets/images/icon-earth-x.svg";
import Curve from "../../../../assets/images/curve.svg";
@@ -12,6 +14,30 @@ import Button from "components/Button";
import { formatMoney } from "utils/format";
function NoMatchingProfilesResultCard({ role }) {
+ const { addedRoles } = useSelector((state) => state.searchedRoles);
+
+ const alreadyAdded = useMemo(() => {
+ if (
+ addedRoles.find(
+ (addedRole) => addedRole.searchId === role.roleSearchRequestId
+ )
+ ) {
+ return true;
+ }
+ return false;
+ }, [addedRoles, role]);
+
+ const dispatch = useDispatch();
+
+ const addRole = useCallback(() => {
+ const searchId = role.roleSearchRequestId;
+ let name = "Custom Role";
+ if (role.jobTitle && role.jobTitle.length) {
+ name = role.jobTitle;
+ }
+ dispatch(addSearchedRole({ searchId, name }));
+ }, [dispatch, role]);
+
return (
@@ -21,6 +47,11 @@ function NoMatchingProfilesResultCard({ role }) {
+
+ {role.jobTitle && role.jobTitle.length
+ ? role.jobTitle
+ : "Custom Role"}
+
We will be looking internally for members matching your requirements
and be back at them in about 2 weeks.
@@ -38,11 +69,20 @@ function NoMatchingProfilesResultCard({ role }) {
/Week
)}
-
-
- Modify Search Criteria
+
+
+
+ Modify Search Criteria
+
+
+
+ {alreadyAdded ? "Added" : "Add Custom Role"}
-
+
);
diff --git a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/styles.module.scss b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/styles.module.scss
index 7334e07f..b39ab76b 100644
--- a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/styles.module.scss
+++ b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/styles.module.scss
@@ -13,7 +13,7 @@
justify-content: flex-start;
align-items: center;
padding: 30px 0 60px 0;
- margin-bottom: 30px;
+ margin-bottom: 14px;
color: #fff;
background-image: linear-gradient(225deg, #555555 0%, #2A2A2A 100%);
position: relative;
@@ -41,6 +41,17 @@
flex-direction: column;
align-items: center;
padding-bottom: 61px;
+ .job-title {
+ @include font-barlow;
+ font-size: 22px;
+ margin-bottom: 18px;
+ font-weight: 600;
+ text-align: center;
+ text-transform: uppercase;
+ // position text over bottom of header image
+ position: relative;
+ z-index: 100;
+ }
p.info-txt {
@include font-roboto;
font-size: 14px;
@@ -82,8 +93,15 @@
}
}
- .button {
+ .button-group {
margin-top: 62px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+
+ .left {
+ margin-right: 30px;
+ }
}
}
diff --git a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx
index 339d725a..bfefd418 100644
--- a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx
+++ b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx
@@ -4,7 +4,12 @@ import React, { useCallback, useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { searchRoles } from "services/teams";
import { isCustomRole, setCurrentStage } from "utils/helpers";
-import { clearMatchingRole, saveMatchingRole, addRoleSearchId, addSearchedRole } from "../../actions";
+import {
+ clearMatchingRole,
+ saveMatchingRole,
+ addRoleSearchId,
+ addSearchedRole,
+} from "../../actions";
import InputContainer from "../InputContainer";
import SearchContainer from "../SearchContainer";
import SubmitContainer from "../SubmitContainer";
@@ -14,19 +19,19 @@ function SearchAndSubmit(props) {
const [searchState, setSearchState] = useState(null);
- const { matchingRole } = useSelector(
- (state) => state.searchedRoles
- );
+ const { matchingRole } = useSelector((state) => state.searchedRoles);
- useEffect(()=> {
- const isFromInputPage = searchObject.role || searchObject.skills && searchObject.skills.length
- || searchObject.jobDescription
+ useEffect(() => {
+ const isFromInputPage =
+ searchObject.role ||
+ (searchObject.skills && searchObject.skills.length) ||
+ searchObject.jobDescription;
// refresh in search page directly
if (matchingRole && !isFromInputPage) {
setCurrentStage(2, stages, setStages);
setSearchState("done");
}
- }, [])
+ }, []);
const dispatch = useDispatch();
@@ -52,8 +57,6 @@ function SearchAndSubmit(props) {
} else if (searchId) {
dispatch(addRoleSearchId(searchId));
}
- // setMatchingRole(res.data);
-
dispatch(saveMatchingRole(res.data));
})
.catch((err) => {
@@ -78,8 +81,6 @@ function SearchAndSubmit(props) {
{
+ setAddAnotherOpen(false);
navigate("../result");
}, [navigate]);
+ const addAnother = useCallback(() => {
+ navigate("/taas/createnewteam");
+ }, [navigate]);
+
const renderLeftSide = () => {
if (searchState === "searching") return ;
if (!isCustomRole(matchingRole)) return ;
@@ -53,23 +57,26 @@ function SearchContainer({
searchState === "searching" ||
(searchState === "done" && (!addedRoles || !addedRoles.length))
}
- onClick={searchState ? onSubmit : onClick ? onClick : search}
+ onClick={() => setAddAnotherOpen(true)}
extraStyleName={completenessStyle}
- buttonLabel={searchState ? "Submit Request" : "Search"}
+ buttonLabel="Submit Request"
stages={stages}
percentage={getPercentage()}
/>
+
setAddAnotherOpen(false)}
+ submitDone={true}
+ onContinueClick={onSubmit}
+ addAnother={addAnother}
+ />
);
}
SearchContainer.propTypes = {
stages: PT.array,
- isCompletenessDisabled: PT.bool,
- onClick: PT.func,
- search: PT.func,
- toRender: PT.func,
completenessStyle: 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 f739dcac..b7bfdc40 100644
--- a/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx
+++ b/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx
@@ -26,7 +26,7 @@ import "./styles.module.scss";
import { isCustomRole, setCurrentStage } from "utils/helpers";
import { clearSearchedRoles } from "../../actions";
import { postTeamRequest } from "services/teams";
-import SuccessCard from "../SuccessCard";
+import NoMatchingProfilesResultCard from "../NoMatchingProfilesResultCard";
function SubmitContainer({
stages,
@@ -35,8 +35,8 @@ function SubmitContainer({
matchingRole,
addedRoles,
}) {
- const [addAnotherOpen, setAddAnotherOpen] = useState(true);
- const [teamDetailsOpen, setTeamDetailsOpen] = useState(false);
+ const [addAnotherOpen, setAddAnotherOpen] = useState(false);
+ const [teamDetailsOpen, setTeamDetailsOpen] = useState(true);
const [teamObject, setTeamObject] = useState(null);
const [requestLoading, setRequestLoading] = useState(false);
@@ -99,7 +99,7 @@ function SubmitContainer({
dispatch(clearSearchedRoles());
// Backend api create project has sync issue, so delay 2 seconds
navigate("/taas/myteams");
- }, 2000)
+ }, 2000);
})
.catch((err) => {
setRequestLoading(false);
@@ -112,7 +112,7 @@ function SubmitContainer({
{!isCustomRole(matchingRole) ? (
) : (
-
+
)}
diff --git a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx
index 16215341..b2859592 100644
--- a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx
+++ b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx
@@ -3,7 +3,7 @@
* Popup form to enter details about the
* team request before submitting.
*/
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
import PT from "prop-types";
import { Form, Field, useField } from "react-final-form";
import { useDispatch } from "react-redux";
@@ -18,6 +18,7 @@ import { deleteSearchedRole } from "../../actions";
import IconCrossLight from "../../../../assets/images/icon-cross-light.svg";
import "./styles.module.scss";
import NumberInput from "components/NumberInput";
+import validator from "./utils/validator";
const Error = ({ name }) => {
const {
@@ -28,13 +29,23 @@ const Error = ({ name }) => {
function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) {
const [showDescription, setShowDescription] = useState(false);
- const [startMonthVisible, setStartMonthVisible] = useState(() => {
- const roles = {};
- addedRoles.forEach(({ searchId }) => {
- roles[searchId] = false;
- });
- return roles;
- });
+ const [startMonthVisible, setStartMonthVisible] = useState({});
+
+ // Ensure role is removed from form state when it is removed from redux store
+ let getFormState;
+ let clearFormField;
+ useEffect(() => {
+ const values = getFormState().values;
+ for (let fieldName of Object.keys(values)) {
+ if (fieldName === "teamName" || fieldName === "teamDescription") {
+ continue;
+ }
+ if (addedRoles.findIndex((role) => role.searchId === fieldName) === -1) {
+ clearFormField(fieldName);
+ setStartMonthVisible((state) => ({ ...state, [fieldName]: false }));
+ }
+ }
+ }, [getFormState, addedRoles, clearFormField]);
const dispatch = useDispatch();
@@ -42,66 +53,6 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) {
setShowDescription((prevState) => !prevState);
};
- 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 validateRole = (role) => {
- const roleErrors = {};
- roleErrors.numberOfResources = validateNumber(role.numberOfResources);
- roleErrors.durationWeeks = validateNumber(role.durationWeeks);
- if (role.startMonth) {
- roleErrors.startMonth = validateMonth(role.startMonth);
- }
-
- return roleErrors;
- };
-
- const validator = (values) => {
- const errors = {};
-
- errors.teamName = validateName(values.teamName);
-
- for (const key of Object.keys(values)) {
- if (key === "teamDescription" || key === "teamName") continue;
- errors[key] = validateRole(values[key]);
- }
-
- return errors;
- };
-
- const validateRequired = value => (value ? undefined : 'Please enter a positive integer')
-
return (