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({station, date, time, tableType}) {
return <>
>
}
function TrainsTableHeader({tableType}) {
return <>
Train
Heure
Destination
>
}
function TrainsTableBody({station, 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}/?station_slug=${station.slug}&date=${date}&time=${time}&offset=${0}&limit=${20}`)
.then(response => response.json())
.then(data => data.results)
.then(data => [...data])
}, [station.id, date, time, tableType])
const trainsQuery = useQuery({
queryKey: ['trains', station.id, tableType],
queryFn: updateTrains,
enabled: !!station.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) =>
)
return <>
{table_rows}
>
}
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/?${new URLSearchParams({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 <>
{trainTypeDisplay}
{trip.short_name}
{trip.headsign}
{getDisplayTime(train, tableType)}
{prettyDelay}
{prettyScheduleRelationship}
{headline.name}
{stopsNames}
>
}
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-IDFM":
case "FR-GES-CTS":
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"
case "CH-ALL":
return route.desc
default:
return trip.short_name?.split(" ")[0]
}
}
function getTrainTypeDisplay(trainType) {
switch (trainType) {
case "TGV INOUI":
return
case "OUIGO":
return
case "ICE":
return
case "Lyria":
return
case "TER":
return
case "Car TER":
return

case "Eurostar":
return
case "Trenitalia":
case "Trenitalia France":
return
case "RENFE":
return
case "NJ":
return
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;