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