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 disabled={!event.waiting_list || !event.waiting_list.length}
120 onClick={toggleEditing}
121 >
122 {isEditing ? <Icon>check</Icon> : <Icon>edit</Icon>}
123 </IconButton>
124 <Typography variant="h5">{t('passenger.title')}</Typography>
125 <Typography variant="overline">
126 {t('passenger.availability.seats', {count: availability})}
127 </Typography>
128 </div>
129 <Divider />
130 <PassengersList
131 passengers={event.waiting_list}
132 addPassenger={addPassenger}
133 onPress={onPress}
134 icon={isEditing ? 'close' : 'chevron_right'}
135 disabled={!isEditing && availability <= 0}
136 />
137 </Paper>
138 <RemoveDialog
139 text={
140 <Trans
141 i18nKey="passenger.actions.remove_alert"
142 values={{
143 name: event.waiting_list ? event.waiting_list[removing] : null,
144 }}
145 components={{italic: <i />, bold: <strong />}}
146 />
147 }
148 open={removing !== null}
149 onClose={() => setRemoving(null)}
150 onRemove={() => removePassenger(removing)}
151 />
152 <CarDialog
153 cars={cars}
154 open={adding !== null}
155 onClose={() => setAdding(null)}
156 onSelect={selectCar}
157 />
158 </>
159 );
160};
161
162const useStyles = makeStyles(theme => ({
163 root: {
164 position: 'relative',
165 },
166 header: {
167 padding: theme.spacing(2),
168 },
169 editBtn: {
170 position: 'absolute',
171 top: 0,
172 right: 0,
173 margin: theme.spacing(1),
174 zIndex: theme.zIndex.speedDial,
175 },
176}));
177
178export default WaitingList;