Skip to content
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
15 changes: 15 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,18 @@ code {
body > iframe[width='0'][height='0'] {
border: 0;
}

/* chromium browsers? */
@media screen and (-webkit-min-device-pixel-ratio:0) and (min-resolution:.001dpcm) {
/* The popup selection of DateTimePickerElement is a dialog at the root of body ... */
.MuiMultiSectionDigitalClockSection-root {
scrollbar-width: thin; /* for Edge thick scroll-bar */
}
}

/** Mozilla Firefox */
@-moz-document url-prefix() {
.MuiMultiSectionDigitalClockSection-root {
scrollbar-width: revert; /* firefox one is good (on ubuntu at least) */
}
}
139 changes: 82 additions & 57 deletions src/pages/announcements/add-announcement-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ import { useCallback, useMemo } from 'react';
import { Grid } from '@mui/material';
import { type DateOrTimeView } from '@mui/x-date-pickers';
import { useIntl } from 'react-intl';
import { SubmitButton, useSnackMessage } from '@gridsuite/commons-ui';
import yup from '../../utils/yup-config';
import { SubmitButton, useSnackMessage, yupConfig as yup } from '@gridsuite/commons-ui';
import { type InferType } from 'yup';
import { type SubmitHandler, useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { FormContainer, SelectElement, TextareaAutosizeElement } from 'react-hook-form-mui';
import {
FormContainer,
FormErrorProvider,
type FormErrorProviderProps,
SelectElement,
TextareaAutosizeElement,
} from 'react-hook-form-mui';
import { DateTimePickerElement, type DateTimePickerElementProps } from 'react-hook-form-mui/date-pickers';
import { UserAdminSrv } from '../../services';
import { getErrorMessage, handleAnnouncementCreationErrors } from '../../utils/error';
Expand All @@ -28,10 +33,12 @@ export type AddAnnouncementFormProps = {
onAnnouncementCreated?: () => void;
};

const MESSAGE_MAX_LENGTH = 200;

const formSchema = yup
.object()
.shape({
[MESSAGE]: yup.string().nullable().trim().min(1).required(),
[MESSAGE]: yup.string().nullable().trim().min(1, 'YupRequired').max(MESSAGE_MAX_LENGTH).required(),
[START_DATE]: yup.string().nullable().datetime().required(),
[END_DATE]: yup
.string()
Expand All @@ -41,7 +48,7 @@ const formSchema = yup
.when(START_DATE, (startDate, schema) =>
schema.test(
'is-after-start',
'End date must be after start date',
'announcements.form.errForm.startDateAfterEndDateErr',
(endDate) => !startDate || !endDate || new Date(endDate) > new Date(startDate as unknown as string)
)
),
Expand All @@ -55,7 +62,13 @@ const formSchema = yup
type FormSchema = InferType<typeof formSchema>;

const datetimePickerTransform: NonNullable<DateTimePickerElementProps<FormSchema>['transform']> = {
input: (value) => (value ? new Date(value) : null),
input: (value) => {
try {
return value ? new Date(value) : null;
} catch {
return null; // RangeError: invalid date
}
},
output: (value) => value?.toISOString() ?? '',
};
const pickerView = ['year', 'month', 'day', 'hours', 'minutes'] as const satisfies readonly DateOrTimeView[];
Expand All @@ -76,6 +89,9 @@ export default function AddAnnouncementForm({ onAnnouncementCreated }: Readonly<
// @ts-expect-error: nullable() is called, so null is accepted as default value
[SEVERITY]: null,
},
criteriaMode: 'all',
mode: 'all',
reValidateMode: 'onBlur', // TODO 'onChange'?
});
const { getValues } = formContext;
const startDateValue = getValues(START_DATE);
Expand All @@ -99,68 +115,77 @@ export default function AddAnnouncementForm({ onAnnouncementCreated }: Readonly<
[onAnnouncementCreated, snackError]
);

// TODO remove when yupConfig has been rework
const onErrorIntl = useCallback<FormErrorProviderProps['onError']>(
(error) =>
error?.message?.includes(' ') // if it's not a token
? error.message
: intl.formatMessage({ id: error.message, defaultMessage: error.message }),
[intl]
);

return (
<FormContainer<FormSchema>
formContext={formContext}
onSuccess={onSubmit}
//criteriaMode="all" ?
mode="onChange" // or maybe mode "all"?
reValidateMode="onChange"
FormProps={{ style: { height: '100%' } }}
>
<Grid container direction="column" spacing={1.5} height="100%">
<Grid item container xs="auto" spacing={1}>
<Grid item xs={12} lg={6}>
<DateTimePickerElement<FormSchema>
name={START_DATE}
label={intl.formatMessage({ id: 'announcements.table.startDate' })}
transform={datetimePickerTransform}
timezone="system"
views={pickerView}
timeSteps={{ hours: 1, minutes: 1, seconds: 0 }}
disablePast
<FormErrorProvider onError={onErrorIntl}>
<Grid container direction="column" spacing={1.5} height="100%">
<Grid item container xs="auto" spacing={1}>
<Grid item xs={12} lg={6}>
<DateTimePickerElement<FormSchema>
name={START_DATE}
label={intl.formatMessage({ id: 'announcements.table.startDate' })}
transform={datetimePickerTransform}
timezone="system"
views={pickerView}
timeSteps={{ hours: 1, minutes: 1, seconds: 0 }}
disablePast
/>
</Grid>
<Grid item xs={12} lg={6}>
<DateTimePickerElement<FormSchema>
name={END_DATE}
label={intl.formatMessage({ id: 'announcements.table.endDate' })}
transform={datetimePickerTransform}
timezone="system"
views={pickerView}
timeSteps={{ hours: 1, minutes: 1, seconds: 0 }}
disablePast
minDateTime={startDateValue ? new Date(startDateValue) : undefined}
/>
</Grid>
</Grid>
<Grid item xs="auto">
<SelectElement<FormSchema>
name={SEVERITY}
label={intl.formatMessage({ id: 'announcements.severity' })}
options={useMemo(
() =>
Object.values(UserAdminSrv.AnnouncementSeverity).map((value) => ({
id: value,
label: intl.formatMessage({ id: `announcements.severity.${value}` }),
})),
[intl]
)}
fullWidth
/>
</Grid>
<Grid item xs={12} lg={6}>
<DateTimePickerElement<FormSchema>
name={END_DATE}
label={intl.formatMessage({ id: 'announcements.table.endDate' })}
transform={datetimePickerTransform}
timezone="system"
views={pickerView}
timeSteps={{ hours: 1, minutes: 1, seconds: 0 }}
disablePast
minDateTime={startDateValue ? new Date(startDateValue) : undefined}
<Grid item xs>
<TextareaAutosizeElement<FormSchema>
name={MESSAGE}
label={intl.formatMessage({ id: 'announcements.form.message' })}
rows={5} // why does it do nothing even if the field is set as multiline?!
fullWidth
//inputProps={{ maxLength: MESSAGE_MAX_LENGTH } satisfies Partial<HTMLInputElement>}
/>
</Grid>
<Grid item xs="auto">
<SubmitButton variant="outlined" type="submit" fullWidth />
</Grid>
</Grid>
<Grid item xs="auto">
<SelectElement<FormSchema>
name={SEVERITY}
label={intl.formatMessage({ id: 'announcements.severity' })}
options={useMemo(
() =>
Object.values(UserAdminSrv.AnnouncementSeverity).map((value) => ({
id: value,
label: intl.formatMessage({ id: `announcements.severity.${value}` }),
})),
[intl]
)}
fullWidth
/>
</Grid>
<Grid item xs>
<TextareaAutosizeElement<FormSchema>
name={MESSAGE}
label={intl.formatMessage({ id: 'announcements.form.message' })}
rows={5} // why does it do nothing even if the field is set as multiline?!
fullWidth
/>
</Grid>
<Grid item xs="auto">
<SubmitButton variant="outlined" type="submit" fullWidth />
</Grid>
</Grid>
</FormErrorProvider>
</FormContainer>
);
}
2 changes: 1 addition & 1 deletion src/pages/announcements/announcements-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default function AnnouncementsPage() {

// Note: using <Stack/> for the columns didn't work
return (
<Grid container spacing={2} marginLeft={'2px' /*Grid.spacing is in px?*/}>
<Grid container spacing={2} marginLeft={'2px' /*Grid.spacing is in px?*/} marginTop={1}>
<Grid item container direction="column" xs={12} sm={6} md={4}>
<Grid item xs="auto">
<Typography variant="subtitle1">
Expand Down
7 changes: 3 additions & 4 deletions src/pages/groups/modification/group-modification-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

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

export const GROUP_NAME = 'name';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ import ProfileModificationForm, {
USER_QUOTA_CASE_NB,
NETWORK_VISUALIZATION_PARAMETERS_ID,
} from './profile-modification-form';
import yup from '../../../utils/yup-config';
import { yupResolver } from '@hookform/resolvers/yup';
import { useForm } from 'react-hook-form';
import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react';
import { CustomMuiDialog, FetchStatus, useSnackMessage } from '@gridsuite/commons-ui';
import { CustomMuiDialog, FetchStatus, useSnackMessage, yupConfig as yup } from '@gridsuite/commons-ui';
import { UserAdminSrv, UserProfile } from '../../../services';
import { UUID } from 'crypto';

Expand Down
7 changes: 3 additions & 4 deletions src/pages/users/modification/user-modification-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

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

export const USER_NAME = 'sub';
Expand Down
9 changes: 6 additions & 3 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@
"linked.path.display.noLink": "no configuration selected.",
"linked.path.display.invalidLink": "invalid configurations link.",

"announcements.programNewMessage": "New message scheduling",
"announcements.programmedMessage": "Programmed messages",
"announcements.programNewMessage": "Schedule a new message",
"announcements.programmedMessage": "Scheduled messages",
"announcements.table.message": "Message",
"announcements.table.startDate": "Start date",
"announcements.table.endDate": "Stop date",
Expand All @@ -125,5 +125,8 @@
"announcements.form.errCreateAnnouncement": "Error while creating announcement:",
"announcements.form.errCreateAnnouncement.noOverlapAllowedErr": "The date overlaps with another announcement date.",
"announcements.form.errCreateAnnouncement.noSameDateErr": "The announcement start and end date must be different.",
"announcements.form.errCreateAnnouncement.startDateAfterEndDateErr": "The start date cannot be after the end date."
"announcements.form.errCreateAnnouncement.startDateAfterEndDateErr": "The start date cannot be after the end date.",
"announcements.form.errForm.startDateAfterEndDateErr": "End date must be after start date.",

"YupRequired": "This field is required"
}
7 changes: 5 additions & 2 deletions src/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,16 @@
"announcements.table.message": "Message",
"announcements.table.startDate": "Date de début",
"announcements.table.endDate": "Date de fin",
"announcements.table.cancel": "Annulé",
"announcements.table.cancel": "Annuler",
"announcements.severity": "Sévérité",
"announcements.severity.INFO": "Information",
"announcements.severity.WARN": "Avertissement",
"announcements.form.message": "Message d'annonce",
"announcements.form.errCreateAnnouncement": "Erreur lors de la création de l'annonce :",
"announcements.form.errCreateAnnouncement.noOverlapAllowedErr": "La date d'annonce chevauche une autre date d'annonce.",
"announcements.form.errCreateAnnouncement.noSameDateErr": "La date de début et de fin d'annonce doivent être différentes.",
"announcements.form.errCreateAnnouncement.startDateAfterEndDateErr": "La date de début d'annonce ne peut pas être après la date de fin."
"announcements.form.errCreateAnnouncement.startDateAfterEndDateErr": "La date de début d'annonce ne peut pas être après la date de fin.",
"announcements.form.errForm.startDateAfterEndDateErr": "La date de fin doit être après la date de début.",

"YupRequired": "Ce champ doit être renseigné"
}
25 changes: 0 additions & 25 deletions src/utils/yup-config.js

This file was deleted.

Loading