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