frontend/containers/Travel/Header.tsx (view raw)
1import moment from 'moment';
2import Linkify from 'linkify-react';
3import Typography from '@mui/material/Typography';
4import IconButton from '@mui/material/IconButton';
5import TuneIcon from '@mui/icons-material/Tune';
6import Box from '@mui/material/Box';
7import Link from '@mui/material/Link';
8import LinearProgress from '@mui/material/LinearProgress';
9import Chip from '@mui/material/Chip';
10import {useTheme} from '@mui/material/styles';
11import {useTranslation} from 'react-i18next';
12import getMapsLink from '../../lib/getMapsLink';
13import useMapStore from '../../stores/useMapStore';
14import usePermissions from '../../hooks/usePermissions';
15import useProfile from '../../hooks/useProfile';
16import DetailsLink from '../DetailsLink';
17import {TravelEntity} from '../../generated/graphql';
18
19interface Props {
20 travel: TravelEntity;
21 toggleEditing: () => void;
22}
23
24const MAPBOX_CONFIGURED = process.env['MAPBOX_CONFIGURED'] || false;
25
26const Header = (props: Props) => {
27 const {travel, toggleEditing} = props;
28 const theme = useTheme();
29 const {t} = useTranslation();
30 const {
31 userPermissions: {canEditTravel},
32 } = usePermissions();
33 const {setFocusOnTravel} = useMapStore();
34 const {userId} = useProfile();
35 const isUserTripCreator =
36 userId && userId === travel.attributes.user?.data?.id;
37
38 const passengersCount = travel?.attributes.passengers?.data.length || 0;
39 const availableSeats = travel?.attributes.seats - passengersCount || 0;
40
41 const tripHasValidCoordinates =
42 travel.attributes.meeting_latitude && travel.attributes.meeting_longitude;
43
44 return (
45 <Box
46 p={2}
47 onClick={() => {
48 setFocusOnTravel(travel);
49 const mapElement = document?.getElementById('map');
50 mapElement?.scrollIntoView({behavior: 'smooth'});
51 }}
52 >
53 {canEditTravel(travel) && (
54 <IconButton
55 size="small"
56 color="primary"
57 sx={{
58 position: 'absolute',
59 top: theme.spacing(1),
60 right: 0,
61 margin: theme.spacing(1),
62 }}
63 onClick={e => {
64 e.stopPropagation();
65 toggleEditing();
66 }}
67 id="EditTravelBtn"
68 >
69 <TuneIcon />
70 </IconButton>
71 )}
72 {!!travel.attributes.departureDate && (
73 <Typography
74 variant="overline"
75 sx={{color: 'GrayText', textTransform: 'capitalize'}}
76 id="TravelDeparture"
77 >
78 {moment(travel.attributes.departureDate).format('dddd LL')}{' '}
79 {travel.attributes.departureTime}
80 </Typography>
81 )}
82 <Typography variant="subtitle1">
83 {travel.attributes.vehicleName}
84 {isUserTripCreator && (
85 <Typography component="span">
86 <Chip sx={{mx: 1}} label={t`generic.me`} variant="outlined" />
87 </Typography>
88 )}
89 </Typography>
90
91 {!!travel.attributes.phone_number && (
92 <Box sx={{marginTop: 2}}>
93 <Typography variant="overline" sx={{color: 'GrayText'}}>
94 {t('travel.fields.phone')}
95 </Typography>
96 <Typography variant="body1" id="TravelPhone">
97 {travel.attributes.phone_number}
98 </Typography>
99 </Box>
100 )}
101 {!!travel.attributes.meeting && (
102 <Box sx={{marginTop: 2}}>
103 <Typography variant="overline" sx={{color: 'GrayText'}}>
104 {t('travel.fields.meeting_point')}
105 </Typography>
106 <Typography variant="body1">
107 <Link
108 component="a"
109 target="_blank"
110 rel="noopener noreferrer"
111 href={getMapsLink(travel.attributes.meeting)}
112 >
113 {travel.attributes.meeting}
114 </Link>
115 </Typography>
116 {MAPBOX_CONFIGURED && !tripHasValidCoordinates && (
117 <Typography
118 variant="overline"
119 color="warning.main"
120 >{t`placeInput.noCoordinates`}</Typography>
121 )}
122 </Box>
123 )}
124 {!!travel.attributes.details && (
125 <Box sx={{marginTop: 2}}>
126 <Typography variant="overline" sx={{color: 'GrayText'}}>
127 {t('travel.fields.details')}
128 </Typography>
129
130 <Typography
131 variant="body1"
132 sx={{whiteSpace: 'pre-line'}}
133 onClick={e => e.stopPropagation()}
134 >
135 <Linkify options={{render: DetailsLink}}>
136 {travel.attributes.details}
137 </Linkify>
138 </Typography>
139 </Box>
140 )}
141 <LinearProgress
142 sx={{
143 width: 1,
144 mt: 2,
145 mb: 1,
146 backgroundColor: 'LightGray',
147 '& .MuiLinearProgress-bar': {
148 backgroundColor: 'Gray',
149 },
150 }}
151 value={(passengersCount / travel?.attributes.seats) * 100}
152 variant="determinate"
153 />
154 <Box display="flex" justifyContent="space-between" sx={{width: 1}}>
155 <Typography variant="body1" sx={{color: 'GrayText'}}>
156 {t('passenger.assign.seats', {count: availableSeats})}
157 </Typography>
158 </Box>
159 </Box>
160 );
161};
162
163export default Header;