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