all repos — caroster @ 1f3eb2266eecf2fb51e87e02d7b630e317be9904

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

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

  1import React, {useReducer, useState, useMemo} 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 {event} = useEvent();
 27  const {addToast} = useToast();
 28  const strapi = useStrapi();
 29  const [isEditing, toggleEditing] = useReducer(i => !i, false);
 30  const [removing, setRemoving] = useState(null);
 31  const [adding, setAdding] = useState(null);
 32  const passengers = event.waiting_list;
 33
 34  const cars = useMemo(
 35    () =>
 36      strapi.stores.cars
 37        ?.filter(car => car?.event?.id === event?.id)
 38        .sort(sortCars),
 39    [strapi.stores.cars, event]
 40  );
 41
 42  const availability = useMemo(() => {
 43    if (!cars) return;
 44    return cars.reduce((count, {seats, passengers = []}) => {
 45      return count + seats - passengers.length;
 46    }, 0);
 47  }, [cars]);
 48
 49  const saveWaitingList = 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
 60  const addPassenger = async passenger => {
 61    return saveWaitingList(
 62      [...(event.waiting_list || []), passenger],
 63      'passenger.errors.cant_add_passenger'
 64    );
 65  };
 66
 67  const removePassenger = index => {
 68    return saveWaitingList(
 69      passengers.filter((_, i) => i !== index),
 70      'passenger.errors.cant_remove_passenger'
 71    );
 72  };
 73
 74  const selectCar = async car => {
 75    try {
 76      await strapi.services.cars.update(car.id, {
 77        passengers: [...(car.passengers || []), passengers[adding]],
 78      });
 79      await strapi.services.events.update(event.id, {
 80        waiting_list: event.waiting_list.filter((_, i) => i !== adding),
 81      });
 82    } catch (error) {
 83      console.error(error);
 84      addToast(t('passenger.errors.cant_select_car'));
 85    }
 86    setAdding(null);
 87  };
 88
 89  return (
 90    <>
 91      <Paper className={classes.root}>
 92        <div className={classes.header}>
 93          <IconButton
 94            size="small"
 95            color="primary"
 96            className={classes.editBtn}
 97            onClick={toggleEditing}
 98          >
 99            {isEditing ? <Icon>check</Icon> : <Icon>edit</Icon>}
100          </IconButton>
101          <Typography variant="h5">{t('passenger.title')}</Typography>
102          <Typography variant="overline">
103            {t('passenger.availability.seats', {count: availability})}
104          </Typography>
105        </div>
106        <Divider />
107        {isEditing ? (
108          <PassengersList
109            hideEmpty
110            places={Number.MAX_SAFE_INTEGER}
111            passengers={passengers}
112            addPassenger={addPassenger}
113            onPress={setRemoving}
114            icon={'close'}
115            disabled={false}
116          />
117        ) : (
118          <PassengersList
119            hideEmpty
120            places={Number.MAX_SAFE_INTEGER}
121            passengers={passengers}
122            addPassenger={addPassenger}
123            onPress={setAdding}
124            icon={'chevron_right'}
125            disabled={availability <= 0}
126          />
127        )}
128      </Paper>
129      <RemoveDialog
130        text={
131          <Trans
132            i18nKey="passenger.actions.remove_alert"
133            values={{name: passengers ? passengers[removing] : null}}
134            components={{italic: <i />, bold: <strong />}}
135          />
136        }
137        open={removing !== null}
138        onClose={() => setRemoving(null)}
139        onRemove={async () => removePassenger(removing)}
140      />
141      <CarDialog
142        cars={cars}
143        open={adding !== null}
144        onClose={() => setAdding(null)}
145        onSelect={selectCar}
146      />
147    </>
148  );
149};
150
151const useStyles = makeStyles(theme => ({
152  root: {
153    position: 'relative',
154  },
155  header: {
156    padding: theme.spacing(2),
157  },
158  editBtn: {
159    position: 'absolute',
160    top: 0,
161    right: 0,
162    margin: theme.spacing(1),
163    zIndex: theme.zIndex.speedDial,
164  },
165}));
166
167export default WaitingList;