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;