all repos — caroster @ 1612fe42101ebc0ab86492e9e6c0ceed14c878d1

[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 'next-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, canSeeTravelDetails},
 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 && canSeeTravelDetails(travel) && (
 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({
 99              phone: travel.attributes.phone_number,
100              phoneCountry: travel.attributes.phoneCountry,
101            })}
102          </Typography>
103        </Box>
104      )}
105      {!!travel.attributes.meeting && (
106        <Box sx={{marginTop: 2}}>
107          <Typography variant="overline" sx={{color: 'GrayText'}}>
108            {t('travel.fields.meeting_point')}
109          </Typography>
110          <Typography variant="body1">
111            <Link
112              component="a"
113              target="_blank"
114              rel="noopener noreferrer"
115              href={getMapsLink(travel.attributes.meeting)}
116            >
117              {travel.attributes.meeting}
118            </Link>
119          </Typography>
120          {MAPBOX_CONFIGURED && !tripHasValidCoordinates && (
121            <Typography
122              variant="overline"
123              color="warning.main"
124            >{t`placeInput.noCoordinates`}</Typography>
125          )}
126        </Box>
127      )}
128      {!!travel.attributes.details && (
129        <Box sx={{marginTop: 2}}>
130          <Typography variant="overline" sx={{color: 'GrayText'}}>
131            {t('travel.fields.details')}
132          </Typography>
133
134          <Typography
135            variant="body1"
136            sx={{whiteSpace: 'pre-line'}}
137            onClick={e => e.stopPropagation()}
138          >
139            <Linkify options={{render: DetailsLink}}>
140              {travel.attributes.details}
141            </Linkify>
142          </Typography>
143        </Box>
144      )}
145      <LinearProgress
146        sx={{
147          width: 1,
148          mt: 2,
149          mb: 1,
150          backgroundColor: 'LightGray',
151          '& .MuiLinearProgress-bar': {
152            backgroundColor: 'Gray',
153          },
154        }}
155        value={(passengersCount / travel?.attributes.seats) * 100}
156        variant="determinate"
157      />
158      <Box display="flex" justifyContent="space-between" sx={{width: 1}}>
159        <Typography variant="body1" sx={{color: 'GrayText'}}>
160          {t('passenger.assign.seats', {count: availableSeats})}
161        </Typography>
162      </Box>
163    </Box>
164  );
165};
166
167export default Header;