all repos — caroster @ ae6f0621a8c844a589741cf29191ddf231c449c4

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

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;