Skip to content

Commit

Permalink
Employ new range date picker
Browse files Browse the repository at this point in the history
  • Loading branch information
ivarnakken committed Dec 13, 2024
1 parent e972567 commit 216223b
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 127 deletions.
2 changes: 1 addition & 1 deletion app/components/Form/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const DatePicker = ({
if (open) {
onFocus();
} else {
handleBlur();
handleBlur(value);
}
};

Expand Down
1 change: 1 addition & 0 deletions app/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ export type TransformEvent = EventBase & {
hasFeedbackQuestion: boolean;
responsibleUsers: PublicUser[];
isForeignLanguage: boolean;
date: [Dateish, Dateish];
};

export type Workplace = {
Expand Down
37 changes: 14 additions & 23 deletions app/routes/events/components/EventAdministrate/Statistics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { usePreparedEffect } from '@webkom/react-prepare';
import moment from 'moment-timezone';
import { useState } from 'react';
import { fetchAllWithType } from 'app/actions/GroupActions';
import DatePicker from 'app/components/Form/DatePicker';
import { DatePicker } from 'app/components/Form';
import { GroupType, type Dateish } from 'app/models';
import EventAttendeeStatistics from 'app/routes/events/components/EventAttendeeStatistics';
import styles from 'app/routes/events/components/EventAttendeeStatistics.module.css';
Expand All @@ -17,41 +17,32 @@ const Statistics = () => {
[],
);

const [viewStartTime, setViewStartTime] = useState<Dateish>(
'2021-01-01T00:00:00.000Z',
);
const [viewEndTime, setViewEndTime] = useState<Dateish>(moment());

const updateViewStartDate = (date: string) => {
setViewStartTime(date);
};
const [dateRange, setDateRange] = useState<[Dateish, Dateish]>([
moment().subtract(1, 'month').toISOString(),
moment().toISOString(),
]);

const updateViewEndDate = (date: string) => {
setViewEndTime(date);
const updateDateRange = (selectedDate: [Dateish, Dateish]) => {
setDateRange(selectedDate);
};

return (
<>
<div className={styles.filterContainer}>
<label>Startdato for sidevisning</label>
<DatePicker
value={viewStartTime as string}
onChange={updateViewStartDate}
onBlur={() => {}}
onFocus={() => {}}
/>
<label>Sluttdato for sidevisning</label>
<label>Velg periode</label>
<DatePicker
value={viewEndTime as string}
onChange={updateViewEndDate}
range
value={dateRange}
onChange={updateDateRange}
showTimePicker={false}
onBlur={() => {}}
onFocus={() => {}}
/>
</div>

<EventAttendeeStatistics
viewStartTime={viewStartTime}
viewEndTime={viewEndTime}
viewStartTime={dateRange[0]}
viewEndTime={dateRange[1]}
/>
</>
);
Expand Down
13 changes: 3 additions & 10 deletions app/routes/events/components/EventEditor/EditorSection/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,9 @@ const Details: React.FC<Props> = ({ values }) => {
</Flex>
<Flex className={styles.editorSectionRow}>
<Field
label="Starter"
name="startTime"
component={DatePicker.Field}
fieldClassName={styles.metaField}
className={styles.formField}
required
/>
<Field
label="Slutter"
name="endTime"
label="Dato"
name="date"
range
component={DatePicker.Field}
fieldClassName={styles.metaField}
className={styles.formField}
Expand Down
27 changes: 17 additions & 10 deletions app/routes/events/components/EventEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import time from 'app/utils/time';
import {
conditionalValidation,
createValidator,
dateRequired,
datesAreInCorrectOrder,
isInteger,
legoEditorRequired,
maxSize,
Expand Down Expand Up @@ -108,8 +110,9 @@ const validate = createValidator({
],
registrationDeadlineHours: [isInteger('Kun hele timer')],
unregistrationDeadlineHours: [isInteger('Kun hele timer')],
endTime: [
timeIsAfter('startTime', 'Sluttidspunkt kan ikke være før starttidspunkt'),
date: [
dateRequired('Du må velge start- og sluttdato'),
datesAreInCorrectOrder('Sluttidspunkt kan ikke være før starttidspunkt'),
],
mergeTime: [
conditionalValidation(
Expand Down Expand Up @@ -245,6 +248,8 @@ const EventEditor = () => {
value: user.id,
})),
isForeignLanguage: event.isForeignLanguage,
date: event.startTime &&
event.endTime && [event.startTime, event.endTime],
eventType: event.eventType && {
label: displayNameForEventType(event.eventType),
value: event.eventType,
Expand All @@ -266,14 +271,16 @@ const EventEditor = () => {
}
: {
title: '',
startTime: time({
hours: 17,
minutes: 15,
}),
endTime: time({
hours: 20,
minutes: 15,
}),
date: [
time({
hours: 17,
minutes: 15,
}),
time({
hours: 20,
minutes: 15,
}),
],
description: '',
text: '',
eventType: '',
Expand Down
4 changes: 2 additions & 2 deletions app/routes/events/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ const calculateMergeTime = (data) =>
// Takes the full data-object and input and transforms the event to the API format.
export const transformEvent = (data: TransformEvent) => ({
...pick(data, eventCreateAndUpdateFields),
startTime: moment(data.startTime).toISOString(),
endTime: moment(data.endTime).toISOString(),
startTime: moment(data.date[0]).toISOString(),
endTime: moment(data.date[1]).toISOString(),
mergeTime: calculateMergeTime(data),
company: data.company && data.company.value,
eventStatusType: data.eventStatusType && data.eventStatusType.value,
Expand Down
92 changes: 46 additions & 46 deletions app/routes/meetings/components/MeetingEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ import { spyValues } from 'app/utils/formSpyUtils';
import { guardLogin } from 'app/utils/replaceUnlessLoggedIn';
import {
createValidator,
datesAreInCorrectOrder,
dateRequired,
ifField,
ifNotField,
legoEditorRequired,
required,
timeIsAfter,
} from 'app/utils/validation';
import type { EntityId } from '@reduxjs/toolkit';
import type { Dateish } from 'app/models';
import type { AutocompleteGroup } from 'app/store/models/Group';
import type { DetailedMeeting } from 'app/store/models/Meeting';
import type { AutocompleteUser } from 'app/store/models/User';
Expand All @@ -75,8 +77,7 @@ export type MeetingFormValues = {
title?: string;
report?: string;
description?: string;
startTime?: string;
endTime?: string;
date?: [Dateish, Dateish];
useMazemap: boolean;
mazemapPoi?: { value: number; label: string };
location?: string;
Expand All @@ -88,17 +89,16 @@ export type MeetingFormValues = {
const validate = createValidator({
title: [required('Du må gi møtet en tittel')],
report: [legoEditorRequired('Referatet kan ikke være tomt')],
date: [
dateRequired('Du må velge start- og sluttdato'),
datesAreInCorrectOrder('Sluttidspunkt kan ikke være før starttidspunkt'),
],
location: [
ifNotField('useMazemap', required('Sted eller Mazemap-rom er påkrevd')),
],
mazemapPoi: [
ifField('useMazemap', required('Sted eller Mazemap-rom er påkrevd')),
],
startTime: [required('Du må velge starttidspunkt')],
endTime: [
required('Du må velge sluttidspunkt'),
timeIsAfter('startTime', 'Sluttidspunkt kan ikke være før starttidspunkt'),
],
});

type MeetingEditorParams = {
Expand Down Expand Up @@ -184,25 +184,33 @@ const MeetingEditor = () => {
}))
: [];

const onSubmit = (values) =>
dispatch(isEditPage ? editMeeting(values) : createMeeting(values)).then(
(result) => {
const id = meetingId || result.payload.result;
const { groups, users } = values;

if (groups || users) {
return dispatch(
inviteUsersAndGroups({
id,
users,
groups,
}),
).then(() => navigate(`/meetings/${id}`));
}
const onSubmit = (values) => {
const formValues = {
...values,
startTime: values.date[0],
endTime: values.date[1],
};
delete formValues.date;

return dispatch(
isEditPage ? editMeeting(formValues) : createMeeting(formValues),
).then((result) => {
const id = meetingId || result.payload.result;
const { groups, users } = values;

if (groups || users) {
return dispatch(
inviteUsersAndGroups({
id,
users,
groups,
}),
).then(() => navigate(`/meetings/${id}`));
}

navigate(`/meetings/${id}`);
},
);
navigate(`/meetings/${id}`);
});
};

const onDeleteMeeting = isEditPage
? () =>
Expand All @@ -215,6 +223,7 @@ const MeetingEditor = () => {
const initialValues = isEditPage
? {
...meeting,
date: [meeting.startTime, meeting.endTime],
reportAuthor: reportAuthor && {
id: reportAuthor.id,
value: reportAuthor.username,
Expand All @@ -229,8 +238,7 @@ const MeetingEditor = () => {
useMazemap: meeting.mazemapPoi !== undefined && meeting.mazemapPoi > 0,
}
: {
startTime: time(16, 15),
endTime: time(18),
date: [time(16, 15), time(18)],
report: EDITOR_EMPTY,
useMazemap: true,
};
Expand Down Expand Up @@ -279,31 +287,23 @@ const MeetingEditor = () => {
<FormSpy subscription={{ values: true }}>
{({ values }) => (
<Field
name="startTime"
label="Starttidspunkt"
name="date"
label="Dato"
range
component={DatePicker.Field}
onBlur={(value: string) => {
const startTime = moment(value);
const endTime = moment(values.endTime);
onBlur={(value: [Dateish, Dateish]) => {
const startTime = moment(value[0]);
const endTime = moment(values.date[1]);
if (endTime.isBefore(startTime)) {
form.change(
'endTime',
startTime
.clone()
.add(2, 'hours')
.set('minute', 0)
.toISOString(),
);
form.change('date', [
startTime,
endTime.clone().add(2, 'hours').set('minute', 0),
]);
}
}}
/>
)}
</FormSpy>
<Field
name="endTime"
label="Sluttidspunkt"
component={DatePicker.Field}
/>
</div>
<Field
label="Bruk mazemap"
Expand Down
29 changes: 29 additions & 0 deletions app/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,32 @@ export function createValidator(
return merge(fieldValidatorErrors, rawValidatorErrors);
};
}

/* Used for DatePicker (supports both single date and range) */
export const dateRequired =
(message = 'Du må velge dato') =>
(value) => {
if (!value) return [false, message] as const;

if (Array.isArray(value)) {
// Range validation
return [value.length === 2 && value[0] && value[1], message] as const;
}

// Single date validation
return [!!value, message] as const;
};

/* Used for DatePicker (supports both single date and range) */
export const datesAreInCorrectOrder =
(message = 'Sluttidspunkt kan ikke være før starttidspunkt') =>
(value) => {
const firstDate = moment(value[0]);
const secondDate = moment(value[1]);

if (firstDate.isAfter(secondDate)) {
return [false, message] as const;
}

return [true] as const;
};
6 changes: 4 additions & 2 deletions cypress/e2e/create_event_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
selectFieldDropdown,
selectEditor,
setDatePickerDate,
setDatePickerTime,
uploadHeader,
NO_OPTIONS_MESSAGE,
} from '../support/utils.js';
Expand Down Expand Up @@ -262,8 +263,9 @@ describe('Create event', () => {
dateObject.setDate(dateObject.getDate() + 1);
const tomorrowDay = dateObject.getDate();

setDatePickerDate('startTime', tomorrowDay, tomorrowDay < todayDay);
setDatePickerDate('endTime', tomorrowDay, tomorrowDay < todayDay);
setDatePickerDate('date', tomorrowDay, tomorrowDay < todayDay);
setDatePickerTime('date', '10', '00', false); // Start time
setDatePickerTime('date', '12', '00', true); // End time

// Select regitrationType
selectField('eventStatusType').click();
Expand Down
Loading

0 comments on commit 216223b

Please sign in to comment.