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 return cars.reduce((count, {seats, passengers = []}) => {
45 return count + seats - passengers.length;
46 }, 0);
47 }, [cars]);
48
49 useEffect(() => {
50 setPassengers(event.waiting_list);
51 }, [event.waiting_list]);
52
53 const addPassenger = async passenger => {
54 try {
55 await strapi.services.events.update(event.id, {
56 waiting_list: [...(event.waiting_list || []), passenger],
57 });
58 } catch (error) {
59 console.error(error);
60 addToast(t('passenger.errors.cant_add_passenger'));
61 }
62 };
63
64 const removePassenger = index => {
65 setPassengers(passengers.filter((_, i) => i !== index));
66 };
67
68 const savePassengers = async () => {
69 try {
70 await strapi.services.events.update(event.id, {waiting_list: passengers});
71 } catch (error) {
72 console.error(error);
73 addToast(t('passenger.errors.cant_save_passengers'));
74 }
75 };
76
77 const selectCar = async car => {
78 try {
79 await strapi.services.cars.update(car.id, {
80 passengers: [...(car.passengers || []), passengers[adding]],
81 });
82 await strapi.services.events.update(event.id, {
83 waiting_list: event.waiting_list.filter((_, i) => i !== adding),
84 });
85 } catch (error) {
86 console.error(error);
87 addToast(t('passenger.errors.cant_select_car'));
88 }
89 setAdding(null);
90 };
91
92 const onEdit = () => {
93 if (isEditing) savePassengers();
94 toggleEditing();
95 };
96
97 const onPress = index => {
98 if (isEditing) setRemoving(index);
99 else setAdding(index);
100 };
101
102 return (
103 <>
104 <Paper className={classes.root}>
105 <div className={classes.header}>
106 <IconButton
107 size="small"
108 color="primary"
109 className={classes.editBtn}
110 onClick={onEdit}
111 >
112 {isEditing ? <Icon>check</Icon> : <Icon>edit</Icon>}
113 </IconButton>
114 <Typography variant="h5">{t('passenger.title')}</Typography>
115 <Typography variant="overline">
116 {t('passenger.availability.seats', {count: availability})}
117 </Typography>
118 </div>
119 <Divider />
120 <PassengersList
121 hideEmpty
122 places={50}
123 passengers={passengers}
124 addPassenger={addPassenger}
125 onPress={onPress}
126 icon={isEditing ? 'close' : 'chevron_right'}
127 disabled={availability <= 0}
128 />
129 </Paper>
130 <RemoveDialog
131 text={
132 <Trans
133 i18nKey="passenger.actions.remove_alert"
134 values={{name: passengers ? passengers[removing] : null}}
135 components={{italic: <i />, bold: <strong />}}
136 />
137 }
138 open={removing !== null}
139 onClose={() => setRemoving(null)}
140 onRemove={() => removePassenger(removing)}
141 />
142 <CarDialog
143 cars={cars}
144 open={adding !== null}
145 onClose={() => setAdding(null)}
146 onSelect={selectCar}
147 />
148 </>
149 );
150};
151
152const useStyles = makeStyles(theme => ({
153 root: {
154 position: 'relative',
155 },
156 header: {
157 padding: theme.spacing(2),
158 },
159 editBtn: {
160 position: 'absolute',
161 top: 0,
162 right: 0,
163 margin: theme.spacing(1),
164 zIndex: theme.zIndex.speedDial,
165 },
166}));
167
168export default WaitingList;