Rename project to Trainvel
This commit is contained in:
361
trainvel-front/src/TrainsTable.js
Normal file
361
trainvel-front/src/TrainsTable.js
Normal file
@ -0,0 +1,361 @@
|
||||
import {
|
||||
Box,
|
||||
styled,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography
|
||||
} from "@mui/material"
|
||||
import {CSSTransition, TransitionGroup} from 'react-transition-group'
|
||||
import {useQueries, useQuery} from "@tanstack/react-query";
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from "react";
|
||||
|
||||
const StyledTableRow = styled(TableRow)(({ theme, tabletype }) => ({
|
||||
'tbody &:nth-of-type(odd)': {
|
||||
backgroundColor: theme.palette.sncf[tabletype].light,
|
||||
},
|
||||
'th, &:nth-of-type(even)': {
|
||||
backgroundColor: theme.palette.sncf[tabletype].dark,
|
||||
},
|
||||
// hide last border
|
||||
'&:last-child td, &:last-child th': {
|
||||
border: 0,
|
||||
},
|
||||
}));
|
||||
|
||||
function TrainsTable({stop, date, time, tableType}) {
|
||||
return <>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TrainsTableHeader tableType={tableType} />
|
||||
<TrainsTableBody stop={stop} date={date} time={time} tableType={tableType} />
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</>
|
||||
}
|
||||
|
||||
function TrainsTableHeader({tableType}) {
|
||||
return <>
|
||||
<TableHead>
|
||||
<StyledTableRow tabletype={tableType}>
|
||||
<TableCell colSpan="2" fontSize={16} fontWeight="bold">Train</TableCell>
|
||||
<TableCell fontSize={16} fontWeight="bold">Heure</TableCell>
|
||||
<TableCell fontSize={16} fontWeight="bold">Destination</TableCell>
|
||||
</StyledTableRow>
|
||||
</TableHead>
|
||||
</>
|
||||
}
|
||||
|
||||
function TrainsTableBody({stop, date, time, tableType}) {
|
||||
const filterTime = useCallback((train) => {
|
||||
if (tableType === "departures")
|
||||
return `${train.departure_date}T${train.departure_time_24h}` >= `${date}T${time}`
|
||||
else
|
||||
return `${train.arrival_date}T${train.arrival_time_24h}` >= `${date}T${time}`
|
||||
}, [date, time, tableType])
|
||||
|
||||
const updateTrains = useCallback(() => {
|
||||
return fetch(`/api/station/next_${tableType}/?stop_id=${stop.id}&date=${date}&time=${time}&offset=${0}&limit=${20}`)
|
||||
.then(response => response.json())
|
||||
.then(data => data.results)
|
||||
.then(data => [...data])
|
||||
}, [stop.id, date, time, tableType])
|
||||
|
||||
const trainsQuery = useQuery({
|
||||
queryKey: ['trains', stop.id, tableType],
|
||||
queryFn: updateTrains,
|
||||
enabled: !!stop.id,
|
||||
})
|
||||
const trains = useMemo(() => trainsQuery.data ?? [], [trainsQuery.data])
|
||||
|
||||
useEffect(() => {
|
||||
let validTrains = trains?.filter(filterTime) ?? []
|
||||
if (trains?.length > 0 && validTrains.length < trains?.length)
|
||||
trainsQuery.refetch().then()
|
||||
}, [trains, filterTime, trainsQuery])
|
||||
|
||||
const nullRef = useRef(null)
|
||||
let table_rows = trains.map((train) => <CSSTransition key={train.id} timeout={500} classNames="shrink" nodeRef={nullRef}>
|
||||
<TrainRow train={train} tableType={tableType} date={date} time={time} />
|
||||
</CSSTransition>)
|
||||
|
||||
return <>
|
||||
<TableBody>
|
||||
<TransitionGroup component={null}>
|
||||
{table_rows}
|
||||
</TransitionGroup>
|
||||
</TableBody>
|
||||
</>
|
||||
}
|
||||
|
||||
function TrainRow({train, tableType, date, time}) {
|
||||
const tripQuery = useQuery({
|
||||
queryKey: ['trip', train.trip],
|
||||
queryFn: () => fetch(`/api/gtfs/trip/${train.trip}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!train.trip,
|
||||
})
|
||||
const trip = tripQuery.data ?? {}
|
||||
|
||||
const routeQuery = useQuery({
|
||||
queryKey: ['route', trip.route],
|
||||
queryFn: () => fetch(`/api/gtfs/route/${trip.route}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!trip.route,
|
||||
})
|
||||
const route = routeQuery.data ?? {}
|
||||
const trainType = getTrainType(train, trip, route)
|
||||
const backgroundColor = getBackgroundColor(train, trip, route)
|
||||
const textColor = getTextColor(train, trip, route)
|
||||
const trainTypeDisplay = getTrainTypeDisplay(trainType)
|
||||
|
||||
const stopTimesQuery = useQuery({
|
||||
queryKey: ['stop_times', trip.id],
|
||||
queryFn: () => fetch(`/api/gtfs/stop_time/?trip=${trip.id}&order=stop_sequence&limit=1000`)
|
||||
.then(response => response.json())
|
||||
.then(data => data.results),
|
||||
enabled: !!trip.id,
|
||||
})
|
||||
const stopTimes = stopTimesQuery.data ?? []
|
||||
const stopIds = stopTimes.map(stop_time => stop_time.stop)
|
||||
|
||||
const stopQueries = useQueries({
|
||||
queries: stopIds.map(stopId => ({
|
||||
queryKey: ['stop', stopId],
|
||||
queryFn: () => fetch(`/api/gtfs/stop/${stopId}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!stopId,
|
||||
})),
|
||||
})
|
||||
const stops = stopTimes.map(((stopTime, i) => ({...stopTime, stop: stopQueries[i]?.data ?? {"name": "…"}}))) ?? []
|
||||
|
||||
let headline = stops[tableType === "departures" ? stops.length - 1 : 0]?.stop ?? {name: "Chargement…"}
|
||||
|
||||
const realtimeTripQuery = useQuery({
|
||||
queryKey: ['realtimeTrip', trip.id, date, time],
|
||||
queryFn: () => fetch(`/api/gtfs-rt/trip_update/${trip.id}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!trip.id,
|
||||
})
|
||||
|
||||
const [realtimeTripData, setRealtimeTripData] = useState({})
|
||||
useEffect(() => {
|
||||
if (realtimeTripQuery.data)
|
||||
setRealtimeTripData(realtimeTripQuery.data)
|
||||
}, [realtimeTripQuery.data])
|
||||
const tripScheduleRelationship = realtimeTripData.schedule_relationship ?? 0
|
||||
|
||||
const realtimeQuery = useQuery({
|
||||
queryKey: ['realtime', train.id, date, time],
|
||||
queryFn: () => fetch(`/api/gtfs-rt/stop_time_update/${train.id}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!train.id,
|
||||
})
|
||||
const [realtimeData, setRealtimeData] = useState({})
|
||||
useEffect(() => {
|
||||
if (realtimeQuery.data)
|
||||
setRealtimeData(realtimeQuery.data)
|
||||
}, [realtimeQuery.data])
|
||||
const stopScheduleRelationship = realtimeData.schedule_relationship ?? 0
|
||||
|
||||
const canceled = tripScheduleRelationship === 3 || stopScheduleRelationship === 1
|
||||
|
||||
const delay = tableType === "departures" ? realtimeData.departure_delay : realtimeData.arrival_delay
|
||||
const prettyDelay = delay && !canceled ? getPrettyDelay(delay) : ""
|
||||
const [prettyScheduleRelationship, scheduleRelationshipColor] = getPrettyScheduleRelationship(tripScheduleRelationship, stopScheduleRelationship)
|
||||
|
||||
let stopsFilter
|
||||
if (canceled)
|
||||
stopsFilter = (stop_time) => true
|
||||
else if (tableType === "departures")
|
||||
stopsFilter = (stop_time) => stop_time.stop_sequence > train.stop_sequence && stop_time.drop_off_type === 0
|
||||
else
|
||||
stopsFilter = (stop_time) => stop_time.stop_sequence < train.stop_sequence && stop_time.pickup_type === 0
|
||||
let stopsNames = stops.filter(stopsFilter).map(stopTime => stopTime?.stop.name ?? "").join(" > ") ?? ""
|
||||
|
||||
return <>
|
||||
<StyledTableRow tabletype={tableType}>
|
||||
<TableCell>
|
||||
<div>
|
||||
<Box display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
width="4em"
|
||||
height="4em"
|
||||
borderRadius="15%"
|
||||
fontWeight="bold"
|
||||
backgroundColor={backgroundColor}
|
||||
color={textColor}>
|
||||
{trainTypeDisplay}
|
||||
</Box>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display="flex" alignItems="center" justifyContent="center" textAlign="center">
|
||||
<div>
|
||||
<div>{trip.short_name}</div>
|
||||
<div>{trip.headsign}</div>
|
||||
</div>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display="flex" alignItems="center" justifyContent="center">
|
||||
<Box>
|
||||
<Box fontWeight="bold" color="#FFED02" fontSize={24}>
|
||||
{getDisplayTime(train, tableType)}
|
||||
</Box>
|
||||
<Box color={delay && delay !== "00:00:00" ? "#e86d2b" : "white"}
|
||||
fontWeight={delay && delay !== "00:00:00" ? "bold" : ""}>
|
||||
{prettyDelay}
|
||||
</Box>
|
||||
<Box color={scheduleRelationshipColor} fontWeight="bold">
|
||||
{prettyScheduleRelationship}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box style={{textDecoration: canceled ? 'line-through': ''}}>
|
||||
<Typography fontSize={24} fontWeight="bold" data-stop-id={headline.id}>{headline.name}</Typography>
|
||||
<span className="stops">{stopsNames}</span>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
</>
|
||||
}
|
||||
|
||||
function getTrainType(train, trip, route) {
|
||||
switch (route.gtfs_feed) {
|
||||
case "FR-SNCF-TGV":
|
||||
case "FR-SNCF-IC":
|
||||
case "FR-SNCF-TER":
|
||||
let trainType = train.stop.split("StopPoint:OCE")[1].split("-")[0]
|
||||
switch (trainType) {
|
||||
case "Train TER":
|
||||
return "TER"
|
||||
case "INTERCITES":
|
||||
return "INTER-CITÉS"
|
||||
case "INTERCITES de nuit":
|
||||
return "INTER-CITÉS de nuit"
|
||||
default:
|
||||
return trainType
|
||||
}
|
||||
case "FR-IDF-TN":
|
||||
return route.short_name
|
||||
case "FR-EUROSTAR":
|
||||
return "Eurostar"
|
||||
case "IT-FRA-TI":
|
||||
return "Trenitalia France"
|
||||
case "ES-RENFE":
|
||||
return "RENFE"
|
||||
case "AT-OBB":
|
||||
if (trip.short_name?.startsWith("NJ"))
|
||||
return "NJ"
|
||||
return "ÖBB"
|
||||
default:
|
||||
return trip.short_name?.split(" ")[0]
|
||||
}
|
||||
}
|
||||
|
||||
function getTrainTypeDisplay(trainType) {
|
||||
switch (trainType) {
|
||||
case "TGV INOUI":
|
||||
return <img src="/tgv_inoui.svg" alt="TGV INOUI" width="80%" />
|
||||
case "OUIGO":
|
||||
return <img src="/ouigo.svg" alt="OUIGO" width="80%" />
|
||||
case "ICE":
|
||||
return <img src="/ice.svg" alt="ICE" width="80%" />
|
||||
case "Lyria":
|
||||
return <img src="/lyria.svg" alt="Lyria" width="80%" />
|
||||
case "TER":
|
||||
return <img src="/ter.svg" alt="TER" width="80%" />
|
||||
case "Car TER":
|
||||
return <div><img src="/bus.svg" alt="Car" width="40%" />
|
||||
<br/>
|
||||
<img src="/ter.svg" alt="TER" width="40%" /></div>
|
||||
case "Eurostar":
|
||||
return <img src="/eurostar_mini.svg" alt="Eurostar" width="80%" />
|
||||
case "Trenitalia":
|
||||
case "Trenitalia France":
|
||||
return <img src="/trenitalia.svg" alt="Frecciarossa" width="80%" />
|
||||
case "RENFE":
|
||||
return <img src="/renfe.svg" alt="RENFE" width="80%" />
|
||||
case "NJ":
|
||||
return <img src="/nightjet.svg" alt="NightJet" width="80%" />
|
||||
default:
|
||||
return trainType
|
||||
}
|
||||
}
|
||||
|
||||
function getBackgroundColor(train, trip, route) {
|
||||
let trainType = getTrainType(train, trip, route)
|
||||
switch (trainType) {
|
||||
case "OUIGO":
|
||||
return "#0096CA"
|
||||
case "Eurostar":
|
||||
return "#00286A"
|
||||
case "NJ":
|
||||
return "#272759"
|
||||
default:
|
||||
if (route.color)
|
||||
return `#${route.color}`
|
||||
return "#FFFFFF"
|
||||
}
|
||||
}
|
||||
|
||||
function getTextColor(train, trip, route) {
|
||||
if (route.text_color)
|
||||
return `#${route.text_color}`
|
||||
else {
|
||||
let trainType = getTrainType(train, trip, route)
|
||||
switch (trainType) {
|
||||
case "OUIGO":
|
||||
return "#FFFFFF"
|
||||
case "TGV INOUI":
|
||||
return "#9B2743"
|
||||
case "ICE":
|
||||
return "#B4B4B4"
|
||||
case "INTER-CITÉS":
|
||||
case "INTER-CITÉS de nuit":
|
||||
return "#404042"
|
||||
default:
|
||||
return "#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDisplayTime(train, tableType) {
|
||||
let time = tableType === "departures" ? train.departure_time : train.arrival_time
|
||||
let day_split = time.split(' ')
|
||||
return day_split[day_split.length - 1].substring(0, 5)
|
||||
}
|
||||
|
||||
function getPrettyDelay(delay) {
|
||||
let delay_split = delay.split(':')
|
||||
let hours = parseInt(delay_split[0])
|
||||
let minutes = parseInt(delay_split[1])
|
||||
let full_minutes = hours * 60 + minutes
|
||||
return full_minutes ? `+${full_minutes} min` : "À l'heure"
|
||||
}
|
||||
|
||||
function getPrettyScheduleRelationship(tripScheduledRelationship, stopScheduledRelationship) {
|
||||
switch (tripScheduledRelationship) {
|
||||
case 1:
|
||||
return ["Ajouté", "#3ebb18"]
|
||||
case 3:
|
||||
return ["Supprimé", "#ff8701"]
|
||||
default:
|
||||
switch (stopScheduledRelationship) {
|
||||
case 1:
|
||||
return ["Supprimé", "#ff8701"]
|
||||
default:
|
||||
return ["", ""]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TrainsTable;
|
Reference in New Issue
Block a user