frontend/containers/Travel/HeaderEditing.tsx (view raw)
1import {useState, useReducer, useCallback, useEffect, useMemo} from 'react';
2import {makeStyles} from '@material-ui/core/styles';
3import Typography from '@material-ui/core/Typography';
4import IconButton from '@material-ui/core/IconButton';
5import Icon from '@material-ui/core/Icon';
6import Button from '@material-ui/core/Button';
7import TextField from '@material-ui/core/TextField';
8import Slider from '@material-ui/core/Slider';
9import {DatePicker, TimePicker} from '@material-ui/pickers';
10import moment from 'moment';
11import {useTranslation} from 'react-i18next';
12import useToastStore from '../../stores/useToastStore';
13import useEventStore from '../../stores/useEventStore';
14import RemoveDialog from '../RemoveDialog';
15import {
16 useUpdateEventMutation,
17 useDeleteTravelMutation,
18} from '../../generated/graphql';
19import useActions from './useActions';
20
21const HeaderEditing = ({travel, toggleEditing}) => {
22 const classes = useStyles();
23 const {t} = useTranslation();
24 const event = useEventStore(s => s.event);
25 const addToast = useToastStore(s => s.addToast);
26 const actions = useActions({travel});
27 const [updateEvent] = useUpdateEventMutation();
28 const [deleteTravel] = useDeleteTravelMutation({
29 refetchQueries: ['eventByUUID'],
30 });
31 const [removing, toggleRemoving] = useReducer(i => !i, false);
32 const dateMoment = useMemo(() => {
33 if (!travel?.departure) return moment();
34 else return moment(travel.departure);
35 }, [travel?.departure]);
36
37 // States
38 const [name, setName] = useState(travel?.vehicle?.name ?? '');
39 const [seats, setSeats] = useState(travel?.vehicle?.seats ?? 4);
40 const [meeting, setMeeting] = useState(travel?.meeting ?? '');
41 const [date, setDate] = useState(dateMoment);
42 const [time, setTime] = useState(dateMoment);
43 const [phone, setPhone] = useState(travel?.vehicle?.phone_number ?? '');
44 const [details, setDetails] = useState(travel?.details ?? '');
45
46 // Click on ESQ closes the form
47 const escFunction = useCallback(
48 evt => {
49 if (evt.keyCode === 27) toggleEditing();
50 },
51 [toggleEditing]
52 );
53
54 useEffect(() => {
55 document.addEventListener('keydown', escFunction, false);
56 return () => {
57 document.removeEventListener('keydown', escFunction, false);
58 };
59 }, [escFunction]);
60
61 const onSave = async event => {
62 if (event.preventDefault) event.preventDefault();
63 const update = {
64 vehicle: {
65 name,
66 seats,
67 phone_number: phone,
68 },
69 travel: {meeting, date, time, details},
70 };
71 await actions.updateTravel(update);
72 toggleEditing();
73 };
74
75 const onRemove = async () => {
76 await actions.removeTravel();
77 toggleEditing();
78 };
79
80 return (
81 <div className={classes.header}>
82 <form onSubmit={onSave}>
83 <IconButton
84 size="small"
85 color="primary"
86 type="submit"
87 className={classes.edit}
88 >
89 <Icon>done</Icon>
90 </IconButton>
91 <DatePicker
92 label={t('travel.creation.date')}
93 fullWidth
94 helperText=" "
95 value={date}
96 onChange={setDate}
97 format="DD/MM/YYYY"
98 cancelLabel={t('generic.cancel')}
99 autoFocus
100 id="NewTravelDate"
101 />
102 <TimePicker
103 label={t('travel.creation.time')}
104 fullWidth
105 helperText=" "
106 value={time}
107 onChange={setTime}
108 cancelLabel={t('generic.cancel')}
109 ampm={false}
110 minutesStep={5}
111 id="NewTravelTime"
112 />
113 <TextField
114 label={t('travel.creation.name')}
115 fullWidth
116 helperText=" "
117 value={name}
118 onChange={e => setName(e.target.value)}
119 name="name"
120 id="EditTravelName"
121 />
122 <TextField
123 label={t('travel.creation.phone')}
124 fullWidth
125 helperText=" "
126 value={phone}
127 onChange={e => setPhone(e.target.value)}
128 name="phone"
129 id="EditTravelPhone"
130 />
131 <TextField
132 label={t('travel.creation.meeting')}
133 fullWidth
134 multiline
135 rowsMax={4}
136 inputProps={{maxLength: 250}}
137 helperText={`${meeting.length}/250`}
138 value={meeting}
139 onChange={e => setMeeting(e.target.value)}
140 name="meeting"
141 id="EditTravelMeeting"
142 />
143 <TextField
144 label={t('travel.creation.notes')}
145 fullWidth
146 multiline
147 rowsMax={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 <div className={classes.slider}>
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 </div>
173 </form>
174 <div className={classes.actions}>
175 <Button
176 variant="outlined"
177 color="primary"
178 onClick={onSave}
179 id="TravelSave"
180 >
181 {t('generic.save')}
182 </Button>
183 <Button
184 variant="outlined"
185 color="primary"
186 onClick={toggleRemoving}
187 id="TravelRemove"
188 >
189 {t('generic.remove')}
190 </Button>
191 </div>
192 <RemoveDialog
193 text={t('travel.actions.remove_alert')}
194 open={removing}
195 onClose={toggleRemoving}
196 onRemove={onRemove}
197 />
198 </div>
199 );
200};
201
202const useStyles = makeStyles(theme => ({
203 header: {
204 padding: theme.spacing(2),
205 },
206 edit: {
207 position: 'absolute',
208 top: 0,
209 right: 0,
210 margin: theme.spacing(1),
211 zIndex: theme.zIndex.speedDial,
212 },
213 section: {
214 marginTop: theme.spacing(2),
215 },
216 slider: {
217 marginTop: theme.spacing(2),
218 },
219 actions: {
220 display: 'flex',
221 flexDirection: 'column',
222 justifyContent: 'center',
223 margin: theme.spacing(2, 0),
224 '& > *:first-child': {
225 marginBottom: theme.spacing(2),
226 },
227 },
228}));
229
230export default HeaderEditing;