all repos — caroster @ 5b6ade1eaab1743a9ef7efed1953585b2b443040

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

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

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