From 62dc5376849d9869b5add68945f64534c402bda4 Mon Sep 17 00:00:00 2001 From: Theo Macron Date: Wed, 24 Jun 2026 15:49:54 +0200 Subject: [PATCH] front: fix TOD virtual track missing for uic/id path step references Signed-off-by: Theo Macron --- .../upsertMapWaypointsInOperationalPoints.ts | 1 + .../hooks/usePathProjection.ts | 7 ++++- .../applications/operationalStudies/types.ts | 4 ++- .../stdcm/utils/fetchPathProperties.ts | 4 +++ .../useGetProjectedTrainOperationalPoints.ts | 6 +++- .../useTrackOccupancy.ts | 29 ++++++------------- front/src/modules/simulationResult/types.ts | 2 ++ 7 files changed, 30 insertions(+), 23 deletions(-) diff --git a/front/src/applications/operationalStudies/helpers/upsertMapWaypointsInOperationalPoints.ts b/front/src/applications/operationalStudies/helpers/upsertMapWaypointsInOperationalPoints.ts index be91c28ba7e..e5d8d5bb7ad 100644 --- a/front/src/applications/operationalStudies/helpers/upsertMapWaypointsInOperationalPoints.ts +++ b/front/src/applications/operationalStudies/helpers/upsertMapWaypointsInOperationalPoints.ts @@ -63,6 +63,7 @@ export function upsertMapWaypointsInOperationalPoints( ...baseFormattedStep, waypointId: step.id, opId: null, + location, } : { ...baseFormattedStep, diff --git a/front/src/applications/operationalStudies/hooks/usePathProjection.ts b/front/src/applications/operationalStudies/hooks/usePathProjection.ts index 21261a7635a..16cf931d371 100644 --- a/front/src/applications/operationalStudies/hooks/usePathProjection.ts +++ b/front/src/applications/operationalStudies/hooks/usePathProjection.ts @@ -73,6 +73,7 @@ const createVirtualOp = ( weight, country_code: '??', is_passenger_station: false, + opRef, }; }; @@ -207,7 +208,10 @@ const usePathProjection = ( pathfinding, path: pathUsedForProjection, geometry: pathProperties.geometry, - operationalPoints, + operationalPoints: operationalPoints.map((op, index) => ({ + ...op, + opRef: pathfindingOpRefs[index], + })), operationalPointReferences: pathfindingOpRefs, projectingOnSimulatedPathException, operationalPointDistances, @@ -245,6 +249,7 @@ const usePathProjection = ( secondary_code: matchedOp.secondary_code, uic: matchedOp.uic, weight, + opRef, }); } else { // NOT MATCHED: Point doesn't exist in infrastructure (e.g., NGE point) diff --git a/front/src/applications/operationalStudies/types.ts b/front/src/applications/operationalStudies/types.ts index c35ad01149f..b8d79e7fddd 100644 --- a/front/src/applications/operationalStudies/types.ts +++ b/front/src/applications/operationalStudies/types.ts @@ -190,7 +190,9 @@ export type ItineraryPathProperties = PathProperties & { export type PathProjectionResultOperationalPoint = Omit< PathProperties['operational_points'][number], 'part' ->; +> & { + opRef: OperationalPointReference; +}; export type PathProjectionResult = { path: PathItem[]; diff --git a/front/src/applications/stdcm/utils/fetchPathProperties.ts b/front/src/applications/stdcm/utils/fetchPathProperties.ts index 289062f38a5..f0566f47c70 100644 --- a/front/src/applications/stdcm/utils/fetchPathProperties.ts +++ b/front/src/applications/stdcm/utils/fetchPathProperties.ts @@ -61,6 +61,10 @@ const fetchPathProperties = async ( ...omit(op, 'id'), waypointId: `${op.id}-${op.position}-${index}`, opId: op.id, + location: { + type: 'operational_point_part_reference' as const, + operational_point: { type: 'id' as const, operational_point: op.id }, + }, }) ); diff --git a/front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useGetProjectedTrainOperationalPoints.ts b/front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useGetProjectedTrainOperationalPoints.ts index b159bdeb8d9..c0d6a3d3761 100644 --- a/front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useGetProjectedTrainOperationalPoints.ts +++ b/front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useGetProjectedTrainOperationalPoints.ts @@ -37,9 +37,13 @@ const useGetProjectedTrainOperationalPoints = ({ const getOperationalPoints = async () => { let operationalPointsWithUniqueIds: PathOperationalPoint[] = projectedOperationalPoints?.map((op, i) => ({ - ...omit(op, 'id'), + ...omit(op, ['id', 'opRef']), waypointId: `${op.id}-${op.position}-${i}`, opId: op.id, + location: { + type: 'operational_point_part_reference' as const, + operational_point: op.opRef, + }, })) || []; operationalPointsWithUniqueIds = diff --git a/front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useTrackOccupancy.ts b/front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useTrackOccupancy.ts index e01f6f626a9..35e65de8618 100644 --- a/front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useTrackOccupancy.ts +++ b/front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useTrackOccupancy.ts @@ -55,23 +55,10 @@ function extractStationLabel( return stationLabel.label; } -function getOperationalPointReference( +function getTrackOccupancyOperationalPointReference( op: PathOperationalPoint | undefined ): OperationalPointReference | undefined { - if (!op) return undefined; - // Normalize empty string ch to null — virtual OPs store ch as '' when the original - // secondary_code was null (see usePathProjection createVirtualOp), and passing '' - // to the backend would filter for OPs with an empty secondary_code rather than any. - const secondaryCode = op.secondary_code || null; - const mainCode = op.main_code; - if (mainCode) return { type: 'trigram', trigram: mainCode, secondary_code: secondaryCode }; - const uic = op.uic; - if (uic) return { type: 'uic', uic, secondary_code: secondaryCode }; - // Only use the opId when it refers to a real infra OP. Virtual OPs (unrecognised, created - // by usePathProjection when pathfinding fails) have a synthetic id like "virtual_op_Zürich" - if (op.opId && !op.opId.startsWith('virtual_op')) - return { type: 'id', operational_point: op.opId }; - return undefined; + return op && op.location.type !== 'track_offset' ? op.location.operational_point : undefined; } /** @@ -383,7 +370,7 @@ const useTrackOccupancy = ({ Array.from(trainScheduleProjectionsById.keys()), (ids) => fetchTrackOccupancy( - getOperationalPointReference(waypoint), + getTrackOccupancyOperationalPointReference(waypoint), waypointId, Object.fromEntries(ids.map((id) => [id, trainScheduleProjectionsById.get(id)!])) ), @@ -431,7 +418,9 @@ const useTrackOccupancy = ({ // refresh zones when reopening waypoint, if TOD was closed. if (!currentSelected && newSelected) { - const opRef = getOperationalPointReference(pathOpsByWaypointId.get(waypointId)); + const opRef = getTrackOccupancyOperationalPointReference( + pathOpsByWaypointId.get(waypointId) + ); if (!opRef) return; const trains = Object.fromEntries(Array.from(trainScheduleProjectionsById.entries())); @@ -505,7 +494,7 @@ const useTrackOccupancy = ({ await Promise.all( [...impactedPathOperationalPointIDs].map(async (waypointId) => { const newZones = await fetchTrackOccupancy( - getOperationalPointReference(pathOpsByWaypointId.get(waypointId)), + getTrackOccupancyOperationalPointReference(pathOpsByWaypointId.get(waypointId)), waypointId, { [draggedTrainId]: newTrainData, @@ -611,7 +600,7 @@ const useTrackOccupancy = ({ waypointId: string; reference: OperationalPointReference; }>((op) => { - const reference = getOperationalPointReference(op); + const reference = getTrackOccupancyOperationalPointReference(op); if (!reference) return []; return [{ waypointId: op.waypointId, reference }]; }); @@ -678,7 +667,7 @@ const useTrackOccupancy = ({ if (addedTrainIDs.size || modifiedTrainIDs.size) { forEach(pathOperationalPointsState, async (_, waypointId) => { const newZones = await fetchTrackOccupancy( - getOperationalPointReference(pathOpsByWaypointId.get(waypointId)), + getTrackOccupancyOperationalPointReference(pathOpsByWaypointId.get(waypointId)), waypointId, Object.fromEntries( [...addedTrainIDs, ...modifiedTrainIDs].map((id) => [ diff --git a/front/src/modules/simulationResult/types.ts b/front/src/modules/simulationResult/types.ts index 9b923941560..2ce6214d47f 100644 --- a/front/src/modules/simulationResult/types.ts +++ b/front/src/modules/simulationResult/types.ts @@ -9,6 +9,7 @@ import type { import type { PacedTrainException, CoreSignalUpdate, + PathItemLocation, PathProperties, RollingStockWithLiveries, SimulationResponseSuccess, @@ -37,6 +38,7 @@ export type EditoastPathOperationalPoint = NonNullable< export type PathOperationalPoint = Omit & { waypointId: string; opId: string | null; + location: PathItemLocation; }; // Space Time Chart