fix(content-releases): check time has not passed if date is today (#20679)

* fix(content-releases): check time has not passed if date is today

* fix(content-releases): apply simone's feedback
This commit is contained in:
Fernando Chávez 2024-07-09 10:30:33 +02:00 committed by GitHub
parent a5a7edb16a
commit f79be3e52e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 69 additions and 32 deletions

View File

@ -17,7 +17,7 @@ import {
import { formatISO } from 'date-fns';
import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
import { Formik, Form, useFormikContext } from 'formik';
import { useIntl } from 'react-intl';
import { MessageDescriptor, useIntl } from 'react-intl';
import { useLocation } from 'react-router-dom';
import { pluginId } from '../pluginId';
@ -111,7 +111,14 @@ export const ReleaseModal = ({
<Form>
<Modal.Body>
<Flex direction="column" alignItems="stretch" gap={6}>
<Field.Root name="name" error={errors.name} required>
<Field.Root
name="name"
error={
errors.name &&
formatMessage({ id: errors.name, defaultMessage: errors.name })
}
required
>
<Field.Label>
{formatMessage({
id: 'content-releases.modal.form.input.label.release-name',
@ -158,7 +165,14 @@ export const ReleaseModal = ({
<>
<Flex gap={4} alignItems="start">
<Box width="100%">
<Field.Root name="date" error={errors.date} required>
<Field.Root
name="date"
error={
errors.date &&
formatMessage({ id: errors.date, defaultMessage: errors.date })
}
required
>
<Field.Label>
{formatMessage({
id: 'content-releases.modal.form.input.label.date',
@ -186,7 +200,14 @@ export const ReleaseModal = ({
</Field.Root>
</Box>
<Box width="100%">
<Field.Root name="time" error={errors.time} required>
<Field.Root
name="time"
error={
errors.time &&
formatMessage({ id: errors.time, defaultMessage: errors.time })
}
required
>
<Field.Label>
{formatMessage({
id: 'content-releases.modal.form.input.label.time',
@ -269,7 +290,13 @@ const TimezoneComponent = ({ timezoneOptions }: { timezoneOptions: ITimezoneOpti
}, [setFieldValue, values.date, values.timezone]);
return (
<Field.Root name="timezone" error={errors.timezone} required>
<Field.Root
name="timezone"
error={
errors.timezone && formatMessage({ id: errors.timezone, defaultMessage: errors.timezone })
}
required
>
<Field.Label>
{formatMessage({
id: 'content-releases.modal.form.input.label.timezone',

View File

@ -28,6 +28,7 @@ import {
} from '@strapi/design-system';
import { Plus } from '@strapi/icons';
import { EmptyDocuments } from '@strapi/icons/symbols';
import { format } from 'date-fns';
import { useIntl } from 'react-intl';
import { useNavigate, useLocation, NavLink } from 'react-router-dom';
import { styled } from 'styled-components';
@ -170,7 +171,7 @@ const StyledAlert = styled(Alert)`
const INITIAL_FORM_VALUES = {
name: '',
date: undefined,
date: format(new Date(), 'yyyy-MM-dd'),
time: '',
isScheduled: true,
scheduledAt: null,

View File

@ -51,6 +51,7 @@
"modal.form.input.label.timezone": "Timezone",
"modal.form.input.clearLabel": "Clear",
"modal.form.button.submit": "{isCreatingRelease, select, true {Continue} other {Save}}",
"modal.form.time.has-passed": "Selected time has already passed.",
"pages.Details.header-subtitle": "{number, plural, =0 {No entries} one {# entry} other {# entries}}",
"pages.Releases.tab-group.label": "Releases list",
"pages.Releases.tab.pending": "Pending ({count})",

View File

@ -1,43 +1,51 @@
import { translatedErrors } from '@strapi/admin/strapi-admin';
import { zonedTimeToUtc } from 'date-fns-tz';
import * as yup from 'yup';
/**
* FormikErrors type enforce us to always return a string as error.
* We need these errors to be translated, so we need to create a hook to be able to use the formatMessage function.
*/
export const RELEASE_SCHEMA = yup
.object()
.shape({
name: yup.string().trim().required({
id: translatedErrors.required.id,
defaultMessage: 'This field is required',
}),
name: yup.string().trim().required(translatedErrors.required.id),
scheduledAt: yup.string().nullable(),
isScheduled: yup.boolean().optional(),
time: yup.string().when('isScheduled', {
is: true,
then: yup.string().trim().required({
id: translatedErrors.required.id,
defaultMessage: 'This field is required',
}),
otherwise: yup.string().nullable(),
}),
time: yup
.string()
.when('isScheduled', {
is: true,
then: yup.string().trim().required(translatedErrors.required.id),
otherwise: yup.string().nullable(),
})
.test(
'time-in-future-if-today',
'content-releases.modal.form.time.has-passed',
function (time) {
const { date, timezone } = this.parent;
if (!date || !timezone || !time) {
return true;
}
// Timezone is in format "UTC&Europe/Paris", so we get the region part for the dates functions
const region = timezone.split('&')[1];
const selectedTime = zonedTimeToUtc(`${date} ${time}`, region);
const now = new Date();
return selectedTime > now;
}
),
timezone: yup.string().when('isScheduled', {
is: true,
then: yup
.string()
.required({
id: translatedErrors.required.id,
defaultMessage: 'This field is required',
})
.nullable(),
then: yup.string().required(translatedErrors.required.id).nullable(),
otherwise: yup.string().nullable(),
}),
date: yup.string().when('isScheduled', {
is: true,
then: yup
.string()
.required({
id: translatedErrors.required.id,
defaultMessage: 'This field is required',
})
.nullable(),
then: yup.string().required(translatedErrors.required.id).nullable(),
otherwise: yup.string().nullable(),
}),
})