all repos — caroster @ 4357e242456ed35ecc4e37b0c79129428acd3bae

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

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

  1import {useState} from 'react';
  2import {useTranslation} from 'react-i18next';
  3import {useTheme} from '@mui/material/styles';
  4import Container from '@mui/material/Container';
  5import Masonry from '@mui/lab/Masonry';
  6import Box from '@mui/material/Box';
  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 Travel from '../Travel';
 13import NoCar from './NoCar';
 14import {Travel as TravelData, TravelEntity} from '../../generated/graphql';
 15import {AddPassengerToTravel} from '../NewPassengerDialog';
 16
 17type TravelType = TravelData & {id: string};
 18
 19interface Props {
 20  toggle: () => void;
 21}
 22
 23const TravelColumns = (props: Props) => {
 24  const theme = useTheme();
 25  const event = useEventStore(s => s.event);
 26  const travels = event?.travels?.data || [];
 27  const {t} = useTranslation();
 28  const addToast = useToastStore(s => s.addToast);
 29  const {addToEvent} = useAddToEvents();
 30  const {profile, userId} = useProfile();
 31
 32  const [newPassengerTravelContext, toggleNewPassengerToTravel] = useState<{
 33    travel: TravelType;
 34  } | null>(null);
 35  const {addPassenger} = usePassengersActions();
 36  const sortedTravels = travels?.slice().sort(sortTravels);
 37
 38  const addSelfToTravel = async (travel: TravelType) => {
 39    try {
 40      await addPassenger({
 41        user: userId,
 42        email: profile.email,
 43        name: profile.username,
 44        travel: travel.id,
 45        event: event.id,
 46      });
 47      addToEvent(event.id);
 48      addToast(t('passenger.success.added_self_to_car'));
 49    } catch (error) {
 50      console.error(error);
 51    }
 52  };
 53
 54  return (
 55    <Box
 56      sx={{
 57        paddingLeft: theme.spacing(1),
 58        paddingRight: theme.spacing(1),
 59        [theme.breakpoints.down('md')]: {
 60          paddingLeft: theme.spacing(),
 61          paddingRight: theme.spacing(),
 62        },
 63        display: 'flex',
 64        flexDirection: 'column',
 65      }}
 66    >
 67      <Box
 68        sx={{
 69          height: '56px',
 70          overflow: 'auto',
 71          '& overflow': '-moz-scrollbars-none',
 72          '-ms-overflow-style': 'none',
 73          '&::-webkit-scrollbar': {
 74            height: '0 !important',
 75          },
 76          '& .slick-dots': {
 77            position: 'static',
 78            '& li': {
 79              display: 'block',
 80              '& button:before': {
 81                fontSize: '12px',
 82              },
 83            },
 84          },
 85          '& .slick-dots li:first-child button:before, & .slick-dots li:last-child button:before':
 86            {
 87              color: theme.palette.primary.main,
 88            },
 89        }}
 90        id="slider-dots"
 91      />
 92      {(travels?.length === 0 && (
 93        <NoCar
 94          image
 95          eventName={event?.name}
 96          title={t('event.no_travel.title')}
 97        />
 98      )) || (
 99        <Masonry columns={{xl: 4, lg: 3, md: 2, sm: 2, xs: 1}} spacing={0}>
100          {sortedTravels?.map(({id, attributes}) => {
101            const travel = {id, ...attributes};
102            return (
103              <Container
104                key={travel.id}
105                maxWidth="sm"
106                sx={{
107                  padding: theme.spacing(1),
108                  marginBottom: theme.spacing(10),
109                  outline: 'none',
110                  '& > *': {
111                    cursor: 'default',
112                  },
113
114                  [theme.breakpoints.down('md')]: {
115                    marginBottom: `calc(${theme.spacing(10)} + 56px)`,
116                  },
117                }}
118              >
119                <Travel
120                  travel={travel}
121                  {...props}
122                  getAddPassengerFunction={(addSelf: boolean) => () =>
123                    addSelf
124                      ? addSelfToTravel(travel)
125                      : toggleNewPassengerToTravel({travel})}
126                />
127              </Container>
128            );
129          })}
130          <Container
131            maxWidth="sm"
132            sx={{
133              padding: theme.spacing(1),
134              marginBottom: theme.spacing(10),
135              outline: 'none',
136              '& > *': {
137                cursor: 'default',
138              },
139
140              [theme.breakpoints.down('md')]: {
141                marginBottom: `calc(${theme.spacing(10)} + 56px)`,
142              },
143            }}
144          >
145            <NoCar
146              eventName={event?.name}
147              title={t('event.no_other_travel.title')}
148            />
149          </Container>
150        </Masonry>
151      )}
152      {!!newPassengerTravelContext && (
153        <AddPassengerToTravel
154          open={!!newPassengerTravelContext}
155          toggle={() => toggleNewPassengerToTravel(null)}
156          travel={newPassengerTravelContext.travel}
157        />
158      )}
159    </Box>
160  );
161};
162
163const sortTravels = (
164  {attributes: a}: TravelEntity,
165  {attributes: b}: TravelEntity
166) => {
167  if (!b) return 1;
168  const dateA = new Date(a.departure).getTime();
169  const dateB = new Date(b.departure).getTime();
170  if (dateA === dateB)
171    return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
172  else return dateA - dateB;
173};
174
175export default TravelColumns;