Skip to content

Commit 85072a9

Browse files
author
karabij
committed
✨(frontend) add meeting register form
add a form which enables the user to register the meeting
1 parent 26278f4 commit 85072a9

File tree

31 files changed

+1049
-298
lines changed

31 files changed

+1049
-298
lines changed

src/frontend/magnify/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@testing-library/react": "13.3.0",
3737
"@testing-library/user-event": "14.2.1",
3838
"@types/jest": "28.1.1",
39+
"@types/luxon": "^3.1.0",
3940
"@types/react": "18.0.12",
4041
"@types/react-time-picker": "^4.0.2",
4142
"@types/styled-components": "5.1.25",
@@ -57,6 +58,7 @@
5758
"jest": "28.1.1",
5859
"jest-css-modules": "2.1.0",
5960
"jest-environment-jsdom": "28.1.1",
61+
"luxon": "^3.1.1",
6062
"msw": "0.47.4",
6163
"postcss": "8.4.14",
6264
"prettier": "2.7.0",

src/frontend/magnify/src/components/design-system/Formik/FormikDatePicker/FormikDatePicker.stories.tsx

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/frontend/magnify/src/components/design-system/Formik/FormikDatePicker/FormikDatePicker.tsx

Lines changed: 0 additions & 102 deletions
This file was deleted.

