frontend/containers/Car/HeaderEditing.js (view raw)
1import {useState, useReducer, useCallback, useEffect, useMemo} from 'react';
2import Typography from '@material-ui/core/Typography';
3import IconButton from '@material-ui/core/IconButton';
4import Icon from '@material-ui/core/Icon';
5import Button from '@material-ui/core/Button';
6import moment from 'moment';
7import {makeStyles} from '@material-ui/core/styles';
8import {useTranslation} from 'react-i18next';
9import TextField from '@material-ui/core/TextField';
10import Slider from '@material-ui/core/Slider';
11import RemoveDialog from '../RemoveDialog';
12import useToastStore from '../../stores/useToastStore';
13import useEventStore from '../../stores/useEventStore';
14import {
15 useUpdateEventMutation,
16 useUpdateCarMutation,
17 useDeleteCarMutation,
18} from '../../generated/graphql';
19
20const HeaderEditing = ({car, toggleEditing}) => {
21 const classes = useStyles();
22 const {t} = useTranslation();
23 const event = useEventStore(s => s.event);
24 const addToast = useToastStore(s => s.addToast);
25 const [updateEvent] = useUpdateEventMutation();
26 const [updateCar] = useUpdateCarMutation();
27 const [deleteCar] = useDeleteCarMutation({refetchQueries: ['eventByUUID']});
28 const [removing, toggleRemoving] = useReducer(i => !i, false);
29 const dateMoment = useMemo(() => {
30 if (!car?.departure) return moment();
31 else return moment(car.departure);
32 }, [car?.departure]);
33
34 // States
35 const [name, setName] = useState(car?.name ?? '');
36 const [seats, setSeats] = useState(car?.seats ?? 4);
37 const [meeting, setMeeting] = useState(car?.meeting ?? '');
38 const [date, setDate] = useState(dateMoment.format('YYYY-MM-DD'));
39 const [time, setTime] = useState(dateMoment.format('HH:mm'));
40 const [phone, setPhone] = useState(car ? car['phone_number'] : '');
41 const [details, setDetails] = useState(car?.details ?? '');
42
43 // Click on ESQ closes the form
44 const escFunction = useCallback(
45 evt => {
46 if (evt.keyCode === 27) toggleEditing();
47 },
48 [toggleEditing]
49 );
50
51 useEffect(() => {
52 document.addEventListener('keydown', escFunction, false);
53 return () => {
54 document.removeEventListener('keydown', escFunction, false);
55 };
56 }, [escFunction]);
57
58 const onSave = async evt => {
59 if (evt.preventDefault) evt.preventDefault();
60 try {
61 // If new seats count is under current passengers count, put excedent in event waiting list
62 if (!!car.passengers && car.passengers.length > seats) {
63 const lostPassengers = car.passengers.slice(seats);
64 if (lostPassengers.length > 0)
65 await updateEvent({
66 variables: {
67 id: event.id,
68 eventUpdate: {
69 waiting_list: [
70 ...(event.waiting_list ?? []),
71 ...lostPassengers,
72 ],
73 },
74 },
75 });
76 }
77 const departure = moment(
78 `${date} ${time}`,
79 'YYYY-MM-DD HH:mm'
80 ).toISOString();
81 await updateCar({
82 variables: {
83 id: car.id,
84 carUpdate: {
85 name,
86 seats,
87 meeting,
88 departure,
89 phone_number: phone,
90 details,
91 passengers: car.passengers ? car.passengers.slice(0, seats) : [],
92 },
93 },
94 });
95 toggleEditing();
96 } catch (error) {
97 console.error(error);
98 addToast('car.errors.cant_update');
99 }
100 return false;
101 };
102
103 const onRemove = async () => {
104 try {
105 // Put passengers in event waiting list (if any)
106 if (Array.isArray(car?.passengers) && car.passengers.length > 0)
107 await updateEvent({
108 variables: {
109 id: event.id,
110 eventUpdate: {
111 waiting_list: [...(event.waiting_list ?? []), ...car.passengers],
112 },
113 },
114 });
115 await deleteCar({
116 variables: {
117 id: car.id,
118 },
119 });
120 addToast(t('car.actions.removed'));
121 toggleEditing();
122 } catch (error) {
123 console.error(error);
124 addToast('car.errors.cant_remove');
125 }
126 };
127
128 return (
129 <div className={classes.header}>
130 <form onSubmit={onSave}>
131 <IconButton
132 size="small"
133 color="primary"
134 type="submit"
135 className={classes.edit}
136 >
137 <Icon>done</Icon>
138 </IconButton>
139 <TextField
140 label={t('car.creation.date')}
141 value={date}
142 onChange={e => setDate(e.target.value)}
143 className={classes.picker}
144 fullWidth
145 id="NewCarDate"
146 name="date"
147 type="date"
148 InputLabelProps={{
149 shrink: true,
150 }}
151 />
152 <TextField
153 label={t('car.creation.time')}
154 value={time}
155 onChange={e => setTime(e.target.value)}
156 className={classes.picker}
157 fullWidth
158 id="NewCarTime"
159 name="time"
160 type="time"
161 InputLabelProps={{
162 shrink: true,
163 }}
164 />
165 <TextField
166 label={t('car.creation.name')}
167 fullWidth
168 autoFocus
169 margin="dense"
170 value={name}
171 onChange={e => setName(e.target.value)}
172 id="EditCarName"
173 name="name"
174 />
175 <TextField
176 label={t('car.creation.phone')}
177 fullWidth
178 autoFocus
179 margin="dense"
180 value={phone}
181 onChange={e => setPhone(e.target.value)}
182 id="EditCarPhone"
183 name="phone"
184 />
185 <TextField
186 label={t('car.creation.meeting')}
187 fullWidth
188 margin="dense"
189 multiline
190 rows={2}
191 value={meeting}
192 onChange={e => setMeeting(e.target.value)}
193 id="EditCarMeeting"
194 name="meeting"
195 />
196 <TextField
197 label={t('car.creation.notes')}
198 fullWidth
199 margin="dense"
200 inputProps={{maxLength: 250}}
201 helperText={`${details.length}/250`}
202 multiline
203 rows={2}
204 value={details}
205 onChange={e => setDetails(e.target.value)}
206 id="EditCarDetails"
207 name="details"
208 />
209 <div className={classes.slider}>
210 <Typography variant="caption">{t('car.creation.seats')}</Typography>
211 <Slider
212 value={seats}
213 onChange={(e, value) => setSeats(value)}
214 step={1}
215 marks={[1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
216 value,
217 label: value,
218 }))}
219 min={1}
220 max={8}
221 valueLabelDisplay="auto"
222 id="EditCarSeats"
223 />
224 </div>
225 </form>
226 <div className={classes.actions}>
227 <Button
228 variant="outlined"
229 color="primary"
230 onClick={onSave}
231 id="CarSave"
232 >
233 {t('generic.save')}
234 </Button>
235 <Button
236 variant="outlined"
237 color="primary"
238 onClick={toggleRemoving}
239 id="CarRemove"
240 >
241 {t('generic.remove')}
242 </Button>
243 </div>
244 <RemoveDialog
245 text={t('car.actions.remove_alert')}
246 open={removing}
247 onClose={toggleRemoving}
248 onRemove={onRemove}
249 />
250 </div>
251 );
252};
253
254const useStyles = makeStyles(theme => ({
255 header: {
256 padding: theme.spacing(2),
257 },
258 edit: {
259 position: 'absolute',
260 top: 0,
261 right: 0,
262 margin: theme.spacing(1),
263 zIndex: theme.zIndex.speedDial,
264 },
265 section: {
266 marginTop: theme.spacing(2),
267 },
268 slider: {
269 marginTop: theme.spacing(2),
270 },
271 actions: {
272 display: 'flex',
273 flexDirection: 'column',
274 justifyContent: 'center',
275 margin: theme.spacing(2, 0),
276 '& > *:first-child': {
277 marginBottom: theme.spacing(2),
278 },
279 },
280 picker: {
281 marginBottom: theme.spacing(2),
282 },
283}));
284
285export default HeaderEditing;