all repos — caroster @ 6d0ac3d26b6ba4c0980e7571cba5c01f8475d765

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

frontend/containers/TravelColumns/index.tsx (view raw)

  1import {useMemo, useReducer, 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 useDisplayTravels from './useDisplayTravels';
 19import useDisplayMarkers from './useDisplayMarkers';
 20import FilterByDate from './FilterByDate';
 21import {Button, Icon, useMediaQuery} from '@mui/material';
 22import useTravelsStore from '../../stores/useTravelsStore';
 23import AddTravelButton from '../AddTravelButton';
 24import MapActions from '../MapActions';
 25
 26interface Props {}
 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 isMobile = useMediaQuery(theme.breakpoints.down('md'));
 37
 38  const [selectedTravel, setSelectedTravel] = useState<TravelEntity>();
 39  const [mapEnabled, toggleMap] = useReducer(i => !i, true);
 40  const datesFilters = useTravelsStore(s => s.datesFilter);
 41  const {addPassenger} = usePassengersActions();
 42  const {displayedTravels} = useDisplayTravels();
 43  useDisplayMarkers({event});
 44
 45  const buttonFilterContent = useMemo(() => {
 46    if (datesFilters.length > 1) return t('event.filter.dates');
 47    else if (datesFilters.length === 1)
 48      return datesFilters.map(date => date.format('dddd Do MMMM'));
 49    else return t('event.filter.allDates');
 50  }, [datesFilters, t]);
 51
 52  const addSelfToTravel = async (travel: TravelEntity) => {
 53    const hasName = profile.firstName && profile.lastName;
 54    const userName = profile.firstName + ' ' + profile.lastName;
 55    try {
 56      await addPassenger({
 57        user: userId,
 58        email: profile.email,
 59        name: hasName ? userName : profile.username,
 60        travel: travel.id,
 61        event: event.id,
 62      });
 63      addToEvent(event.id);
 64      addToast(t('passenger.success.added_self_to_car'));
 65    } catch (error) {
 66      console.error(error);
 67    }
 68  };
 69
 70  const isCarosterPlus = event?.enabled_modules?.includes('caroster-plus');
 71
 72  const haveGeopoints =
 73    (!!event?.latitude && !!event?.longitude) ||
 74    travels?.some(
 75      ({attributes: {meeting_latitude, meeting_longitude}}) =>
 76        meeting_latitude && meeting_longitude
 77    );
 78  const showMap = mapEnabled && haveGeopoints;
 79
 80  if (!event || travels?.length === 0)
 81    return (
 82      <NoCar
 83        noTravel
 84        eventName={event?.name}
 85        title={t('event.no_travel.title')}
 86        isCarosterPlus={isCarosterPlus}
 87      />
 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      <MapActions />
101      <Box
102        px={3}
103        pb={2}
104        pt={showMap ? 2 : isMobile ? 22 : 18}
105        display="flex"
106        gap={2}
107        maxWidth="100%"
108        flexWrap="wrap"
109      >
110        <FilterByDate dates={dates} buttonFilterContent={buttonFilterContent} />
111        <AddTravelButton />
112        {haveGeopoints && (
113          <Button
114            sx={{width: {xs: 1, sm: 'auto'}}}
115            onClick={toggleMap}
116            startIcon={<Icon>{mapEnabled ? 'visibility_off' : 'map'}</Icon>}
117          >
118            {mapEnabled ? t`travel.hideMap` : t`travel.showMap`}
119          </Button>
120        )}
121      </Box>
122      <Box
123        p={0}
124        pt={showMap ? 0 : 3}
125        pb={11}
126        sx={{
127          overflowX: 'hidden',
128          overflowY: 'auto',
129          maxHeight: showMap ? '50vh' : '100vh',
130          [theme.breakpoints.down('md')]: {
131            maxHeight: showMap ? '50vh' : '100vh',
132            px: 1,
133          },
134        }}
135      >
136        <Masonry columns={{xl: 4, lg: 3, md: 2, sm: 2, xs: 1}} spacing={0}>
137          {displayedTravels?.map(travel => {
138            return (
139              <MasonryContainer key={travel.id}>
140                <Travel
141                  travel={travel}
142                  onAddSelf={() => addSelfToTravel(travel)}
143                  onAddOther={() => setSelectedTravel(travel)}
144                  {...props}
145                />
146              </MasonryContainer>
147            );
148          })}
149          <MasonryContainer key="no_other_travel">
150            <NoCar
151              eventName={event?.name}
152              title={t('event.no_other_travel.title')}
153              isCarosterPlus={isCarosterPlus}
154            />
155          </MasonryContainer>
156        </Masonry>
157      </Box>
158      {!!selectedTravel && (
159        <AddPassengerToTravel
160          open={!!selectedTravel}
161          toggle={() => setSelectedTravel(null)}
162          travel={selectedTravel}
163        />
164      )}
165    </>
166  );
167};
168
169export default TravelColumns;