frontend/containers/TravelColumns/index.tsx (view raw)
1import {useMemo, useState} from 'react';
2import Masonry from '@mui/lab/Masonry';
3import Box from '@mui/material/Box';
4import moment from 'moment';
5import {useTranslation} from 'next-i18next';
6import {useTheme} from '@mui/material/styles';
7import useEventStore from '../../stores/useEventStore';
8import useToastStore from '../../stores/useToastStore';
9import useProfile from '../../hooks/useProfile';
10import useAddToEvents from '../../hooks/useAddToEvents';
11import usePassengersActions from '../../hooks/usePassengersActions';
12import Map from '../Map';
13import Travel from '../Travel';
14import NoCar from './NoCar';
15import {TravelEntity} from '../../generated/graphql';
16import {AddPassengerToTravel} from '../NewPassengerDialog';
17import MasonryContainer from './MasonryContainer';
18import LoginToAttend from '../LoginToAttend/LoginToAttend';
19import usePermissions from '../../hooks/usePermissions';
20import useDisplayTravels from './useDisplayTravels';
21import useDisplayMarkers from './useDisplayMarkers';
22import FilterByDate from '../../components/FilterByDate';
23
24interface Props {
25 toggle: () => void;
26}
27
28const TravelColumns = (props: Props) => {
29 const theme = useTheme();
30 const event = useEventStore(s => s.event);
31 const travels = event?.travels?.data || [];
32 const {t} = useTranslation();
33 const addToast = useToastStore(s => s.addToast);
34 const {addToEvent} = useAddToEvents();
35 const {profile, userId} = useProfile();
36 const {
37 userPermissions: {canAddTravel},
38 } = usePermissions();
39
40 const [selectedTravel, setSelectedTravel] = useState<TravelEntity>();
41 const {addPassenger} = usePassengersActions();
42 const {selectedDates, setSelectedDates, displayedTravels} =
43 useDisplayTravels();
44 useDisplayMarkers({event, selectedDates});
45
46 const buttonFilterContent = useMemo(() => {
47 if (selectedDates.length > 1) return t('event.filter.dates');
48 else if (selectedDates.length === 1)
49 return selectedDates.map(date => date.format('dddd Do MMMM'));
50 else return t('event.filter.allDates');
51 }, [selectedDates, t]);
52
53 const addSelfToTravel = async (travel: TravelEntity) => {
54 const hasName = profile.firstName && profile.lastName;
55 const userName = profile.firstName + ' ' + profile.lastName;
56 try {
57 await addPassenger({
58 user: userId,
59 email: profile.email,
60 name: hasName ? userName : profile.username,
61 travel: travel.id,
62 event: event.id,
63 });
64 addToEvent(event.id);
65 addToast(t('passenger.success.added_self_to_car'));
66 } catch (error) {
67 console.error(error);
68 }
69 };
70
71 const isCarosterPlus = event?.enabled_modules?.includes('caroster-plus');
72
73 if (!event || travels?.length === 0)
74 return (
75 <NoCar
76 showImage
77 eventName={event?.name}
78 title={t('event.no_travel.title')}
79 isCarosterPlus={isCarosterPlus}
80 />
81 );
82
83 const showMap =
84 (event.latitude && event.longitude) ||
85 travels.some(
86 ({attributes: {meeting_latitude, meeting_longitude}}) =>
87 meeting_latitude && meeting_longitude
88 );
89
90 const dates = Array.from(
91 new Set(travels.map(travel => travel?.attributes?.departureDate))
92 )
93 .map(date => moment(date))
94 .filter(date => date.isValid())
95 .sort((a, b) => (a.isAfter(b) ? 1 : -1));
96
97 return (
98 <>
99 {showMap && <Map />}
100 <FilterByDate
101 dates={dates}
102 setSelectedDates={setSelectedDates}
103 buttonFilterContent={buttonFilterContent}
104 />
105 <Box
106 p={0}
107 pt={showMap ? 4 : 13}
108 pb={11}
109 sx={{
110 overflowX: 'hidden',
111 overflowY: 'auto',
112 maxHeight: showMap ? '50vh' : '100vh',
113 [theme.breakpoints.down('md')]: {
114 maxHeight: showMap ? '50vh' : '100vh',
115 px: 1,
116 },
117 }}
118 >
119 <Masonry columns={{xl: 4, lg: 3, md: 2, sm: 2, xs: 1}} spacing={0}>
120 {!canAddTravel() && (
121 <MasonryContainer key="no_other_travel">
122 <LoginToAttend title={t('event.loginToAttend')} />
123 </MasonryContainer>
124 )}
125 {displayedTravels?.map(travel => {
126 return (
127 <MasonryContainer key={travel.id}>
128 <Travel
129 travel={travel}
130 onAddSelf={() => addSelfToTravel(travel)}
131 onAddOther={() => setSelectedTravel(travel)}
132 {...props}
133 />
134 </MasonryContainer>
135 );
136 })}
137 <MasonryContainer key="no_other_travel">
138 <NoCar
139 eventName={event?.name}
140 title={t('event.no_other_travel.title')}
141 isCarosterPlus={isCarosterPlus}
142 />
143 </MasonryContainer>
144 </Masonry>
145 </Box>
146 {!!selectedTravel && (
147 <AddPassengerToTravel
148 open={!!selectedTravel}
149 toggle={() => setSelectedTravel(null)}
150 travel={selectedTravel}
151 />
152 )}
153 </>
154 );
155};
156
157export default TravelColumns;