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