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