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 autoFocus
110 />
111 <TimePicker
112 label={t('travel.creation.time')}
113 slotProps={{
114 textField: {
115 sx: {width: '100%', pb: 2},
116 },
117 }}
118 value={time}
119 onChange={setTime}
120 ampm={false}
121 minutesStep={5}
122 />
123 <TextField
124 label={t('travel.creation.name')}
125 fullWidth
126 sx={{pb: 2}}
127 value={name}
128 onChange={e => setName(e.target.value)}
129 name="name"
130 id="EditTravelName"
131 />
132 <TextField
133 label={t('travel.creation.phone')}
134 fullWidth
135 sx={{pb: 2}}
136 value={phone}
137 onChange={e => setPhone(e.target.value)}
138 name="phone"
139 id="EditTravelPhone"
140 />
141 <PlaceInput
142 label={t('travel.creation.meeting')}
143 textFieldProps={{sx: {pb: 2}}}
144 place={meeting}
145 latitude={meeting_latitude}
146 longitude={meeting_longitude}
147 onSelect={({place, latitude, longitude}) => {
148 setMeeting(place);
149 setMeetingLatitude(latitude);
150 setMeetingLongitude(longitude);
151 }}
152 />
153 <TextField
154 label={t('travel.creation.notes')}
155 fullWidth
156 sx={{pb: 2}}
157 multiline
158 maxRows={4}
159 inputProps={{maxLength: 250}}
160 helperText={`${details.length}/250`}
161 value={details}
162 onChange={e => setDetails(e.target.value)}
163 name="details"
164 id="EditTravelDetails"
165 />
166 <Box sx={{marginTop: theme.spacing(2)}}>
167 <Typography variant="caption">
168 {t('travel.creation.seats')}
169 </Typography>
170 <Slider
171 value={seats}
172 onChange={(e, value) => setSeats(value)}
173 step={1}
174 marks={[1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
175 value,
176 label: value,
177 }))}
178 min={1}
179 max={8}
180 valueLabelDisplay="auto"
181 id="EditTravelSeats"
182 />
183 </Box>
184 </form>
185 <Box
186 sx={{
187 display: 'flex',
188 flexDirection: 'column',
189 justifyContent: 'center',
190 margin: theme.spacing(2, 0),
191 '& > *:first-child': {
192 marginBottom: theme.spacing(2),
193 },
194 }}
195 >
196 <Button
197 variant="contained"
198 color="primary"
199 onClick={onSave}
200 id="TravelSave"
201 >
202 {t('generic.save')}
203 </Button>
204 <Button
205 variant="outlined"
206 color="primary"
207 onClick={toggleRemoving}
208 id="TravelRemove"
209 >
210 {t('generic.remove')}
211 </Button>
212 </Box>
213 <RemoveDialog
214 text={
215 isCarosterPlus
216 ? t`travel.actions.remove_alert.caroster_plus`
217 : t`travel.actions.remove_alert`
218 }
219 open={removing}
220 onClose={toggleRemoving}
221 onRemove={onRemove}
222 />
223 </Box>
224 );
225};
226
227export default HeaderEditing;