From 913bfb1e8f3950089efab7368069ae19f3d4211e Mon Sep 17 00:00:00 2001 From: Clara Ni Date: Fri, 20 Dec 2024 10:46:22 +0100 Subject: [PATCH] front: implement new function to update departureTime after train drag --- .../components/Scenario/ScenarioContent.tsx | 36 +-- .../hooks/useScenarioData.ts | 209 ++++++++++++------ .../views/SimulationResults.tsx | 64 +++--- .../ManchetteWithSpaceTimeChart.tsx | 105 +++++---- .../components/Timetable/Timetable.tsx | 8 +- 5 files changed, 239 insertions(+), 183 deletions(-) diff --git a/front/src/applications/operationalStudies/components/Scenario/ScenarioContent.tsx b/front/src/applications/operationalStudies/components/Scenario/ScenarioContent.tsx index 95b0152de9f..08ebd428886 100644 --- a/front/src/applications/operationalStudies/components/Scenario/ScenarioContent.tsx +++ b/front/src/applications/operationalStudies/components/Scenario/ScenarioContent.tsx @@ -15,13 +15,11 @@ import useScenarioData from 'applications/operationalStudies/hooks/useScenarioDa import ImportTrainSchedule from 'applications/operationalStudies/views/ImportTrainSchedule'; import ManageTrainSchedule from 'applications/operationalStudies/views/ManageTrainSchedule'; import SimulationResults from 'applications/operationalStudies/views/SimulationResults'; -import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; import type { InfraWithState, ScenarioResponse, TimetableDetailedResult, TrainScheduleResult, - TrainScheduleBase, } from 'common/api/osrdEditoastApi'; import ScenarioLoaderMessage from 'modules/scenario/components/ScenarioLoaderMessage'; import TimetableManageTrainSchedule from 'modules/trainschedule/components/ManageTrainSchedule/TimetableManageTrainSchedule'; @@ -45,7 +43,6 @@ const ScenarioContent = ({ }: ScenarioDescriptionProps) => { const { t } = useTranslation('operationalStudies/scenario'); const dispatch = useAppDispatch(); - const [putTrainScheduleById] = osrdEditoastApi.endpoints.putTrainScheduleById.useMutation(); const [displayTrainScheduleManagement, setDisplayTrainScheduleManagement] = useState( MANAGE_TRAIN_SCHEDULE_TYPES.none @@ -54,14 +51,13 @@ const ScenarioContent = ({ const [trainIdToEdit, setTrainIdToEdit] = useState(); const [isMacro, setIsMacro] = useState(false); const { - selectedTrainId, - selectedTrainSummary, trainScheduleSummaries, trainSchedules, projectionData, conflicts, upsertTrainSchedules, removeTrains, + updateTrainDepartureTime, } = useScenarioData(scenario, timetable, infra); const [ngeDto, setNgeDto] = useState(); @@ -87,31 +83,6 @@ const ScenarioContent = ({ } }, [isMacro]); - const handleTrainDrop = async (trainId: number, newDepartureTime: Date) => { - const draggedTrain = trainSchedules?.find((train) => train.id === trainId); - const updatedDepartureTime = newDepartureTime.toISOString(); - if (!draggedTrain?.constraint_distribution) { - console.error('Train ou distribution des contraintes non trouvé'); - return; - } - - const updatedTrainSchedule: TrainScheduleBase = { - ...draggedTrain, - start_time: updatedDepartureTime || draggedTrain.start_time, - }; - try { - const trainScheduleResult = await putTrainScheduleById({ - id: trainId, - trainScheduleForm: updatedTrainSchedule, - }).unwrap(); - upsertTrainSchedules([trainScheduleResult], false); - } catch (error) { - console.error(error); - } finally { - console.log('Train schedule updated'); - } - }; - const handleNGEOperation = (event: NGEEvent, netzgrafikDto: NetzgrafikDto) => { handleOperation({ event, @@ -164,7 +135,6 @@ const ScenarioContent = ({ ) )} diff --git a/front/src/applications/operationalStudies/hooks/useScenarioData.ts b/front/src/applications/operationalStudies/hooks/useScenarioData.ts index 00077d7eec7..f43b712f4d6 100644 --- a/front/src/applications/operationalStudies/hooks/useScenarioData.ts +++ b/front/src/applications/operationalStudies/hooks/useScenarioData.ts @@ -10,37 +10,33 @@ import { type TimetableDetailedResult, type TrainScheduleResult, } from 'common/api/osrdEditoastApi'; -import { setFailure } from 'reducers/main'; -import { - getSelectedTrainId, - getTrainIdUsedForProjection, -} from 'reducers/simulationResults/selectors'; -import { useAppDispatch } from 'store'; -import { castErrorToFailure } from 'utils/error'; +import { useOsrdConfSelectors } from 'common/osrdContext'; +import { getTrainIdUsedForProjection } from 'reducers/simulationResults/selectors'; import { mapBy } from 'utils/types'; import useAutoUpdateProjection from './useAutoUpdateProjection'; import useLazyLoadTrains from './useLazyLoadTrains'; import usePathProjection from './usePathProjection'; +import formatTrainScheduleSummaries from '../helpers/formatTrainScheduleSummaries'; const useScenarioData = ( scenario: ScenarioResponse, timetable: TimetableDetailedResult, infra: InfraWithState ) => { - const dispatch = useAppDispatch(); + const { getElectricalProfileSetId } = useOsrdConfSelectors(); + const electricalProfileSetId = useSelector(getElectricalProfileSetId); const trainIdUsedForProjection = useSelector(getTrainIdUsedForProjection); - const selectedTrainId = useSelector(getSelectedTrainId); const [trainSchedules, setTrainSchedules] = useState(); const [trainIdsToFetch, setTrainIdsToFetch] = useState(); - const { data: rawTrainSchedules, error: fetchTrainSchedulesError } = - osrdEditoastApi.endpoints.postTrainSchedule.useQuery({ - body: { - ids: timetable.train_ids, - }, - }); + const [fetchTrainSchedules] = osrdEditoastApi.endpoints.postTrainSchedule.useLazyQuery(); + const [putTrainScheduleById] = osrdEditoastApi.endpoints.putTrainScheduleById.useMutation(); + const [postTrainScheduleSimulationSummary] = + osrdEditoastApi.endpoints.postTrainScheduleSimulationSummary.useLazyQuery(); + const { data: { results: rollingStocks } = { results: null } } = + osrdEditoastApi.endpoints.getLightRollingStock.useQuery({ pageSize: 1000 }); const projectionPath = usePathProjection(infra); @@ -59,15 +55,16 @@ const useScenarioData = ( trainSchedules, }); - const { data: conflicts } = osrdEditoastApi.endpoints.getTimetableByIdConflicts.useQuery( - { - id: scenario.timetable_id, - infraId: scenario.infra_id, - }, - { - skip: !allTrainsLoaded, - } - ); + const { data: conflicts, refetch: refetchConflicts } = + osrdEditoastApi.endpoints.getTimetableByIdConflicts.useQuery( + { + id: scenario.timetable_id, + infraId: scenario.infra_id, + }, + { + skip: !allTrainsLoaded, + } + ); const trainScheduleSummaries = useMemo( () => sortBy(Array.from(trainScheduleSummariesById.values()), 'startTime'), @@ -87,13 +84,18 @@ const useScenarioData = ( useAutoUpdateProjection(infra, timetable.train_ids, trainScheduleSummaries); useEffect(() => { - if (!rawTrainSchedules) { - setTrainSchedules(undefined); - } else { + const fetchTrains = async () => { + const rawTrainSchedules = await fetchTrainSchedules({ + body: { + ids: timetable.train_ids, + }, + }).unwrap(); const sortedTrainSchedules = sortBy(rawTrainSchedules, 'start_time'); setTrainSchedules(sortedTrainSchedules); - } - }, [rawTrainSchedules]); + }; + + fetchTrains(); + }, []); // first load of the trainScheduleSummaries useEffect(() => { @@ -103,32 +105,24 @@ const useScenarioData = ( } }, [trainSchedules, infra.state]); - useEffect(() => { - if (fetchTrainSchedulesError) { - dispatch(setFailure(castErrorToFailure(fetchTrainSchedulesError))); - } - }, [fetchTrainSchedulesError]); - const upsertTrainSchedules = useCallback( - (trainSchedulesToUpsert: TrainScheduleResult[], shallRemoveProjectedTrain: boolean = true) => { + (trainSchedulesToUpsert: TrainScheduleResult[]) => { + setProjectedTrainsById((prev) => { + const newProjectedTrainsById = new Map(prev); + trainSchedulesToUpsert.forEach((trainSchedule) => { + newProjectedTrainsById.delete(trainSchedule.id); + }); + return newProjectedTrainsById; + }); + setTrainSchedules((prev) => { const newTrainSchedulesById = { ...keyBy(prev, 'id'), ...keyBy(trainSchedulesToUpsert, 'id'), }; - const newTrainSchedules = sortBy(Object.values(newTrainSchedulesById), 'start_time'); - return newTrainSchedules; + return sortBy(Object.values(newTrainSchedulesById), 'start_time'); }); - if (shallRemoveProjectedTrain) { - setProjectedTrainsById((prev) => { - const newProjectedTrainsById = new Map(prev); - trainSchedulesToUpsert.forEach((trainSchedule) => { - newProjectedTrainsById.delete(trainSchedule.id); - }); - return newProjectedTrainsById; - }); - } const sortedTrainSchedulesToUpsert = sortBy(trainSchedulesToUpsert, 'start_time'); setTrainIdsToFetch(sortedTrainSchedulesToUpsert.map((trainSchedule) => trainSchedule.id)); }, @@ -161,29 +155,104 @@ const useScenarioData = ( }); }, []); - return { - selectedTrainId, - selectedTrainSummary: selectedTrainId - ? trainScheduleSummariesById.get(selectedTrainId) - : undefined, - trainScheduleSummaries, - trainSchedules, - projectionData: - trainScheduleUsedForProjection && projectionPath - ? { - trainSchedule: trainScheduleUsedForProjection, - ...projectionPath, - projectedTrains, - projectionLoaderData: { - allTrainsProjected, - totalTrains: timetable.train_ids.length, - }, - } - : undefined, - conflicts, - removeTrains, - upsertTrainSchedules, - }; + /** Update only depature time of a train */ + const updateTrainDepartureTime = useCallback( + async (trainId: number, newDeparture: Date) => { + const trainSchedule = trainSchedules?.find((train) => train.id === trainId); + + if (!trainSchedule) { + throw new Error('Train non trouvé'); + } + + const trainScheduleResult = await putTrainScheduleById({ + id: trainId, + trainScheduleForm: { + ...trainSchedule, + start_time: newDeparture.toISOString(), + }, + }).unwrap(); + + setProjectedTrainsById((prev) => { + const newProjectedTrainsById = new Map(prev); + newProjectedTrainsById.set(trainScheduleResult.id, { + ...newProjectedTrainsById.get(trainScheduleResult.id)!, + departureTime: newDeparture, + }); + return newProjectedTrainsById; + }); + + setTrainSchedules((prev) => { + const newTrainSchedulesById = { + ...keyBy(prev, 'id'), + ...keyBy([trainScheduleResult], 'id'), + }; + const newTrainSchedules = sortBy(Object.values(newTrainSchedulesById), 'start_time'); + return newTrainSchedules; + }); + + // update its summary + const rawSummaries = await postTrainScheduleSimulationSummary({ + body: { + infra_id: scenario.infra_id, + ids: [trainId], + electrical_profile_set_id: electricalProfileSetId, + }, + }).unwrap(); + const summaries = formatTrainScheduleSummaries( + [trainId], + rawSummaries, + mapBy([trainScheduleResult], 'id'), + rollingStocks! + ); + setTrainScheduleSummariesById((prev) => { + const newTrainScheduleSummariesById = new Map(prev); + newTrainScheduleSummariesById.set(trainId, summaries.get(trainId)!); + return newTrainScheduleSummariesById; + }); + + // fetch conflicts + refetchConflicts(); + }, + [trainSchedules] + ); + + const results = useMemo( + () => ({ + trainScheduleSummaries, + trainSchedules, + projectionData: + trainScheduleUsedForProjection && projectionPath + ? { + trainSchedule: trainScheduleUsedForProjection, + ...projectionPath, + projectedTrains, + projectionLoaderData: { + allTrainsProjected, + totalTrains: timetable.train_ids.length, + }, + } + : undefined, + conflicts, + removeTrains, + upsertTrainSchedules, + updateTrainDepartureTime, + }), + [ + trainScheduleSummaries, + trainSchedules, + trainScheduleUsedForProjection, + projectionPath, + projectedTrains, + allTrainsProjected, + timetable.train_ids.length, + conflicts, + removeTrains, + upsertTrainSchedules, + updateTrainDepartureTime, + ] + ); + + return results; }; export default useScenarioData; diff --git a/front/src/applications/operationalStudies/views/SimulationResults.tsx b/front/src/applications/operationalStudies/views/SimulationResults.tsx index 5cf5c224fa8..f339cda8272 100644 --- a/front/src/applications/operationalStudies/views/SimulationResults.tsx +++ b/front/src/applications/operationalStudies/views/SimulationResults.tsx @@ -4,7 +4,7 @@ import { ChevronLeft, ChevronRight } from '@osrd-project/ui-icons'; import cx from 'classnames'; import { useTranslation } from 'react-i18next'; -import type { Conflict } from 'common/api/osrdEditoastApi'; +import { type Conflict } from 'common/api/osrdEditoastApi'; import SimulationWarpedMap from 'common/Map/WarpedMap/SimulationWarpedMap'; import ResizableSection from 'common/ResizableSection'; import ManchetteWithSpaceTimeChartWrapper, { @@ -22,10 +22,12 @@ import type { ProjectionData } from 'modules/simulationResult/types'; import TimesStopsOutput from 'modules/timesStops/TimesStopsOutput'; import type { TrainScheduleWithDetails } from 'modules/trainschedule/components/Timetable/types'; import { updateViewport, type Viewport } from 'reducers/map'; +import { updateSelectedTrainId } from 'reducers/simulationResults'; import { useAppDispatch } from 'store'; import { getPointCoordinates } from 'utils/geometry'; import useSimulationResults from '../hooks/useSimulationResults'; +import type { TrainSpaceTimeData } from '../types'; const SPEED_SPACE_CHART_HEIGHT = 521.5; const HANDLE_TAB_RESIZE_HEIGHT = 20; @@ -36,9 +38,9 @@ type SimulationResultsProps = { collapsedTimetable: boolean; infraId?: number; projectionData?: ProjectionData; - selectedTrainSummary?: TrainScheduleWithDetails; + trainScheduleSummaries?: TrainScheduleWithDetails[]; conflicts?: Conflict[]; - handleTrainDrop: (trainId: number, newDepartureTime: Date) => void; + updateTrainDepartureTime: (trainId: number, newDepartureTime: Date) => void; }; const SimulationResults = ({ @@ -46,9 +48,9 @@ const SimulationResults = ({ collapsedTimetable, infraId, projectionData, - selectedTrainSummary, + trainScheduleSummaries, conflicts = [], - handleTrainDrop, + updateTrainDepartureTime, }: SimulationResultsProps) => { const { t } = useTranslation('simulation'); const dispatch = useAppDispatch(); @@ -79,21 +81,14 @@ const SimulationResults = ({ pathProperties ); - const [projectPathTrainResult, setProjectPathTrainResult] = useState( - projectionData?.projectedTrains.filter((train) => train.spaceTimeCurves.length > 0) || [] - ); + const [projectPathTrainResult, setProjectPathTrainResult] = useState([]); useEffect(() => { - console.log('setProjectPathTrainResult'); - setProjectPathTrainResult( - projectionData?.projectedTrains.filter((train) => train.spaceTimeCurves.length > 0) || [] - ); + if (projectionData?.projectedTrains) { + setProjectPathTrainResult(projectionData?.projectedTrains || []); + } }, [projectionData]); - useEffect(() => { - console.log('i'); - }, [projectPathTrainResult]); // Compute path items coordinates in order to place them on the map - const pathItemsCoordinates = path && pathProperties && @@ -121,6 +116,30 @@ const SimulationResults = ({ const conflictZones = useProjectedConflicts(infraId, conflicts, projectionData?.path); + const selectedTrainSummary = useMemo( + () => trainScheduleSummaries?.find((train) => train.id === selectedTrainSchedule?.id), + [trainScheduleSummaries, selectedTrainSchedule] + ); + + const handleTrainDrag = async ( + draggedTrain: TrainSpaceTimeData, + newDepartureTime: Date, + { stopPanning }: { stopPanning: boolean } + ) => { + if (stopPanning) { + // update in the database + dispatch(updateSelectedTrainId(draggedTrain.id)); + updateTrainDepartureTime(draggedTrain.id, newDepartureTime); + } else { + // update in the state + setProjectPathTrainResult( + projectPathTrainResult.map((train) => + train.id === draggedTrain.id ? { ...train, departureTime: newDepartureTime } : train + ) + ); + } + }; + useEffect(() => { if (extViewport !== undefined) { dispatch( @@ -195,18 +214,7 @@ const SimulationResults = ({ conflicts={conflictZones} projectionLoaderData={projectionData.projectionLoaderData} height={manchetteWithSpaceTimeChartHeight - MANCHETTE_HEIGHT_DIFF} - handleTrainDrag={async (trainId, newDepartureTime, isPanning) => { - // Update the departure time of the train in the projection data - setProjectPathTrainResult( - projectPathTrainResult.map((res) => - res.id === trainId ? { ...res, departureTime: newDepartureTime } : res - ) - ); - - if (!isPanning) { - handleTrainDrop(trainId, newDepartureTime); - } - }} + handleTrainDrag={handleTrainDrag} /> diff --git a/front/src/modules/simulationResult/components/ManchetteWithSpaceTimeChart/ManchetteWithSpaceTimeChart.tsx b/front/src/modules/simulationResult/components/ManchetteWithSpaceTimeChart/ManchetteWithSpaceTimeChart.tsx index 652928c1e52..73462a3698d 100644 --- a/front/src/modules/simulationResult/components/ManchetteWithSpaceTimeChart/ManchetteWithSpaceTimeChart.tsx +++ b/front/src/modules/simulationResult/components/ManchetteWithSpaceTimeChart/ManchetteWithSpaceTimeChart.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef, useState, useCallback } from 'react'; +import { useMemo, useRef, useState, useCallback, useEffect } from 'react'; import { KebabHorizontal } from '@osrd-project/ui-icons'; import Manchette, { type WaypointMenuData } from '@osrd-project/ui-manchette'; @@ -50,7 +50,11 @@ type ManchetteWithSpaceTimeChartProps = { totalTrains: number; allTrainsProjected: boolean; }; - handleTrainDrag?: (trainId: number, newDepartureTime: Date, isPanning: boolean) => void; + handleTrainDrag?: ( + draggedTrain: TrainSpaceTimeData, + newDepartureTime: Date, + { stopPanning }: { stopPanning: boolean } + ) => Promise; selectTrainPath?: (trainId: number) => void; height?: number; }; @@ -70,19 +74,24 @@ const ManchetteWithSpaceTimeChartWrapper = ({ height = MANCHETTE_WITH_SPACE_TIME_CHART_DEFAULT_HEIGHT, handleTrainDrag, }: ManchetteWithSpaceTimeChartProps) => { + const dispatch = useAppDispatch(); + const manchetteWithSpaceTimeCharWrappertRef = useRef(null); const manchetteWithSpaceTimeChartRef = useRef(null); - const [hoveredItem, setHoveredItem] = useState(null); - const [interactionState, setInteractionState] = useState< - | { type: 'idle' } - | { type: 'draggingItem'; id: number; initialDepartureTime: Date } - | { type: 'defaultBehavior' } - >({ type: 'idle' }); - const dispatch = useAppDispatch(); + const [hoveredItem, setHoveredItem] = useState(null); + const [draggingState, setDraggingState] = useState<{ + draggedTrain: TrainSpaceTimeData; + initialDepartureTime: Date; + }>(); const [waypointsPanelIsOpen, setWaypointsPanelIsOpen] = useState(false); + const [tmpSelectedTrain, setTmpSelectedTrain] = useState(selectedTrainScheduleId); + useEffect(() => { + setTmpSelectedTrain(selectedTrainScheduleId); + }, [selectedTrainScheduleId]); + // Cut the space time chart curves if the first or last waypoints are hidden const { filteredProjectPathTrainResult: cutProjectedTrains, filteredConflicts: cutConflicts } = useMemo(() => { @@ -180,7 +189,7 @@ const ManchetteWithSpaceTimeChartWrapper = ({ manchetteWaypoints, cutProjectedTrains, manchetteWithSpaceTimeChartRef, - selectedTrainScheduleId, + tmpSelectedTrain, height ); @@ -202,44 +211,47 @@ const ManchetteWithSpaceTimeChartWrapper = ({ })); }); - const onPanOverloaded: SpaceTimeChartProps['onPan'] = (payload) => { - if (!payload.isPanning) { - if (interactionState.type === 'draggingItem' && handleTrainDrag) { - const path = projectPathTrainResult.find((res) => res.id === interactionState.id); - if (path) { - handleTrainDrag(path.id, path.departureTime, false); - } else { - console.error(`No path found with id ${interactionState.id}`); - } + const onPanOverloaded: SpaceTimeChartProps['onPan'] = async (payload) => { + const { isPanning } = payload; + + if (!handleTrainDrag) { + // if no handleTrainDrag, we pan normally + spaceTimeChartProps.onPan(payload); + return; + } + + // if dragging + if (draggingState) { + const { draggedTrain, initialDepartureTime } = draggingState; + const timeDiff = payload.data.time - payload.initialData.time; + const newDeparture = new Date(initialDepartureTime.getTime() + timeDiff); + + await handleTrainDrag(draggedTrain, newDeparture, { stopPanning: !isPanning }); + + // stop dragging if necessary + if (!isPanning) { + setDraggingState(undefined); } - setInteractionState({ type: 'idle' }); - } else if ( - interactionState.type === 'idle' && - handleTrainDrag && - hoveredItem && - 'pathId' in hoveredItem.element - ) { - const hoveredPathId = getIdFromTrainPath(hoveredItem.element.pathId); - const path = projectPathTrainResult.find((res) => res.id === hoveredPathId); - if (path) { - setInteractionState({ - type: 'draggingItem', - id: hoveredPathId, - initialDepartureTime: path.departureTime, + return; + } + + // if not dragging, we check if we should start dragging + if (hoveredItem && 'pathId' in hoveredItem.element) { + const hoveredTrainId = getIdFromTrainPath(hoveredItem.element.pathId); + const train = projectPathTrainResult.find((res) => res.id === hoveredTrainId); + if (train) { + setTmpSelectedTrain(train.id); + setDraggingState({ + draggedTrain: train, + initialDepartureTime: train.departureTime, }); } else { - console.error(`No path found with id ${hoveredPathId}`); + console.error(`No train found with id ${hoveredTrainId}`); } - } else if (interactionState.type === 'draggingItem' && handleTrainDrag) { - const { id, initialDepartureTime } = interactionState; - const timeDiff = payload.data.time - payload.initialData.time; - const initialDepartureTimeMS = new Date(initialDepartureTime).getTime(); - const newDepartureTimeMS = initialDepartureTimeMS + timeDiff; - const newDepartureTime = new Date(newDepartureTimeMS); - handleTrainDrag(id, newDepartureTime, true); - } else { - spaceTimeChartProps.onPan(payload); } + + // if no hovered train, we pan normally + spaceTimeChartProps.onPan(payload); }; const waypointMenuData = useWaypointMenu(waypointsPanelData); @@ -268,17 +280,12 @@ const ManchetteWithSpaceTimeChartWrapper = ({ ); const handleClick: SpaceTimeChartProps['onClick'] = () => { - if (hoveredItem && 'pathId' in hoveredItem.element) { + if (!draggingState && hoveredItem && 'pathId' in hoveredItem.element) { const trainId = getIdFromTrainPath(hoveredItem.element.pathId); dispatch(updateSelectedTrainId(trainId)); } }; - console.log( - 'projectPathTrainResult', - projectPathTrainResult.map((train) => train.departureTime.toISOString()).join(',') - ); - return (
diff --git a/front/src/modules/trainschedule/components/Timetable/Timetable.tsx b/front/src/modules/trainschedule/components/Timetable/Timetable.tsx index 1d2579aa573..6df63bb644c 100644 --- a/front/src/modules/trainschedule/components/Timetable/Timetable.tsx +++ b/front/src/modules/trainschedule/components/Timetable/Timetable.tsx @@ -10,7 +10,10 @@ import type { Conflict, InfraState, TrainScheduleResult } from 'common/api/osrdE import i18n from 'i18n'; import ConflictsList from 'modules/conflict/components/ConflictsList'; import { updateSelectedTrainId } from 'reducers/simulationResults'; -import { getTrainIdUsedForProjection } from 'reducers/simulationResults/selectors'; +import { + getSelectedTrainId, + getTrainIdUsedForProjection, +} from 'reducers/simulationResults/selectors'; import { useAppDispatch } from 'store'; import TimetableToolbar from './TimetableToolbar'; @@ -20,7 +23,6 @@ import type { TrainScheduleWithDetails } from './types'; type TimetableProps = { setDisplayTrainScheduleManagement: (mode: string) => void; infraState: InfraState; - selectedTrainId?: number; conflicts?: Conflict[]; upsertTrainSchedules: (trainSchedules: TrainScheduleResult[]) => void; setTrainIdToEdit: (trainId?: number) => void; @@ -36,7 +38,6 @@ const formatDepartureDate = (d: Date) => dayjs(d).locale(i18n.language).format(' const Timetable = ({ setDisplayTrainScheduleManagement, infraState, - selectedTrainId, conflicts, upsertTrainSchedules, removeTrains, @@ -47,6 +48,7 @@ const Timetable = ({ dtoImport, }: TimetableProps) => { const { t } = useTranslation(['operationalStudies/scenario', 'common/itemTypes']); + const selectedTrainId = useSelector(getSelectedTrainId); const [displayedTrainSchedules, setDisplayedTrainSchedules] = useState< TrainScheduleWithDetails[]