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 type="submit" className={classes.editBtn}>
100 <Icon>done</Icon>
101 </IconButton>
102 <DateTimePicker
103 label={t('event.creation.date')}
104 value={date}
105 onChange={setDate}
106 className={classes.textField}
107 fullWidth
108 format="LLLL"
109 disablePast
110 id="EditCarDateTime"
111 name="date"
112 cancelLabel={t('generic.cancel')}
113 />
114 <TextField
115 className={classes.textField}
116 label={t('car.creation.name')}
117 fullWidth
118 autoFocus
119 margin="dense"
120 value={name}
121 onChange={e => setName(e.target.value)}
122 id="EditCarName"
123 name="name"
124 />
125 <TextField
126 className={classes.textField}
127 label={t('car.creation.phone')}
128 fullWidth
129 autoFocus
130 margin="dense"
131 value={phone}
132 onChange={e => setPhone(e.target.value)}
133 id="EditCarPhone"
134 name="phone"
135 />
136 <TextField
137 className={classes.textField}
138 label={t('car.creation.meeting')}
139 fullWidth
140 margin="dense"
141 multiline
142 rows={2}
143 value={meeting}
144 onChange={e => setMeeting(e.target.value)}
145 id="EditCarMeeting"
146 name="meeting"
147 />
148 <TextField
149 className={classes.textField}
150 label={t('car.creation.notes')}
151 fullWidth
152 margin="dense"
153 multiline
154 rows={2}
155 value={details}
156 onChange={e => setDetails(e.target.value)}
157 id="EditCarDetails"
158 name="details"
159 />
160 <div className={classes.slider}>
161 <Typography variant="caption">{t('car.creation.seats')}</Typography>
162 <Slider
163 value={seats}
164 onChange={(e, value) => setSeats(value)}
165 step={1}
166 marks={[1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
167 value,
168 label: value,
169 }))}
170 min={1}
171 max={8}
172 valueLabelDisplay="auto"
173 id="EditCarSeats"
174 />
175 </div>
176 </form>
177 <div className={classes.actions}>
178 <Button
179 variant="outlined"
180 color="primary"
181 onClick={onSave}
182 id="CarSave"
183 >
184 {t('generic.save')}
185 </Button>
186 <Button
187 variant="outlined"
188 color="primary"
189 onClick={toggleRemoving}
190 id="CarRemove"
191 >
192 {t('generic.remove')}
193 </Button>
194 </div>
195 <RemoveDialog
196 text={t('car.actions.remove_alert')}
197 open={removing}
198 onClose={toggleRemoving}
199 onRemove={onRemove}
200 />
201 </div>
202 );
203};
204
205const useStyles = makeStyles(theme => ({
206 header: {padding: theme.spacing(2)},
207 editBtn: {
208 position: 'absolute',
209 top: 0,
210 right: 0,
211 zIndex: theme.zIndex.speedDial,
212 },
213 section: {
214 marginTop: theme.spacing(2),
215 },
216 slider: {
217 marginTop: theme.spacing(2),
218 },
219 actions: {
220 display: 'flex',
221 flexDirection: 'column',
222 justifyContent: 'center',
223 margin: theme.spacing(2, 0),
224 '& > *:first-child': {
225 marginBottom: theme.spacing(2),
226 },
227 },
228}));
229
230export default HeaderEditing;