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;