Skip to content

Commit eb33bcb

Browse files
authored
Custom UI for EngagementDateOverrideConflict error (#1653)
1 parent 1b04453 commit eb33bcb

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

src/api/errorHandling/error.types.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ApolloError } from '@apollo/client';
22
import { assert } from 'ts-essentials';
3-
import { ProductStep } from '../schema.graphql';
3+
import { ProductStep, Project } from '../schema.graphql';
44

55
interface CordErrorExtensions {
66
codes: readonly Code[];
@@ -26,6 +26,7 @@ export interface ErrorMap {
2626
Duplicate: DuplicateError;
2727
Unauthorized: InputError;
2828
StepNotPlanned: StepNotPlannedError;
29+
EngagementDateOverrideConflict: EngagementDateOverrideConflictError;
2930

3031
/**
3132
* This is a special one that allows a default handler for any
@@ -91,6 +92,18 @@ export interface StepNotPlannedError extends InputError {
9192
index: number;
9293
}
9394

95+
export interface EngagementDateOverrideConflictError extends InputError {
96+
readonly project: Pick<Project, 'id' | 'name' | 'mouStart' | 'mouEnd'>;
97+
readonly engagements: ReadonlyArray<
98+
Readonly<{
99+
id: string;
100+
label: string;
101+
point: 'start' | 'end';
102+
date: string;
103+
}>
104+
>;
105+
}
106+
94107
export const isErrorCode = <K extends keyof ErrorMap>(
95108
errorInfo: ErrorInfo,
96109
code: K

src/scenes/Projects/Update/UpdateProjectDialog.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { useMutation } from '@apollo/client';
2+
import { Box, Typography } from '@mui/material';
23
import { many, Many } from '@seedcompany/common';
4+
import { FORM_ERROR } from 'final-form';
35
import { pick } from 'lodash';
46
import { ComponentType, useMemo } from 'react';
57
import { Except, Merge } from 'type-fest';
68
import { invalidateProps } from '~/api';
79
import { SensitivityList, UpdateProject } from '~/api/schema.graphql';
810
import {
11+
CalendarDate,
912
DisplayFieldRegionFragment,
1013
DisplayLocationFragment,
1114
ExtractStrict,
@@ -24,6 +27,8 @@ import {
2427
} from '../../../components/form';
2528
import { LocationField } from '../../../components/form/Lookup';
2629
import { FieldRegionField } from '../../../components/form/Lookup/FieldRegion';
30+
import { FormattedDate } from '../../../components/Formatters';
31+
import { Link } from '../../../components/Routing';
2732
import {
2833
updateEngagementDateRanges,
2934
updatePartnershipsDateRanges,
@@ -219,6 +224,40 @@ export const UpdateProjectDialog = ({
219224
},
220225
});
221226
}}
227+
errorHandlers={{
228+
EngagementDateOverrideConflict: ({ engagements }) => {
229+
const rendered = (
230+
<>
231+
<Typography variant="body2" gutterBottom>
232+
{engagements.length === 1
233+
? 'An engagement has a date outside the new range'
234+
: 'Some engagements have dates outside the new range'}
235+
</Typography>
236+
<Box component="ul" sx={{ m: 0, paddingInlineStart: 4 }}>
237+
{engagements.map((eng) => (
238+
<li key={eng.id + eng.point}>
239+
{eng.point === 'start' ? 'Start' : 'End'} date of{' '}
240+
<Link to={`/engagements/${eng.id}`} color="inherit">
241+
{eng.label}
242+
</Link>{' '}
243+
is <FormattedDate date={CalendarDate.fromISO(eng.date)} />
244+
</li>
245+
))}
246+
</Box>
247+
</>
248+
);
249+
const points = new Set(engagements.map((eng) => eng.point));
250+
return {
251+
[FORM_ERROR]: rendered,
252+
// Mark the field(s) as invalid,
253+
// even though we show the error in the unified spot.
254+
project: {
255+
...(points.has('start') ? { mouStart: ' ' } : {}),
256+
...(points.has('end') ? { mouEnd: ' ' } : {}),
257+
},
258+
};
259+
},
260+
}}
222261
>
223262
<SubmitError />
224263
<FieldGroup prefix="project">

0 commit comments

Comments
 (0)