all repos — caroster @ 3e13ed81f9961ed582333d0e9296921db2ed91d9

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

frontend/containers/WaitingList/index.tsx (view raw)

  1import {useReducer, useState, useMemo, useCallback} from 'react';
  2import router from 'next/dist/client/router';
  3import Container from '@mui/material/Container';
  4import TuneIcon from '@mui/icons-material/Tune';
  5import Box from '@mui/material/Box';
  6import Typography from '@mui/material/Typography';
  7import IconButton from '@mui/material/IconButton';
  8import Icon from '@mui/material/Icon';
  9import Paper from '@mui/material/Paper';
 10import Divider from '@mui/material/Divider';
 11import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
 12import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';
 13import {Trans, useTranslation} from 'react-i18next';
 14import useToastStore from '../../stores/useToastStore';
 15import useEventStore from '../../stores/useEventStore';
 16import usePassengersActions from '../../hooks/usePassengersActions';
 17import PassengersList from '../PassengersList';
 18import RemoveDialog from '../RemoveDialog';
 19import AddPassengerButtons from '../AddPassengerButtons';
 20import AssignButton from './AssignButton';
 21import { useTheme } from '@mui/material/styles';
 22import useMediaQuery from '@mui/material/useMediaQuery';
 23
 24interface Props {
 25  getToggleNewPassengerDialogFunction: (addSelf: boolean) => () => void;
 26  canAddSelf: boolean;
 27  registered: boolean;
 28}
 29
 30const WaitingList = ({
 31  getToggleNewPassengerDialogFunction,
 32  canAddSelf,
 33  registered
 34}: Props) => {
 35  const {t} = useTranslation();
 36  const event = useEventStore(s => s.event);
 37  const theme = useTheme();
 38  const mobile = useMediaQuery(theme.breakpoints.down('md'));
 39  const addToast = useToastStore(s => s.addToast);
 40  const [isEditing, toggleEditing] = useReducer(i => !i, false);
 41  const [removingPassenger, setRemovingPassenger] = useState(null);
 42  const travels =
 43    event?.travels?.data?.length > 0
 44      ? event?.travels?.data.slice().sort(sortTravels)
 45      : [];
 46  const {removePassenger} = usePassengersActions();
 47
 48  const availability = useMemo(() => {
 49    if (!travels) return;
 50    return travels.reduce((count, {attributes: {seats, passengers}}) => {
 51      if (!seats) return 0;
 52      else if (!passengers) return count + seats;
 53      return count + seats - passengers?.data?.length;
 54    }, 0);
 55  }, [travels]);
 56
 57  const removePassengerCallback = useCallback(removePassenger, [event]);
 58
 59  const onPress = useCallback(
 60    (passengerId: string) => {
 61      const selectedPassenger = event?.waitingPassengers?.data.find(
 62        item => item.id === passengerId
 63      );
 64      if (isEditing) setRemovingPassenger(selectedPassenger);
 65      else router.push(`/e/${event.uuid}/assign/${selectedPassenger.id}`);
 66    },
 67    [isEditing, event]
 68  );
 69
 70  const onRemove = async () => {
 71    try {
 72      await removePassengerCallback(removingPassenger.id);
 73      addToast(t('passenger.deleted'));
 74    } catch (error) {
 75      console.error(error);
 76      addToast(t('passenger.errors.cant_remove_passenger'));
 77    }
 78  };
 79
 80  const ListButton = isEditing
 81    ? ({onClick}: {onClick: () => void}) => (
 82        <ListItemSecondaryAction>
 83          <IconButton size="small" color="primary" onClick={onClick}>
 84            <CancelOutlinedIcon />
 85          </IconButton>
 86        </ListItemSecondaryAction>
 87      )
 88    : ({onClick, disabled}: {onClick: () => void; disabled: boolean}) => (
 89        <AssignButton onClick={onClick} tabIndex={-1} disabled={disabled} />
 90      );
 91
 92  return (
 93    <Container maxWidth="sm" sx={{mt: 11, mx: 0, px: mobile ? 2 : 4}}>
 94      <Paper sx={{width: '480px', maxWidth: '100%', position: 'relative'}}>
 95        <Box p={2}>
 96          <IconButton
 97            size="small"
 98            color="primary"
 99            sx={{
100              position: 'absolute',
101              top: 0,
102              right: 0,
103              margin: 1,
104            }}
105            disabled={!event?.waitingPassengers?.data?.length}
106            onClick={toggleEditing}
107          >
108            {isEditing ? <Icon>check</Icon> : <TuneIcon />}
109          </IconButton>
110          <Typography variant="h5">{t('passenger.title')}</Typography>
111          <Typography variant="overline">
112            {t('passenger.availability.seats', {count: availability})}
113          </Typography>
114        </Box>
115        <Divider />
116        <AddPassengerButtons
117          getOnClickFunction={getToggleNewPassengerDialogFunction}
118          canAddSelf={canAddSelf}
119          registered={registered}
120          variant="waitingList"
121        />
122        <Divider />
123        {event?.waitingPassengers?.data?.length > 0 && (
124          <PassengersList
125            passengers={event.waitingPassengers.data}
126            onPress={onPress}
127            Button={ListButton}
128          />
129        )}
130      </Paper>
131      <RemoveDialog
132        text={
133          <Trans
134            i18nKey="passenger.actions.remove_alert"
135            values={{
136              name: removingPassenger?.name,
137            }}
138            components={{italic: <i />, bold: <strong />}}
139          />
140        }
141        open={!!removingPassenger}
142        onClose={() => setRemovingPassenger(null)}
143        onRemove={onRemove}
144      />
145    </Container>
146  );
147};
148
149const sortTravels = (a, b) => {
150  const dateA = new Date(a.departure).getTime();
151  const dateB = new Date(b.departure).getTime();
152  if (dateA === dateB)
153    return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
154  else return dateA - dateB;
155};
156
157export default WaitingList;