all repos — caroster @ 64425eee42c3bbb4a10591bf227f141cc690b8c0

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

frontend/containers/WaitingList/TravelDialog.tsx (view raw)

  1import moment from 'moment';
  2import {styled} from '@mui/material/styles';
  3import Link from '@mui/material/Link';
  4import Typography from '@mui/material/Typography';
  5import Button from '@mui/material/Button';
  6import Slide from '@mui/material/Slide';
  7import Dialog from '@mui/material/Dialog';
  8import AppBar from '@mui/material/AppBar';
  9import Toolbar from '@mui/material/Toolbar';
 10import ListItem from '@mui/material/ListItem';
 11import List from '@mui/material/List';
 12import IconButton from '@mui/material/IconButton';
 13import Icon from '@mui/material/Icon';
 14import Box from '@mui/material/Box';
 15import {useTranslation} from 'react-i18next';
 16import {forwardRef} from 'react';
 17import getMapsLink from '../../lib/getMapsLink';
 18import ShareEvent from '../ShareEvent';
 19import {Passenger, TravelEntity, Travel} from '../../generated/graphql';
 20
 21const PREFIX = 'TravelDialog';
 22
 23const classes = {
 24  offset: `${PREFIX}-offset`,
 25  rtlBox: `${PREFIX}-rtlBox`,
 26  info: `${PREFIX}-info`,
 27  listItem: `${PREFIX}-listItem`,
 28  date: `${PREFIX}-date`,
 29  button: `${PREFIX}-button`,
 30  noTravel: `${PREFIX}-noTravel`,
 31  noTravelImage: `${PREFIX}-noTravelImage`,
 32  share: `${PREFIX}-share`,
 33};
 34
 35const StyledSlide = styled(Slide)(({theme}) => ({
 36  [`& .${classes.offset}`]: {
 37    paddingTop: theme.spacing(7),
 38  },
 39
 40  [`& .${classes.rtlBox}`]: {
 41    display: 'flex',
 42    padding: 0,
 43    margin: 0,
 44    direction: 'rtl',
 45    [theme.breakpoints.down('md')]: {
 46      display: 'block',
 47      paddingBottom: theme.spacing(1),
 48    },
 49  },
 50
 51  [`& .${classes.info}`]: {
 52    padding: theme.spacing(0, 4, 0, 0),
 53    width: '350px',
 54    [theme.breakpoints.down('md')]: {
 55      padding: theme.spacing(0.5, 1),
 56      width: '100%',
 57      textAlign: 'left',
 58    },
 59  },
 60
 61  [`& .${classes.listItem}`]: {
 62    display: 'flex',
 63    justifyContent: 'left',
 64    [theme.breakpoints.down('md')]: {
 65      display: 'block',
 66      textAlign: 'center',
 67    },
 68  },
 69
 70  [`& .${classes.date}`]: {
 71    textTransform: 'capitalize',
 72    padding: theme.spacing(0, 0, 0.5, 0),
 73  },
 74
 75  [`& .${classes.button}`]: {
 76    padding: theme.spacing(1, 15),
 77    margin: theme.spacing(1),
 78  },
 79
 80  [`& .${classes.noTravel}`]: {
 81    margin: '120px auto 0 auto',
 82    width: '330px',
 83    maxWidth: '100%',
 84    textAlign: 'center',
 85  },
 86
 87  [`& .${classes.noTravelImage}`]: {
 88    width: 'calc(100% - 2px)',
 89    [theme.breakpoints.down('md')]: {
 90      width: 'calc(50% - 2px)',
 91    },
 92  },
 93
 94  [`& .${classes.share}`]: {
 95    marginTop: theme.spacing(2),
 96    backgroundColor: '#fff',
 97  },
 98}));
 99
100interface Props {
101  eventName: string;
102  travels: Array<TravelEntity>;
103  passenger: Passenger;
104  open: boolean;
105  onClose: () => void;
106  onSelect: (travel: Travel & {id: string}) => void;
107}
108
109const TravelDialog = ({
110  eventName,
111  travels,
112  passenger,
113  open,
114  onClose,
115  onSelect,
116}: Props) => {
117  const {t} = useTranslation();
118
119  const availableTravels = travels?.filter(
120    ({attributes}) =>
121      attributes.passengers &&
122      attributes?.seats > attributes.passengers.data.length
123  );
124
125  return (
126    <Dialog
127      fullScreen
128      open={open}
129      onClose={onClose}
130      TransitionComponent={Transition}
131    >
132      <AppBar>
133        <Toolbar>
134          <IconButton onClick={onClose} color="inherit" size="large">
135            <Icon>arrow_back_ios</Icon>
136          </IconButton>
137          <Typography variant="h5">
138            {t('passenger.creation.available_cars')}
139          </Typography>
140        </Toolbar>
141      </AppBar>
142      {(availableTravels.length === 0 && (
143        <Box className={classes.noTravel}>
144          <Typography variant="h5">
145            {t('passenger.creation.no_travel.title')}
146          </Typography>
147          <img className={classes.noTravelImage} src="/assets/car.png" />
148          <Typography>
149            {t('passenger.creation.no_travel.desc', {name: passenger?.name})}
150          </Typography>
151          <ShareEvent
152            className={classes.share}
153            title={`Caroster ${eventName}`}
154          />
155        </Box>
156      )) || (
157        <div className={classes.offset}>
158          <List disablePadding>
159            {availableTravels.map(({id, attributes}, i) => {
160              const travel = {id, ...attributes};
161              const passengersCount = travel?.passengers?.data.length || 0;
162              const counter = `${passengersCount} / ${travel?.seats || 0}`;
163              return (
164                <ListItem key={i} divider className={classes.listItem}>
165                  <Box className={classes.rtlBox}>
166                    <Box className={classes.info}>
167                      {travel.departure && (
168                        <Typography
169                          variant="subtitle1"
170                          className={classes.date}
171                        >
172                          {t('passenger.creation.departure')}
173                          {moment(travel.departure).format('LLLL')}
174                        </Typography>
175                      )}
176                      <Link
177                        target="_blank"
178                        rel="noreferrer"
179                        href={getMapsLink(travel.meeting)}
180                        onClick={e => e.preventDefault}
181                      >
182                        {travel.meeting}
183                      </Link>
184                    </Box>
185                    <Box className={classes.info}>
186                      <Typography variant="h6">{travel.vehicleName}</Typography>
187                      <Typography variant="body2">
188                        {t('passenger.creation.seats', {seats: counter})}
189                      </Typography>
190                    </Box>
191                  </Box>
192                  <Button
193                    color="primary"
194                    variant="contained"
195                    onClick={() => onSelect(travel)}
196                    className={classes.button}
197                  >
198                    {t('passenger.creation.assign')}
199                  </Button>
200                </ListItem>
201              );
202            })}
203          </List>
204        </div>
205      )}
206    </Dialog>
207  );
208};
209
210const Transition = forwardRef(function Transition(props, ref) {
211  return <StyledSlide direction="up" ref={ref} {...props} />;
212});
213
214export default TravelDialog;