Skip to content

Commit

Permalink
refactor: Move train station timeline in own component (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
viliket authored Sep 10, 2023
1 parent f95265c commit 1fd26dd
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 169 deletions.
177 changes: 8 additions & 169 deletions src/components/TrainInfoContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { useState } from 'react';

import { Timeline, TimelineContent, TimelineItem } from '@mui/lab';
import { Box, Link, Grid, Skeleton } from '@mui/material';
import { parseISO } from 'date-fns';
import RouterLink from 'next/link';
import { useTranslation } from 'react-i18next';
import { Box, Skeleton } from '@mui/material';

import { gqlClients } from '../graphql/client';
import {
Expand All @@ -13,26 +9,17 @@ import {
Wagon,
} from '../graphql/generated/digitraffic';
import { formatEET } from '../utils/date';
import getTrainCurrentStation from '../utils/getTrainCurrentStation';
import getTrainLatestArrivalRow from '../utils/getTrainLatestArrivalRow';
import getTrainLatestDepartureTimeTableRow from '../utils/getTrainLatestDepartureTimeTableRow';
import getTrainPreviousStation from '../utils/getTrainPreviousStation';
import {
getTrainScheduledDepartureTime,
getTrainStationName,
} from '../utils/train';
import { getTrainScheduledDepartureTime } from '../utils/train';

import TimelineRouteStopSeparator from './TimelineRouteStopSeparator';
import TimeTableRowTime from './TimeTableRowTime';
import TrainComposition from './TrainComposition';
import TrainStationTimeline from './TrainStationTimeline';
import TrainWagonDetailsDialog from './TrainWagonDetailsDialog';

type TrainInfoContainerProps = {
train?: TrainDetailsFragment | null;
};

function TrainInfoContainer({ train }: TrainInfoContainerProps) {
const { t } = useTranslation();
const [wagonDialogOpen, setWagonDialogOpen] = useState(false);
const [selectedWagon, setSelectedWagon] = useState<Wagon | null>(null);
const departureDate = train ? getTrainScheduledDepartureTime(train) : null;
Expand Down Expand Up @@ -66,108 +53,6 @@ function TrainInfoContainer({ train }: TrainInfoContainerProps) {
setWagonDialogOpen(true);
};

const getStops = () => {
if (!train) return null;
const trainCurrentStation = getTrainCurrentStation(train);
const trainPreviousStation = getTrainPreviousStation(train);
const trainLatestArrivalRow = getTrainLatestArrivalRow(train);
const trainLatestDepartureRow = getTrainLatestDepartureTimeTableRow(train);

const timeTableRows = (realTimeTrain ?? train).timeTableGroups;

return timeTableRows?.map((g, i, { length }) => {
const r = g.departure ?? g.arrival;
if (!r) return <></>;
const station = r.station;
const stationPassed =
(r.actualTime != null && parseISO(r.actualTime) < new Date()) ||
(trainLatestDepartureRow != null &&
r.scheduledTime < trainLatestDepartureRow?.scheduledTime);
return (
<TimelineItem
key={station.name + r.type + r.scheduledTime}
sx={{
'&:before': {
display: 'none',
},
}}
>
<TimelineRouteStopSeparator
passed={stationPassed}
isVehicleAtStation={
trainCurrentStation?.shortCode === station.shortCode &&
trainLatestArrivalRow?.scheduledTime === g.arrival?.scheduledTime
}
wasVehicleAtStation={
trainCurrentStation == null &&
trainPreviousStation?.shortCode === station.shortCode &&
trainLatestDepartureRow?.scheduledTime ===
g.departure?.scheduledTime
}
isFinalStop={i === length - 1}
platformSide={g.stationPlatformSide}
/>
<TimelineContent>
<Grid container spacing={2}>
<Grid item xs={6}>
<Link
component={RouterLink}
href={`/${getTrainStationName(station)}`}
color="inherit"
underline="none"
sx={{ fontWeight: 500 }}
>
{getTrainStationName(station)}
</Link>
<Box sx={{ color: 'text.secondary' }}>
{t('track')} {r.commercialTrack}
</Box>
</Grid>
<Grid item xs={3} sx={{ marginTop: '1rem' }}>
{g.arrival ? <TimeTableRowTime row={g.arrival} /> : '-'}
</Grid>
<Grid item xs={3} sx={{ marginTop: '1rem' }}>
{g.departure ? <TimeTableRowTime row={g.departure} /> : '-'}
</Grid>
<Grid item xs={12}>
{realTimeTrain && (
<div style={{ textAlign: 'center' }}>
<TrainComposition
train={realTimeTrain}
stationTimeTableRowGroup={g}
onWagonClick={handleWagonClick}
/>
</div>
)}
<Box
sx={(theme) => ({
borderBottom: `solid 1px ${theme.palette.divider}`,
})}
></Box>
</Grid>
</Grid>
</TimelineContent>
</TimelineItem>
);
});
};

const getLoadingSkeleton = () => {
return (
<Box sx={{ padding: '1rem' }}>
{Array.from(Array(7).keys()).map((i) => (
<Skeleton
key={i}
variant="rectangular"
width="100%"
height={50}
sx={{ marginTop: '1rem' }}
/>
))}
</Box>
);
};

return (
<>
<Box
Expand Down Expand Up @@ -202,57 +87,11 @@ function TrainInfoContainer({ train }: TrainInfoContainerProps) {
</Box>
)}
</Box>
<Timeline
sx={(theme) => ({
marginTop: 0,
paddingTop: 0,
'.MuiTimelineContent-root': {
...theme.typography.body2,
paddingRight: 0,
},
})}
>
<TimelineItem
sx={(theme) => ({
position: 'sticky',
top: '3.5rem',
zIndex: 1002,
backgroundColor: theme.palette.common.secondaryBackground.default,
boxShadow: `inset 0px -1px 1px ${theme.palette.divider}`,
minHeight: 'auto',
marginX: '-16px', // Negate parent padding to span whole horizontal space
padding: '0.4rem',
paddingRight: '16px', // Original parent padding
paddingLeft: 'calc(16px + 12px)', // Original parent padding + timeline dot width
marginBottom: '0.4rem',
'&:before': {
display: 'none',
},
})}
>
<TimelineContent>
<Grid
container
spacing={2}
sx={(theme) => ({
fontWeight: theme.typography.fontWeightMedium,
})}
>
<Grid item xs={6}>
{t('station')}
</Grid>
<Grid item xs={3}>
{t('arrival')}
</Grid>
<Grid item xs={3}>
{t('departure')}
</Grid>
</Grid>
</TimelineContent>
</TimelineItem>
{getStops()}
{!train && getLoadingSkeleton()}
</Timeline>
<TrainStationTimeline
train={train}
realTimeTrain={realTimeTrain}
onWagonClick={handleWagonClick}
/>
{train && (
<TrainWagonDetailsDialog
train={train}
Expand Down
184 changes: 184 additions & 0 deletions src/components/TrainStationTimeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { Timeline, TimelineContent, TimelineItem } from '@mui/lab';
import { Box, Link, Grid, Skeleton, Divider } from '@mui/material';
import { parseISO } from 'date-fns';
import RouterLink from 'next/link';
import { useTranslation } from 'react-i18next';

import { TrainDetailsFragment, Wagon } from '../graphql/generated/digitraffic';
import getTrainCurrentStation from '../utils/getTrainCurrentStation';
import getTrainLatestArrivalRow from '../utils/getTrainLatestArrivalRow';
import getTrainLatestDepartureTimeTableRow from '../utils/getTrainLatestDepartureTimeTableRow';
import getTrainPreviousStation from '../utils/getTrainPreviousStation';
import { getTrainStationName } from '../utils/train';

import TimelineRouteStopSeparator from './TimelineRouteStopSeparator';
import TimeTableRowTime from './TimeTableRowTime';
import TrainComposition from './TrainComposition';

type TrainStationTimelineProps = {
train?: TrainDetailsFragment | null;
realTimeTrain?: TrainDetailsFragment | null;
onWagonClick: (w: Wagon) => void;
};

const TrainStationTimeline = ({
train,
realTimeTrain,
onWagonClick,
}: TrainStationTimelineProps) => {
const { t } = useTranslation();

const getStops = () => {
if (!train) return null;
const trainCurrentStation = getTrainCurrentStation(train);
const trainPreviousStation = getTrainPreviousStation(train);
const trainLatestArrivalRow = getTrainLatestArrivalRow(train);
const trainLatestDepartureRow = getTrainLatestDepartureTimeTableRow(train);

const timeTableRows = (realTimeTrain ?? train).timeTableGroups;

return timeTableRows?.map((g, i, { length }) => {
const r = g.departure ?? g.arrival;
if (!r) return <></>;
const station = r.station;
const stationPassed =
(r.actualTime != null && parseISO(r.actualTime) < new Date()) ||
(trainLatestDepartureRow != null &&
r.scheduledTime < trainLatestDepartureRow?.scheduledTime);
return (
<TimelineItem
key={station.name + r.type + r.scheduledTime}
sx={{
'&:before': {
display: 'none',
},
}}
>
<TimelineRouteStopSeparator
passed={stationPassed}
isVehicleAtStation={
trainCurrentStation?.shortCode === station.shortCode &&
trainLatestArrivalRow?.scheduledTime === g.arrival?.scheduledTime
}
wasVehicleAtStation={
trainCurrentStation == null &&
trainPreviousStation?.shortCode === station.shortCode &&
trainLatestDepartureRow?.scheduledTime ===
g.departure?.scheduledTime
}
isFinalStop={i === length - 1}
platformSide={g.stationPlatformSide}
/>
<TimelineContent>
<Grid container spacing={2}>
<Grid item xs={6}>
<Link
component={RouterLink}
href={`/${getTrainStationName(station)}`}
color="inherit"
underline="none"
sx={{ fontWeight: 500 }}
>
{getTrainStationName(station)}
</Link>
<Box sx={{ color: 'text.secondary' }}>
{t('track')} {r.commercialTrack}
</Box>
</Grid>
<Grid item xs={3} sx={{ marginTop: '1rem' }}>
{g.arrival ? <TimeTableRowTime row={g.arrival} /> : '-'}
</Grid>
<Grid item xs={3} sx={{ marginTop: '1rem' }}>
{g.departure ? <TimeTableRowTime row={g.departure} /> : '-'}
</Grid>
<Grid item xs={12}>
{realTimeTrain && (
<div style={{ textAlign: 'center' }}>
<TrainComposition
train={realTimeTrain}
stationTimeTableRowGroup={g}
onWagonClick={onWagonClick}
/>
</div>
)}
<Divider />
</Grid>
</Grid>
</TimelineContent>
</TimelineItem>
);
});
};

const getLoadingSkeleton = () => {
return (
<Box sx={{ padding: '1rem' }}>
{Array.from(Array(7).keys()).map((i) => (
<Skeleton
key={i}
variant="rectangular"
width="100%"
height={50}
sx={{ marginTop: '1rem' }}
/>
))}
</Box>
);
};

return (
<Timeline
sx={(theme) => ({
marginTop: 0,
paddingTop: 0,
'.MuiTimelineContent-root': {
...theme.typography.body2,
paddingRight: 0,
},
})}
>
<TimelineItem
sx={(theme) => ({
position: 'sticky',
top: '3.5rem',
zIndex: 1002,
backgroundColor: theme.palette.common.secondaryBackground.default,
boxShadow: `inset 0px -1px 1px ${theme.palette.divider}`,
minHeight: 'auto',
marginX: '-16px', // Negate parent padding to span whole horizontal space
padding: '0.4rem',
paddingRight: '16px', // Original parent padding
paddingLeft: 'calc(16px + 12px)', // Original parent padding + timeline dot width
marginBottom: '0.4rem',
'&:before': {
display: 'none',
},
})}
>
<TimelineContent>
<Grid
container
spacing={2}
sx={(theme) => ({
fontWeight: theme.typography.fontWeightMedium,
})}
>
<Grid item xs={6}>
{t('station')}
</Grid>
<Grid item xs={3}>
{t('arrival')}
</Grid>
<Grid item xs={3}>
{t('departure')}
</Grid>
</Grid>
</TimelineContent>
</TimelineItem>
{getStops()}
{!train && getLoadingSkeleton()}
</Timeline>
);
};

export default TrainStationTimeline;

0 comments on commit 1fd26dd

Please sign in to comment.