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