all repos — caroster @ 5ed83071ddb9096ff61df7bcbb763178d4445e4d

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

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