src/frontend/magnify/src/components/design-system/Formik/FormikDatePicker/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import withFormik from '@bbbtech/storybook-formik';
2+
import { ComponentMeta, ComponentStory } from '@storybook/react';
3+
import React from 'react';
4+
import { useIntl } from 'react-intl';
5+
import { getSuggestions } from '../../../meetings/RegisterMeetingForm/utils';
6+
import FormikDateTimePicker from './FormikDateTimePicker';
7+
8+
export default {
9+
title: 'Formik/DateTimePicker',
10+
component: FormikDateTimePicker,
11+
decorators: [withFormik],
12+
initialValues: { date: new Date() },
13+
} as ComponentMeta<typeof FormikDateTimePicker>;
14+
15+
const Template: ComponentStory<typeof FormikDateTimePicker> = (args, context) => (
16+
<div>
17+
{context.parameters.title}
18+
<FormikDateTimePicker {...args} />
19+
</div>
20+
);
21+
22+
export const basicDateTimePicker = Template.bind({});
23+
const intl = useIntl();
24+
25+
basicDateTimePicker.args = {
26+
timeName: 'time',
27+
dateName: 'date',
28+
frenchSuggestions: getSuggestions('fr'),
29+
localTimeSuggestions: getSuggestions(intl.locale),
30+
};
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { ErrorMessage, useField, useFormikContext } from 'formik';
2+
import { DateInput, DropButton, Box, Text } from 'grommet';
3+
import { CaretDown } from 'grommet-icons';
4+
import { DateTime, Settings } from 'luxon';
5+
import React, { FunctionComponent, useState } from 'react';
6+
import { useIntl } from 'react-intl';
7+
import TimePicker, { TimePickerValue } from 'react-time-picker';
8+
import SuggestionButton from './SuggestionButton';
9+
10+
export interface formikDateTimePickerProps {
11+
dateName: string;
12+
timeName: string;
13+
frenchSuggestions: string[];
14+
localTimeSuggestions: string[];
15+
label: string;
16+
}
17+
18+
const nextYear = new Date();
19+
nextYear.setFullYear(new Date().getFullYear() + 1);
20+
21+
const FormikDateTimePicker: FunctionComponent<formikDateTimePickerProps> = ({ ...props }) => {
22+
const [open, setOpen] = useState<boolean | undefined>(undefined);
23+
const [dateField] = useField(props.dateName);
24+
const [timeField] = useField(props.timeName);
25+
26+
const formikContext = useFormikContext();
27+
const intl = useIntl();
28+
Settings.defaultLocale = intl.locale;
29+
30+
const isToday =
31+
DateTime.fromISO(dateField.value).toFormat('MM-dd-yyyy') ==
32+
DateTime.now().toFormat('MM-dd-yyyy');
33+
const beforeToday =
34+
DateTime.fromISO(dateField.value).toFormat('MM-dd-yyyy') <
35+
DateTime.now().toFormat('MM-dd-yyyy');
36+
37+
const onTimeChange = (value: string) => {
38+
formikContext.setFieldValue(props.timeName, value.toString());
39+
setOpen(false);
40+
};
41+
42+
const onDateChange = (event: { value: string | string[] }) => {
43+
let value: string;
44+
if (Array.isArray(event.value)) {
45+
value = '';
46+
if (event.value.length > 0) {
47+
value = event.value[0];
48+
}
49+
} else {
50+
value = event.value;
51+
}
52+
formikContext.setFieldValue(props.dateName, value);
53+
};
54+
55+
React.useEffect(() => {
56+
console.log(formikContext.errors, formikContext.values);
57+
}, [formikContext.values, formikContext.errors]);
58+
59+
const suggestionButtons = props.localTimeSuggestions.map((value: string, index: number) => (
60+
<SuggestionButton
61+
key={value}
62+
beforeToday={beforeToday}
63+
buttonValue={value}
64+
choiceValue={timeField.value}
65+
frenchButtonValue={props.frenchSuggestions[index]}
66+
isToday={isToday}
67+
onClick={onTimeChange}
68+
/>
69+
));
70+
71+
return (
72+
<Box gap={'5px'}>
73+
{props.label != '' && (
74+
<label htmlFor={props.dateName}>
75+
<Text size={'xsmall'} weight={'bold'}>
76+
{props.label}
77+
</Text>
78+
</label>
79+
)}
80+
<div>
81+
<Box align="center" basis="1" direction="column" gap="small">
82+
<DateInput
83+
{...dateField}
84+
format={intl.locale === 'fr' ? 'jj/mm/aaaa' : 'yyyy/mm/dd'}
85+
name={props.dateName}
86+
onChange={onDateChange}
87+
value={dateField.value ? new Date(dateField.value).toISOString() : ''}
88+
calendarProps={{
89+
bounds: [new Date().toISOString(), nextYear.toISOString()],
90+
size: 'small',
91+
}}
92+
></DateInput>
93+
94+
<Box align="center" direction="row" gap="small">
95+
<TimePicker
96+
{...timeField}
97+
disableClock
98+
locale={intl.locale}
99+
name={props.timeName}
100+
onChange={(value: TimePickerValue) => onTimeChange(value.toString())}
101+
></TimePicker>
102+
<DropButton
103+
dropAlign={{ top: 'bottom' }}
104+
onClose={() => setOpen(false)}
105+
open={open}
106+
dropContent={
107+
<Box align="center" basis="small" direction="column" gap="5px">
108+
{suggestionButtons}
109+
</Box>
110+
}
111+
onOpen={() => {
112+
setOpen(true);
113+
}}
114+
>
115+
<CaretDown size="15px" />
116+
</DropButton>
117+
</Box>
118+
</Box>
119+
</div>
120+
</Box>
121+
);
122+
};
123+
124+
export default FormikDateTimePicker;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Box, Button, Text } from 'grommet';
2+
import { normalizeColor } from 'grommet/utils';
3+
import React, { FunctionComponent, ReactElement } from 'react';
4+
import { useTheme } from 'styled-components';
5+
import { mergeDateTime } from './utils';
6+
7+
const today = new Date().toISOString();
8+
9+
export interface suggestionButtonProps {
10+
buttonValue: string;
11+
frenchButtonValue: string;
12+
choiceValue: string;
13+
onClick: (value: string) => void;
14+
isToday: boolean;
15+
beforeToday: boolean;
16+
}
17+
18+
const SuggestionButton: FunctionComponent<suggestionButtonProps> = ({ ...props }): ReactElement => {
19+
const isChosenButton: boolean = props.frenchButtonValue == props.choiceValue;
20+
const chosenDateTime = mergeDateTime(today, props.frenchButtonValue);
21+
const isButtonBeforeNow: boolean = chosenDateTime ? chosenDateTime < today : false;
22+
const theme = useTheme();
23+
return (
24+
<Button
25+
color={isChosenButton ? `${normalizeColor('light-2', theme)}` : 'black'}
26+
disabled={props.beforeToday || (props.isToday && isButtonBeforeNow)}
27+
fill={isChosenButton ? 'horizontal' : false}
28+
justify="center"
29+
margin={{ top: 'xsmall' }}
30+
primary={isChosenButton}
31+
onClick={() => {
32+
props.onClick(props.frenchButtonValue);
33+
}}
34+
>
35+
<Box alignContent="center" alignSelf="center">
36+
<Text color="black" textAlign="center">
37+
{props.buttonValue}
38+
</Text>
39+
</Box>
40+
</Button>
41+
);
42+
};
43+
44+
export default SuggestionButton;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default, formikDateTimePickerProps } from '../FormikDateTimePicker/FormikDateTimePicker';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { DateTime, Duration } from 'luxon';
2+
3+
export const splitDateTime = (dateTimeISO: string | null): { date: string; time: string } => {
4+
if (!dateTimeISO) {
5+
return { date: '', time: '' };
6+
}
7+
const dateTime = DateTime.fromISO(dateTimeISO);
8+
return {
9+
date: dateTime.toISODate(),
10+
time: dateTime.toLocaleString(DateTime.TIME_24_SIMPLE),
11+
};
12+
};
13+
14+
export const mergeDateTime = (
15+
dateString: string | null,
16+
timeString: string | null,
17+
): string | null => {
18+
if (!dateString || !timeString) {
19+
return null;
20+
}
21+
try {
22+
const time = Duration.fromISOTime(timeString);
23+
const dateTime = DateTime.fromISO(dateString).set({
24+
hour: time.hours,
25+
minute: time.minutes,
26+
});
27+
return dateTime.toISO();
28+
} catch (e) {
29+
return null;
30+
}
31+
};

0 commit comments

Comments
 (0)