all repos — caroster @ aa7e34b3fb18f1f2e4b57ddbafef5f68523acbd9

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