Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / applications / calendar / src / app / components / eventModal / rows / EndsRow.tsx
blob077c17d6784421b3600c0503ab9f092ebcab7fb4
1 import { isValid } from 'date-fns';
2 import { c, msgid } from 'ttag';
4 import { DateInput, IntegerInput, Option, SelectTwo } from '@proton/components';
5 import { END_TYPE, FREQUENCY_COUNT_MAX, MAXIMUM_DATE } from '@proton/shared/lib/calendar/constants';
6 import type { WeekStartsOn } from '@proton/shared/lib/date-fns-utc/interface';
7 import type { DateTimeModel, EventModelErrors, FrequencyModel } from '@proton/shared/lib/interfaces/calendar';
8 import clsx from '@proton/utils/clsx';
10 const { NEVER, UNTIL, AFTER_N_TIMES } = END_TYPE;
12 export const UNTIL_ID = 'event-occurrence-until';
13 export const COUNT_ID = 'event-occurrence-count';
15 interface Props {
16     frequencyModel: FrequencyModel;
17     start: DateTimeModel;
18     displayWeekNumbers?: boolean;
19     weekStartsOn: WeekStartsOn;
20     errors: EventModelErrors;
21     isSubmitted: boolean;
22     onChange: (value: FrequencyModel) => void;
23     displayStacked?: boolean;
25 const EndsRow = ({
26     frequencyModel,
27     start,
28     displayWeekNumbers,
29     weekStartsOn,
30     errors,
31     isSubmitted,
32     onChange,
33     displayStacked = false,
34 }: Props) => {
35     const handleChangeEndType = (type: END_TYPE) => {
36         onChange({ ...frequencyModel, ends: { ...frequencyModel.ends, type } });
37     };
38     const handleChangeEndCount = (count: number | undefined) => {
39         if (count !== undefined && (count > FREQUENCY_COUNT_MAX || count < 1)) {
40             return;
41         }
42         onChange({ ...frequencyModel, ends: { ...frequencyModel.ends, count } });
43     };
44     const handleChangeEndUntil = (until: Date | undefined) => {
45         if (!until || !isValid(until)) {
46             return;
47         }
48         onChange({ ...frequencyModel, ends: { ...frequencyModel.ends, until } });
49     };
51     const safeCountPlural = frequencyModel.ends.count || 1; // Can get undefined through the input
53     const options = [
54         {
55             value: NEVER,
56             text: c('Custom frequency option').t`Never`,
57         },
58         {
59             value: UNTIL,
60             text: c('Custom frequency option').t`On date…`,
61         },
62         {
63             value: AFTER_N_TIMES,
64             text: c('Custom frequency option').t`After…`,
65         },
66     ];
68     return (
69         <div className={clsx('flex-1', displayStacked && 'mt-4')}>
70             <label
71                 className={clsx(displayStacked && 'text-semibold')}
72                 htmlFor="event-ends-radio"
73                 id="label-event-ends"
74             >{c('Label').t`Ends`}</label>
76             <div className="flex flex-nowrap flex-1 flex-column sm:flex-row">
77                 <div className="sm:flex-1 mt-2">
78                     <SelectTwo
79                         value={frequencyModel.ends.type}
80                         onChange={({ value }) => {
81                             const newValue = value as END_TYPE;
82                             handleChangeEndType?.(newValue);
83                         }}
84                         title={c('Title').t`Select when this event will stop happening`}
85                         aria-describedby="label-event-ends"
86                         id="event-ends"
87                     >
88                         {options.map(({ value, text }) => (
89                             <Option key={value} value={value} title={text} />
90                         ))}
91                     </SelectTwo>
92                 </div>
94                 {frequencyModel.ends.type === UNTIL && (
95                     <div className="sm:flex-1 mt-2 ml-0 sm:ml-2">
96                         <label htmlFor={UNTIL_ID} className="sr-only">{c('Title').t`Select event's last date`}</label>
97                         <DateInput
98                             id={UNTIL_ID}
99                             value={frequencyModel.ends.until}
100                             min={start.date}
101                             defaultDate={start.date}
102                             onChange={handleChangeEndUntil}
103                             onFocus={() => handleChangeEndType(UNTIL)}
104                             displayWeekNumbers={displayWeekNumbers}
105                             weekStartsOn={weekStartsOn}
106                             aria-invalid={isSubmitted && !!errors.until}
107                             isSubmitted={isSubmitted}
108                             max={MAXIMUM_DATE}
109                             title={c('Title').t`Select event's last date`}
110                             aria-describedby="label-event-ends event-ends"
111                         />
112                     </div>
113                 )}
115                 {frequencyModel.ends.type === AFTER_N_TIMES && (
116                     <div className="flex flex-nowrap items-center sm:flex-1 mt-2 ml-0 sm:ml-2">
117                         <div className="max-w-custom" style={{ '--max-w-custom': '6em' }}>
118                             <label htmlFor={COUNT_ID} className="sr-only">{c('Title')
119                                 .t`Choose how many times this event will repeat`}</label>
120                             <IntegerInput
121                                 id={COUNT_ID}
122                                 value={frequencyModel.ends.count}
123                                 min={1}
124                                 onChange={handleChangeEndCount}
125                                 onFocus={() => handleChangeEndType(AFTER_N_TIMES)}
126                                 onBlur={() => {
127                                     if (!frequencyModel.ends.count) {
128                                         handleChangeEndCount(1);
129                                     }
130                                 }}
131                                 aria-invalid={isSubmitted && !!errors.count}
132                                 isSubmitted={isSubmitted}
133                                 title={c('Title').t`Choose how many times this event will repeat`}
134                             />
135                         </div>
136                         <div className="shrink-0 ml-2">
137                             {c('Custom frequency option').ngettext(msgid`time`, `times`, safeCountPlural)}
138                         </div>
139                     </div>
140                 )}
141             </div>
142         </div>
143     );
146 export default EndsRow;