all repos — caroster @ 3ede449ef52ab927e5e9bee37b447b5c86a65723

[Octree] Group carpool to your event https://caroster.io

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;