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