1 import { useMemo, useState } from 'react';
3 import { c } from 'ttag';
7 MemoizedIconRow as IconRow,
12 } from '@proton/components';
13 import { DATE_INPUT_ID, MAXIMUM_DATE, MINIMUM_DATE } from '@proton/shared/lib/calendar/constants';
14 import type { WeekStartsOn } from '@proton/shared/lib/date-fns-utc/interface';
15 import { convertUTCDateTimeToZone, fromUTCDate, toUTCDate } from '@proton/shared/lib/date/timezone';
16 import type { EventModel } from '@proton/shared/lib/interfaces/calendar';
17 import clsx from '@proton/utils/clsx';
19 import getFrequencyModelChange from '../eventForm/getFrequencyModelChange';
20 import { getAllDayCheck } from '../eventForm/stateActions';
21 import { getDateTime, getDateTimeState, getTimeInUtc } from '../eventForm/time';
22 import useDateTimeFormHandlers from '../hooks/useDateTimeFormHandlers';
23 import AllDayCheckbox from '../inputs/AllDayCheckbox';
27 setModel: (value: EventModel) => void;
28 displayWeekNumbers: boolean;
29 weekStartsOn: WeekStartsOn;
34 export const DateTimeRow = ({ model, setModel, displayWeekNumbers, weekStartsOn, endError, tzid }: Props) => {
35 const { start, end, frequencyModel, isAllDay } = model;
37 handleChangeStartDate,
38 handleChangeStartTime,
44 } = useDateTimeFormHandlers({ model, setModel });
45 const canToggleTzSelector = start.tzid === end.tzid && start.tzid === tzid;
46 const [showTzSelector, setShowTzSelector] = useState<boolean>(!canToggleTzSelector);
47 const handleChangeStart = (tzid: string) => {
48 const startUtcDate = getTimeInUtc(start, false);
49 const newStartUtcDate = toUTCDate(convertUTCDateTimeToZone(fromUTCDate(startUtcDate), tzid));
50 const newStart = getDateTimeState(newStartUtcDate, tzid);
51 const newFrequencyModel = getFrequencyModelChange(start, newStart, frequencyModel);
56 frequencyModel: newFrequencyModel,
59 const handleChangeEnd = (tzid: string) => {
60 const endUtcDate = getTimeInUtc(end, false);
61 const newEndUtcDate = toUTCDate(convertUTCDateTimeToZone(fromUTCDate(endUtcDate), tzid));
65 end: getDateTimeState(newEndUtcDate, tzid),
69 const startDateTime = useMemo(() => getDateTime(start), [start]);
70 const endDateTime = useMemo(() => getDateTime(end), [end]);
72 const { viewportWidth } = useActiveBreakpoint();
75 <IconRow id={DATE_INPUT_ID} icon="clock" title={c('Label').t`Date and time`}>
76 <div className={clsx([isAllDay && 'w-full md:w-1/2'])}>
77 <div className="flex flex-nowrap flex-column md:flex-row mb-2">
78 <div className="flex flex-nowrap md:flex-1 grow">
79 <div className="flex *:min-size-auto flex-1 grow-custom" style={{ '--grow-custom': '1.25' }}>
82 className={clsx(['flex-1', viewportWidth['<=small'] && 'h-full'])}
85 onChange={handleChangeStartDate}
86 displayWeekNumbers={displayWeekNumbers}
87 weekStartsOn={weekStartsOn}
90 title={c('Title').t`Select event start date`}
95 <div className="ml-2 flex-1">
99 onChange={handleChangeStartTime}
100 title={c('Title').t`Select event start time`}
106 {!isAllDay && showTzSelector && (
108 className="field ml-0 md:ml-2 mt-2 md:mt-0 mb-2 md:mb-2 md:flex-1"
109 id="event-start-timezone-select"
110 data-testid="create-event-modal/start:time-zone-dropdown"
111 timezone={start.tzid}
112 onChange={handleChangeStart}
114 title={c('Title').t`Select the time zone for the event start time`}
115 telemetrySource="event_start"
120 <div className="flex flex-nowrap flex-column md:flex-row mb-2">
121 <div className="flex flex-nowrap md:flex-1 grow">
122 <div className="flex *:min-size-auto flex-1 grow-custom" style={{ '--grow-custom': '1.25' }}>
125 className={clsx(['flex-1', viewportWidth['<=small'] && 'h-full'])}
128 onChange={handleChangeEndDate}
129 aria-invalid={!!endError}
130 displayWeekNumbers={displayWeekNumbers}
131 weekStartsOn={weekStartsOn}
134 title={c('Title').t`Select event end date`}
139 <div className="ml-2 flex-1">
143 onChange={handleChangeEndTime}
144 aria-invalid={!!endError}
145 displayDuration={isDuration}
147 title={c('Title').t`Select event end time`}
153 {!isAllDay && showTzSelector && (
155 className="field ml-0 md:ml-2 mt-2 md:mt-0 mb-2 md:mb-2 md:flex-1"
156 id="event-end-timezone-select"
157 data-testid="create-event-modal/end:time-zone-dropdown"
159 onChange={handleChangeEnd}
161 title={c('Title').t`Select the time zone for the event end time`}
162 telemetrySource="event_end"
168 <div className="flex justify-space-between gap-2">
172 ? c('Title').t`Event is happening on defined time slot`
173 : c('Title').t`Event is happening all day`
176 onChange={(isAllDay) => setModel({ ...model, ...getAllDayCheck({ oldModel: model, isAllDay }) })}
180 canToggleTzSelector &&
184 data-testid="hide-tz"
185 onClick={() => setShowTzSelector(false)}
186 title={c('Title').t`Hide time zones for event start and end times`}
188 {c('Action').t`Hide time zones`}
193 data-testid="show-tz"
194 onClick={() => setShowTzSelector(true)}
195 title={c('Title').t`Show time zones for event start and end times`}
197 {c('Action').t`Show time zones`}