Skip to content

Commit d2e67cd

Browse files
Fix announcements (review) (#120)
1 parent 53dc9d2 commit d2e67cd

File tree

9 files changed

+116
-98
lines changed

9 files changed

+116
-98
lines changed

src/index.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,18 @@ code {
1919
body > iframe[width='0'][height='0'] {
2020
border: 0;
2121
}
22+
23+
/* chromium browsers? */
24+
@media screen and (-webkit-min-device-pixel-ratio:0) and (min-resolution:.001dpcm) {
25+
/* The popup selection of DateTimePickerElement is a dialog at the root of body ... */
26+
.MuiMultiSectionDigitalClockSection-root {
27+
scrollbar-width: thin; /* for Edge thick scroll-bar */
28+
}
29+
}
30+
31+
/** Mozilla Firefox */
32+
@-moz-document url-prefix() {
33+
.MuiMultiSectionDigitalClockSection-root {
34+
scrollbar-width: revert; /* firefox one is good (on ubuntu at least) */
35+
}
36+
}

src/pages/announcements/add-announcement-form.tsx

Lines changed: 82 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ import { useCallback, useMemo } from 'react';
99
import { Grid } from '@mui/material';
1010
import { type DateOrTimeView } from '@mui/x-date-pickers';
1111
import { useIntl } from 'react-intl';
12-
import { SubmitButton, useSnackMessage } from '@gridsuite/commons-ui';
13-
import yup from '../../utils/yup-config';
12+
import { SubmitButton, useSnackMessage, yupConfig as yup } from '@gridsuite/commons-ui';
1413
import { type InferType } from 'yup';
1514
import { type SubmitHandler, useForm } from 'react-hook-form';
1615
import { yupResolver } from '@hookform/resolvers/yup';
17-
import { FormContainer, SelectElement, TextareaAutosizeElement } from 'react-hook-form-mui';
16+
import {
17+
FormContainer,
18+
FormErrorProvider,
19+
type FormErrorProviderProps,
20+
SelectElement,
21+
TextareaAutosizeElement,
22+
} from 'react-hook-form-mui';
1823
import { DateTimePickerElement, type DateTimePickerElementProps } from 'react-hook-form-mui/date-pickers';
1924
import { UserAdminSrv } from '../../services';
2025
import { getErrorMessage, handleAnnouncementCreationErrors } from '../../utils/error';
@@ -28,10 +33,12 @@ export type AddAnnouncementFormProps = {
2833
onAnnouncementCreated?: () => void;
2934
};
3035

36+
const MESSAGE_MAX_LENGTH = 200;
37+
3138
const formSchema = yup
3239
.object()
3340
.shape({
34-
[MESSAGE]: yup.string().nullable().trim().min(1).required(),
41+
[MESSAGE]: yup.string().nullable().trim().min(1, 'YupRequired').max(MESSAGE_MAX_LENGTH).required(),
3542
[START_DATE]: yup.string().nullable().datetime().required(),
3643
[END_DATE]: yup
3744
.string()
@@ -41,7 +48,7 @@ const formSchema = yup
4148
.when(START_DATE, (startDate, schema) =>
4249
schema.test(
4350
'is-after-start',
44-
'End date must be after start date',
51+
'announcements.form.errForm.startDateAfterEndDateErr',
4552
(endDate) => !startDate || !endDate || new Date(endDate) > new Date(startDate as unknown as string)
4653
)
4754
),
@@ -55,7 +62,13 @@ const formSchema = yup
5562
type FormSchema = InferType<typeof formSchema>;
5663

5764
const datetimePickerTransform: NonNullable<DateTimePickerElementProps<FormSchema>['transform']> = {
58-
input: (value) => (value ? new Date(value) : null),
65+
input: (value) => {
66+
try {
67+
return value ? new Date(value) : null;
68+
} catch {
69+
return null; // RangeError: invalid date
70+
}
71+
},
5972
output: (value) => value?.toISOString() ?? '',
6073
};
6174
const pickerView = ['year', 'month', 'day', 'hours', 'minutes'] as const satisfies readonly DateOrTimeView[];
@@ -76,6 +89,9 @@ export default function AddAnnouncementForm({ onAnnouncementCreated }: Readonly<
7689
// @ts-expect-error: nullable() is called, so null is accepted as default value
7790
[SEVERITY]: null,
7891
},
92+
criteriaMode: 'all',
93+
mode: 'all',
94+
reValidateMode: 'onBlur', // TODO 'onChange'?
7995
});
8096
const { getValues } = formContext;
8197
const startDateValue = getValues(START_DATE);
@@ -99,68 +115,77 @@ export default function AddAnnouncementForm({ onAnnouncementCreated }: Readonly<
99115
[onAnnouncementCreated, snackError]
100116
);
101117

118+
// TODO remove when yupConfig has been rework
119+
const onErrorIntl = useCallback<FormErrorProviderProps['onError']>(
120+
(error) =>
121+
error?.message?.includes(' ') // if it's not a token
122+
? error.message
123+
: intl.formatMessage({ id: error.message, defaultMessage: error.message }),
124+
[intl]
125+
);
126+
102127
return (
103128
<FormContainer<FormSchema>
104129
formContext={formContext}
105130
onSuccess={onSubmit}
106-
//criteriaMode="all" ?
107-
mode="onChange" // or maybe mode "all"?
108-
reValidateMode="onChange"
109131
FormProps={{ style: { height: '100%' } }}
110132
>
111-
<Grid container direction="column" spacing={1.5} height="100%">
112-
<Grid item container xs="auto" spacing={1}>
113-
<Grid item xs={12} lg={6}>
114-
<DateTimePickerElement<FormSchema>
115-
name={START_DATE}
116-
label={intl.formatMessage({ id: 'announcements.table.startDate' })}
117-
transform={datetimePickerTransform}
118-
timezone="system"
119-
views={pickerView}
120-
timeSteps={{ hours: 1, minutes: 1, seconds: 0 }}
121-
disablePast
133+
<FormErrorProvider onError={onErrorIntl}>
134+
<Grid container direction="column" spacing={1.5} height="100%">
135+
<Grid item container xs="auto" spacing={1}>
136+
<Grid item xs={12} lg={6}>
137+
<DateTimePickerElement<FormSchema>
138+
name={START_DATE}
139+
label={intl.formatMessage({ id: 'announcements.table.startDate' })}
140+
transform={datetimePickerTransform}
141+
timezone="system"
142+
views={pickerView}
143+
timeSteps={{ hours: 1, minutes: 1, seconds: 0 }}
144+
disablePast
145+
/>
146+
</Grid>
147+
<Grid item xs={12} lg={6}>
148+
<DateTimePickerElement<FormSchema>
149+
name={END_DATE}
150+
label={intl.formatMessage({ id: 'announcements.table.endDate' })}
151+
transform={datetimePickerTransform}
152+
timezone="system"
153+
views={pickerView}
154+
timeSteps={{ hours: 1, minutes: 1, seconds: 0 }}
155+
disablePast
156+
minDateTime={startDateValue ? new Date(startDateValue) : undefined}
157+
/>
158+
</Grid>
159+
</Grid>
160+
<Grid item xs="auto">
161+
<SelectElement<FormSchema>
162+
name={SEVERITY}
163+
label={intl.formatMessage({ id: 'announcements.severity' })}
164+
options={useMemo(
165+
() =>
166+
Object.values(UserAdminSrv.AnnouncementSeverity).map((value) => ({
167+
id: value,
168+
label: intl.formatMessage({ id: `announcements.severity.${value}` }),
169+
})),
170+
[intl]
171+
)}
172+
fullWidth
122173
/>
123174
</Grid>
124-
<Grid item xs={12} lg={6}>
125-
<DateTimePickerElement<FormSchema>
126-
name={END_DATE}
127-
label={intl.formatMessage({ id: 'announcements.table.endDate' })}
128-
transform={datetimePickerTransform}
129-
timezone="system"
130-
views={pickerView}
131-
timeSteps={{ hours: 1, minutes: 1, seconds: 0 }}
132-
disablePast
133-
minDateTime={startDateValue ? new Date(startDateValue) : undefined}
175+
<Grid item xs>
176+
<TextareaAutosizeElement<FormSchema>
177+
name={MESSAGE}
178+
label={intl.formatMessage({ id: 'announcements.form.message' })}
179+
rows={5} // why does it do nothing even if the field is set as multiline?!
180+
fullWidth
181+
//inputProps={{ maxLength: MESSAGE_MAX_LENGTH } satisfies Partial<HTMLInputElement>}
134182
/>
135183
</Grid>
184+
<Grid item xs="auto">
185+
<SubmitButton variant="outlined" type="submit" fullWidth />
186+
</Grid>
136187
</Grid>
137-
<Grid item xs="auto">
138-
<SelectElement<FormSchema>
139-
name={SEVERITY}
140-
label={intl.formatMessage({ id: 'announcements.severity' })}
141-
options={useMemo(
142-
() =>
143-
Object.values(UserAdminSrv.AnnouncementSeverity).map((value) => ({
144-
id: value,
145-
label: intl.formatMessage({ id: `announcements.severity.${value}` }),
146-
})),
147-
[intl]
148-
)}
149-
fullWidth
150-
/>
151-
</Grid>
152-
<Grid item xs>
153-
<TextareaAutosizeElement<FormSchema>
154-
name={MESSAGE}
155-
label={intl.formatMessage({ id: 'announcements.form.message' })}
156-
rows={5} // why does it do nothing even if the field is set as multiline?!
157-
fullWidth
158-
/>
159-
</Grid>
160-
<Grid item xs="auto">
161-
<SubmitButton variant="outlined" type="submit" fullWidth />
162-
</Grid>
163-
</Grid>
188+
</FormErrorProvider>
164189
</FormContainer>
165190
);
166191
}

src/pages/announcements/announcements-page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export default function AnnouncementsPage() {
122122

123123
// Note: using <Stack/> for the columns didn't work
124124
return (
125-
<Grid container spacing={2} marginLeft={'2px' /*Grid.spacing is in px?*/}>
125+
<Grid container spacing={2} marginLeft={'2px' /*Grid.spacing is in px?*/} marginTop={1}>
126126
<Grid item container direction="column" xs={12} sm={6} md={4}>
127127
<Grid item xs="auto">
128128
<Typography variant="subtitle1">

src/pages/groups/modification/group-modification-form.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
77

8-
import { TextInput } from '@gridsuite/commons-ui';
9-
import Grid from '@mui/material/Grid';
10-
import React, { FunctionComponent } from 'react';
11-
import yup from '../../../utils/yup-config';
8+
import { type FunctionComponent } from 'react';
9+
import { Grid } from '@mui/material';
10+
import { TextInput, yupConfig as yup } from '@gridsuite/commons-ui';
1211
import TableSelection from '../../common/table-selection';
1312

1413
export const GROUP_NAME = 'name';

src/pages/profiles/modification/profile-modification-dialog.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@ import ProfileModificationForm, {
1717
USER_QUOTA_CASE_NB,
1818
NETWORK_VISUALIZATION_PARAMETERS_ID,
1919
} from './profile-modification-form';
20-
import yup from '../../../utils/yup-config';
2120
import { yupResolver } from '@hookform/resolvers/yup';
2221
import { useForm } from 'react-hook-form';
2322
import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react';
24-
import { CustomMuiDialog, FetchStatus, useSnackMessage } from '@gridsuite/commons-ui';
23+
import { CustomMuiDialog, FetchStatus, useSnackMessage, yupConfig as yup } from '@gridsuite/commons-ui';
2524
import { UserAdminSrv, UserProfile } from '../../../services';
2625
import { UUID } from 'crypto';
2726

src/pages/users/modification/user-modification-form.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
77

8-
import { AutocompleteInput, TextInput } from '@gridsuite/commons-ui';
9-
import Grid from '@mui/material/Grid';
10-
import React, { FunctionComponent } from 'react';
11-
import yup from '../../../utils/yup-config';
8+
import { type FunctionComponent } from 'react';
9+
import { Grid } from '@mui/material';
10+
import { AutocompleteInput, TextInput, yupConfig as yup } from '@gridsuite/commons-ui';
1211
import TableSelection from '../../common/table-selection';
1312

1413
export const USER_NAME = 'sub';

src/translations/en.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@
112112
"linked.path.display.noLink": "no configuration selected.",
113113
"linked.path.display.invalidLink": "invalid configurations link.",
114114

115-
"announcements.programNewMessage": "New message scheduling",
116-
"announcements.programmedMessage": "Programmed messages",
115+
"announcements.programNewMessage": "Schedule a new message",
116+
"announcements.programmedMessage": "Scheduled messages",
117117
"announcements.table.message": "Message",
118118
"announcements.table.startDate": "Start date",
119119
"announcements.table.endDate": "Stop date",
@@ -125,5 +125,8 @@
125125
"announcements.form.errCreateAnnouncement": "Error while creating announcement:",
126126
"announcements.form.errCreateAnnouncement.noOverlapAllowedErr": "The date overlaps with another announcement date.",
127127
"announcements.form.errCreateAnnouncement.noSameDateErr": "The announcement start and end date must be different.",
128-
"announcements.form.errCreateAnnouncement.startDateAfterEndDateErr": "The start date cannot be after the end date."
128+
"announcements.form.errCreateAnnouncement.startDateAfterEndDateErr": "The start date cannot be after the end date.",
129+
"announcements.form.errForm.startDateAfterEndDateErr": "End date must be after start date.",
130+
131+
"YupRequired": "This field is required"
129132
}

src/translations/fr.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,16 @@
118118
"announcements.table.message": "Message",
119119
"announcements.table.startDate": "Date de début",
120120
"announcements.table.endDate": "Date de fin",
121-
"announcements.table.cancel": "Annulé",
121+
"announcements.table.cancel": "Annuler",
122122
"announcements.severity": "Sévérité",
123123
"announcements.severity.INFO": "Information",
124124
"announcements.severity.WARN": "Avertissement",
125125
"announcements.form.message": "Message d'annonce",
126126
"announcements.form.errCreateAnnouncement": "Erreur lors de la création de l'annonce :",
127127
"announcements.form.errCreateAnnouncement.noOverlapAllowedErr": "La date d'annonce chevauche une autre date d'annonce.",
128128
"announcements.form.errCreateAnnouncement.noSameDateErr": "La date de début et de fin d'annonce doivent être différentes.",
129-
"announcements.form.errCreateAnnouncement.startDateAfterEndDateErr": "La date de début d'annonce ne peut pas être après la date de fin."
129+
"announcements.form.errCreateAnnouncement.startDateAfterEndDateErr": "La date de début d'annonce ne peut pas être après la date de fin.",
130+
"announcements.form.errForm.startDateAfterEndDateErr": "La date de fin doit être après la date de début.",
131+
132+
"YupRequired": "Ce champ doit être renseigné"
130133
}

src/utils/yup-config.js

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)