Skip to content

Commit 3aca5e9

Browse files
authored
Merge pull request #3797 from Northeastern-Electric-Racing/#3667-maintenence-error-handling-for-saving-a-task-without-assigning
#3667 maintenence error handling for saving a task without assigning
2 parents d454683 + d6fa3ac commit 3aca5e9

File tree

4 files changed

+63
-22
lines changed

4 files changed

+63
-22
lines changed

src/frontend/src/components/NERFormModal.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ReactNode } from 'react';
22
import { FieldValues, UseFormHandleSubmit, UseFormReset } from 'react-hook-form';
33
import NERModal, { NERModalProps } from './NERModal';
4+
import { useToast } from '../hooks/toasts.hooks';
45

56
interface NERFormModalProps<T extends FieldValues> extends NERModalProps {
67
reset: UseFormReset<T>;
@@ -31,18 +32,23 @@ const NERFormModal = ({
3132
titleChildren,
3233
actionsLeftChildren
3334
}: NERFormModalProps<any>) => {
35+
const toast = useToast();
3436
/**
3537
* Wrapper function for onSubmit so that form data is reset after submit
3638
*/
3739
const onSubmitWrapper = async (data: any) => {
38-
await onFormSubmit(data);
39-
reset();
40+
try {
41+
await onFormSubmit(data);
42+
reset();
43+
} catch (e: unknown) {
44+
if (e instanceof Error) toast.error(e.message, 6000);
45+
}
4046
};
4147

42-
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
48+
const handleFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
4349
e.preventDefault();
4450
e.stopPropagation(); // Prevent event bubbling
45-
handleUseFormSubmit(onSubmitWrapper)(e);
51+
await handleUseFormSubmit(onSubmitWrapper)(e);
4652
};
4753

4854
return (

src/frontend/src/pages/ProjectDetailPage/ProjectViewContainer/TaskList/TaskFormModal.tsx

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,14 @@ import { yupResolver } from '@hookform/resolvers/yup';
22
import { Autocomplete, FormControl, FormHelperText, FormLabel, Grid, MenuItem, TextField } from '@mui/material';
33
import { DatePicker } from '@mui/x-date-pickers';
44
import { Controller, useForm } from 'react-hook-form';
5-
import { countWords, isGuest, isUnderWordCount, Task, TaskPriority, TeamPreview } from 'shared';
5+
import { countWords, isGuest, isUnderWordCount, Task, TaskPriority, TaskStatus, TeamPreview } from 'shared';
66
import { useAllUsers, useCurrentUser } from '../../../../hooks/users.hooks';
77
import * as yup from 'yup';
88
import { taskUserToAutocompleteOption } from '../../../../utils/task.utils';
99
import NERFormModal from '../../../../components/NERFormModal';
1010
import LoadingIndicator from '../../../../components/LoadingIndicator';
1111
import ErrorPage from '../../../ErrorPage';
1212

13-
const schema = yup.object().shape({
14-
notes: yup.string().optional(),
15-
startDate: yup.date().optional(),
16-
deadline: yup.date().optional(),
17-
priority: yup.mixed<TaskPriority>().oneOf(Object.values(TaskPriority)).required(),
18-
assignees: yup.array().required(),
19-
title: yup.string().required(),
20-
taskId: yup.string().required()
21-
});
22-
2313
export interface EditTaskFormInput {
2414
taskId: string;
2515
title: string;
@@ -32,14 +22,53 @@ export interface EditTaskFormInput {
3222

3323
interface TaskFormModalProps {
3424
task?: Task;
25+
status?: Task['status'];
3526
teams: TeamPreview[];
3627
modalShow: boolean;
3728
onHide: () => void;
3829
onSubmit: (data: EditTaskFormInput) => Promise<void>;
3930
onReset?: () => void;
4031
}
4132

42-
const TaskFormModal: React.FC<TaskFormModalProps> = ({ task, onSubmit, modalShow, onHide, onReset }) => {
33+
const TaskFormModal: React.FC<TaskFormModalProps> = ({ task, status, onSubmit, modalShow, onHide, onReset }) => {
34+
let schema;
35+
36+
if (status === TaskStatus.IN_PROGRESS) {
37+
schema = yup.object().shape({
38+
notes: yup
39+
.string()
40+
.optional()
41+
.test((value) => {
42+
if (!value) return true;
43+
const wordCount = countWords(value);
44+
return wordCount < 250;
45+
}),
46+
startDate: yup.date().optional(),
47+
deadline: yup.date().required('Deadline is required for In Progress tasks'),
48+
priority: yup.mixed<TaskPriority>().oneOf(Object.values(TaskPriority)).required(),
49+
assignees: yup.array().required().min(1, 'At least one assignee is required for In Progress tasks'),
50+
title: yup.string().required(),
51+
taskId: yup.string().required()
52+
});
53+
} else {
54+
schema = yup.object().shape({
55+
notes: yup
56+
.string()
57+
.optional()
58+
.test((value) => {
59+
if (!value) return true;
60+
const wordCount = countWords(value);
61+
return wordCount < 250;
62+
}),
63+
startDate: yup.date().optional(),
64+
deadline: yup.date().optional(),
65+
priority: yup.mixed<TaskPriority>().oneOf(Object.values(TaskPriority)).required(),
66+
assignees: yup.array().required(),
67+
title: yup.string().required(),
68+
taskId: yup.string().required()
69+
});
70+
}
71+
4372
const user = useCurrentUser();
4473

4574
const { data: users, isLoading, isError, error } = useAllUsers();
@@ -162,6 +191,7 @@ const TaskFormModal: React.FC<TaskFormModalProps> = ({ task, onSubmit, modalShow
162191
/>
163192
)}
164193
/>
194+
<FormHelperText error={!!errors.assignees}>{errors.assignees?.message}</FormHelperText>
165195
</FormControl>
166196
</Grid>
167197
<Grid item md={6}>
@@ -200,6 +230,7 @@ const TaskFormModal: React.FC<TaskFormModalProps> = ({ task, onSubmit, modalShow
200230
/>
201231
)}
202232
/>
233+
<FormHelperText error={!!errors.deadline}>{errors.deadline?.message}</FormHelperText>
203234
</FormControl>
204235
</Grid>
205236
<Grid item xs={12} md={12}>

src/frontend/src/pages/ProjectDetailPage/ProjectViewContainer/TaskList/v2/TaskColumn.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export const TaskColumn = ({
7171
return (
7272
<>
7373
<TaskFormModal
74+
status={status}
7475
onSubmit={handleCreateTask}
7576
onHide={() => setShowCreateTaskModal(false)}
7677
modalShow={showCreateTaskModal}

src/frontend/src/tests/pages/WorkPackageDetailPage/StageGateWorkPackageModalContainer/StageGateWorkPackageModal.test.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { render, screen, routerWrapperBuilder } from '../../../test-support/test
77
import { wbsPipe } from '../../../../utils/pipes';
88
import { exampleWbs1 } from '../../../test-support/test-data/wbs-numbers.stub';
99
import StageGateWorkPackageModal from '../../../../pages/WorkPackageDetailPage/StageGateWorkPackageModalContainer/StageGateWorkPackageModal';
10+
import { ToastProvider } from '../../../../components/Toast/ToastProvider';
1011

1112
/**
1213
* Mock function for submitting the form, use if there is additional functionality added while submitting
@@ -24,12 +25,14 @@ const renderComponent = (modalShow: boolean) => {
2425
const RouterWrapper = routerWrapperBuilder({});
2526
return render(
2627
<RouterWrapper>
27-
<StageGateWorkPackageModal
28-
modalShow={modalShow}
29-
onHide={mockHandleHide}
30-
onSubmit={mockHandleSubmit}
31-
wbsNum={exampleWbs1}
32-
/>
28+
<ToastProvider>
29+
<StageGateWorkPackageModal
30+
modalShow={modalShow}
31+
onHide={mockHandleHide}
32+
onSubmit={mockHandleSubmit}
33+
wbsNum={exampleWbs1}
34+
/>
35+
</ToastProvider>
3336
</RouterWrapper>
3437
);
3538
};

0 commit comments

Comments
 (0)