Skip to content

Commit bfda00a

Browse files
authored
fix: add blackout dates validation for non supported browsers (#203)
* fix: add blackout dates validation for non supported browsers * refactor: update functions name
1 parent 7464aa5 commit bfda00a

File tree

5 files changed

+121
-24
lines changed

5 files changed

+121
-24
lines changed

src/pages-and-resources/discussions/app-config-form/apps/legacy/LegacyConfigForm.jsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,24 @@ function LegacyConfigForm({
2424
const legacyFormValidationSchema = Yup.object().shape({
2525
blackoutDates: Yup.array(
2626
Yup.object().shape({
27-
startDate: Yup.date().required(intl.formatMessage(messages.blackoutStartDateRequired)),
28-
endDate: Yup.date().required(intl.formatMessage(messages.blackoutEndDateRequired)).when('startDate', {
27+
startDate: Yup.string().checkFormat(
28+
intl.formatMessage(messages.blackoutStartDateInValidFormat), 'date',
29+
).required(intl.formatMessage(messages.blackoutStartDateRequired)),
30+
endDate: Yup.string().checkFormat(
31+
intl.formatMessage(messages.blackoutEndDateInValidFormat), 'date',
32+
).required(intl.formatMessage(messages.blackoutEndDateRequired)).when('startDate', {
2933
is: (startDate) => startDate,
30-
then: Yup.date().min(Yup.ref('startDate'), intl.formatMessage(messages.blackoutEndDateInPast)),
34+
then: Yup.string().compare(intl.formatMessage(messages.blackoutEndDateInPast), 'date'),
35+
}),
36+
startTime: Yup.string().checkFormat(
37+
intl.formatMessage(messages.blackoutStartTimeInValidFormat), 'time',
38+
),
39+
endTime: Yup.string().checkFormat(
40+
intl.formatMessage(messages.blackoutEndTimeInValidFormat), 'time',
41+
).when('startTime', {
42+
is: (startTime) => startTime,
43+
then: Yup.string().compare(intl.formatMessage(messages.blackoutEndTimeInPast), 'time'),
3144
}),
32-
startTime: Yup.string(),
33-
endTime: Yup.string().compare(intl.formatMessage(messages.blackoutEndTimeInPast)),
3445
}),
3546
),
3647
discussionTopics: Yup.array(

src/pages-and-resources/discussions/app-config-form/apps/shared/messages.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,26 @@ const messages = defineMessages({
270270
defaultMessage: 'End time cannot be before start time',
271271
description: 'Tells the user that the blackout end time cannot be in past and cannot be before start time',
272272
},
273+
blackoutStartTimeInValidFormat: {
274+
id: 'authoring.blackoutDates.startTime.inValidFormat',
275+
defaultMessage: 'Enter a valid start time',
276+
description: 'Tells the user that the blackout start time format is in valid',
277+
},
278+
blackoutEndTimeInValidFormat: {
279+
id: 'authoring.blackoutDates.endTime.inValidFormat',
280+
defaultMessage: 'Enter a valid end time',
281+
description: 'Tells the user that the blackout end time format is in valid',
282+
},
283+
blackoutStartDateInValidFormat: {
284+
id: 'authoring.blackoutDates.startDate.inValidFormat',
285+
defaultMessage: 'Enter a valid start Date',
286+
description: 'Tells the user that the blackout start date format is in valid',
287+
},
288+
blackoutEndDateInValidFormat: {
289+
id: 'authoring.blackoutDates.endDate.inValidFormat',
290+
defaultMessage: 'Enter a valid end date',
291+
description: 'Tells the user that the blackout end date format is in valid',
292+
},
273293
deleteAltText: {
274294
id: 'authoring.topics.delete',
275295
defaultMessage: 'Delete Topic',

src/pages-and-resources/discussions/app-config-form/utils.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import moment from 'moment';
22
import _ from 'lodash';
33
import { getIn } from 'formik';
4-
54
import { blackoutDatesStatus as constants } from '../data/constants';
65

76
export const filterItemFromObject = (array, key, value) => (
@@ -27,14 +26,29 @@ export const checkStatus = ([startDate, endDate]) => {
2726
return status;
2827
};
2928

30-
export const formatDate = (date, time) => (time ? `${date}T${time}` : date);
29+
export const validTimeFormats = ['hh:mm A', 'HH:mm'];
30+
export const mergeDateTime = (date, time) => ((date && time) ? `${date}T${time}` : date);
3131
export const isSameDay = (startDate, endDate) => moment(startDate).isSame(endDate, 'day');
3232
export const isSameMonth = (startDate, endDate) => moment(startDate).isSame(endDate, 'month');
3333
export const isSameYear = (startDate, endDate) => moment(startDate).isSame(endDate, 'year');
34+
export const getTime = (dateTime) => dateTime.split('T')[1] || '';
35+
export const hasValidDateFormat = (date) => moment(date, ['MM/DD/YYYY', 'YYYY-MM-DD'], true).isValid();
36+
export const hasValidTimeFormat = (time) => time && moment(time, validTimeFormats, true).isValid();
37+
export const normalizeTime = (time) => time && moment(time, validTimeFormats, true).format('HH:mm');
38+
export const normalizeDate = (date) => moment(
39+
date, ['MM/DD/YYYY', 'YYYY-MM-DDTHH:mm', 'YYYY-MM-DD'], true,
40+
).format('YYYY-MM-DD');
41+
42+
export const decodeDateTime = (date, time) => {
43+
const nDate = normalizeDate(date);
44+
const nTime = normalizeTime(time);
45+
46+
return moment(mergeDateTime(nDate, nTime));
47+
};
3448

3549
export const sortBlackoutDatesByStatus = (data, status, order) => (
3650
_.orderBy(data.filter(date => date.status === status),
37-
[(obj) => moment(formatDate(obj.startDate, obj.startTime))], [order])
51+
[(obj) => decodeDateTime(obj.startDate, obj.startTime)], [order])
3852
);
3953

4054
export const formatBlackoutDates = ({
@@ -47,8 +61,8 @@ export const formatBlackoutDates = ({
4761
const isTimeAvailable = Boolean(startTime && endTime);
4862
const mStartDate = moment(startDate);
4963
const mEndDate = moment(endDate);
50-
const mStartDateTime = moment(`${startDate}T${startTime}`);
51-
const mEndDateTime = moment(`${endDate}T${endTime}`);
64+
const mStartDateTime = decodeDateTime(startDate, startTime);
65+
const mEndDateTime = decodeDateTime(endDate, endTime);
5266

5367
if (hasSameDay && !isTimeAvailable) {
5468
formattedDate = mStartDate.format('MMMM D, YYYY');

src/pages-and-resources/discussions/data/api.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { getConfig, camelCaseObject } from '@edx/frontend-platform';
22
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
33
import _ from 'lodash';
4-
import moment from 'moment';
54
import { v4 as uuid } from 'uuid';
65

7-
import { checkStatus, sortBlackoutDatesByStatus, formatDate } from '../app-config-form/utils';
6+
import {
7+
checkStatus,
8+
sortBlackoutDatesByStatus,
9+
mergeDateTime,
10+
normalizeDate,
11+
normalizeTime,
12+
getTime,
13+
} from '../app-config-form/utils';
814
import { blackoutDatesStatus as constants } from './constants';
915

1016
function normalizeLtiConfig(data) {
@@ -43,10 +49,10 @@ export function normalizeBlackoutDates(data) {
4349

4450
const normalizeData = data.map(([startDate, endDate]) => ({
4551
id: uuid(),
46-
startDate: moment(startDate).format('YYYY-MM-DD'),
47-
startTime: startDate.split('T')[1] || '',
48-
endDate: moment(endDate).format('YYYY-MM-DD'),
49-
endTime: endDate.split('T')[1] || '',
52+
startDate: normalizeDate(startDate),
53+
startTime: getTime(startDate),
54+
endDate: normalizeDate(endDate),
55+
endTime: getTime(endDate),
5056
status: checkStatus([startDate, endDate]),
5157
}));
5258

@@ -119,10 +125,16 @@ function normalizeApps(data) {
119125
};
120126
}
121127

122-
export function denormalizeBlackoutDate(date) {
128+
export function denormalizeBlackoutDate(blackoutPeriod) {
123129
return [
124-
formatDate(date.startDate, date.startTime),
125-
formatDate(date.endDate, date.endTime),
130+
mergeDateTime(
131+
normalizeDate(blackoutPeriod.startDate),
132+
normalizeTime(blackoutPeriod.startTime),
133+
),
134+
mergeDateTime(
135+
normalizeDate(blackoutPeriod.endDate),
136+
normalizeTime(blackoutPeriod.endTime),
137+
),
126138
];
127139
}
128140

src/utils.js

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import { useContext, useEffect } from 'react';
33
import { useDispatch, useSelector } from 'react-redux';
44
import { useMediaQuery } from 'react-responsive';
55
import * as Yup from 'yup';
6-
import moment from 'moment';
76

87
import { RequestStatus } from './data/constants';
98
import { getCourseAppSettingValue, getLoadingStatus } from './pages-and-resources/data/selectors';
109
import { fetchCourseAppSettings, updateCourseAppSetting } from './pages-and-resources/data/thunks';
1110
import { PagesAndResourcesContext } from './pages-and-resources/PagesAndResourcesProvider';
11+
import {
12+
hasValidDateFormat, hasValidTimeFormat, decodeDateTime,
13+
} from './pages-and-resources/discussions/app-config-form/utils';
1214

1315
export const executeThunk = async (thunk, dispatch, getState) => {
1416
await thunk(dispatch, getState);
@@ -91,13 +93,28 @@ export function setupYupExtensions() {
9193
});
9294
});
9395

94-
Yup.addMethod(Yup.string, 'compare', function compare(message) {
96+
Yup.addMethod(Yup.string, 'compare', function compare(message, type) {
9597
return this.test('isGreater', message, function isGreater() {
96-
if (!this.parent || !this.parent.startTime || !this.parent.endTime) {
98+
// This function compare 2 dates or 2 times. It return no error if dateInstance/timeInstance is empty
99+
// of if startTime or endTime is not present for time comparesion
100+
// or startDate or endDate is not present for date comparesion
101+
102+
if (!this.parent
103+
|| (!(this.parent.startTime && this.parent.endTime) && type === 'time')
104+
|| (!(this.parent.startDate && this.parent.endDate) && type === 'date')
105+
) {
97106
return true;
98107
}
99-
const isInvalidStartDateTime = moment(`${moment(this.parent.startDate).format('YYYY-MM-DD')}T${this.parent.startTime}`)
100-
.isSameOrAfter(moment(`${moment(this.parent.endDate).format('YYYY-MM-DD')}T${this.parent.endTime}`));
108+
109+
const startDateTime = decodeDateTime(this.parent.startDate, this.parent.startTime);
110+
const endDateTime = decodeDateTime(this.parent.endDate, this.parent.endTime);
111+
let isInvalidStartDateTime;
112+
113+
if (type === 'date') {
114+
isInvalidStartDateTime = startDateTime.isAfter(endDateTime);
115+
} else if (type === 'time') {
116+
isInvalidStartDateTime = startDateTime.isSameOrAfter(endDateTime);
117+
}
101118

102119
if (isInvalidStartDateTime) {
103120
throw this.createError({
@@ -108,4 +125,27 @@ export function setupYupExtensions() {
108125
return true;
109126
});
110127
});
128+
129+
Yup.addMethod(Yup.string, 'checkFormat', function checkFormat(message, type) {
130+
return this.test('isValidFormat', message, function isValidFormat() {
131+
if (!this.originalValue) {
132+
return true;
133+
}
134+
let isValid;
135+
136+
if (type === 'date') {
137+
isValid = hasValidDateFormat(this.originalValue);
138+
} else if (type === 'time') {
139+
isValid = hasValidTimeFormat(this.originalValue);
140+
}
141+
142+
if (!isValid) {
143+
throw this.createError({
144+
path: `${this.path}`,
145+
error: message,
146+
});
147+
}
148+
return true;
149+
});
150+
});
111151
}

0 commit comments

Comments
 (0)