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