frontend/containers/TravelColumns/index.tsx (view raw)
1import {useMemo, useRef, useState} from 'react';
2import {makeStyles} from '@material-ui/core/styles';
3import Container from '@material-ui/core/Container';
4import Slider from 'react-slick';
5import {useTranslation} from 'react-i18next';
6import {Travel as TravelData, TravelEntity} from '../../generated/graphql';
7import useEventStore from '../../stores/useEventStore';
8import useToastStore from '../../stores/useToastStore';
9import useProfile from '../../hooks/useProfile';
10import useAddToEvents from '../../hooks/useAddToEvents';
11import {AddPassengerToTravel} from '../NewPassengerDialog';
12import Travel from '../Travel';
13import sliderSettings from './_SliderSettings';
14import usePassengersActions from '../../hooks/usePassengersActions';
15import NoCar from './NoCar';
16
17type TravelType = TravelData & {id: string};
18
19interface Props {
20 toggle: () => void;
21}
22
23const TravelColumns = (props: Props) => {
24 const event = useEventStore(s => s.event);
25 const travels = event?.travels?.data || [];
26 const slider = useRef(null);
27 const {t} = useTranslation();
28 const addToast = useToastStore(s => s.addToast);
29 const {addToEvent} = useAddToEvents();
30 const {profile, userId, connected} = useProfile();
31 const classes = useStyles();
32 const [newPassengerTravelContext, toggleNewPassengerToTravel] = useState<{
33 travel: TravelType;
34 } | null>(null);
35 const {addPassenger} = usePassengersActions();
36 const sortedTravels = travels?.slice().sort(sortTravels);
37
38 const canAddSelf = useMemo(() => {
39 if (!connected) return false;
40 const isInWaitingList = event?.waitingPassengers?.data.some(
41 passenger => passenger.attributes.user?.data?.id === `${userId}`
42 );
43 const isInTravel = event?.travels?.data.some(travel =>
44 travel.attributes.passengers?.data.some(
45 passenger => passenger.attributes.user?.data?.id === `${userId}`
46 )
47 );
48 return !(isInWaitingList || isInTravel);
49 }, [event, userId]);
50
51 const addSelfToTravel = async (travel: TravelType) => {
52 try {
53 await addPassenger({
54 user: userId,
55 email: profile.email,
56 name: profile.username,
57 travel: travel.id,
58 });
59 addToEvent(event.id);
60 addToast(t('passenger.success.added_self_to_car'));
61 } catch (error) {
62 console.error(error);
63 }
64 };
65
66 return (
67 <div className={classes.container}>
68 <div className={classes.dots} id="slider-dots" />
69 {(travels?.length === 0 && (
70 <NoCar
71 image
72 eventName={event?.name}
73 title={t('event.no_travel.title')}
74 />
75 )) || (
76 <Slider ref={slider} {...sliderSettings}>
77 {sortedTravels?.map(({id, attributes}) => {
78 const travel = {id, ...attributes};
79 return (
80 <Container
81 key={travel.id}
82 maxWidth="sm"
83 className={classes.slide}
84 >
85 <Travel
86 travel={travel}
87 {...props}
88 canAddSelf={canAddSelf}
89 getAddPassengerFunction={(addSelf: boolean) => () =>
90 addSelf
91 ? addSelfToTravel(travel)
92 : toggleNewPassengerToTravel({travel})}
93 />
94 </Container>
95 );
96 })}
97 <Container maxWidth="sm" className={classes.slide}>
98 <NoCar
99 eventName={event?.name}
100 title={t('event.no_other_travel.title')}
101 />
102 </Container>
103 </Slider>
104 )}
105 {!!newPassengerTravelContext && (
106 <AddPassengerToTravel
107 open={!!newPassengerTravelContext}
108 toggle={() => toggleNewPassengerToTravel(null)}
109 travel={newPassengerTravelContext.travel}
110 />
111 )}
112 </div>
113 );
114};
115
116const sortTravels = (
117 {attributes: a}: TravelEntity,
118 {attributes: b}: TravelEntity
119) => {
120 if (!b) return 1;
121 const dateA = new Date(a.departure).getTime();
122 const dateB = new Date(b.departure).getTime();
123 if (dateA === dateB)
124 return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
125 else return dateA - dateB;
126};
127
128const useStyles = makeStyles(theme => ({
129 container: {
130 paddingLeft: theme.spacing(6),
131 paddingRight: theme.spacing(6),
132 [theme.breakpoints.down('sm')]: {
133 paddingLeft: theme.spacing(),
134 paddingRight: theme.spacing(),
135 },
136 display: 'flex',
137 flexDirection: 'column',
138 },
139 dots: {
140 height: '56px',
141 overflow: 'auto',
142 '& overflow': '-moz-scrollbars-none',
143 '-ms-overflow-style': 'none',
144 '&::-webkit-scrollbar': {
145 height: '0 !important',
146 },
147 '& .slick-dots': {
148 position: 'static',
149 '& li': {
150 display: 'block',
151 '& button:before': {
152 fontSize: '12px',
153 },
154 },
155 },
156 '& .slick-dots li:first-child button:before, & .slick-dots li:last-child button:before':
157 {
158 color: theme.palette.primary.main,
159 },
160 },
161 slide: {
162 padding: theme.spacing(1),
163 marginBottom: theme.spacing(10),
164 outline: 'none',
165 '& > *': {
166 cursor: 'default',
167 },
168
169 [theme.breakpoints.down('sm')]: {
170 marginBottom: `${theme.spacing(10) + 56}px`,
171 },
172 },
173}));
174
175export default TravelColumns;