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