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 {useTranslation} from 'react-i18next';
5import {useTheme} from '@mui/material/styles';
6import useEventStore from '../../stores/useEventStore';
7import useToastStore from '../../stores/useToastStore';
8import useProfile from '../../hooks/useProfile';
9import useAddToEvents from '../../hooks/useAddToEvents';
10import usePassengersActions from '../../hooks/usePassengersActions';
11import Map from '../Map';
12import Travel from '../Travel';
13import NoCar from './NoCar';
14import {TravelEntity} from '../../generated/graphql';
15import {AddPassengerToTravel} from '../NewPassengerDialog';
16import MasonryContainer from './MasonryContainer';
17import LoginToAttend from './LoginToAttend';
18import usePermissions from '../../hooks/usePermissions';
19import useDisplayTravels from './useDisplayTravels';
20import useDisplayMarkers from './useDisplayMarkers';
21import FilterByDate from '../../components/FilterByDate';
22import moment from 'moment';
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 description={
80 isCarosterPlus
81 ? t('event.no_travel.plus.desc')
82 : t('event.no_travel.desc')
83 }
84 />
85 );
86
87 const showMap =
88 (event.latitude && event.longitude) ||
89 travels.some(
90 ({attributes: {meeting_latitude, meeting_longitude}}) =>
91 meeting_latitude && meeting_longitude
92 );
93
94 const dates = Array.from(
95 new Set(travels.map(travel => travel?.attributes?.departure))
96 )
97 .map(date => moment(date))
98 .filter(date => date.isValid())
99 .sort((a, b) => (a.isAfter(b) ? 1 : -1));
100
101 return (
102 <>
103 {showMap && <Map />}
104 <FilterByDate
105 dates={dates}
106 setSelectedDates={setSelectedDates}
107 buttonFilterContent={buttonFilterContent}
108 />
109 <Box
110 p={0}
111 pt={showMap ? 4 : 13}
112 pb={11}
113 sx={{
114 overflowX: 'hidden',
115 overflowY: 'auto',
116 maxHeight: showMap ? '50vh' : '100vh',
117 [theme.breakpoints.down('md')]: {
118 maxHeight: showMap ? '50vh' : '100vh',
119 px: 1,
120 },
121 }}
122 >
123 <Masonry columns={{xl: 4, lg: 3, md: 2, sm: 2, xs: 1}} spacing={0}>
124 {!canAddTravel() && (
125 <MasonryContainer key="no_other_travel">
126 <LoginToAttend />
127 </MasonryContainer>
128 )}
129 {displayedTravels?.map(travel => {
130 return (
131 <MasonryContainer key={travel.id}>
132 <Travel
133 travel={travel}
134 onAddSelf={() => addSelfToTravel(travel)}
135 onAddOther={() => setSelectedTravel(travel)}
136 {...props}
137 />
138 </MasonryContainer>
139 );
140 })}
141 <MasonryContainer key="no_other_travel">
142 <NoCar
143 eventName={event?.name}
144 title={t('event.no_other_travel.title')}
145 description={
146 isCarosterPlus
147 ? t('event.no_travel.plus.desc')
148 : t('event.no_travel.desc')
149 }
150 />
151 </MasonryContainer>
152 </Masonry>
153 </Box>
154 {!!selectedTravel && (
155 <AddPassengerToTravel
156 open={!!selectedTravel}
157 toggle={() => setSelectedTravel(null)}
158 travel={selectedTravel}
159 />
160 )}
161 </>
162 );
163};
164
165export default TravelColumns;