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