frontend/containers/NewTravelDialog/index.tsx (view raw)
1import {useState, forwardRef, useMemo, useEffect} from 'react';
2import {makeStyles} from '@material-ui/core/styles';
3import Dialog from '@material-ui/core/Dialog';
4import DialogActions from '@material-ui/core/DialogActions';
5import DialogContent from '@material-ui/core/DialogContent';
6import DialogTitle from '@material-ui/core/DialogTitle';
7import Button from '@material-ui/core/Button';
8import Slide from '@material-ui/core/Slide';
9import TextField from '@material-ui/core/TextField';
10import Slider from '@material-ui/core/Slider';
11import Typography from '@material-ui/core/Typography';
12import {DatePicker, TimePicker} from '@material-ui/pickers';
13import moment, {Moment} from 'moment';
14import {useTranslation} from 'react-i18next';
15import useEventStore from '../../stores/useEventStore';
16import useActions from './useActions';
17import {Vehicle} from '../../generated/graphql';
18
19interface Props {
20 context: {
21 vehicle: Vehicle;
22 opened: boolean;
23 };
24 toggle: ({opened: boolean}) => void;
25}
26
27const NewTravelDialog = ({context, toggle}: Props) => {
28 const {t} = useTranslation();
29 const classes = useStyles();
30 const event = useEventStore(s => s.event);
31 const {createTravel} = useActions({event});
32
33 const dateMoment = useMemo(() => {
34 if (!event?.date) return moment();
35 else return moment(event.date);
36 }, [event?.date]);
37
38 // States
39 const [name, setName] = useState('');
40 const [seats, setSeats] = useState(4);
41 const [meeting, setMeeting] = useState('');
42 const [date, setDate] = useState(dateMoment);
43 const [time, setTime] = useState(dateMoment);
44 const [phone, setPhone] = useState('');
45 const [details, setDetails] = useState('');
46 const canCreate = !!name && !!seats;
47
48 const clearState = () => {
49 setName('');
50 setSeats(4);
51 setMeeting('');
52 setDate(moment());
53 setPhone('');
54 setDetails('');
55 };
56
57 useEffect(() => {
58 if (context.vehicle) {
59 setName(context.vehicle.name);
60 setSeats(context.vehicle.seats);
61 setPhone(context.vehicle.phone_number);
62 }
63 }, [context.vehicle]);
64
65 const onCreate = async e => {
66 if (e.preventDefault) e.preventDefault();
67
68 const travel = {
69 meeting,
70 details,
71 seats,
72 vehicleName: name,
73 phone_number: phone,
74 departure: formatDate(date, time),
75 event: event.id,
76 };
77 const createVehicle = !context.vehicle;
78
79 await createTravel({...travel, createVehicle});
80 toggle({opened: false});
81
82 clearState();
83 };
84
85 return (
86 <Dialog
87 fullWidth
88 maxWidth="xs"
89 open={context?.opened}
90 onClose={() => {
91 toggle({opened: false});
92 clearState();
93 }}
94 TransitionComponent={Transition}
95 >
96 <form onSubmit={onCreate}>
97 <DialogTitle>{t('travel.creation.title')}</DialogTitle>
98 <DialogContent>
99 <DatePicker
100 label={t('travel.creation.date')}
101 fullWidth
102 helperText=" "
103 value={date}
104 onChange={setDate}
105 format="DD/MM/YYYY"
106 cancelLabel={t('generic.cancel')}
107 autoFocus
108 id="NewTravelDateTime"
109 />
110 <TimePicker
111 label={t('travel.creation.time')}
112 fullWidth
113 helperText=" "
114 value={time}
115 onChange={setTime}
116 cancelLabel={t('generic.cancel')}
117 ampm={false}
118 minutesStep={5}
119 id="NewTravelTime"
120 />
121 <TextField
122 label={t('travel.creation.name')}
123 fullWidth
124 helperText=" "
125 value={name}
126 onChange={e => setName(e.target.value)}
127 name="name"
128 id="NewTravelName"
129 />
130 <TextField
131 label={t('travel.creation.phone')}
132 fullWidth
133 helperText=" "
134 value={phone}
135 onChange={e => setPhone(e.target.value)}
136 name="phone"
137 id="NewTravelPhone"
138 />
139 <TextField
140 label={t('travel.creation.meeting')}
141 fullWidth
142 multiline
143 rowsMax={4}
144 inputProps={{maxLength: 250}}
145 helperText={`${meeting.length}/250`}
146 value={meeting}
147 onChange={e => setMeeting(e.target.value)}
148 name="meeting"
149 id="NewTravelMeeting"
150 />
151 <TextField
152 label={t('travel.creation.notes')}
153 fullWidth
154 multiline
155 rowsMax={4}
156 inputProps={{maxLength: 250}}
157 helperText={`${details.length}/250`}
158 value={details}
159 onChange={e => setDetails(e.target.value)}
160 name="details"
161 id="NewTravelDetails"
162 />
163 <div className={classes.slider}>
164 <Typography variant="caption">
165 {t('travel.creation.seats')}
166 </Typography>
167 <Slider
168 value={seats}
169 onChange={(e, value) => setSeats(value)}
170 step={1}
171 marks={MARKS}
172 min={1}
173 max={MARKS.length}
174 valueLabelDisplay="auto"
175 id="NewTravelSeats"
176 />
177 </div>
178 </DialogContent>
179 <DialogActions>
180 <Button
181 color="primary"
182 id="NewTravelCancel"
183 onClick={() => toggle({opened: false})}
184 tabIndex={-1}
185 >
186 {t('generic.cancel')}
187 </Button>
188 <Button
189 color="primary"
190 variant="contained"
191 type="submit"
192 disabled={!canCreate}
193 aria-disabled={!canCreate}
194 id="NewTravelSubmit"
195 >
196 {t('generic.create')}
197 </Button>
198 </DialogActions>
199 </form>
200 </Dialog>
201 );
202};
203
204const Transition = forwardRef(function Transition(props, ref) {
205 return <Slide direction="up" ref={ref} {...props} />;
206});
207
208const formatDate = (date: Moment, time: Moment) => {
209 return moment(
210 `${moment(date).format('YYYY-MM-DD')} ${moment(time).format('HH:mm')}`,
211 'YYYY-MM-DD HH:mm'
212 ).toISOString();
213};
214
215const MARKS = [1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
216 value,
217 label: value,
218}));
219
220const useStyles = makeStyles(theme => ({
221 slider: {
222 marginTop: theme.spacing(2),
223 },
224}));
225
226export default NewTravelDialog;