all repos — caroster @ 1cc727f3c230f02f4f837deb8a5d7780737ee2c4

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

app/src/containers/WaitingList/index.js (view raw)

  1import React, {useReducer, useState, useMemo, useCallback} from 'react';
  2import Typography from '@material-ui/core/Typography';
  3import IconButton from '@material-ui/core/IconButton';
  4import Icon from '@material-ui/core/Icon';
  5import Paper from '@material-ui/core/Paper';
  6import Divider from '@material-ui/core/Divider';
  7import {makeStyles} from '@material-ui/core/styles';
  8import {Trans, useTranslation} from 'react-i18next';
  9import {useStrapi} from 'strapi-react-context';
 10import {useEvent} from '../../contexts/Event';
 11import {useToast} from '../../contexts/Toast';
 12import PassengersList from '../PassengersList';
 13import RemoveDialog from '../RemoveDialog';
 14import CarDialog from './CarDialog';
 15
 16const sortCars = (a, b) => {
 17  const dateA = new Date(a.departure).getTime();
 18  const dateB = new Date(b.departure).getTime();
 19  if (dateA === dateB) return new Date(a.createdAt) - new Date(b.createdAt);
 20  else return dateA - dateB;
 21};
 22
 23const WaitingList = ({car}) => {
 24  const classes = useStyles();
 25  const {t} = useTranslation();
 26  const strapi = useStrapi();
 27  const {event} = useEvent();
 28  const {addToast} = useToast();
 29  const [isEditing, toggleEditing] = useReducer(i => !i, false);
 30  const [removing, setRemoving] = useState(null);
 31  const [adding, setAdding] = useState(null);
 32
 33  const cars = useMemo(
 34    () =>
 35      strapi.stores.cars
 36        ?.filter(car => car?.event?.id === event?.id)
 37        .sort(sortCars),
 38    [strapi.stores.cars, event]
 39  );
 40
 41  const availability = useMemo(() => {
 42    if (!cars) return;
 43    return cars.reduce((count, {seats, passengers = []}) => {
 44      return count + seats - passengers.length;
 45    }, 0);
 46  }, [cars]);
 47
 48  const saveWaitingList = useCallback(
 49    async (waitingList, i18nError) => {
 50      try {
 51        await strapi.services.events.update(event.id, {
 52          waiting_list: waitingList,
 53        });
 54      } catch (error) {
 55        console.error(error);
 56        addToast(t(i18nError));
 57      }
 58    },
 59    [event] // eslint-disable-line
 60  );
 61
 62  const addPassenger = useCallback(
 63    async passenger => {
 64      return saveWaitingList(
 65        [...(event.waiting_list || []), passenger],
 66        'passenger.errors.cant_add_passenger'
 67      );
 68    },
 69    [event] // eslint-disable-line
 70  );
 71
 72  const removePassenger = useCallback(
 73    async index => {
 74      return saveWaitingList(
 75        event.waiting_list.filter((_, i) => i !== index),
 76        'passenger.errors.cant_remove_passenger'
 77      );
 78    },
 79    [event] // eslint-disable-line
 80  );
 81
 82  const selectCar = useCallback(
 83    async car => {
 84      try {
 85        await strapi.services.cars.update(car.id, {
 86          passengers: [...(car.passengers || []), event.waiting_list[adding]],
 87        });
 88        await strapi.services.events.update(event.id, {
 89          waiting_list: event.waiting_list.filter((_, i) => i !== adding),
 90        });
 91      } catch (error) {
 92        console.error(error);
 93        addToast(t('passenger.errors.cant_select_car'));
 94      }
 95      setAdding(null);
 96    },
 97    [event, adding] // eslint-disable-line
 98  );
 99
100  const onPress = useCallback(
101    index => {
102      if (isEditing) setRemoving(index);
103      else setAdding(index);
104    },
105    [isEditing]
106  );
107
108  return (
109    <>
110      <Paper className={classes.root}>
111        <div className={classes.header}>
112          <IconButton
113            size="small"
114            color="primary"
115            className={classes.editBtn}
116            onClick={toggleEditing}
117          >
118            {isEditing ? <Icon>check</Icon> : <Icon>edit</Icon>}
119          </IconButton>
120          <Typography variant="h5">{t('passenger.title')}</Typography>
121          <Typography variant="overline">
122            {t('passenger.availability.seats', {count: availability})}
123          </Typography>
124        </div>
125        <Divider />
126        <PassengersList
127          passengers={event.waiting_list}
128          addPassenger={addPassenger}
129          onPress={onPress}
130          icon={isEditing ? 'close' : 'chevron_right'}
131          disabled={!isEditing && availability <= 0}
132        />
133      </Paper>
134      <RemoveDialog
135        text={
136          <Trans
137            i18nKey="passenger.actions.remove_alert"
138            values={{
139              name: event.waiting_list ? event.waiting_list[removing] : null,
140            }}
141            components={{italic: <i />, bold: <strong />}}
142          />
143        }
144        open={removing !== null}
145        onClose={() => setRemoving(null)}
146        onRemove={() => removePassenger(removing)}
147      />
148      <CarDialog
149        cars={cars}
150        open={adding !== null}
151        onClose={() => setAdding(null)}
152        onSelect={selectCar}
153      />
154    </>
155  );
156};
157
158const useStyles = makeStyles(theme => ({
159  root: {
160    position: 'relative',
161  },
162  header: {
163    padding: theme.spacing(2),
164  },
165  editBtn: {
166    position: 'absolute',
167    top: 0,
168    right: 0,
169    margin: theme.spacing(1),
170    zIndex: theme.zIndex.speedDial,
171  },
172}));
173
174export default WaitingList;