all repos — caroster @ 94ed421247e41b427d90cf078b579daf30b8842b

[Octree] Group carpool to your event https://caroster.io

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';
 18import {Box, Divider} from '@material-ui/core';
 19import FAQLink from './FAQLink';
 20
 21interface Props {
 22  context: {
 23    vehicle: Vehicle;
 24    opened: boolean;
 25  };
 26  toggle: ({opened: boolean}) => void;
 27}
 28
 29const NewTravelDialog = ({context, toggle}: Props) => {
 30  const {t} = useTranslation();
 31  const classes = useStyles();
 32  const event = useEventStore(s => s.event);
 33  const {createTravel} = useActions({event});
 34
 35  const dateMoment = useMemo(() => {
 36    if (!event?.date) return moment();
 37    else return moment(event.date);
 38  }, [event?.date]);
 39
 40  // States
 41  const [name, setName] = useState('');
 42  const [seats, setSeats] = useState(4);
 43  const [meeting, setMeeting] = useState('');
 44  const [date, setDate] = useState(dateMoment);
 45  const [time, setTime] = useState(dateMoment);
 46  const [phone, setPhone] = useState('');
 47  const [details, setDetails] = useState('');
 48  const canCreate = !!name && !!seats;
 49
 50  const clearState = () => {
 51    setName('');
 52    setSeats(4);
 53    setMeeting('');
 54    setDate(moment());
 55    setPhone('');
 56    setDetails('');
 57  };
 58
 59  useEffect(() => {
 60    if (context.vehicle) {
 61      setName(context.vehicle.name);
 62      setSeats(context.vehicle.seats);
 63      setPhone(context.vehicle.phone_number);
 64    }
 65  }, [context.vehicle]);
 66
 67  const onCreate = async e => {
 68    if (e.preventDefault) e.preventDefault();
 69
 70    const travel = {
 71      meeting,
 72      details,
 73      seats,
 74      vehicleName: name,
 75      phone_number: phone,
 76      departure: formatDate(date, time),
 77      event: event.id,
 78    };
 79    const createVehicle = !context.vehicle;
 80
 81    await createTravel({...travel, createVehicle});
 82    toggle({opened: false});
 83
 84    clearState();
 85  };
 86
 87  return (
 88    <Dialog
 89      fullWidth
 90      maxWidth="xs"
 91      open={context?.opened}
 92      onClose={() => {
 93        toggle({opened: false});
 94        clearState();
 95      }}
 96      TransitionComponent={Transition}
 97    >
 98      <form onSubmit={onCreate}>
 99        <DialogTitle className={classes.title}>
100          {t('travel.creation.title')}
101        </DialogTitle>
102        <DialogContent className={classes.content}>
103          <Typography className={classes.sectionTitle}>
104            {t('travel.creation.car.title')}
105          </Typography>
106          <TextField
107            variant="outlined"
108            size="small"
109            className={classes.field}
110            label={t('travel.creation.name')}
111            fullWidth
112            helperText=" "
113            value={name}
114            onChange={e => setName(e.target.value)}
115            name="name"
116            id="NewTravelName"
117          />
118          <TextField
119            variant="outlined"
120            size="small"
121            className={classes.field}
122            label={t('travel.creation.phone')}
123            fullWidth
124            helperText=" "
125            value={phone}
126            onChange={e => setPhone(e.target.value)}
127            name="phone"
128            FormHelperTextProps={{
129              component: () => (
130                <FAQLink
131                  link={t('travel.creation.phoneHelper.faq')}
132                  text={t('travel.creation.phoneHelper.why')}
133                />
134              ),
135            }}
136            id="NewTravelPhone"
137          />
138          <div className={classes.slider}>
139            <Typography variant="caption">
140              {t('travel.creation.seats')}
141            </Typography>
142            <Slider
143              value={seats}
144              onChange={(e, value) => setSeats(value)}
145              step={1}
146              marks={MARKS}
147              min={1}
148              max={MARKS.length}
149              valueLabelDisplay="auto"
150              id="NewTravelSeats"
151            />
152          </div>
153          <Divider className={classes.divider} />
154          <Typography className={classes.sectionTitle}>
155            {t('travel.creation.travel.title')}
156          </Typography>
157          <Box className={classes.halfWidthWrapper}>
158            <DatePicker
159              className={classes.halfWidthField}
160              inputVariant="outlined"
161              size="small"
162              label={t('travel.creation.date')}
163              helperText=" "
164              value={date}
165              onChange={setDate}
166              format="DD/MM/YYYY"
167              cancelLabel={t('generic.cancel')}
168              autoFocus
169              id="NewTravelDateTime"
170            />
171            <TimePicker
172              className={classes.halfWidthField}
173              inputVariant="outlined"
174              size="small"
175              label={t('travel.creation.time')}
176              helperText=" "
177              value={time}
178              onChange={setTime}
179              cancelLabel={t('generic.cancel')}
180              ampm={false}
181              minutesStep={5}
182              id="NewTravelTime"
183            />
184          </Box>
185          <TextField
186            variant="outlined"
187            size="small"
188            className={classes.field}
189            label={t('travel.creation.meeting')}
190            fullWidth
191            multiline
192            rowsMax={4}
193            inputProps={{maxLength: 250}}
194            helperText={`${meeting.length}/250`}
195            value={meeting}
196            onChange={e => setMeeting(e.target.value)}
197            name="meeting"
198            id="NewTravelMeeting"
199          />
200          <TextField
201            variant="outlined"
202            size="small"
203            className={classes.field}
204            label={t('travel.creation.notes')}
205            fullWidth
206            multiline
207            rowsMax={4}
208            inputProps={{maxLength: 250}}
209            helperText={`${details.length}/250`}
210            value={details}
211            onChange={e => setDetails(e.target.value)}
212            name="details"
213            id="NewTravelDetails"
214          />
215        </DialogContent>
216        <DialogActions className={classes.actions}>
217          <Button
218            color="primary"
219            id="NewTravelCancel"
220            onClick={() => toggle({opened: false})}
221            tabIndex={-1}
222          >
223            {t('generic.cancel')}
224          </Button>
225          <Button
226            color="primary"
227            variant="contained"
228            type="submit"
229            disabled={!canCreate}
230            aria-disabled={!canCreate}
231            id="NewTravelSubmit"
232          >
233            {t('travel.creation.submit')}
234          </Button>
235        </DialogActions>
236      </form>
237    </Dialog>
238  );
239};
240
241const Transition = forwardRef(function Transition(props, ref) {
242  return <Slide direction="up" ref={ref} {...props} />;
243});
244
245const formatDate = (date: Moment, time: Moment) => {
246  return moment(
247    `${moment(date).format('YYYY-MM-DD')} ${moment(time).format('HH:mm')}`,
248    'YYYY-MM-DD HH:mm'
249  ).toISOString();
250};
251
252const MARKS = [1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
253  value,
254  label: value,
255}));
256
257const addSpacing = (theme, ratio) => ({
258  margin: `0 ${theme.spacing(3 * ratio)}px`,
259  width: `calc(100% - ${theme.spacing(6 * ratio)}px)`,
260});
261
262const useStyles = makeStyles(theme => ({
263  title: {
264    paddingBottom: 0,
265  },
266  sectionTitle: {
267    ...addSpacing(theme, 1),
268    paddingBottom: theme.spacing(1.5),
269  },
270  content: {
271    padding: `${theme.spacing(2)}px 0`,
272  },
273  field: {
274    ...addSpacing(theme, 1),
275    paddingBottom: theme.spacing(1)
276  },
277  halfWidthWrapper: {
278    ...addSpacing(theme, .5)
279  },
280  halfWidthField: {
281    margin: `0 ${theme.spacing(1.5)}px`,
282    width: `calc(50% - ${theme.spacing(3)}px)`,
283  },
284  slider: {
285    ...addSpacing(theme, 1),
286  },
287  divider: {
288    margin: `${theme.spacing(2)}px 0`,
289  },
290  actions: {
291    paddingTop: 0,
292  }
293}));
294
295export default NewTravelDialog;