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
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,24 @@ function LegacyConfigForm({
const legacyFormValidationSchema = Yup.object().shape({
blackoutDates: Yup.array(
Yup.object().shape({
startDate: Yup.date().required(intl.formatMessage(messages.blackoutStartDateRequired)),
endDate: Yup.date().required(intl.formatMessage(messages.blackoutEndDateRequired)).when('startDate', {
startDate: Yup.string().checkFormat(
intl.formatMessage(messages.blackoutStartDateInValidFormat), 'date',
).required(intl.formatMessage(messages.blackoutStartDateRequired)),
endDate: Yup.string().checkFormat(
intl.formatMessage(messages.blackoutEndDateInValidFormat), 'date',
).required(intl.formatMessage(messages.blackoutEndDateRequired)).when('startDate', {
is: (startDate) => startDate,
then: Yup.date().min(Yup.ref('startDate'), intl.formatMessage(messages.blackoutEndDateInPast)),
then: Yup.string().compare(intl.formatMessage(messages.blackoutEndDateInPast), 'date'),
}),
startTime: Yup.string().checkFormat(
intl.formatMessage(messages.blackoutStartTimeInValidFormat), 'time',
),
endTime: Yup.string().checkFormat(
intl.formatMessage(messages.blackoutEndTimeInValidFormat), 'time',
).when('startTime', {
is: (startTime) => startTime,
then: Yup.string().compare(intl.formatMessage(messages.blackoutEndTimeInPast), 'time'),
}),
startTime: Yup.string(),
endTime: Yup.string().compare(intl.formatMessage(messages.blackoutEndTimeInPast)),
}),
),
discussionTopics: Yup.array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,26 @@ const messages = defineMessages({
defaultMessage: 'End time cannot be before start time',
description: 'Tells the user that the blackout end time cannot be in past and cannot be before start time',
},
blackoutStartTimeInValidFormat: {
id: 'authoring.blackoutDates.startTime.inValidFormat',
defaultMessage: 'Enter a valid start time',
description: 'Tells the user that the blackout start time format is in valid',
},
blackoutEndTimeInValidFormat: {
id: 'authoring.blackoutDates.endTime.inValidFormat',
defaultMessage: 'Enter a valid end time',
description: 'Tells the user that the blackout end time format is in valid',
},
blackoutStartDateInValidFormat: {
id: 'authoring.blackoutDates.startDate.inValidFormat',
defaultMessage: 'Enter a valid start Date',
description: 'Tells the user that the blackout start date format is in valid',
},
blackoutEndDateInValidFormat: {
id: 'authoring.blackoutDates.endDate.inValidFormat',
defaultMessage: 'Enter a valid end date',
description: 'Tells the user that the blackout end date format is in valid',
},
deleteAltText: {
id: 'authoring.topics.delete',
defaultMessage: 'Delete Topic',
Expand Down
24 changes: 19 additions & 5 deletions src/pages-and-resources/discussions/app-config-form/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import moment from 'moment';
import _ from 'lodash';
import { getIn } from 'formik';

import { blackoutDatesStatus as constants } from '../data/constants';

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

export const formatDate = (date, time) => (time ? `${date}T${time}` : date);
export const validTimeFormats = ['hh:mm A', 'HH:mm'];
export const mergeDateTime = (date, time) => ((date && time) ? `${date}T${time}` : date);
export const isSameDay = (startDate, endDate) => moment(startDate).isSame(endDate, 'day');
export const isSameMonth = (startDate, endDate) => moment(startDate).isSame(endDate, 'month');
export const isSameYear = (startDate, endDate) => moment(startDate).isSame(endDate, 'year');
export const getTime = (dateTime) => dateTime.split('T')[1] || '';
export const hasValidDateFormat = (date) => moment(date, ['MM/DD/YYYY', 'YYYY-MM-DD'], true).isValid();
export const hasValidTimeFormat = (time) => time && moment(time, validTimeFormats, true).isValid();
export const normalizeTime = (time) => time && moment(time, validTimeFormats, true).format('HH:mm');
export const normalizeDate = (date) => moment(
date, ['MM/DD/YYYY', 'YYYY-MM-DDTHH:mm', 'YYYY-MM-DD'], true,
).format('YYYY-MM-DD');

export const decodeDateTime = (date, time) => {
const nDate = normalizeDate(date);
const nTime = normalizeTime(time);

return moment(mergeDateTime(nDate, nTime));
};

export const sortBlackoutDatesByStatus = (data, status, order) => (
_.orderBy(data.filter(date => date.status === status),
[(obj) => moment(formatDate(obj.startDate, obj.startTime))], [order])
[(obj) => decodeDateTime(obj.startDate, obj.startTime)], [order])
);

export const formatBlackoutDates = ({
Expand All @@ -47,8 +61,8 @@ export const formatBlackoutDates = ({
const isTimeAvailable = Boolean(startTime && endTime);
const mStartDate = moment(startDate);
const mEndDate = moment(endDate);
const mStartDateTime = moment(`${startDate}T${startTime}`);
const mEndDateTime = moment(`${endDate}T${endTime}`);
const mStartDateTime = decodeDateTime(startDate, startTime);
const mEndDateTime = decodeDateTime(endDate, endTime);

if (hasSameDay && !isTimeAvailable) {
formattedDate = mStartDate.format('MMMM D, YYYY');
Expand Down
30 changes: 21 additions & 9 deletions src/pages-and-resources/discussions/data/api.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { getConfig, camelCaseObject } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import _ from 'lodash';
import moment from 'moment';
import { v4 as uuid } from 'uuid';

import { checkStatus, sortBlackoutDatesByStatus, formatDate } from '../app-config-form/utils';
import {
checkStatus,
sortBlackoutDatesByStatus,
mergeDateTime,
normalizeDate,
normalizeTime,
getTime,
} from '../app-config-form/utils';
import { blackoutDatesStatus as constants } from './constants';

function normalizeLtiConfig(data) {
Expand Down Expand Up @@ -43,10 +49,10 @@ export function normalizeBlackoutDates(data) {

const normalizeData = data.map(([startDate, endDate]) => ({
id: uuid(),
startDate: moment(startDate).format('YYYY-MM-DD'),
startTime: startDate.split('T')[1] || '',
endDate: moment(endDate).format('YYYY-MM-DD'),
endTime: endDate.split('T')[1] || '',
startDate: normalizeDate(startDate),
startTime: getTime(startDate),
endDate: normalizeDate(endDate),
endTime: getTime(endDate),
status: checkStatus([startDate, endDate]),
}));

Expand Down Expand Up @@ -119,10 +125,16 @@ function normalizeApps(data) {
};
}

export function denormalizeBlackoutDate(date) {
export function denormalizeBlackoutDate(blackoutPeriod) {
return [
formatDate(date.startDate, date.startTime),
formatDate(date.endDate, date.endTime),
mergeDateTime(
normalizeDate(blackoutPeriod.startDate),
normalizeTime(blackoutPeriod.startTime),
),
mergeDateTime(
normalizeDate(blackoutPeriod.endDate),
normalizeTime(blackoutPeriod.endTime),
),
];
}

Expand Down
50 changes: 45 additions & 5 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { useContext, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useMediaQuery } from 'react-responsive';
import * as Yup from 'yup';
import moment from 'moment';

import { RequestStatus } from './data/constants';
import { getCourseAppSettingValue, getLoadingStatus } from './pages-and-resources/data/selectors';
import { fetchCourseAppSettings, updateCourseAppSetting } from './pages-and-resources/data/thunks';
import { PagesAndResourcesContext } from './pages-and-resources/PagesAndResourcesProvider';
import {
hasValidDateFormat, hasValidTimeFormat, decodeDateTime,
} from './pages-and-resources/discussions/app-config-form/utils';

export const executeThunk = async (thunk, dispatch, getState) => {
await thunk(dispatch, getState);
Expand Down Expand Up @@ -91,13 +93,28 @@ export function setupYupExtensions() {
});
});

Yup.addMethod(Yup.string, 'compare', function compare(message) {
Yup.addMethod(Yup.string, 'compare', function compare(message, type) {
return this.test('isGreater', message, function isGreater() {
if (!this.parent || !this.parent.startTime || !this.parent.endTime) {
// This function compare 2 dates or 2 times. It return no error if dateInstance/timeInstance is empty
// of if startTime or endTime is not present for time comparesion
// or startDate or endDate is not present for date comparesion

if (!this.parent
|| (!(this.parent.startTime && this.parent.endTime) && type === 'time')
|| (!(this.parent.startDate && this.parent.endDate) && type === 'date')
) {
return true;
}
const isInvalidStartDateTime = moment(`${moment(this.parent.startDate).format('YYYY-MM-DD')}T${this.parent.startTime}`)
.isSameOrAfter(moment(`${moment(this.parent.endDate).format('YYYY-MM-DD')}T${this.parent.endTime}`));

const startDateTime = decodeDateTime(this.parent.startDate, this.parent.startTime);
const endDateTime = decodeDateTime(this.parent.endDate, this.parent.endTime);
let isInvalidStartDateTime;

if (type === 'date') {
isInvalidStartDateTime = startDateTime.isAfter(endDateTime);
} else if (type === 'time') {
isInvalidStartDateTime = startDateTime.isSameOrAfter(endDateTime);
}

if (isInvalidStartDateTime) {
throw this.createError({
Expand All @@ -108,4 +125,27 @@ export function setupYupExtensions() {
return true;
});
});

Yup.addMethod(Yup.string, 'checkFormat', function checkFormat(message, type) {
return this.test('isValidFormat', message, function isValidFormat() {
if (!this.originalValue) {
return true;
}
let isValid;

if (type === 'date') {
isValid = hasValidDateFormat(this.originalValue);
} else if (type === 'time') {
isValid = hasValidTimeFormat(this.originalValue);
}

if (!isValid) {
throw this.createError({
path: `${this.path}`,
error: message,
});
}
return true;
});
});
}