frontend/containers/Travel/HeaderEditing.tsx (view raw)
1import {useState, useReducer, useCallback, useEffect, useMemo} from 'react';
2import Typography from '@mui/material/Typography';
3import Button from '@mui/material/Button';
4import TextField from '@mui/material/TextField';
5import Slider from '@mui/material/Slider';
6import moment, {Moment} from 'moment';
7import {useTheme} from '@mui/material/styles';
8import {DatePicker} from '@mui/x-date-pickers/DatePicker';
9import {TimePicker} from '@mui/x-date-pickers/TimePicker';
10import {useTranslation} from 'react-i18next';
11import RemoveDialog from '../RemoveDialog';
12import useActions from './useActions';
13import Box from '@mui/material/Box';
14import PlaceInput from '../PlaceInput';
15import useEventStore from '../../stores/useEventStore';
16
17const HeaderEditing = ({travel, toggleEditing}) => {
18 const {t} = useTranslation();
19 const theme = useTheme();
20 const actions = useActions({travel});
21 const isCarosterPlus = useEventStore(s =>
22 s.event.enabled_modules?.includes('caroster-plus')
23 );
24 const [removing, toggleRemoving] = useReducer(i => !i, false);
25 const dateMoment = useMemo(
26 () => (travel?.departure ? moment(travel.departure) : null),
27 [travel?.departure]
28 );
29
30 // States
31 const [name, setName] = useState(travel?.vehicleName ?? '');
32 const [seats, setSeats] = useState(travel?.seats ?? 4);
33 const [meeting, setMeeting] = useState(travel?.meeting ?? '');
34 const [meeting_latitude, setMeetingLatitude] = useState(
35 travel?.meeting_latitude
36 );
37 const [meeting_longitude, setMeetingLongitude] = useState(
38 travel?.meeting_longitude
39 );
40 const [date, setDate] = useState(dateMoment);
41 const [time, setTime] = useState(dateMoment);
42 const [phone, setPhone] = useState(travel?.phone_number ?? '');
43 const [details, setDetails] = useState(travel?.details ?? '');
44
45 // Click on ESQ closes the form
46 const escFunction = useCallback(
47 evt => {
48 if (evt.keyCode === 27) toggleEditing();
49 },
50 [toggleEditing]
51 );
52
53 useEffect(() => {
54 document.addEventListener('keydown', escFunction, false);
55 return () => {
56 document.removeEventListener('keydown', escFunction, false);
57 };
58 }, [escFunction]);
59
60 const onSave = async event => {
61 if (event.preventDefault) event.preventDefault();
62 const travelUpdate = {
63 meeting,
64 meeting_latitude,
65 meeting_longitude,
66 details,
67 seats,
68 phone_number: phone,
69 vehicleName: name,
70 departure: formatDate(date, time),
71 };
72 await actions.updateTravel(travelUpdate);
73 toggleEditing();
74 };
75
76 const onRemove = async () => {
77 await actions.removeTravel(
78 isCarosterPlus
79 ? t`travel.actions.removed.caroster_plus`
80 : t`travel.actions.removed`
81 );
82 toggleEditing();
83 };
84
85 return (
86 <Box sx={{padding: 2}}>
87 <form onSubmit={onSave}>
88 <DatePicker
89 slotProps={{
90 textField: {
91 sx: {width: '100%', pb: 2},
92 },
93 }}
94 format="DD/MM/YYYY"
95 label={t('travel.creation.date')}
96 value={date}
97 onChange={setDate}
98 autoFocus
99 />
100 <TimePicker
101 label={t('travel.creation.time')}
102 slotProps={{
103 textField: {
104 sx: {width: '100%', pb: 2},
105 },
106 }}
107 value={time}
108 onChange={setTime}
109 ampm={false}
110 minutesStep={5}
111 />
112 <TextField
113 label={t('travel.creation.name')}
114 fullWidth
115 sx={{pb: 2}}
116 value={name}
117 onChange={e => setName(e.target.value)}
118 name="name"
119 id="EditTravelName"
120 />
121 <TextField
122 label={t('travel.creation.phone')}
123 fullWidth
124 sx={{pb: 2}}
125 value={phone}
126 onChange={e => setPhone(e.target.value)}
127 name="phone"
128 id="EditTravelPhone"
129 />
130 <PlaceInput
131 label={t('travel.creation.meeting')}
132 textFieldProps={{sx: {pb: 2}}}
133 place={meeting}
134 latitude={meeting_latitude}
135 longitude={meeting_longitude}
136 onSelect={({place, latitude, longitude}) => {
137 setMeeting(place);
138 setMeetingLatitude(latitude);
139 setMeetingLongitude(longitude);
140 }}
141 />
142 <TextField
143 label={t('travel.creation.notes')}
144 fullWidth
145 sx={{pb: 2}}
146 multiline
147 maxRows={4}
148 inputProps={{maxLength: 250}}
149 helperText={`${details.length}/250`}
150 value={details}
151 onChange={e => setDetails(e.target.value)}
152 name="details"
153 id="EditTravelDetails"
154 />
155 <Box sx={{marginTop: theme.spacing(2)}}>
156 <Typography variant="caption">
157 {t('travel.creation.seats')}
158 </Typography>
159 <Slider
160 value={seats}
161 onChange={(e, value) => setSeats(value)}
162 step={1}
163 marks={[1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
164 value,
165 label: value,
166 }))}
167 min={1}
168 max={8}
169 valueLabelDisplay="auto"
170 id="EditTravelSeats"
171 />
172 </Box>
173 </form>
174 <Box
175 sx={{
176 display: 'flex',
177 flexDirection: 'column',
178 justifyContent: 'center',
179 margin: theme.spacing(2, 0),
180 '& > *:first-child': {
181 marginBottom: theme.spacing(2),
182 },
183 }}
184 >
185 <Button
186 variant="contained"
187 color="primary"
188 onClick={onSave}
189 id="TravelSave"
190 >
191 {t('generic.save')}
192 </Button>
193 <Button
194 variant="outlined"
195 color="primary"
196 onClick={toggleRemoving}
197 id="TravelRemove"
198 >
199 {t('generic.remove')}
200 </Button>
201 </Box>
202 <RemoveDialog
203 text={
204 isCarosterPlus
205 ? t`travel.actions.remove_alert.caroster_plus`
206 : t`travel.actions.remove_alert`
207 }
208 open={removing}
209 onClose={toggleRemoving}
210 onRemove={onRemove}
211 />
212 </Box>
213 );
214};
215
216const formatDate = (date: Moment, time: Moment) => {
217 return moment(
218 `${moment(date).format('YYYY-MM-DD')} ${moment(time).format('HH:mm')}`,
219 'YYYY-MM-DD HH:mm'
220 ).toISOString();
221};
222
223export default HeaderEditing;