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

Issue #356 #373 #364 #377

Merged
merged 3 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ export const ACTION_TYPE = {
*/
ADD_MATCHING_ROLE: "ADD_MATCHING_ROLE",
DELETE_MATCHING_ROLE: "DELETE_MATCHING_ROLE",
EDIT_MATCHING_ROLE: "EDIT_MATCHING_ROLE",
};

/**
Expand Down
10 changes: 10 additions & 0 deletions src/routes/CreateNewTeam/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ const deleteMatchingRole = () => ({
type: ACTION_TYPE.DELETE_MATCHING_ROLE,
});

const editMatchingRole = (role) => ({
type: ACTION_TYPE.EDIT_MATCHING_ROLE,
payload: role,
});

export const clearSearchedRoles = () => (dispatch, getState) => {
dispatch(clearRoles());
updateLocalStorage(getState().searchedRoles);
Expand All @@ -51,6 +56,11 @@ export const addRoleSearchId = (id) => (dispatch, getState) => {
updateLocalStorage(getState().searchedRoles);
};

export const editRoleAction = (role) => (dispatch, getState) => {
dispatch(editMatchingRole(role));
updateLocalStorage(getState().searchedRoles);
};

export const deleteSearchedRole = (id) => (dispatch, getState) => {
dispatch(deleteRole(id));
updateLocalStorage(getState().searchedRoles);
Expand Down
165 changes: 165 additions & 0 deletions src/routes/CreateNewTeam/components/EditRoleModal/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* Edit Role Modal
* Popup form to enter details about current role
*/
import React, { useEffect, useState } from "react";
import PT from "prop-types";
import { Form, Field, useField } from "react-final-form";
import FormField from "components/FormField";
import BaseCreateModal from "../BaseCreateModal";
import Button from "components/Button";
import MonthPicker from "components/MonthPicker";
import InformationTooltip from "components/InformationTooltip";
import IconCrossLight from "../../../../assets/images/icon-cross-light.svg";
import "./styles.module.scss";
import NumberInput from "components/NumberInput";
import { validator, validateExists, validateMin, composeValidators } from "./utils/validator";

const Error = ({ name }) => {
const {
meta: { dirty, error },
} = useField(name, { subscription: { dirty: true, error: true } });
return dirty && error ? <span styleName="error">{error}</span> : null;
};

function EditRoleModal({ open, onClose, submitForm, role }) {
const [startMonthVisible, setStartMonthVisible] = useState(false);

return (
<Form
onSubmit={submitForm}
mutators={{
clearField: ([fieldName], state, { changeValue }) => {
changeValue(state, fieldName, () => undefined);
},
}}
validate={validator}
>
{({
handleSubmit,
hasValidationErrors,
form: {
mutators: { clearField },
getState,
},
}) => {
return (
<BaseCreateModal
open={open}
onClose={onClose}
title="Edit Role"
subtitle="Please provide your team details before submitting a request."
buttons={
<Button
type="primary"
size="medium"
onClick={handleSubmit}
disabled={hasValidationErrors}
>
Submit
</Button>
}
disableFocusTrap
>
<div styleName="modal-body">
<table styleName="table">
<tr>
<th># of resources</th>
<th>Duration (weeks)</th>
<th>Start month</th>
</tr>
<tr styleName="role-row">
<td>
<Field
validate={composeValidators(validateExists, validateMin(1))}
name="numberOfResources"
initialValue={role.numberOfResources}
>
{({ input, meta }) => (
<NumberInput
name={input.name}
value={input.value}
onChange={input.onChange}
onBlur={input.onBlur}
onFocus={input.onFocus}
min="1"
error={meta.touched && meta.error}
/>
)}
</Field>
<Error name="numberOfResources" />
</td>
<td>
<Field
validate={composeValidators(validateExists, validateMin(4))}
name="durationWeeks"
initialValue={role.durationWeeks}
>
{({ input, meta }) => (
<NumberInput
name={input.name}
value={input.value}
onChange={input.onChange}
onBlur={input.onBlur}
onFocus={input.onFocus}
min="4"
error={meta.touched && meta.error}
/>
)}
</Field>
<Error name="durationWeeks" />
</td>
<td>
{startMonthVisible ? (
<>
<Field
name="startMonth"
initialValue={Date.now()}
>
{(props) => (
<MonthPicker
name={props.input.name}
value={props.input.value}
onChange={props.input.onChange}
onBlur={props.input.onBlur}
onFocus={props.input.onFocus}
/>
)}
</Field>
<Error name="startMonth" />
</>
) : (
<div styleName="flex-container">
<button
styleName="toggle-button"
onClick={() =>
setStartMonthVisible(true)
}
>
Add Start Month
</button>
<InformationTooltip
iconSize="14px"
text="Requested start month for this position."
/>
</div>
)}
</td>
</tr>
</table>
</div>
</BaseCreateModal>
);
}}
</Form>
);
}

EditRoleModal.propTypes = {
open: PT.bool,
onClose: PT.func,
submitForm: PT.func,
role: PT.object,
};

export default EditRoleModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
@import "styles/include";

.toggle-button {
@include font-roboto;
outline: none;
border: none;
background: none;
font-size: 12px;
font-weight: 500;
color: #137D60;
padding: 1px 6px 0 6px;

&.toggle-description {
margin-top: 12px;
> span {
font-size: 18px;
vertical-align: middle;
}
}
}

.table {
margin-top: 40px;
width: 100%;
th {
@include font-roboto;
font-size: 12px;
color: #555;
padding-bottom: 7px;
border-bottom: 1px solid #d4d4d4;

&.bold {
font-weight: 700;
}
}

.role-row {
td {
padding: 18px 18px 18px 0;
vertical-align: top;
@include font-barlow;
font-weight: 600;
font-size: 16px;
color: #2a2a2a;
border-bottom: 1px solid #e9e9e9;

&:last-child {
padding-right: 0;
}

input {
@include font-roboto;
font-size: 14px;
line-height: normal;
height: 34px;
&[type="number"] {
width: 98px;
}
}
}
}
}

.flex-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
width: 118px;
margin-top: 13px;
}

.error {
font-size: 14px;
font-weight: 400;
color: red;
display: block;
}

.delete-role {
border: none;
background: none;

margin-top: 13px;

&:hover {
g {
stroke: red;
}
}
}

.modal-body {
overflow-x: auto;
textarea {
height: 95px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const composeValidators = (...validators) => (value) =>
validators.reduce((error, validator) => error || validator(value), undefined);

const validateMin = (min) => (value) =>
isNaN(value) || value >= min ? undefined : `Should be greater than ${min}`;

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 validator = (role) => {
const roleErrors = {};
roleErrors.numberOfResources = validateNumber(role.numberOfResources);
roleErrors.durationWeeks = validateNumber(role.durationWeeks);
if (role.startMonth) {
roleErrors.startMonth = validateMonth(role.startMonth);
}

return roleErrors;
};

const validateExists = (value) => {
return value === undefined ? "Please enter a positive integer" : undefined;
};

export { validator, validateExists, validateMin, composeValidators };
Loading