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, addEvent] // eslint-disable-line
63 );
64
65 const addPassenger = useCallback(
66 async passenger =>
67 saveWaitingList(
68 [...(event.waiting_list || []), passenger],
69 'passenger.errors.cant_add_passenger'
70 ),
71 [event, saveWaitingList] // eslint-disable-line
72 );
73
74 const removePassenger = useCallback(
75 async index => {
76 return saveWaitingList(
77 event.waiting_list.filter((_, i) => i !== index),
78 'passenger.errors.cant_remove_passenger'
79 );
80 },
81 [event, saveWaitingList] // eslint-disable-line
82 );
83
84 const selectCar = useCallback(
85 async car => {
86 try {
87 await strapi.services.cars.update(car.id, {
88 passengers: [...(car.passengers || []), event.waiting_list[adding]],
89 });
90 await strapi.services.events.update(event.id, {
91 waiting_list: event.waiting_list.filter((_, i) => i !== adding),
92 });
93 } catch (error) {
94 console.error(error);
95 addToast(t('passenger.errors.cant_select_car'));
96 }
97 setAdding(null);
98 },
99 [event, adding] // eslint-disable-line
100 );
101
102 const onPress = useCallback(
103 index => {
104 if (isEditing) setRemoving(index);
105 else setAdding(index);
106 },
107 [isEditing]
108 );
109
110 return (
111 <>
112 <Paper className={classes.root}>
113 <div className={classes.header}>
114 <IconButton
115 size="small"
116 color="primary"
117 className={classes.editBtn}
118 disabled={!event.waiting_list || !event.waiting_list.length}
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;