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

Commit 975a7fc

Browse files
authored
Merge pull request #377 from yoution/issue-356
Issue #356 #373 #364
2 parents c7b3739 + 2090bc8 commit 975a7fc

File tree

12 files changed

+438
-22
lines changed

12 files changed

+438
-22
lines changed

src/constants/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ export const ACTION_TYPE = {
264264
*/
265265
ADD_MATCHING_ROLE: "ADD_MATCHING_ROLE",
266266
DELETE_MATCHING_ROLE: "DELETE_MATCHING_ROLE",
267+
EDIT_MATCHING_ROLE: "EDIT_MATCHING_ROLE",
267268
};
268269

269270
/**

src/routes/CreateNewTeam/actions/index.js

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ const deleteMatchingRole = () => ({
3636
type: ACTION_TYPE.DELETE_MATCHING_ROLE,
3737
});
3838

39+
const editMatchingRole = (role) => ({
40+
type: ACTION_TYPE.EDIT_MATCHING_ROLE,
41+
payload: role,
42+
});
43+
3944
export const clearSearchedRoles = () => (dispatch, getState) => {
4045
dispatch(clearRoles());
4146
updateLocalStorage(getState().searchedRoles);
@@ -51,6 +56,11 @@ export const addRoleSearchId = (id) => (dispatch, getState) => {
5156
updateLocalStorage(getState().searchedRoles);
5257
};
5358

59+
export const editRoleAction = (role) => (dispatch, getState) => {
60+
dispatch(editMatchingRole(role));
61+
updateLocalStorage(getState().searchedRoles);
62+
};
63+
5464
export const deleteSearchedRole = (id) => (dispatch, getState) => {
5565
dispatch(deleteRole(id));
5666
updateLocalStorage(getState().searchedRoles);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* Edit Role Modal
3+
* Popup form to enter details about current role
4+
*/
5+
import React, { useEffect, useState } from "react";
6+
import PT from "prop-types";
7+
import { Form, Field, useField } from "react-final-form";
8+
import FormField from "components/FormField";
9+
import BaseCreateModal from "../BaseCreateModal";
10+
import Button from "components/Button";
11+
import MonthPicker from "components/MonthPicker";
12+
import InformationTooltip from "components/InformationTooltip";
13+
import IconCrossLight from "../../../../assets/images/icon-cross-light.svg";
14+
import "./styles.module.scss";
15+
import NumberInput from "components/NumberInput";
16+
import { validator, validateExists, validateMin, composeValidators } from "./utils/validator";
17+
18+
const Error = ({ name }) => {
19+
const {
20+
meta: { dirty, error },
21+
} = useField(name, { subscription: { dirty: true, error: true } });
22+
return dirty && error ? <span styleName="error">{error}</span> : null;
23+
};
24+
25+
function EditRoleModal({ open, onClose, submitForm, role }) {
26+
const [startMonthVisible, setStartMonthVisible] = useState(false);
27+
28+
return (
29+
<Form
30+
onSubmit={submitForm}
31+
mutators={{
32+
clearField: ([fieldName], state, { changeValue }) => {
33+
changeValue(state, fieldName, () => undefined);
34+
},
35+
}}
36+
validate={validator}
37+
>
38+
{({
39+
handleSubmit,
40+
hasValidationErrors,
41+
form: {
42+
mutators: { clearField },
43+
getState,
44+
},
45+
}) => {
46+
return (
47+
<BaseCreateModal
48+
open={open}
49+
onClose={onClose}
50+
title="Edit Role"
51+
subtitle="Please provide your team details before submitting a request."
52+
buttons={
53+
<Button
54+
type="primary"
55+
size="medium"
56+
onClick={handleSubmit}
57+
disabled={hasValidationErrors}
58+
>
59+
Submit
60+
</Button>
61+
}
62+
disableFocusTrap
63+
>
64+
<div styleName="modal-body">
65+
<table styleName="table">
66+
<tr>
67+
<th># of resources</th>
68+
<th>Duration (weeks)</th>
69+
<th>Start month</th>
70+
</tr>
71+
<tr styleName="role-row">
72+
<td>
73+
<Field
74+
validate={composeValidators(validateExists, validateMin(1))}
75+
name="numberOfResources"
76+
initialValue={role.numberOfResources}
77+
>
78+
{({ input, meta }) => (
79+
<NumberInput
80+
name={input.name}
81+
value={input.value}
82+
onChange={input.onChange}
83+
onBlur={input.onBlur}
84+
onFocus={input.onFocus}
85+
min="1"
86+
error={meta.touched && meta.error}
87+
/>
88+
)}
89+
</Field>
90+
<Error name="numberOfResources" />
91+
</td>
92+
<td>
93+
<Field
94+
validate={composeValidators(validateExists, validateMin(4))}
95+
name="durationWeeks"
96+
initialValue={role.durationWeeks}
97+
>
98+
{({ input, meta }) => (
99+
<NumberInput
100+
name={input.name}
101+
value={input.value}
102+
onChange={input.onChange}
103+
onBlur={input.onBlur}
104+
onFocus={input.onFocus}
105+
min="4"
106+
error={meta.touched && meta.error}
107+
/>
108+
)}
109+
</Field>
110+
<Error name="durationWeeks" />
111+
</td>
112+
<td>
113+
{startMonthVisible ? (
114+
<>
115+
<Field
116+
name="startMonth"
117+
initialValue={Date.now()}
118+
>
119+
{(props) => (
120+
<MonthPicker
121+
name={props.input.name}
122+
value={props.input.value}
123+
onChange={props.input.onChange}
124+
onBlur={props.input.onBlur}
125+
onFocus={props.input.onFocus}
126+
/>
127+
)}
128+
</Field>
129+
<Error name="startMonth" />
130+
</>
131+
) : (
132+
<div styleName="flex-container">
133+
<button
134+
styleName="toggle-button"
135+
onClick={() =>
136+
setStartMonthVisible(true)
137+
}
138+
>
139+
Add Start Month
140+
</button>
141+
<InformationTooltip
142+
iconSize="14px"
143+
text="Requested start month for this position."
144+
/>
145+
</div>
146+
)}
147+
</td>
148+
</tr>
149+
</table>
150+
</div>
151+
</BaseCreateModal>
152+
);
153+
}}
154+
</Form>
155+
);
156+
}
157+
158+
EditRoleModal.propTypes = {
159+
open: PT.bool,
160+
onClose: PT.func,
161+
submitForm: PT.func,
162+
role: PT.object,
163+
};
164+
165+
export default EditRoleModal;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
@import "styles/include";
2+
3+
.toggle-button {
4+
@include font-roboto;
5+
outline: none;
6+
border: none;
7+
background: none;
8+
font-size: 12px;
9+
font-weight: 500;
10+
color: #137D60;
11+
padding: 1px 6px 0 6px;
12+
13+
&.toggle-description {
14+
margin-top: 12px;
15+
> span {
16+
font-size: 18px;
17+
vertical-align: middle;
18+
}
19+
}
20+
}
21+
22+
.table {
23+
margin-top: 40px;
24+
width: 100%;
25+
th {
26+
@include font-roboto;
27+
font-size: 12px;
28+
color: #555;
29+
padding-bottom: 7px;
30+
border-bottom: 1px solid #d4d4d4;
31+
32+
&.bold {
33+
font-weight: 700;
34+
}
35+
}
36+
37+
.role-row {
38+
td {
39+
padding: 18px 18px 18px 0;
40+
vertical-align: top;
41+
@include font-barlow;
42+
font-weight: 600;
43+
font-size: 16px;
44+
color: #2a2a2a;
45+
border-bottom: 1px solid #e9e9e9;
46+
47+
&:last-child {
48+
padding-right: 0;
49+
}
50+
51+
input {
52+
@include font-roboto;
53+
font-size: 14px;
54+
line-height: normal;
55+
height: 34px;
56+
&[type="number"] {
57+
width: 98px;
58+
}
59+
}
60+
}
61+
}
62+
}
63+
64+
.flex-container {
65+
display: flex;
66+
flex-direction: row;
67+
align-items: center;
68+
justify-content: flex-start;
69+
width: 118px;
70+
margin-top: 13px;
71+
}
72+
73+
.error {
74+
font-size: 14px;
75+
font-weight: 400;
76+
color: red;
77+
display: block;
78+
}
79+
80+
.delete-role {
81+
border: none;
82+
background: none;
83+
84+
margin-top: 13px;
85+
86+
&:hover {
87+
g {
88+
stroke: red;
89+
}
90+
}
91+
}
92+
93+
.modal-body {
94+
overflow-x: auto;
95+
textarea {
96+
height: 95px;
97+
}
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const composeValidators = (...validators) => (value) =>
2+
validators.reduce((error, validator) => error || validator(value), undefined);
3+
4+
const validateMin = (min) => (value) =>
5+
isNaN(value) || value >= min ? undefined : `Should be greater than ${min}`;
6+
7+
const validateName = (name) => {
8+
if (!name || name.trim().length === 0) {
9+
return "Please enter a team name.";
10+
}
11+
return undefined;
12+
};
13+
14+
const validateNumber = (number) => {
15+
const converted = Number(number);
16+
17+
if (
18+
Number.isNaN(converted) ||
19+
converted !== Math.floor(converted) ||
20+
converted < 1
21+
) {
22+
return "Please enter a positive integer";
23+
}
24+
return undefined;
25+
};
26+
27+
const validateMonth = (monthString) => {
28+
const then = new Date(monthString);
29+
const now = new Date();
30+
const thenYear = then.getFullYear();
31+
const nowYear = now.getFullYear();
32+
const thenMonth = then.getMonth();
33+
const nowMonth = now.getMonth();
34+
35+
if (thenYear < nowYear || (thenYear === nowYear && thenMonth < nowMonth)) {
36+
return "Start month may not be before current month";
37+
}
38+
return undefined;
39+
};
40+
41+
const validator = (role) => {
42+
const roleErrors = {};
43+
roleErrors.numberOfResources = validateNumber(role.numberOfResources);
44+
roleErrors.durationWeeks = validateNumber(role.durationWeeks);
45+
if (role.startMonth) {
46+
roleErrors.startMonth = validateMonth(role.startMonth);
47+
}
48+
49+
return roleErrors;
50+
};
51+
52+
const validateExists = (value) => {
53+
return value === undefined ? "Please enter a positive integer" : undefined;
54+
};
55+
56+
export { validator, validateExists, validateMin, composeValidators };

0 commit comments

Comments
 (0)