frontend/containers/NewTravelDialog/index.tsx (view raw)
1import {useState, forwardRef, useMemo, useEffect} from 'react';
2import {makeStyles} from '@material-ui/core/styles';
3import Dialog from '@material-ui/core/Dialog';
4import DialogActions from '@material-ui/core/DialogActions';
5import DialogContent from '@material-ui/core/DialogContent';
6import DialogTitle from '@material-ui/core/DialogTitle';
7import Button from '@material-ui/core/Button';
8import Slide from '@material-ui/core/Slide';
9import TextField from '@material-ui/core/TextField';
10import Slider from '@material-ui/core/Slider';
11import Typography from '@material-ui/core/Typography';
12import {DatePicker, TimePicker} from '@material-ui/pickers';
13import moment from 'moment';
14import {useTranslation} from 'react-i18next';
15import useEventStore from '../../stores/useEventStore';
16import useActions from './useActions';
17import useProfile from '../../hooks/useProfile';
18
19const NewTravelDialog = ({context, toggle}) => {
20 const {t} = useTranslation();
21 const classes = useStyles();
22 const {user} = useProfile();
23 const event = useEventStore(s => s.event);
24 const actions = useActions({event});
25
26 const dateMoment = useMemo(() => {
27 if (!event?.date) return moment();
28 else return moment(event.date);
29 }, [event?.date]);
30
31 // States
32 const [name, setName] = useState('');
33 const [seats, setSeats] = useState(4);
34 const [meeting, setMeeting] = useState('');
35 const [date, setDate] = useState(dateMoment);
36 const [time, setTime] = useState(dateMoment);
37 const [phone, setPhone] = useState('');
38 const [details, setDetails] = useState('');
39 const canCreate = !!name && !!seats;
40
41 useEffect(() => {
42 if (context.vehicle) {
43 setName(context.vehicle.name);
44 setSeats(context.vehicle.seats);
45 setPhone(context.vehicle.phone_number);
46 }
47 }, [context.vehicle]);
48
49 const onCreate = async e => {
50 if (e.preventDefault) e.preventDefault();
51
52 const travel = {
53 meeting,
54 date,
55 time,
56 details,
57 vehicle: {
58 name,
59 seats,
60 phone_number: phone,
61 },
62 };
63 if (context.vehicle && user) {
64 // The authenticated user choose an existing vehicle and assign it to the travel
65 await actions.createTravel({
66 ...travel,
67 vehicle: context.vehicle,
68 });
69 } else if (user) {
70 // The autenticated user create a vehicle and assign it to the travel
71 await actions.createTravel({
72 ...travel,
73 vehicle: {...travel.vehicle, user: user.id, created_by: user.id},
74 });
75 } else {
76 // The anonymous user create a vehicle and assign it to the travel
77 await actions.createTravel(travel);
78 }
79
80 toggle({opened: false});
81
82 // Clear states
83 setName('');
84 setSeats(4);
85 setMeeting('');
86 setDate(moment());
87 setPhone('');
88 setDetails('');
89 };
90
91 return (
92 <Dialog
93 fullWidth
94 maxWidth="xs"
95 open={context?.opened}
96 onClose={() => toggle({opened: false})}
97 TransitionComponent={Transition}
98 >
99 <form onSubmit={onCreate}>
100 <DialogTitle>{t('travel.creation.title')}</DialogTitle>
101 <DialogContent>
102 <DatePicker
103 label={t('travel.creation.date')}
104 fullWidth
105 helperText=" "
106 value={date}
107 onChange={setDate}
108 format="DD/MM/YYYY"
109 cancelLabel={t('generic.cancel')}
110 autoFocus
111 id="NewTravelDateTime"
112 />
113 <TimePicker
114 label={t('travel.creation.time')}
115 fullWidth
116 helperText=" "
117 value={time}
118 onChange={setTime}
119 cancelLabel={t('generic.cancel')}
120 ampm={false}
121 minutesStep={5}
122 id="NewTravelTime"
123 />
124 <TextField
125 label={t('travel.creation.name')}
126 fullWidth
127 helperText=" "
128 value={name}
129 disabled={!!context.vehicle}
130 onChange={e => setName(e.target.value)}
131 name="name"
132 id="NewTravelName"
133 />
134 <TextField
135 label={t('travel.creation.phone')}
136 fullWidth
137 helperText=" "
138 value={phone}
139 disabled={!!context.vehicle}
140 onChange={e => setPhone(e.target.value)}
141 name="phone"
142 id="NewTravelPhone"
143 />
144 <TextField
145 label={t('travel.creation.meeting')}
146 fullWidth
147 multiline
148 rowsMax={4}
149 inputProps={{maxLength: 250}}
150 helperText={`${meeting.length}/250`}
151 value={meeting}
152 onChange={e => setMeeting(e.target.value)}
153 name="meeting"
154 id="NewTravelMeeting"
155 />
156 <TextField
157 label={t('travel.creation.notes')}
158 fullWidth
159 multiline
160 rowsMax={4}
161 inputProps={{maxLength: 250}}
162 helperText={`${details.length}/250`}
163 value={details}
164 onChange={e => setDetails(e.target.value)}
165 name="details"
166 id="NewTravelDetails"
167 />
168 <div className={classes.slider}>
169 <Typography variant="caption">
170 {t('travel.creation.seats')}
171 </Typography>
172 <Slider
173 value={seats}
174 disabled={!!context.vehicle}
175 onChange={(e, value) => setSeats(value)}
176 step={1}
177 marks={MARKS}
178 min={1}
179 max={MARKS.length}
180 valueLabelDisplay="auto"
181 id="NewTravelSeats"
182 />
183 </div>
184 </DialogContent>
185 <DialogActions>
186 <Button
187 color="primary"
188 id="NewTravelCancel"
189 onClick={() => toggle({opened: false})}
190 tabIndex={-1}
191 >
192 {t('generic.cancel')}
193 </Button>
194 <Button
195 color="primary"
196 variant="contained"
197 type="submit"
198 disabled={!canCreate}
199 aria-disabled={!canCreate}
200 id="NewTravelSubmit"
201 >
202 {t('generic.create')}
203 </Button>
204 </DialogActions>
205 </form>
206 </Dialog>
207 );
208};
209
210const Transition = forwardRef(function Transition(props, ref) {
211 return <Slide direction="up" ref={ref} {...props} />;
212});
213
214const MARKS = [1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
215 value,
216 label: value,
217}));
218
219const useStyles = makeStyles(theme => ({
220 slider: {
221 marginTop: theme.spacing(2),
222 },
223}));
224
225export default NewTravelDialog;