all repos — caroster @ 7aa17436c75503123cefb51191e8a757883333e9

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