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 if (!event || travels?.length === 0)
73 return (
74 <NoCar
75 showImage
76 eventName={event?.name}
77 title={t('event.no_travel.title')}
78 />
79 );
80
81 const showMap =
82 (event.latitude && event.longitude) ||
83 travels.some(
84 ({attributes: {meeting_latitude, meeting_longitude}}) =>
85 meeting_latitude && meeting_longitude
86 );
87
88 const dates = Array.from(
89 new Set(travels.map(travel => travel?.attributes?.departure))
90 )
91 .map(date => moment(date))
92 .filter(date => date.isValid())
93 .sort((a, b) => (a.isAfter(b) ? 1 : -1));
94
95 return (
96 <>
97 {showMap && <Map />}
98 <FilterByDate
99 dates={dates}
100 setSelectedDates={setSelectedDates}
101 buttonFilterContent={buttonFilterContent}
102 />
103 <Box
104 p={0}
105 pt={showMap ? 4 : 13}
106 pb={11}
107 sx={{
108 overflowX: 'hidden',
109 overflowY: 'auto',
110 maxHeight: showMap ? '50vh' : '100vh',
111 [theme.breakpoints.down('md')]: {
112 maxHeight: showMap ? '50vh' : '100vh',
113 px: 1,
114 },
115 }}
116 >
117 <Masonry columns={{xl: 4, lg: 3, md: 2, sm: 2, xs: 1}} spacing={0}>
118 {!canAddTravel() && (
119 <MasonryContainer key="no_other_travel">
120 <LoginToAttend />
121 </MasonryContainer>
122 )}
123 {displayedTravels?.map(travel => {
124 return (
125 <MasonryContainer key={travel.id}>
126 <Travel
127 travel={travel}
128 onAddSelf={() => addSelfToTravel(travel)}
129 onAddOther={() => setSelectedTravel(travel)}
130 {...props}
131 />
132 </MasonryContainer>
133 );
134 })}
135 <MasonryContainer key="no_other_travel">
136 <NoCar
137 eventName={event?.name}
138 title={t('event.no_other_travel.title')}
139 />
140 </MasonryContainer>
141 </Masonry>
142 </Box>
143 {!!selectedTravel && (
144 <AddPassengerToTravel
145 open={!!selectedTravel}
146 toggle={() => setSelectedTravel(null)}
147 travel={selectedTravel}
148 />
149 )}
150 </>
151 );
152};
153
154export default TravelColumns;