diff --git a/package-lock.json b/package-lock.json
index c5832bbb..da18caae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1249,6 +1249,25 @@
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
},
+ "@hypnosphi/create-react-context": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
+ "integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==",
+ "requires": {
+ "gud": "^1.0.0",
+ "warning": "^4.0.3"
+ },
+ "dependencies": {
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ }
+ }
+ },
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -14434,24 +14453,24 @@
}
},
"react-datepicker": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.4.1.tgz",
- "integrity": "sha512-ASyVb7UmVx1vzeITidD7Cr/EXRXhKyjjbSkBndPc1MipYq4rqQ3eMFgvRQzpsXc3JmIMFgICm7nmN6Otc1GE/Q==",
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.8.0.tgz",
+ "integrity": "sha512-iFVNEp8DJoX5yEvEiciM7sJKmLGrvE70U38KhpG13XrulNSijeHw1RZkhd/0UmuXR71dcZB/kdfjiidifstZjw==",
"requires": {
"classnames": "^2.2.6",
"date-fns": "^2.0.1",
"prop-types": "^15.7.2",
- "react-onclickoutside": "^6.9.0",
- "react-popper": "^1.3.4"
+ "react-onclickoutside": "^6.10.0",
+ "react-popper": "^1.3.8"
},
"dependencies": {
"react-popper": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz",
- "integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==",
+ "version": "1.3.11",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
+ "integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==",
"requires": {
"@babel/runtime": "^7.1.2",
- "create-react-context": "^0.3.0",
+ "@hypnosphi/create-react-context": "^0.3.1",
"deep-equal": "^1.1.1",
"popper.js": "^1.14.4",
"prop-types": "^15.6.1",
@@ -14548,9 +14567,9 @@
}
},
"react-onclickoutside": {
- "version": "6.10.0",
- "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.10.0.tgz",
- "integrity": "sha512-7i2L3ef+0ILXpL6P+Hg304eCQswh4jl3ynwR71BSlMU49PE2uk31k8B2GkP6yE9s2D4jTGKnzuSpzWxu4YxfQQ=="
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.11.2.tgz",
+ "integrity": "sha512-640486eSwU/t5iD6yeTlefma8dI3bxPXD93hM9JGKyYITAd0P1JFkkcDeyHZRqNpY/fv1YW0Fad9BXr44OY8wQ=="
},
"react-outside-click-handler": {
"version": "1.3.0",
diff --git a/package.json b/package.json
index 4215d91d..61cd6967 100644
--- a/package.json
+++ b/package.json
@@ -71,7 +71,7 @@
"prop-types": "^15.7.2",
"react": "^16.12.0",
"react-avatar": "^3.9.7",
- "react-datepicker": "^3.4.1",
+ "react-datepicker": "^3.8.0",
"react-dom": "^16.12.0",
"react-final-form": "^6.5.2",
"react-final-form-arrays": "^3.1.3",
diff --git a/src/assets/images/icon-information-circle.svg b/src/assets/images/icon-information-circle.svg
new file mode 100644
index 00000000..2047ab02
--- /dev/null
+++ b/src/assets/images/icon-information-circle.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/src/assets/images/icon-thick-calendar.svg b/src/assets/images/icon-thick-calendar.svg
new file mode 100644
index 00000000..222202ca
--- /dev/null
+++ b/src/assets/images/icon-thick-calendar.svg
@@ -0,0 +1,23 @@
+
+
+ 89C4F8D6-2320-499E-BC02-40DE84B9F35C
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/FormField/index.jsx b/src/components/FormField/index.jsx
index f43d48fc..c7de9520 100644
--- a/src/components/FormField/index.jsx
+++ b/src/components/FormField/index.jsx
@@ -82,6 +82,7 @@ const FormField = ({ field }) => {
onBlur={input.onBlur}
onFocus={input.onFocus}
className={meta.error && meta.touched ? "error" : ""}
+ maxLength={field.maxLength}
/>
)}
{field.type === FORM_FIELD_TYPE.DATE && (
diff --git a/src/components/InformationTooltip/index.jsx b/src/components/InformationTooltip/index.jsx
new file mode 100644
index 00000000..996168a2
--- /dev/null
+++ b/src/components/InformationTooltip/index.jsx
@@ -0,0 +1,87 @@
+/**
+ * Information Tooltip
+ * Icon which reveals a tooltip on mouse hover
+ */
+import React, { useCallback, useState } from "react";
+import PT from "prop-types";
+import { usePopper } from "react-popper";
+import IconInformationCircle from "../../assets/images/icon-information-circle.svg";
+import "./styles.module.scss";
+
+function InformationTooltip({ text, iconSize = "16px" }) {
+ const [isTooltipShown, setIsTooltipShown] = useState(false);
+ const [referenceElement, setReferenceElement] = useState(null);
+ const [popperElement, setPopperElement] = useState(null);
+ const [arrowElement, setArrowElement] = useState(null);
+ const { styles, attributes } = usePopper(referenceElement, popperElement, {
+ placement: "top",
+ modifiers: [
+ {
+ name: "flip",
+ options: {
+ fallbackPlacements: ["top"],
+ },
+ },
+ {
+ name: "offset",
+ options: {
+ // use offset to move the tooltip slightly up
+ offset: [0, 12],
+ },
+ },
+ {
+ name: "arrow",
+ // padding should be equal to border-radius of the tooltip
+ options: { element: arrowElement, padding: 5 },
+ },
+ ],
+ });
+
+ const showTooltip = useCallback(() => {
+ setIsTooltipShown(true);
+ }, []);
+
+ const hideTooltip = useCallback(() => {
+ setIsTooltipShown(false);
+ }, []);
+
+ const iconStyle = {
+ width: iconSize,
+ height: iconSize,
+ };
+
+ return (
+
+
+
+
+ {isTooltipShown && (
+
+ )}
+
+ );
+}
+
+InformationTooltip.propTypes = {
+ iconSize: PT.string,
+ text: PT.string,
+};
+
+export default InformationTooltip;
diff --git a/src/components/InformationTooltip/styles.module.scss b/src/components/InformationTooltip/styles.module.scss
new file mode 100644
index 00000000..bf1084c1
--- /dev/null
+++ b/src/components/InformationTooltip/styles.module.scss
@@ -0,0 +1,34 @@
+@import "styles/include";
+
+.circle {
+ * {
+ fill: #aaa;
+ }
+}
+
+.tooltip {
+ border-radius: 5px;
+ box-shadow: 0px 5px 25px #c6c6c6;
+ width: 165px;
+ z-index: 1000;
+}
+
+.tooltip-arrow {
+ bottom: -9px;
+ border-style: solid;
+ border-width: 10px 8px 0 8px;
+ border-color: #2a2a2a transparent transparent transparent;
+ height: 0;
+ width: 0;
+}
+
+.tooltip-content {
+ border-radius: 5px;
+ @include font-roboto;
+ font-size: 12px;
+ line-height: 16px;
+ font-weight: 400;
+ color: #fff;
+ background-color: #2a2a2a;
+ padding: 10px;
+}
\ No newline at end of file
diff --git a/src/components/MarkdownEditor/index.jsx b/src/components/MarkdownEditor/index.jsx
index 6c38b851..83939222 100644
--- a/src/components/MarkdownEditor/index.jsx
+++ b/src/components/MarkdownEditor/index.jsx
@@ -53,6 +53,9 @@ const MarkdownEditor = (props) => {
]}
plugins={[]}
/>
+ {props.errorMessage && (
+ {props.errorMessage}
+ )}
);
};
diff --git a/src/components/MarkdownEditor/styles.module.scss b/src/components/MarkdownEditor/styles.module.scss
index d444b248..13c0aa00 100644
--- a/src/components/MarkdownEditor/styles.module.scss
+++ b/src/components/MarkdownEditor/styles.module.scss
@@ -12,21 +12,6 @@
overflow-y: auto;
background: #fafafb;
}
- .message {
- @include font-roboto;
-
- width: 100%;
- text-align: center;
- min-height: 40px;
- line-height: 20px;
- padding: 9px 10px;
- margin: 10px 0 5px;
- font-size: 14px;
- color: #ff5b52;
- border: 1px solid #ffd5d1;
- cursor: auto;
- outline: none;
- }
}
.editor-container {
:global {
@@ -72,3 +57,19 @@
}
}
}
+
+.message {
+ @include font-roboto;
+
+ width: 100%;
+ text-align: center;
+ min-height: 40px;
+ line-height: 20px;
+ padding: 9px 10px;
+ margin: 10px 0 5px;
+ font-size: 14px;
+ color: #ff5b52;
+ border: 1px solid #ffd5d1;
+ cursor: auto;
+ outline: none;
+}
\ No newline at end of file
diff --git a/src/components/MonthPicker/index.jsx b/src/components/MonthPicker/index.jsx
new file mode 100644
index 00000000..9b1f1b85
--- /dev/null
+++ b/src/components/MonthPicker/index.jsx
@@ -0,0 +1,45 @@
+/**
+ * Month Picker
+ * An styled input component for selecting date by month.
+ * Compatible with react-final-form
+ */
+import React from "react";
+import PT from "prop-types";
+import DatePicker from "react-datepicker";
+import "./styles.module.scss";
+
+function getCurrMonthYear() {
+ const now = new Date();
+ const year = now.getFullYear();
+ const month = now.getMonth();
+ return new Date(`${year}-${month + 1}`);
+}
+
+function MonthPicker({ name, value, onChange, onBlur, onFocus }) {
+ return (
+
+
+
+ );
+}
+
+MonthPicker.propTypes = {
+ name: PT.string,
+ value: PT.any,
+ onChange: PT.func,
+ onBlur: PT.func,
+};
+
+export default MonthPicker;
diff --git a/src/components/MonthPicker/styles.module.scss b/src/components/MonthPicker/styles.module.scss
new file mode 100644
index 00000000..ba93e757
--- /dev/null
+++ b/src/components/MonthPicker/styles.module.scss
@@ -0,0 +1,60 @@
+@import "styles/include";
+
+.month-picker {
+ input {
+ width: 118px;
+ background-image: url("../../assets/images/icon-thick-calendar.svg");
+ background-repeat: no-repeat;
+ background-position: 95px 7px;
+ }
+
+ :global {
+ .react-datepicker__header {
+ @include font-barlow;
+ font-size: 20px;
+ color: #2a2a2a;
+ font-weight: 600;
+ padding-top: 14px;
+ padding-bottom: 14px;
+ background-color: transparent;
+ border-bottom: 1px solid #d4d4d4;
+ margin: 0 16px;
+ }
+
+ .react-datepicker__month-text {
+ @include font-roboto;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 40px;
+ width: 40px;
+ height: 40px;
+ margin: 8px;
+ border-radius: 100%;
+ }
+ .react-datepicker__month--selected {
+ border-radius: 100%;
+ background-color: #0AB88A;
+ }
+
+ .react-datepicker__navigation {
+ top: 18px;
+ border: 2px solid transparent;
+ height: 12px;
+ width: 12px;
+ }
+ .react-datepicker__navigation--next, .react-datepicker__navigation--previous {
+ border-right-color: transparent;
+ border-bottom-color: transparent;
+ border-left-color: #137D60;
+ border-top-color: #137D60;
+ }
+ .react-datepicker__navigation--next {
+ right: 24px;
+ transform: rotate(135deg);
+ }
+ .react-datepicker__navigation--previous {
+ left: 24px;
+ transform: rotate(-45deg);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/NumberInput/index.jsx b/src/components/NumberInput/index.jsx
new file mode 100644
index 00000000..3456308f
--- /dev/null
+++ b/src/components/NumberInput/index.jsx
@@ -0,0 +1,56 @@
+/**
+ * Number Input
+ * A custom number input to be used in forms
+ * Removed default buttons and adds custom buttons
+ */
+import React from "react";
+import PT from "prop-types";
+import cn from "classnames";
+import "./styles.module.scss";
+
+function NumberInput({ name, value, onChange, onBlur, onFocus, min, error }) {
+ const incrementVal = (step) => {
+ const newVal = +value + step;
+ if (typeof newVal === "number" && !isNaN(newVal)) {
+ onChange(newVal);
+ }
+ };
+ const decrementVal = (step) => {
+ const newVal = value - step;
+ if (newVal >= min) {
+ onChange(value - step);
+ }
+ };
+
+ return (
+
+ decrementVal(1)}>
+ –
+
+
+ incrementVal(1)}>
+ +
+
+
+ );
+}
+
+NumberInput.propTypes = {
+ name: PT.string,
+ value: PT.string,
+ onChange: PT.func,
+ onFocus: PT.func,
+ onBlur: PT.func,
+ min: PT.string,
+ error: PT.bool,
+};
+
+export default NumberInput;
diff --git a/src/components/NumberInput/styles.module.scss b/src/components/NumberInput/styles.module.scss
new file mode 100644
index 00000000..e5941c56
--- /dev/null
+++ b/src/components/NumberInput/styles.module.scss
@@ -0,0 +1,43 @@
+@import "styles/include";
+
+// remove default buttons and style input as text input
+.input {
+ appearance: textfield;
+ text-align: center;
+
+ &.error {
+ border-color: #fe665d;
+ }
+}
+.input::-webkit-inner-spin-button,
+.input::-webkit-outer-spin-button {
+ appearance: none;
+ margin: 0;
+}
+
+.container {
+ position: relative;
+ width: fit-content;
+}
+
+.left-button,
+.right-button {
+ @include font-roboto;
+ font-size: 22px;
+ color: #137D60;
+ outline: none;
+ border: none;
+ background: none;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ padding: 0 11px;
+}
+
+.left-button {
+ left: 0;
+}
+
+.right-button {
+ right: 0;
+}
\ No newline at end of file
diff --git a/src/components/TextArea/index.jsx b/src/components/TextArea/index.jsx
index 8ec9e7e5..fc247056 100644
--- a/src/components/TextArea/index.jsx
+++ b/src/components/TextArea/index.jsx
@@ -18,6 +18,7 @@ function TextArea(props) {
autoFocus={props.autoFocus}
onBlur={props.onBlur}
onFocus={props.onFocus}
+ maxLength={props.maxLength}
/>
);
}
diff --git a/src/constants/index.js b/src/constants/index.js
index 5bc6228f..46a60d98 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -257,7 +257,7 @@ export const ACTION_TYPE = {
CLEAR_SEARCHED_ROLES: "CLEAR_SEARCHED_ROLES",
ADD_SEARCHED_ROLE: "ADD_SEARCHED_ROLE",
ADD_ROLE_SEARCH_ID: "ADD_ROLE_SEARCH_ID",
- REPLACE_SEARCHED_ROLES: "REPLACE_SEARCHED_ROLES",
+ DELETE_SEARCHED_ROLE: "DELETE_SEARCHED_ROLE",
};
/**
@@ -357,6 +357,6 @@ export const INTERVIEW_POPUP_MEDIA_URL =
export const MAX_ALLOWED_INTERVIEWS = 3;
/**
- * Matching rate to show in CreateNewTeam ResultCard
+ * Custom role names to remove from RoleList component
*/
-export const MATCHING_RATE = "80";
+export const CUSTOM_ROLE_NAMES = ["custom", "niche"];
diff --git a/src/root.component.jsx b/src/root.component.jsx
index f1e5c3b7..bdd3b79b 100644
--- a/src/root.component.jsx
+++ b/src/root.component.jsx
@@ -25,7 +25,7 @@ export default function Root() {
-
+
@@ -34,9 +34,9 @@ export default function Root() {
-
-
-
+
+
+
{/* Global config for Toastr popups */}
diff --git a/src/routes/CreateNewTeam/actions/index.js b/src/routes/CreateNewTeam/actions/index.js
index bb78565f..a67e6569 100644
--- a/src/routes/CreateNewTeam/actions/index.js
+++ b/src/routes/CreateNewTeam/actions/index.js
@@ -22,9 +22,9 @@ const addPreviousSearchId = (id) => ({
payload: id,
});
-export const replaceSearchedRoles = (roles) => ({
- type: ACTION_TYPE.REPLACE_SEARCHED_ROLES,
- payload: { roles, lastRoleId: roles[roles.length - 1].searchId },
+const deleteRole = (id) => ({
+ type: ACTION_TYPE.DELETE_SEARCHED_ROLE,
+ payload: id,
});
export const clearSearchedRoles = () => (dispatch, getState) => {
@@ -41,3 +41,8 @@ export const addRoleSearchId = (id) => (dispatch, getState) => {
dispatch(addPreviousSearchId(id));
updateLocalStorage(getState().searchedRoles);
};
+
+export const deleteSearchedRole = (id) => (dispatch, getState) => {
+ dispatch(deleteRole(id));
+ updateLocalStorage(getState().searchedRoles);
+};
diff --git a/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx b/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx
index 2c1d9a6e..ece5e080 100644
--- a/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx
+++ b/src/routes/CreateNewTeam/components/AddedRolesAccordion/index.jsx
@@ -19,7 +19,8 @@ function AddedRolesAccordion({ addedRoles }) {
{addedRoles.length}{" "}
- {addedRoles.length > 1 ? "roles have" : "role has"} been added.
+ {addedRoles.length > 1 ? "positions have" : "position has"} been
+ added.
diff --git a/src/routes/CreateNewTeam/components/CircularProgressBar/index.jsx b/src/routes/CreateNewTeam/components/CircularProgressBar/index.jsx
index 7a19f03e..06ea736c 100644
--- a/src/routes/CreateNewTeam/components/CircularProgressBar/index.jsx
+++ b/src/routes/CreateNewTeam/components/CircularProgressBar/index.jsx
@@ -14,7 +14,7 @@ const CircularProgressBar = ({ size, progress, children, strokeWidth }) => {
const radius = size / 2 - strokeWidth / 2;
const circumference = 2 * Math.PI * radius;
useEffect(() => {
- const progressOffset = ((100 - progress) / 100) * circumference;
+ const progressOffset = (1 - progress) * circumference;
setOffset(progressOffset);
circleRef.current.style = "transition: stroke-dashoffset 850ms ease-in-out";
}, [setOffset, progress, circumference, offset]);
diff --git a/src/routes/CreateNewTeam/components/ItemList/index.jsx b/src/routes/CreateNewTeam/components/ItemList/index.jsx
index 12033b13..56b6496a 100644
--- a/src/routes/CreateNewTeam/components/ItemList/index.jsx
+++ b/src/routes/CreateNewTeam/components/ItemList/index.jsx
@@ -37,7 +37,7 @@ function ItemList({
header {
padding: 16px 24px;
@@ -33,9 +33,9 @@ input:not([type="checkbox"]).filter-input {
color: #2a2a2a;
font-size: 14px;
height: 40px;
- line-height: 38px;
+ line-height: normal;
outline: none;
- padding: 0 15px;
+ padding: 8px 15px;
&:not(:focus) {
background-image: url("../../../../assets/images/icon-search.svg");
diff --git a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx
index 2b1fd779..4daee929 100644
--- a/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx
+++ b/src/routes/CreateNewTeam/components/NoMatchingProfilesResultCard/index.jsx
@@ -4,12 +4,14 @@
*/
import React from "react";
import { Link } from "@reach/router";
+import PT from "prop-types";
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";
+import { formatMoney } from "utils/format";
-function NoMatchingProfilesResultCard() {
+function NoMatchingProfilesResultCard({ role }) {
return (
@@ -23,12 +25,20 @@ function NoMatchingProfilesResultCard() {
We will be looking internally for members matching your requirements
and be back at them in about 2 weeks.
-
-
Niche Rate
-
$1,200
-
/Week
-
-
+ {role.rates && role.name ? (
+
+
{role.name} Rate
+
{formatMoney(role.rates[0].global)}
+
/Week
+
+ ) : (
+
+
Custom Rate
+
$1,200
+
/Week
+
+ )}
+
Modify Search Criteria
@@ -38,4 +48,8 @@ function NoMatchingProfilesResultCard() {
);
}
+NoMatchingProfilesResultCard.propTypes = {
+ role: PT.object,
+};
+
export default NoMatchingProfilesResultCard;
diff --git a/src/routes/CreateNewTeam/components/ResultCard/index.jsx b/src/routes/CreateNewTeam/components/ResultCard/index.jsx
index c6920c22..07f21c13 100644
--- a/src/routes/CreateNewTeam/components/ResultCard/index.jsx
+++ b/src/routes/CreateNewTeam/components/ResultCard/index.jsx
@@ -15,7 +15,6 @@ import IconTeamMeetingChat from "../../../../assets/images/icon-team-meeting-cha
import Curve from "../../../../assets/images/curve.svg";
import CircularProgressBar from "../CircularProgressBar";
import Button from "components/Button";
-import { MATCHING_RATE } from "constants";
import { formatMoney } from "utils/format";
function formatRate(value) {
@@ -23,18 +22,27 @@ function formatRate(value) {
return formatMoney(value);
}
+function formatPercent(value) {
+ return `${Math.round(value * 100)}%`;
+}
+
function ResultCard({ role }) {
const {
numberOfMembersAvailable,
isExternalMember,
+ skillsMatch,
rates: [rates],
+ jobTitle,
+ name,
+ timeToCandidate,
+ timeToInterview,
} = role;
const [userHandle, setUserHandle] = useState(null);
const [showRates, setShowRates] = useState(false);
useEffect(() => {
getAuthUserProfile().then((res) => {
- setUserHandle(res.handle || null);
+ setUserHandle(res?.handle || null);
});
}, []);
@@ -44,12 +52,15 @@ function ResultCard({ role }) {
We have matching profiles
- We have qualified candidates who match {MATCHING_RATE}% or more of
- your job requirements.
+ We have qualified candidates who match {formatPercent(skillsMatch)}
+ {skillsMatch < 1 ? " or more " : " "} of your job requirements.
+
+ {jobTitle && jobTitle.length ? jobTitle : name}
+
Qualified candidates within
-
24h
+
{timeToCandidate}h
Interviews can start within
-
48h
+
{timeToInterview}h
@@ -217,12 +228,12 @@ function ResultCard({ role }) {
- {MATCHING_RATE}%
- Matching rate
+ {formatPercent(skillsMatch)}
+ Skills Match
}
/>
diff --git a/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss b/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss
index 95ec69ae..6babd0a1 100644
--- a/src/routes/CreateNewTeam/components/ResultCard/styles.module.scss
+++ b/src/routes/CreateNewTeam/components/ResultCard/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, #0ab88a 0%, #137d60 100%);
position: relative;
@@ -40,6 +40,18 @@
}
}
+.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;
+}
+
.button-group {
display: flex;
flex-direction: row;
diff --git a/src/routes/CreateNewTeam/components/RoleDetailsModal/index.jsx b/src/routes/CreateNewTeam/components/RoleDetailsModal/index.jsx
index a10f2de7..153a8a96 100644
--- a/src/routes/CreateNewTeam/components/RoleDetailsModal/index.jsx
+++ b/src/routes/CreateNewTeam/components/RoleDetailsModal/index.jsx
@@ -48,7 +48,7 @@ function RoleDetailsModal({ roleId, open, onClose }) {
[role, imgError]
);
- const skills = role ? role.listOfSkills : [];
+ const skills = role && role.listOfSkills ? role.listOfSkills : [];
const hideSkills = () => {
onClose();
@@ -90,19 +90,21 @@ function RoleDetailsModal({ roleId, open, onClose }) {
Skills
- {showSkills ? (
-
- {skills.map((skill, i) => (
-
- {skill}
-
- ))}
-
- ) : (
-
-
-
- )}
+
+ {showSkills ? (
+
+ {skills.map((skill, i) => (
+
+ {skill}
+
+ ))}
+
+ ) : (
+
+
+
+ )}
+
);
diff --git a/src/routes/CreateNewTeam/components/RoleDetailsModal/styles.module.scss b/src/routes/CreateNewTeam/components/RoleDetailsModal/styles.module.scss
index e7470ce5..8aa70eac 100644
--- a/src/routes/CreateNewTeam/components/RoleDetailsModal/styles.module.scss
+++ b/src/routes/CreateNewTeam/components/RoleDetailsModal/styles.module.scss
@@ -14,12 +14,23 @@
}
.markdown-container {
- // not adds specificity to override style
- p:not(table) {
- @include font-roboto;
- color: #2a2a2a;
- font-size: 16px;
- line-height: 26px;
+ :global {
+ // resets styles in markdown-viewer
+ .tui-editor-contents {
+ @include font-roboto;
+ color: #2a2a2a;
+ font-size: 16px;
+ line-height: 26px;
+ ul {
+ list-style: initial;
+ >li {
+ margin-bottom: 10px;
+ &::before {
+ background: none;
+ }
+ }
+ }
+ }
}
}
@@ -47,3 +58,8 @@
margin-bottom: 10px;
font-size: 12px;
}
+
+.content {
+ height: 180px;
+ overflow-y: auto;
+}
diff --git a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx
index b801e2d1..778fd984 100644
--- a/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx
+++ b/src/routes/CreateNewTeam/components/SearchAndSubmit/index.jsx
@@ -1,23 +1,71 @@
import { Router } from "@reach/router";
-import React from "react";
-import { useSelector } from "react-redux";
+import _ from "lodash";
+import React, { useCallback, useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { searchRoles } from "services/teams";
+import { isCustomRole, setCurrentStage } from "utils/helpers";
+import { addRoleSearchId, addSearchedRole } from "../../actions";
import SearchContainer from "../SearchContainer";
import SubmitContainer from "../SubmitContainer";
function SearchAndSubmit(props) {
+ const { stages, setStages, searchObject, onClick } = props;
+
+ const [searchState, setSearchState] = useState(null);
+ const [matchingRole, setMatchingRole] = useState(null);
+
const { addedRoles, previousSearchId } = useSelector(
(state) => state.searchedRoles
);
+ const dispatch = useDispatch();
+
+ const search = useCallback(() => {
+ setCurrentStage(1, stages, setStages);
+ setSearchState("searching");
+ setMatchingRole(null);
+ const searchObjectCopy = { ...searchObject };
+ if (previousSearchId) {
+ searchObjectCopy.previousRoleSearchRequestId = previousSearchId;
+ }
+ searchRoles(searchObjectCopy)
+ .then((res) => {
+ const name = _.get(res, "data.name");
+ const searchId = _.get(res, "data.roleSearchRequestId");
+ if (name && !isCustomRole({ name })) {
+ dispatch(addSearchedRole({ searchId, name }));
+ } else if (searchId) {
+ dispatch(addRoleSearchId(searchId));
+ }
+ setMatchingRole(res.data);
+ })
+ .catch((err) => {
+ console.error(err);
+ })
+ .finally(() => {
+ setCurrentStage(2, stages, setStages);
+ setSearchState("done");
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [dispatch, previousSearchId, searchObject]);
+
return (
+
-
);
}
diff --git a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx
index 8e91bc3b..a79c5bbc 100644
--- a/src/routes/CreateNewTeam/components/SearchContainer/index.jsx
+++ b/src/routes/CreateNewTeam/components/SearchContainer/index.jsx
@@ -5,73 +5,37 @@
* search pages. Contains logic and supporting
* components for searching for roles.
*/
-import React, { useCallback, useState } from "react";
+import React, { useCallback } from "react";
import PT from "prop-types";
-import _ from "lodash";
-import { useDispatch } from "react-redux";
import AddedRolesAccordion from "../AddedRolesAccordion";
import Completeness from "../Completeness";
import SearchCard from "../SearchCard";
import ResultCard from "../ResultCard";
import NoMatchingProfilesResultCard from "../NoMatchingProfilesResultCard";
-import { searchRoles } from "services/teams";
-import { setCurrentStage } from "utils/helpers";
-import { addRoleSearchId, addSearchedRole } from "../../actions";
+import { isCustomRole } from "utils/helpers";
import "./styles.module.scss";
function SearchContainer({
stages,
- setStages,
isCompletenessDisabled,
toRender,
- searchObject,
+ onClick,
+ search,
completenessStyle,
navigate,
addedRoles,
- previousSearchId,
+ searchState,
+ matchingRole,
}) {
- const [searchState, setSearchState] = useState(null);
- const [matchingRole, setMatchingRole] = useState(null);
-
- const dispatch = useDispatch();
-
const onSubmit = useCallback(() => {
- navigate("result", { state: { matchingRole } });
- }, [navigate, matchingRole]);
-
- const search = () => {
- setCurrentStage(1, stages, setStages);
- setSearchState("searching");
- setMatchingRole(null);
- const searchObjectCopy = { ...searchObject };
- if (previousSearchId) {
- searchObjectCopy.previousRoleSearchRequestId = previousSearchId;
- }
- searchRoles(searchObjectCopy)
- .then((res) => {
- const name = _.get(res, "data.name");
- const searchId = _.get(res, "data.roleSearchRequestId");
- if (name && !name.toLowerCase().includes("niche")) {
- setMatchingRole(res.data);
- dispatch(addSearchedRole({ searchId, name }));
- } else if (searchId) {
- dispatch(addRoleSearchId(searchId));
- }
- })
- .catch((err) => {
- console.error(err);
- })
- .finally(() => {
- setCurrentStage(2, stages, setStages);
- setSearchState("done");
- });
- };
+ navigate("result");
+ }, [navigate]);
const renderLeftSide = () => {
- if (!searchState) return toRender;
+ if (!searchState) return toRender(search);
if (searchState === "searching") return ;
- if (matchingRole) return ;
- return ;
+ if (!isCustomRole(matchingRole)) return ;
+ return ;
};
const getPercentage = useCallback(() => {
@@ -90,9 +54,9 @@ function SearchContainer({
isDisabled={
isCompletenessDisabled ||
searchState === "searching" ||
- (searchState === "done" && !matchingRole)
+ (searchState === "done" && (!addedRoles || !addedRoles.length))
}
- onClick={searchState ? onSubmit : search}
+ onClick={searchState ? onSubmit : onClick ? onClick : search}
extraStyleName={completenessStyle}
buttonLabel={searchState ? "Submit Request" : "Search"}
stages={stages}
@@ -105,14 +69,15 @@ function SearchContainer({
SearchContainer.propTypes = {
stages: PT.array,
- setStages: PT.func,
isCompletenessDisabled: PT.bool,
- searchObject: PT.object,
- toRender: PT.node,
+ onClick: PT.func,
+ search: PT.func,
+ toRender: PT.func,
completenessStyle: PT.string,
navigate: PT.func,
addedRoles: PT.array,
- previousSearchId: PT.string,
+ searchState: PT.string,
+ matchingRole: PT.object,
};
export default SearchContainer;
diff --git a/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx b/src/routes/CreateNewTeam/components/SubmitContainer/index.jsx
index 55c493bb..aacd05e4 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 { setCurrentStage } from "utils/helpers";
+import { isCustomRole, setCurrentStage } from "utils/helpers";
import { clearSearchedRoles } from "../../actions";
import { postTeamRequest } from "services/teams";
import SuccessCard from "../SuccessCard";
@@ -32,11 +32,9 @@ function SubmitContainer({
stages,
setStages,
completenessStyle,
- location,
+ matchingRole,
addedRoles,
}) {
- const matchingRole = location?.state?.matchingRole;
-
const [addAnotherOpen, setAddAnotherOpen] = useState(true);
const [teamDetailsOpen, setTeamDetailsOpen] = useState(false);
const [teamObject, setTeamObject] = useState(null);
@@ -46,20 +44,16 @@ function SubmitContainer({
useEffect(() => {
setCurrentStage(2, stages, setStages);
- if (!addedRoles || addedRoles.length === 0) {
- navigate("/taas/myteams/createnewteam");
- }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// redirects user if they enter the page URL directly
- // without adding any roles.
+ // without adding any roles or delete all roles.
useLayoutEffect(() => {
if (!addedRoles || addedRoles.length === 0) {
- navigate("/taas/myteams/createnewteam");
+ navigate("/taas/createnewteam");
}
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ }, [addedRoles]);
const openTeamDetails = () => {
setAddAnotherOpen(false);
@@ -67,7 +61,7 @@ function SubmitContainer({
};
const addAnother = () => {
- navigate("/taas/myteams/createnewteam");
+ navigate("/taas/createnewteam");
};
const assembleTeam = (formData) => {
@@ -78,13 +72,14 @@ function SubmitContainer({
if (key === "teamName" || key === "teamDescription") {
continue;
}
- const position = _.pick(
- formData[key],
- "numberOfResources",
- "durationWeeks",
- "startMonth"
+ const position = _.mapValues(formData[key], (val, key) =>
+ key === "startMonth" ? val : parseInt(val, 10)
);
+ if (position.startMonth === null) {
+ delete position.startMonth;
+ }
+
position.roleSearchRequestId = key;
position.roleName = addedRoles.find((role) => role.searchId === key).name;
@@ -99,10 +94,9 @@ function SubmitContainer({
const requestTeam = useCallback(() => {
setRequestLoading(true);
postTeamRequest(teamObject)
- .then((res) => {
- const projectId = _.get(res, ["data", "projectId"]);
+ .then(() => {
dispatch(clearSearchedRoles());
- navigate(`/taas/myteams/${projectId}`);
+ navigate("/taas/myteams");
})
.catch((err) => {
setRequestLoading(false);
@@ -112,7 +106,11 @@ function SubmitContainer({
return (
- {matchingRole ?
:
}
+ {!isCustomRole(matchingRole) ? (
+
+ ) : (
+
+ )}
We have matching profiles
-
- We have qualified candidates who match {MATCHING_RATE}% or more of
- your job requirements.
-
+ We have qualified candidates who match your job requirements.
@@ -30,7 +26,7 @@ function SuccessCard() {
Please use the button to the right to submit your request, or the
button below to search for additional roles.
-
+
Continue Search
diff --git a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx
index 716d375d..11802f60 100644
--- a/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx
+++ b/src/routes/CreateNewTeam/components/TeamDetailsModal/index.jsx
@@ -6,18 +6,24 @@
import React, { useState } from "react";
import PT from "prop-types";
import { Form, Field, useField } from "react-final-form";
+import { useDispatch } from "react-redux";
import FormField from "components/FormField";
import BaseCreateModal from "../BaseCreateModal";
import { FORM_FIELD_TYPE } from "constants/";
import { formatPlural } from "utils/format";
import Button from "components/Button";
+import MonthPicker from "components/MonthPicker";
+import InformationTooltip from "components/InformationTooltip";
+import { deleteSearchedRole } from "../../actions";
+import IconCrossLight from "../../../../assets/images/icon-cross-light.svg";
import "./styles.module.scss";
+import NumberInput from "components/NumberInput";
const Error = ({ name }) => {
const {
- meta: { touched, error },
- } = useField(name, { subscription: { touched: true, error: true } });
- return touched && error ?
{error} : null;
+ meta: { dirty, error },
+ } = useField(name, { subscription: { dirty: true, error: true } });
+ return dirty && error ?
{error} : null;
};
function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) {
@@ -30,6 +36,8 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) {
return roles;
});
+ const dispatch = useDispatch();
+
const toggleDescription = () => {
setShowDescription((prevState) => !prevState);
};
@@ -95,10 +103,21 @@ function TeamDetailsModal({ open, onClose, submitForm, addedRoles }) {
return (