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