From b4648bbaaddd7d14925d4b0753986a4abf1219c0 Mon Sep 17 00:00:00 2001 From: theocrsb Date: Wed, 20 Nov 2024 13:44:18 +0100 Subject: [PATCH] front: select op with map when add train -bold pr on hover (for PRs on same track) -select pr on map and choose track Signed-off-by: theocrsb --- .../common/Map/Layers/OperationalPoints.tsx | 22 ++- .../ManageTrainScheduleMap/RenderPopup.tsx | 161 ++++++++++++++---- .../components/ManageTrainSchedule/Map.tsx | 15 +- front/src/reducers/osrdconf/helpers.ts | 60 +++++-- front/src/styles/scss/common/map/_popup.scss | 6 + 5 files changed, 213 insertions(+), 51 deletions(-) diff --git a/front/src/common/Map/Layers/OperationalPoints.tsx b/front/src/common/Map/Layers/OperationalPoints.tsx index 9c9a1c4c805..b65029b7497 100644 --- a/front/src/common/Map/Layers/OperationalPoints.tsx +++ b/front/src/common/Map/Layers/OperationalPoints.tsx @@ -10,9 +10,15 @@ interface Props { colors: Theme; layerOrder: number; infraID: number | undefined; + operationnalPointId?: string; } -export default function OperationalPoints({ colors, layerOrder, infraID }: Props) { +export default function OperationalPoints({ + colors, + layerOrder, + infraID, + operationnalPointId, +}: Props) { const point: LayerProps = { type: 'circle', 'source-layer': 'operational_points', @@ -42,7 +48,12 @@ export default function OperationalPoints({ colors, layerOrder, infraID }: Props ['concat', ' ', ['get', 'extensions_sncf_ch']], ], ], - 'text-font': ['Roboto Condensed'], + 'text-font': [ + 'case', + ['==', ['get', 'id'], operationnalPointId || ''], + ['literal', ['Roboto Bold']], + ['literal', ['Roboto Condensed']], + ], 'text-size': 12, 'text-anchor': 'left', 'text-justify': 'left', @@ -100,7 +111,12 @@ export default function OperationalPoints({ colors, layerOrder, infraID }: Props ['get', 'extensions_sncf_ch'], ], ], - 'text-font': ['Roboto Condensed'], + 'text-font': [ + 'case', + ['==', ['get', 'id'], operationnalPointId || ''], + ['literal', ['Roboto Bold']], + ['literal', ['Roboto Condensed']], + ], 'text-size': 11, 'text-anchor': 'left', 'text-allow-overlap': false, diff --git a/front/src/modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup.tsx b/front/src/modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup.tsx index a7a6047f19c..401f918847d 100644 --- a/front/src/modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup.tsx +++ b/front/src/modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup.tsx @@ -1,6 +1,8 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useMemo } from 'react'; +import { Select } from '@osrd-project/ui-core'; import { point } from '@turf/helpers'; import { useTranslation } from 'react-i18next'; import { IoFlag } from 'react-icons/io5'; @@ -13,11 +15,13 @@ import { editoastToEditorEntity } from 'applications/editor/data/api'; import type { TrackSectionEntity } from 'applications/editor/tools/trackEdition/types'; import { calculateDistanceAlongTrack } from 'applications/editor/tools/utils'; import { useManageTrainScheduleContext } from 'applications/operationalStudies/hooks/useManageTrainScheduleContext'; +import { useScenarioContext } from 'applications/operationalStudies/hooks/useScenarioContext'; import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; -import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; +import { osrdEditoastApi, type OperationalPoint } from 'common/api/osrdEditoastApi'; import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; import { setPointIti } from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/setPointIti'; -import type { PathStep } from 'reducers/osrdconf/types'; +import { type PathStep } from 'reducers/osrdconf/types'; +import { getPointCoordinates } from 'utils/geometry'; type FeatureInfoClickType = { displayPopup: boolean; @@ -34,13 +38,31 @@ function RenderPopup({ pathProperties }: RenderPopupProps) { const { launchPathfinding } = useManageTrainScheduleContext(); const osrdConfActions = useOsrdConfActions(); const { t } = useTranslation(['operationalStudies/manageTrainSchedule']); + const featureInfoClick: FeatureInfoClickType = useSelector(getFeatureInfoClick); const infraId = useSelector(getInfraID); const origin = useSelector(getOrigin); const destination = useSelector(getDestination); + const isOperationalPoint = useMemo(() => { + const properties = featureInfoClick?.feature?.properties; + return !!properties?.track_id || !!properties?.track_name; + }, [featureInfoClick]); + + const { getTrackSectionsByIds } = useScenarioContext(); + const [trackOffset, setTrackOffset] = useState(0); + const [clickedOp, setClickedOp] = useState< + PathStep & { + tracks: { + trackName?: string; + coordinates?: number[]; + isSelected: boolean; + }[]; + } + >(); + const [getTrackEntity] = osrdEditoastApi.endpoints.postInfraByInfraIdObjectsAndObjectType.useLazyQuery(); @@ -52,11 +74,13 @@ function RenderPopup({ pathProperties }: RenderPopupProps) { !featureInfoClick.coordinates ) return; - const trackId = featureInfoClick.feature.properties.id; + + const objectId = featureInfoClick.feature.properties.id; + const result = await getTrackEntity({ infraId: infraId!, - objectType: 'TrackSection', - body: [trackId], + objectType: isOperationalPoint ? 'OperationalPoint' : 'TrackSection', + body: [objectId], }).unwrap(); if (!result.length) { @@ -64,13 +88,44 @@ function RenderPopup({ pathProperties }: RenderPopupProps) { return; } - const trackEntity = editoastToEditorEntity(result[0], 'TrackSection'); - const offset = calculateDistanceAlongTrack( - trackEntity, - point(featureInfoClick.coordinates.slice(0, 2)).geometry, - 'millimeters' - ); - setTrackOffset(offset); + if (isOperationalPoint) { + const trackId = featureInfoClick.feature.properties.track_id; + const clickedTrack = await getTrackEntity({ + infraId: infraId!, + objectType: 'TrackSection', + body: [trackId], + }).unwrap(); + + const { parts } = result[0].railjson as OperationalPoint; + const trackIds = parts.map((part) => part.track); + const tracks = await getTrackSectionsByIds(trackIds); + + const trackPartCoordinates = parts.map((step) => { + const track = tracks[step.track]; + return { + trackName: track.extensions?.sncf?.track_name as string, + coordinates: getPointCoordinates(track.geo, track.length, step.position), + isSelected: step.track === clickedTrack[0].obj_id, + }; + }); + + setClickedOp({ + id: nextId(), + secondary_code: result[0].railjson.extensions.sncf.ch, + uic: result[0].railjson.extensions.identifier.uic, + tracks: trackPartCoordinates, + }); + } else { + setClickedOp(undefined); + // if operationnalPoint we already have coordinates + const trackEntity = editoastToEditorEntity(result[0], 'TrackSection'); + const offset = calculateDistanceAlongTrack( + trackEntity, + point(featureInfoClick.coordinates.slice(0, 2)).geometry, + 'millimeters' + ); + setTrackOffset(offset); + } }; if (featureInfoClick.displayPopup) { @@ -89,41 +144,82 @@ function RenderPopup({ pathProperties }: RenderPopupProps) { const { properties: trackProperties } = featureInfoClick.feature; const coordinates = featureInfoClick.coordinates.slice(0, 2); - const pathStepProperties: PathStep = { - id: nextId(), - coordinates, - track: trackProperties.id, - offset: Math.round(trackOffset), // offset needs to be an integer - kp: trackProperties.kp, - metadata: { - lineCode: trackProperties.extensions_sncf_line_code, - lineName: trackProperties.extensions_sncf_line_name, - trackName: trackProperties.extensions_sncf_track_name, - trackNumber: trackProperties.extensions_sncf_track_number, - }, - }; + let pathStepProperties: PathStep; + if (isOperationalPoint && clickedOp) { + pathStepProperties = { + ...clickedOp, + }; + } else { + pathStepProperties = { + id: nextId(), + coordinates, + track: trackProperties.id, + offset: Math.round(trackOffset), // offset needs to be an integer + kp: trackProperties.kp, + metadata: { + lineCode: trackProperties.extensions_sncf_line_code, + lineName: trackProperties.extensions_sncf_line_name, + trackName: trackProperties.extensions_sncf_track_name, + trackNumber: trackProperties.extensions_sncf_track_number, + }, + } as PathStep; + } return (
- {featureInfoClick.feature.properties.extensions_sncf_track_name} - {featureInfoClick.feature.properties.extensions_sncf_line_code} + {isOperationalPoint && trackProperties.extensions_sncf_track_name} + {trackProperties.extensions_sncf_line_code}
- {featureInfoClick.feature.properties.extensions_sncf_line_name} + {isOperationalPoint ? ( + <> + {trackProperties.extensions_identifier_name}
+ {trackProperties.extensions_sncf_trigram} {trackProperties.extensions_sncf_ch} + + ) : ( + trackProperties.extensions_sncf_line_name + )}
+ {isOperationalPoint && clickedOp?.tracks && ( +