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