From 941a073114704565043cbb533e453ed241c1649f Mon Sep 17 00:00:00 2001 From: Clara Ni Date: Wed, 4 Oct 2023 14:09:59 +0200 Subject: [PATCH] front: remove not rtk endpoints - use rtk in editor/data/api - getAttachedItems - getCompatibleRoutes - getRoutesFromWaypoint - getEntities - getRouteTrackRanges - remove MapSearchSignalBox (not used anymore) --- editoast/openapi.yaml | 11 +- editoast/openapi_legacy.yaml | 6 +- front/src/applications/editor/Map.tsx | 29 ++-- .../editor/components/EntitySumUp.tsx | 37 ++--- front/src/applications/editor/data/api.ts | 127 ++++-------------- front/src/applications/editor/nav.tsx | 5 +- .../editor/tools/pointEdition/components.tsx | 74 +++++----- .../editor/tools/pointEdition/tool-factory.ts | 32 ++--- .../catenary/CatenaryEditionLayers.tsx | 53 ++++---- .../SpeedSectionEditionLayers.tsx | 53 ++++---- .../components/EditRouteMetadata.tsx | 79 ++++++----- .../routeEdition/components/EditRoutePath.tsx | 28 ++-- .../routeEdition/components/Endpoints.tsx | 5 +- .../routeEdition/components/WayPointInput.tsx | 7 +- .../editor/tools/routeEdition/utils.ts | 107 +++++++++++---- .../editor/tools/selection/tool.tsx | 8 +- .../editor/tools/switchEdition/components.tsx | 53 ++++---- .../editor/tools/trackEdition/components.tsx | 14 +- front/src/common/Map/Search/MapSearch.tsx | 6 - .../common/Map/Search/MapSearchSignalBox.tsx | 114 ---------------- .../Map/WarpedMap/SimulationWarpedMap.tsx | 5 +- .../src/common/Map/WarpedMap/core/helpers.ts | 8 +- front/src/common/api/osrdEditoastApi.ts | 12 +- front/src/common/requests.ts | 2 +- front/src/utils/error.ts | 6 + 25 files changed, 415 insertions(+), 466 deletions(-) delete mode 100644 front/src/common/Map/Search/MapSearchSignalBox.tsx create mode 100644 front/src/utils/error.ts diff --git a/editoast/openapi.yaml b/editoast/openapi.yaml index ba7d0ab9592..fa7bbab7727 100644 --- a/editoast/openapi.yaml +++ b/editoast/openapi.yaml @@ -3344,6 +3344,10 @@ paths: $ref: '#/components/schemas/DirectionalTrackRange' minItems: 1 type: array + required: + - track_ranges + - detectors + - switches_directions type: object type: array description: Paths, containing track ranges, detectors and switches with their directions. If no path is found, an empty list is returned. @@ -3385,7 +3389,9 @@ paths: schema: description: A list of routes seperated by comma example: route1,route2,route3 - type: string + items: + type: string + type: array responses: '200': content: @@ -3446,6 +3452,9 @@ paths: items: type: string type: array + required: + - starting + - ending type: object description: All routes that starting and ending by the given waypoint summary: Retrieve all routes that starting and ending by the given waypoint (detector or buffer stop) diff --git a/editoast/openapi_legacy.yaml b/editoast/openapi_legacy.yaml index 300bbf4d1fa..9bf5e65355d 100644 --- a/editoast/openapi_legacy.yaml +++ b/editoast/openapi_legacy.yaml @@ -703,6 +703,7 @@ paths: items: type: string example: ["route3", "route4"] + required: [starting, ending] /infra/{id}/routes/track_ranges/: get: @@ -720,7 +721,9 @@ paths: - in: query name: routes schema: - type: string + type: array + items: + type: string description: A list of routes seperated by comma example: "route1,route2,route3" required: true @@ -800,6 +803,7 @@ paths: example: { "switch1": "left", "switch2": "right" } additionalProperties: type: string + required: [track_ranges, detectors, switches_directions] /infra/{id}/objects/{object_type}/: post: diff --git a/front/src/applications/editor/Map.tsx b/front/src/applications/editor/Map.tsx index c94ce612740..c46c5bd564f 100644 --- a/front/src/applications/editor/Map.tsx +++ b/front/src/applications/editor/Map.tsx @@ -211,20 +211,23 @@ const MapUnplugged: FC> = ({ : e; if (toolState.hovered && activeTool.onClickEntity) { if (toolState.hovered.type) { - getEntity(infraID as number, toolState.hovered.id, toolState.hovered.type).then( - (entity) => { - if (activeTool.onClickEntity) { - // Those features lack a proper "geometry", and have a "_geometry" - // instead. This fixes it: - entity = { - ...entity, - // eslint-disable-next-line no-underscore-dangle,@typescript-eslint/no-explicit-any - geometry: entity.geometry || (entity as any)._geometry, - }; - activeTool.onClickEntity(entity, eventWithFeature, extendedContext); - } + getEntity( + infraID as number, + toolState.hovered.id, + toolState.hovered.type, + dispatch + ).then((entity) => { + if (activeTool.onClickEntity) { + // Those features lack a proper "geometry", and have a "_geometry" + // instead. This fixes it: + entity = { + ...entity, + // eslint-disable-next-line no-underscore-dangle,@typescript-eslint/no-explicit-any + geometry: entity.geometry || (entity as any)._geometry, + }; + activeTool.onClickEntity(entity, eventWithFeature, extendedContext); } - ); + }); } } if (activeTool.onClickMap) { diff --git a/front/src/applications/editor/components/EntitySumUp.tsx b/front/src/applications/editor/components/EntitySumUp.tsx index fb30487e118..8f09efa94c0 100644 --- a/front/src/applications/editor/components/EntitySumUp.tsx +++ b/front/src/applications/editor/components/EntitySumUp.tsx @@ -1,7 +1,8 @@ import React, { FC, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { flatMap, forEach, isNumber, uniq } from 'lodash'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { Dispatch } from 'redux'; import { TFunction } from 'i18next'; import cx from 'classnames'; @@ -46,7 +47,8 @@ const DEFAULT_CLASSES = { async function getAdditionalEntities( infra: number, - entity: EditorEntity + entity: EditorEntity, + dispatch: Dispatch ): Promise> { switch (entity.objType) { case 'Signal': @@ -55,7 +57,7 @@ async function getAdditionalEntities( const trackId = (entity as SignalEntity).properties.track; if (trackId) { try { - return { [trackId]: await getEntity(infra, trackId, 'TrackSection') }; + return { [trackId]: await getEntity(infra, trackId, 'TrackSection', dispatch) }; } catch (e) { return {}; } @@ -66,19 +68,21 @@ async function getAdditionalEntities( const trackIDs = flatMap((entity as SwitchEntity).properties.ports, (port) => port.track ? [port.track] : [] ); - return getEntities(infra, trackIDs, 'TrackSection'); + return getEntities(infra, trackIDs, 'TrackSection', dispatch); } case 'Route': { const route = entity as RouteEntity; const entryPoint = await getEntity( infra, route.properties.entry_point.id, - route.properties.entry_point.type + route.properties.entry_point.type, + dispatch ); const exitPoint = await getEntity( infra, route.properties.exit_point.id, - route.properties.exit_point.type + route.properties.exit_point.type, + dispatch ); return { entryPoint, exitPoint }; } @@ -282,6 +286,7 @@ const EntitySumUp: FC< | { id: string; objType: EditoastType; entity?: undefined } ) > = ({ entity, id, objType, classes, status }) => { + const dispatch = useDispatch(); const { t } = useTranslation(); const infraID = useSelector(getInfraID); const [state, setState] = useState< @@ -296,7 +301,7 @@ const EntitySumUp: FC< setState({ type: 'loading' }); if (entity) { - getAdditionalEntities(infraID as number, entity).then((additionalEntities) => { + getAdditionalEntities(infraID as number, entity, dispatch).then((additionalEntities) => { setState({ type: 'ready', entity, @@ -304,15 +309,17 @@ const EntitySumUp: FC< }); }); } else { - getEntity(infraID as number, id as string, objType as EditoastType).then( + getEntity(infraID as number, id as string, objType as EditoastType, dispatch).then( (fetchedEntity) => { - getAdditionalEntities(infraID as number, fetchedEntity).then((additionalEntities) => { - setState({ - type: 'ready', - entity: fetchedEntity, - additionalEntities, - }); - }); + getAdditionalEntities(infraID as number, fetchedEntity, dispatch).then( + (additionalEntities) => { + setState({ + type: 'ready', + entity: fetchedEntity, + additionalEntities, + }); + } + ); } ); } diff --git a/front/src/applications/editor/data/api.ts b/front/src/applications/editor/data/api.ts index 4aff09e868e..6b72024b2ad 100644 --- a/front/src/applications/editor/data/api.ts +++ b/front/src/applications/editor/data/api.ts @@ -1,25 +1,12 @@ import { groupBy, uniq, toPairs } from 'lodash'; +import { Dispatch } from 'redux'; -import { get, post } from '../../../common/requests'; import { PostInfraByIdObjectsAndObjectTypeApiResponse, - PostInfraByIdObjectsAndObjectTypeApiArg, - GetInfraByIdRoutesAndWaypointTypeWaypointIdApiResponse, - GetInfraByIdRoutesAndWaypointTypeWaypointIdApiArg, - GetInfraByIdRoutesTrackRangesApiResponse, - GetInfraByIdAttachedAndTrackIdApiResponse, - GetInfraByIdAttachedAndTrackIdApiArg, -} from '../../../common/api/osrdEditoastApi'; -import { - Direction, - EditorEntity, - EditorEntityType, - TrackRange, - WayPoint, - WayPointEntity, -} from '../../../types'; -import { EditoastType } from '../tools/types'; -import { RouteCandidate } from '../tools/routeEdition/types'; + osrdEditoastApi, +} from 'common/api/osrdEditoastApi'; +import { EditoastType } from 'applications/editor/tools/types'; +import { EditorEntity } from 'types/editor'; export function editoastToEditorEntity( entity: PostInfraByIdObjectsAndObjectTypeApiResponse[0], @@ -37,17 +24,21 @@ export function editoastToEditorEntity( * Returns a list of entities from editoast */ export async function getEntities( - infra: number | string, + infraId: number, ids: string[], - type: T['objType'] + type: T['objType'], + dispatch: Dispatch ): Promise> { const uniqIDs = uniq(ids); - const res = await post< - PostInfraByIdObjectsAndObjectTypeApiArg['body'], - PostInfraByIdObjectsAndObjectTypeApiResponse - >(`/editoast/infra/${infra}/objects/${type}/`, uniqIDs); + const results = await dispatch( + osrdEditoastApi.endpoints.postInfraByIdObjectsAndObjectType.initiate({ + id: infraId as number, + objectType: type, + body: uniqIDs, + }) + ).unwrap(); - return res.reduce( + return results.reduce( (iter, entry, i) => ({ ...iter, [uniqIDs[i]]: editoastToEditorEntity(entry, type), @@ -60,11 +51,12 @@ export async function getEntities( * Returns an entity from editoast: */ export async function getEntity( - infra: number | string, + infra: number, id: string, - type: T['objType'] -): Promise { - const result = await getEntities(infra, [id], type); + type: T['objType'], + dispatch: Dispatch +) { + const result = await getEntities(infra, [id], type, dispatch); if (!result || !result[id]) throw new Error(`getEntity: No entity found for type ${type} and id ${id}`); @@ -72,85 +64,18 @@ export async function getEntity( } export async function getMixedEntities( - infra: number | string, - defs: { id: string; type: EditoastType }[] -): Promise> { + infra: number, + defs: { id: string; type: EditoastType }[], + dispatch: Dispatch +) { const groupedDefs = groupBy(defs, 'type'); const entities = await Promise.all( toPairs(groupedDefs).map(([type, values]) => { const ids = values.map(({ id }) => id); - return getEntities(infra, ids, type as EditoastType); + return getEntities(infra, ids, type as EditoastType, dispatch); }) ); return entities.reduce((acc, curr) => ({ ...acc, ...curr }), {} as Record); } - -/** - * Returns all routes starting from or ending to a waypoint: - */ -export async function getRoutesFromWaypoint( - infra: number | string, - type: EditorEntityType, - id: GetInfraByIdRoutesAndWaypointTypeWaypointIdApiArg['waypointId'] -): Promise { - if (type !== 'BufferStop' && type !== 'Detector') - throw new Error(`${type} elements are not valid waypoints.`); - return get( - `/editoast/infra/${infra}/routes/${type}/${id}` - ); -} - -export async function getAttachedItems( - infra: number | string, - id: GetInfraByIdAttachedAndTrackIdApiArg['trackId'] -): Promise { - return get( - `/editoast/infra/${infra}/attached/${id}` - ); -} - -export async function getRouteTrackRanges( - infra: number | string, - ids: string[] -): Promise> { - const res = await get( - `/editoast/infra/${infra}/routes/track_ranges/?routes=${encodeURIComponent(ids.join(','))}` - ); - - return res.reduce( - (iter, o, i) => ({ - ...iter, - [ids[i]]: o.type === 'Computed' ? o.track_ranges : null, - }), - {} as Record - ); -} - -export async function getCompatibleRoutes( - infra: number | string, - entryPoint: WayPoint, - entryPointDirection: Direction, - exitPoint: WayPoint -): Promise[]> { - const extremities = await getMixedEntities(infra, [entryPoint, exitPoint]); - const entryPointEntity = extremities[entryPoint.id]; - const exitPointEntity = extremities[exitPoint.id]; - - if (!entryPointEntity) - throw new Error(`Entry point ${entryPoint.id} (${entryPoint.type}) not found`); - if (!exitPointEntity) throw new Error(`Exit point ${exitPoint.id} (${exitPoint.type}) not found`); - - return post(`/editoast/infra/${infra}/pathfinding/`, { - starting: { - track: entryPointEntity.properties.track as string, - position: entryPointEntity.properties.position as number, - direction: entryPointDirection, - }, - ending: { - track: exitPointEntity.properties.track as string, - position: exitPointEntity.properties.position as number, - }, - }); -} diff --git a/front/src/applications/editor/nav.tsx b/front/src/applications/editor/nav.tsx index 1e2b2351e46..1c9fd74f9ba 100644 --- a/front/src/applications/editor/nav.tsx +++ b/front/src/applications/editor/nav.tsx @@ -159,14 +159,15 @@ const NavButtons: NavButton[][] = [ id: 'infra-errors', icon: BsFillExclamationOctagonFill, labelTranslationKey: 'Editor.nav.infra-errors', - async onClick({ openModal, closeModal, setViewport }, { switchTool }) { + async onClick({ openModal, closeModal, setViewport, dispatch }, { switchTool }) { openModal( { const entity = await getEntity( infraID, item.information.obj_id, - item.information.obj_type + item.information.obj_type, + dispatch ); // select the item in the editor scope if (entity.objType === 'Route') { diff --git a/front/src/applications/editor/tools/pointEdition/components.tsx b/front/src/applications/editor/tools/pointEdition/components.tsx index 6b167c06237..2637ff9a385 100644 --- a/front/src/applications/editor/tools/pointEdition/components.tsx +++ b/front/src/applications/editor/tools/pointEdition/components.tsx @@ -21,13 +21,14 @@ import { RouteEntity, } from 'types'; import { SIGNALS_TO_SYMBOLS } from 'common/Map/Consts/SignalsNames'; +import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; import { PointEditionState } from './types'; import EditorForm from '../../components/EditorForm'; import { cleanSymbolType, flattenEntity, NEW_ENTITY_ID } from '../../data/utils'; import { EditoastType } from '../types'; import EditorContext from '../../context'; import EntitySumUp from '../../components/EntitySumUp'; -import { getEntities, getEntity, getRoutesFromWaypoint } from '../../data/api'; +import { getEntities, getEntity } from '../../data/api'; import { Spinner } from '../../../../common/Loader'; import { getEditRouteState } from '../routeEdition/utils'; import { getMap } from '../../../../reducers/map/selectors'; @@ -40,6 +41,7 @@ export const POINT_LAYER_ID = 'pointEditionTool/new-entity'; * Generic component to show routes starting or ending from the edited waypoint: */ export const RoutesList: FC<{ type: EditoastType; id: string }> = ({ type, id }) => { + const dispatch = useDispatch(); const { t } = useTranslation(); const infraID = useSelector(getInfraID); const [routesState, setRoutesState] = useState< @@ -49,34 +51,38 @@ export const RoutesList: FC<{ type: EditoastType; id: string }> = ({ type, id }) | { type: 'error'; message: string } >({ type: 'idle' }); const { switchTool } = useContext(EditorContext) as ExtendedEditorContextType; + const [getRoutesFromWaypoint] = + osrdEditoastApi.endpoints.getInfraByIdRoutesAndWaypointTypeWaypointId.useLazyQuery(); useEffect(() => { - if (routesState.type === 'idle') { - setRoutesState({ type: 'loading' }); - getRoutesFromWaypoint(`${infraID}`, type, id) - .then((res) => { - const starting = res.starting || []; - const ending = res.ending || []; - - if (starting.length || ending.length) { - getEntities(`${infraID}`, [...starting, ...ending], 'Route') - .then((entities) => { - setRoutesState({ - type: 'ready', - starting: starting.map((routeId) => entities[routeId]), - ending: ending.map((routeId) => entities[routeId]), + if (routesState.type === 'idle' && infraID) { + if (type !== 'BufferStop' && type !== 'Detector') { + setRoutesState({ type: 'error', message: `${type} elements are not valid waypoints.` }); + } else { + setRoutesState({ type: 'loading' }); + getRoutesFromWaypoint({ id: infraID, waypointType: type, waypointId: id }) + .unwrap() + .then(({ starting = [], ending = [] }) => { + if (starting.length || ending.length) { + getEntities(infraID, [...starting, ...ending], 'Route', dispatch) + .then((entities) => { + setRoutesState({ + type: 'ready', + starting: starting.map((routeId) => entities[routeId]), + ending: ending.map((routeId) => entities[routeId]), + }); + }) + .catch((err) => { + setRoutesState({ type: 'error', message: err.message }); }); - }) - .catch((err) => { - setRoutesState({ type: 'error', message: err.message }); - }); - } else { - setRoutesState({ type: 'ready', starting: [], ending: [] }); - } - }) - .catch((err) => { - setRoutesState({ type: 'error', message: err.message }); - }); + } else { + setRoutesState({ type: 'ready', starting: [], ending: [] }); + } + }) + .catch((err) => { + setRoutesState({ type: 'error', message: err.message }); + }); + } } }, [routesState]); @@ -198,16 +204,18 @@ export const PointEditionLeftPanel: FC<{ type: EditoastType }> = (infraID as number, trackId, 'TrackSection').then((track) => { - setTrackState({ type: 'ready', id: trackId, track }); + getEntity(infraID as number, trackId, 'TrackSection', dispatch).then( + (track) => { + setTrackState({ type: 'ready', id: trackId, track }); - if (!firstLoading) { - const { position } = state.entity.properties; - const point = along(track, position, { units: 'meters' }); + if (!firstLoading) { + const { position } = state.entity.properties; + const point = along(track, position, { units: 'meters' }); - setState({ ...state, entity: { ...state.entity, geometry: point.geometry } }); + setState({ ...state, entity: { ...state.entity, geometry: point.geometry } }); + } } - }); + ); } }, [infraID, setState, state, state.entity.properties.track, trackState.id, trackState.type]); diff --git a/front/src/applications/editor/tools/pointEdition/tool-factory.ts b/front/src/applications/editor/tools/pointEdition/tool-factory.ts index 430afdb7510..97715c4335b 100644 --- a/front/src/applications/editor/tools/pointEdition/tool-factory.ts +++ b/front/src/applications/editor/tools/pointEdition/tool-factory.ts @@ -83,7 +83,7 @@ function getPointEditionTool({ ], // Interactions: - onClickMap(_e, { setState, state, infraID }) { + onClickMap(_e, { setState, state, infraID, dispatch }) { const { isHoveringTarget, entity, nearestPoint } = state; if (entity.geometry && !isEqual(entity.geometry, NULL_GEOMETRY) && isHoveringTarget) { setState({ @@ -104,19 +104,21 @@ function getPointEditionTool({ // retrieve the track section to be sure that the computation of the distance will be good // we can't trust maplibre, because the stored gemetry is not necessary the real one - getEntity(infraID as number, newEntity.properties.track, 'TrackSection').then((track) => { - newEntity.properties.position = nearestPointOnLine( - (track as Feature).geometry, - newEntity.geometry as Point, - { units: 'meters' } - ).properties?.location; + getEntity(infraID as number, newEntity.properties.track, 'TrackSection', dispatch).then( + (track) => { + newEntity.properties.position = nearestPointOnLine( + (track as Feature).geometry, + newEntity.geometry as Point, + { units: 'meters' } + ).properties?.location; - setState({ - ...state, - entity: newEntity, - nearestPoint: null, - }); - }); + setState({ + ...state, + entity: newEntity, + nearestPoint: null, + }); + } + ); } }, onMove(e, { setState, state }) { @@ -176,12 +178,12 @@ function getPointEditionTool({ }, // Lifecycle: - onMount({ state: { entity }, infraID }) { + onMount({ state: { entity }, infraID, dispatch }) { const trackId = entity.properties?.track; if (typeof trackId !== 'string') return; - getEntity(infraID as number, trackId, 'TrackSection').then((track) => { + getEntity(infraID as number, trackId, 'TrackSection', dispatch).then((track) => { const dbPosition = entity.properties.position; const computedPosition = nearestPointOnLine( (track as Feature).geometry, diff --git a/front/src/applications/editor/tools/rangeEdition/catenary/CatenaryEditionLayers.tsx b/front/src/applications/editor/tools/rangeEdition/catenary/CatenaryEditionLayers.tsx index 757a2eb5e9c..7c67d12ce81 100644 --- a/front/src/applications/editor/tools/rangeEdition/catenary/CatenaryEditionLayers.tsx +++ b/front/src/applications/editor/tools/rangeEdition/catenary/CatenaryEditionLayers.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import React, { FC, useContext, useEffect, useMemo } from 'react'; import { getMap } from 'reducers/map/selectors'; import EditorContext from 'applications/editor/context'; @@ -20,6 +20,7 @@ import EntitySumUp from '../../../components/EntitySumUp'; import { LayerType } from '../../types'; export const CatenaryEditionLayers: FC = () => { + const dispatch = useDispatch(); const { t } = useTranslation(); const { renderingFingerprint, @@ -90,17 +91,20 @@ export const CatenaryEditionLayers: FC = () => { ), })); - getEntities(infraId as number, missingTrackIDs, 'TrackSection').then( - (res) => { - setState((s) => ({ - ...s, - trackSectionsCache: { - ...s.trackSectionsCache, - ...mapValues(res, (track) => ({ type: 'success', track } as TrackState)), - }, - })); - } - ); + getEntities( + infraId as number, + missingTrackIDs, + 'TrackSection', + dispatch + ).then((res) => { + setState((s) => ({ + ...s, + trackSectionsCache: { + ...s.trackSectionsCache, + ...mapValues(res, (track) => ({ type: 'success', track } as TrackState)), + }, + })); + }); } }, [entity.properties?.track_ranges]); @@ -115,17 +119,20 @@ export const CatenaryEditionLayers: FC = () => { }, })); - getEntity(infraId as number, hoveredItem.id, 'TrackSection').then( - (track) => { - setState((s) => ({ - ...s, - trackSectionsCache: { - ...s.trackSectionsCache, - [hoveredItem.id]: { type: 'success', track }, - }, - })); - } - ); + getEntity( + infraId as number, + hoveredItem.id, + 'TrackSection', + dispatch + ).then((track) => { + setState((s) => ({ + ...s, + trackSectionsCache: { + ...s.trackSectionsCache, + [hoveredItem.id]: { type: 'success', track }, + }, + })); + }); } }, [hoveredItem]); diff --git a/front/src/applications/editor/tools/rangeEdition/speedSection/SpeedSectionEditionLayers.tsx b/front/src/applications/editor/tools/rangeEdition/speedSection/SpeedSectionEditionLayers.tsx index 3a796c9be1d..4efe12db055 100644 --- a/front/src/applications/editor/tools/rangeEdition/speedSection/SpeedSectionEditionLayers.tsx +++ b/front/src/applications/editor/tools/rangeEdition/speedSection/SpeedSectionEditionLayers.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import React, { FC, useContext, useEffect, useMemo } from 'react'; import { getMap } from 'reducers/map/selectors'; import EditorContext from 'applications/editor/context'; @@ -25,6 +25,7 @@ import EntitySumUp from '../../../components/EntitySumUp'; import { LayerType } from '../../types'; export const SpeedSectionEditionLayers: FC = () => { + const dispatch = useDispatch(); const { t } = useTranslation(); const { renderingFingerprint, @@ -111,17 +112,20 @@ export const SpeedSectionEditionLayers: FC = () => { ), })); - getEntities(infraId as number, missingTrackIDs, 'TrackSection').then( - (res) => { - setState((s) => ({ - ...s, - trackSectionsCache: { - ...s.trackSectionsCache, - ...mapValues(res, (track) => ({ type: 'success', track } as TrackState)), - }, - })); - } - ); + getEntities( + infraId as number, + missingTrackIDs, + 'TrackSection', + dispatch + ).then((res) => { + setState((s) => ({ + ...s, + trackSectionsCache: { + ...s.trackSectionsCache, + ...mapValues(res, (track) => ({ type: 'success', track } as TrackState)), + }, + })); + }); } }, [entity.properties?.track_ranges]); @@ -136,17 +140,20 @@ export const SpeedSectionEditionLayers: FC = () => { }, })); - getEntity(infraId as number, hoveredItem.id, 'TrackSection').then( - (track) => { - setState((s) => ({ - ...s, - trackSectionsCache: { - ...s.trackSectionsCache, - [hoveredItem.id]: { type: 'success', track }, - }, - })); - } - ); + getEntity( + infraId as number, + hoveredItem.id, + 'TrackSection', + dispatch + ).then((track) => { + setState((s) => ({ + ...s, + trackSectionsCache: { + ...s.trackSectionsCache, + [hoveredItem.id]: { type: 'success', track }, + }, + })); + }); } }, [hoveredItem]); diff --git a/front/src/applications/editor/tools/routeEdition/components/EditRouteMetadata.tsx b/front/src/applications/editor/tools/routeEdition/components/EditRouteMetadata.tsx index cbd00178240..395e2f8bcb1 100644 --- a/front/src/applications/editor/tools/routeEdition/components/EditRouteMetadata.tsx +++ b/front/src/applications/editor/tools/routeEdition/components/EditRouteMetadata.tsx @@ -121,30 +121,32 @@ export const EditRouteMetadataPanel: FC<{ state: EditRouteMetadataState }> = ({ const baseState = getEmptyCreateRouteState() as EditRoutePathState; setIsLoading(true); - getMixedEntities(infraID as number, [entry_point, exit_point]).then( - (entities) => { - const entryPointEntity = entities[entry_point.id]; - const exitPointEntity = entities[exit_point.id]; - setIsLoading(false); - setState({ - ...baseState, - routeState: { - ...baseState.routeState, - entryPoint: { - id: entry_point.id, - type: entry_point.type, - position: entryPointEntity.geometry.coordinates, - }, - entryPointDirection: entry_point_direction, - exitPoint: { - id: exit_point.id, - type: exit_point.type, - position: exitPointEntity.geometry.coordinates, - }, + getMixedEntities( + infraID as number, + [entry_point, exit_point], + dispatch + ).then((entities) => { + const entryPointEntity = entities[entry_point.id]; + const exitPointEntity = entities[exit_point.id]; + setIsLoading(false); + setState({ + ...baseState, + routeState: { + ...baseState.routeState, + entryPoint: { + id: entry_point.id, + type: entry_point.type, + position: entryPointEntity.geometry.coordinates, }, - }); - } - ); + entryPointDirection: entry_point_direction, + exitPoint: { + id: exit_point.id, + type: exit_point.type, + position: exitPointEntity.geometry.coordinates, + }, + }, + }); + }); }} > {t('Editor.tools.routes-edition.alternative-routes')} @@ -183,6 +185,7 @@ export const EditRouteMetadataPanel: FC<{ state: EditRouteMetadataState }> = ({ }; export const EditRouteMetadataLayers: FC<{ state: EditRouteMetadataState }> = ({ state }) => { + const dispatch = useDispatch(); const { t } = useTranslation(); const infraID = useSelector(getInfraID); const mapStyle = useSelector(getMapStyle); @@ -226,7 +229,7 @@ export const EditRouteMetadataLayers: FC<{ state: EditRouteMetadataState }> = ({ if (geometryState.type === 'ready' && geometryState.feature?.properties.id !== id) { setGeometryState({ type: 'loading' }); - getRouteGeometryByRouteId(infraID as number, id) + getRouteGeometryByRouteId(infraID as number, id, dispatch) .then((feature) => { setGeometryState({ type: 'ready', @@ -241,19 +244,21 @@ export const EditRouteMetadataLayers: FC<{ state: EditRouteMetadataState }> = ({ }); const { entry_point, exit_point } = state.routeEntity.properties; - getMixedEntities(infraID as number, [entry_point, exit_point]).then( - (entities) => { - const entryPointEntity = entities[entry_point.id]; - const exitPointEntity = entities[exit_point.id]; - setGeometryState({ - type: 'ready', - feature: lineString( - [entryPointEntity.geometry.coordinates, exitPointEntity.geometry.coordinates], - { id } - ), - }); - } - ); + getMixedEntities( + infraID as number, + [entry_point, exit_point], + dispatch + ).then((entities) => { + const entryPointEntity = entities[entry_point.id]; + const exitPointEntity = entities[exit_point.id]; + setGeometryState({ + type: 'ready', + feature: lineString( + [entryPointEntity.geometry.coordinates, exitPointEntity.geometry.coordinates], + { id } + ), + }); + }); }); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/front/src/applications/editor/tools/routeEdition/components/EditRoutePath.tsx b/front/src/applications/editor/tools/routeEdition/components/EditRoutePath.tsx index dfc0402e284..707ee4670f0 100644 --- a/front/src/applications/editor/tools/routeEdition/components/EditRoutePath.tsx +++ b/front/src/applications/editor/tools/routeEdition/components/EditRoutePath.tsx @@ -1,6 +1,6 @@ import React, { FC, useCallback, useContext, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import chroma from 'chroma-js'; import { Layer, LineLayer, Popup, Source } from 'react-map-gl/maplibre'; import { featureCollection } from '@turf/helpers'; @@ -12,14 +12,14 @@ import { FiSearch } from 'react-icons/fi'; import { EditRoutePathState, OptionsStateType, RouteEditionState } from '../types'; import EditorContext from '../../../context'; -import { getCompatibleRoutes, getEntity } from '../../../data/api'; +import { getEntity } from '../../../data/api'; import { EditorEntity, OmitLayer, RouteEntity, WayPointEntity } from '../../../../../types'; import { LoaderFill } from '../../../../../common/Loader'; import { getRoutesLineLayerProps } from '../../../../../common/Map/Layers/Routes'; import colors from '../../../../../common/Map/Consts/colors'; import { osrdEditoastApi } from '../../../../../common/api/osrdEditoastApi'; import { nestEntity, entityToCreateOperation } from '../../../data/utils'; -import { getEditRouteState, getRouteGeometries } from '../utils'; +import { getCompatibleRoutesPayload, getEditRouteState, getRouteGeometries } from '../utils'; import EntitySumUp from '../../../components/EntitySumUp'; import { EditEndpoints } from './Endpoints'; import { getInfraID } from '../../../../../reducers/osrdconf/selectors'; @@ -27,6 +27,7 @@ import { getMapStyle } from '../../../../../reducers/map/selectors'; import { ExtendedEditorContextType } from '../../editorContextTypes'; export const EditRoutePathLeftPanel: FC<{ state: EditRoutePathState }> = ({ state }) => { + const dispatch = useDispatch(); const { t } = useTranslation(); const [editorSave] = osrdEditoastApi.endpoints.postInfraById.useMutation({}); const { setState } = useContext(EditorContext) as ExtendedEditorContextType; @@ -35,6 +36,8 @@ export const EditRoutePathLeftPanel: FC<{ state: EditRoutePathState }> = ({ stat const [includeReleaseDetectors, setIncludeReleaseDetectors] = useState(true); const { entryPoint, exitPoint, entryPointDirection } = state.routeState; + const [postPathfinding] = osrdEditoastApi.endpoints.postInfraByIdPathfinding.useMutation(); + const searchCandidates = useCallback(async () => { if (!entryPoint || !exitPoint || !entryPointDirection || state.optionsState.type === 'loading') return; @@ -43,19 +46,28 @@ export const EditRoutePathLeftPanel: FC<{ state: EditRoutePathState }> = ({ stat optionsState: { type: 'loading' }, }); - const candidates = await getCompatibleRoutes( + const payload = await getCompatibleRoutesPayload( infraID as number, entryPoint, entryPointDirection, - exitPoint + exitPoint, + dispatch ); + const candidates = await postPathfinding({ id: infraID as number, body: payload }).unwrap(); + const candidateColors = chroma .scale(['#321BF7CC', '#37B5F0CC', '#F0901FCC', '#F7311BCC', '#D124E0CC']) .mode('lch') .colors(candidates.length) .map((str) => chroma(str).css()); - const features = await getRouteGeometries(infraID as number, entryPoint, exitPoint, candidates); + const features = await getRouteGeometries( + infraID as number, + entryPoint, + exitPoint, + candidates, + dispatch + ); setState({ optionsState: { @@ -219,8 +231,8 @@ export const EditRoutePathLeftPanel: FC<{ state: EditRoutePathState }> = ({ stat if (typeof newRouteId !== 'string') throw new Error('Cannot find ID of newly created route.'); - getEntity(`${infraID}`, newRouteId, 'Route').then((route) => - setState(getEditRouteState(route)) + getEntity(infraID as number, newRouteId, 'Route', dispatch).then( + (route) => setState(getEditRouteState(route)) ); }} > diff --git a/front/src/applications/editor/tools/routeEdition/components/Endpoints.tsx b/front/src/applications/editor/tools/routeEdition/components/Endpoints.tsx index 9277269ee42..7169ef38c58 100644 --- a/front/src/applications/editor/tools/routeEdition/components/Endpoints.tsx +++ b/front/src/applications/editor/tools/routeEdition/components/Endpoints.tsx @@ -4,7 +4,7 @@ import { BsArrowBarRight, BsBoxArrowInRight } from 'react-icons/bs'; import { HiSwitchVertical } from 'react-icons/hi'; import { FaFlagCheckered } from 'react-icons/fa'; import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { getInfraID } from 'reducers/osrdconf/selectors'; import WayPointInput from './WayPointInput'; @@ -96,6 +96,7 @@ export const EditEndpoints: FC<{ state: RouteState; onChange: (newState: RouteSt }; const ExtremityDisplay: FC = ({ type, id }) => { + const dispatch = useDispatch(); const { t } = useTranslation(); const { switchTool } = useContext(EditorContext); const infraID = useSelector(getInfraID); @@ -108,7 +109,7 @@ const ExtremityDisplay: FC = ({ type, id }) => { className="btn btn-primary btn-sm" title={t('common.open')} onClick={() => { - getEntity(infraID as number, id, type).then((entity) => { + getEntity(infraID as number, id, type, dispatch).then((entity) => { if (type === 'Detector') { switchTool({ toolType: TOOL_TYPES.DETECTOR_EDITION, diff --git a/front/src/applications/editor/tools/routeEdition/components/WayPointInput.tsx b/front/src/applications/editor/tools/routeEdition/components/WayPointInput.tsx index a47c508ad99..4099031ce20 100644 --- a/front/src/applications/editor/tools/routeEdition/components/WayPointInput.tsx +++ b/front/src/applications/editor/tools/routeEdition/components/WayPointInput.tsx @@ -1,6 +1,6 @@ import React, { FC, useCallback, useContext, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { FaMapMarkedAlt, FaTimesCircle } from 'react-icons/fa'; import { Position } from 'geojson'; @@ -18,6 +18,7 @@ const WayPointInput: FC<{ wayPoint: WayPoint | null; onChange: (newWayPoint: WayPoint & { position: Position }) => void; }> = ({ endPoint, wayPoint, onChange }) => { + const dispatch = useDispatch(); const { state, setState } = useContext( EditorContext ) as ExtendedEditorContextType; @@ -68,8 +69,8 @@ const WayPointInput: FC<{ ) { if (wayPoint) { setEntityState({ type: 'loading' }); - getEntity(infraID as number, wayPoint.id, wayPoint.type).then((entity) => - setEntityState({ type: 'data', entity }) + getEntity(infraID as number, wayPoint.id, wayPoint.type, dispatch).then( + (entity) => setEntityState({ type: 'data', entity }) ); } else { setEntityState({ type: 'empty' }); diff --git a/front/src/applications/editor/tools/routeEdition/utils.ts b/front/src/applications/editor/tools/routeEdition/utils.ts index 659b76e7347..139a9a3adb3 100644 --- a/front/src/applications/editor/tools/routeEdition/utils.ts +++ b/front/src/applications/editor/tools/routeEdition/utils.ts @@ -3,17 +3,20 @@ import { Feature, LineString, Position } from 'geojson'; import { lineString, point } from '@turf/helpers'; import lineSlice from '@turf/line-slice'; -import { RouteCandidate, RouteEditionState } from './types'; +import { Dispatch } from 'redux'; +import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; +import { getEntities, getEntity, getMixedEntities } from 'applications/editor/data/api'; +import { DEFAULT_COMMON_TOOL_STATE } from 'applications/editor/tools/commonToolState'; +import { RouteCandidate, RouteEditionState } from 'applications/editor/tools/routeEdition/types'; import { + Direction, PartialButFor, RouteEntity, TrackRange, TrackSectionEntity, WayPoint, WayPointEntity, -} from '../../../../types'; -import { getEntities, getEntity, getMixedEntities, getRouteTrackRanges } from '../../data/api'; -import { DEFAULT_COMMON_TOOL_STATE } from '../commonToolState'; +} from 'types'; export function getEmptyCreateRouteState(): RouteEditionState { return { @@ -118,37 +121,48 @@ export function computeRouteGeometry( } export async function getRouteGeometry( - infra: string | number, + infra: number, entryPoint: WayPointEntity, exitPoint: WayPointEntity, - trackRanges: TrackRange[] + trackRanges: TrackRange[], + dispatch: Dispatch ): Promise> { if (!trackRanges.length) return lineString([]); const tracks = await getEntities( infra, trackRanges.map((trackRange) => trackRange.track), - 'TrackSection' + 'TrackSection', + dispatch ); return computeRouteGeometry(tracks, entryPoint, exitPoint, trackRanges); } -export async function getRouteGeometryByRoute( - infra: string | number, - route: RouteEntity +async function getRouteGeometryByRoute( + infra: number, + route: RouteEntity, + dispatch: Dispatch ): Promise> { - const trackRanges = (await getRouteTrackRanges(infra, [route.properties.id]))[ - route.properties.id - ]; - const extremities = await getMixedEntities(infra, [ - route.properties.entry_point, - route.properties.exit_point, - ]); + const trackRangesResult = await dispatch( + osrdEditoastApi.endpoints.getInfraByIdRoutesTrackRanges.initiate({ + id: infra as number, + routes: [route.properties.id], + }) + ).unwrap(); + if (trackRangesResult.length === 0 || trackRangesResult[0].type !== 'Computed') { + throw new Error('Some track ranges could not be computed yet.'); + } + const trackRanges = trackRangesResult[0].track_ranges; + + const extremities = await getMixedEntities( + infra, + [route.properties.entry_point, route.properties.exit_point], + dispatch + ); const entryPoint = extremities[route.properties.entry_point.id]; const exitPoint = extremities[route.properties.exit_point.id]; - if (!trackRanges) throw new Error('Some track ranges could not be computed yet.'); if (!entryPoint) throw new Error( `Entry point ${route.properties.entry_point.id} (${route.properties.entry_point.type}) for route ${route.properties.id} not found` @@ -158,25 +172,31 @@ export async function getRouteGeometryByRoute( `Exit point ${route.properties.exit_point.id} (${route.properties.exit_point.type}) for route ${route.properties.id} not found` ); - return getRouteGeometry(infra, entryPoint, exitPoint, trackRanges); + return getRouteGeometry(infra, entryPoint, exitPoint, trackRanges, dispatch); } export async function getRouteGeometryByRouteId( - infra: string | number, - routeId: string + infra: number, + routeId: string, + dispatch: Dispatch ): Promise> { - const route = await getEntity(infra, routeId, 'Route'); + const route = await getEntity(infra, routeId, 'Route', dispatch); - return getRouteGeometryByRoute(infra, route); + return getRouteGeometryByRoute(infra, route, dispatch); } export async function getRouteGeometries( - infra: string | number, + infra: number, entryPoint: WayPoint, exitPoint: WayPoint, - candidates: RouteCandidate[] + candidates: RouteCandidate[], + dispatch: Dispatch ): Promise[]> { - const extremities = await getMixedEntities(infra, [entryPoint, exitPoint]); + const extremities = await getMixedEntities( + infra, + [entryPoint, exitPoint], + dispatch + ); const entryPointEntity = extremities[entryPoint.id]; const exitPointEntity = extremities[exitPoint.id]; @@ -190,8 +210,41 @@ export async function getRouteGeometries( infra, entryPointEntity, exitPointEntity, - candidate.track_ranges as TrackRange[] + candidate.track_ranges as TrackRange[], + dispatch ) ) ); } + +export async function getCompatibleRoutesPayload( + infra: number, + entryPoint: WayPoint, + entryPointDirection: Direction, + exitPoint: WayPoint, + dispatch: Dispatch +) { + const extremities = await getMixedEntities( + infra, + [entryPoint, exitPoint], + dispatch + ); + const entryPointEntity = extremities[entryPoint.id]; + const exitPointEntity = extremities[exitPoint.id]; + + if (!entryPointEntity) + throw new Error(`Entry point ${entryPoint.id} (${entryPoint.type}) not found`); + if (!exitPointEntity) throw new Error(`Exit point ${exitPoint.id} (${exitPoint.type}) not found`); + + return { + starting: { + track: entryPointEntity.properties.track as string, + position: entryPointEntity.properties.position as number, + direction: entryPointDirection, + }, + ending: { + track: exitPointEntity.properties.track as string, + position: exitPointEntity.properties.position as number, + }, + }; +} diff --git a/front/src/applications/editor/tools/selection/tool.tsx b/front/src/applications/editor/tools/selection/tool.tsx index c6708df3ec3..94513990f36 100644 --- a/front/src/applications/editor/tools/selection/tool.tsx +++ b/front/src/applications/editor/tools/selection/tool.tsx @@ -245,7 +245,7 @@ const SelectionTool: Tool = { selection, }); }, - onClickMap(e, { setState, state, infraID }) { + onClickMap(e, { setState, state, infraID, dispatch }) { const position = e.lngLat; const map = e.target; @@ -278,7 +278,8 @@ const SelectionTool: Tool = { }, ] : [] - ) + ), + dispatch ).then((entities) => { setState({ isLoading: false, @@ -334,7 +335,8 @@ const SelectionTool: Tool = { }, ] : [] - ) + ), + dispatch ).then((entities) => { setState({ isLoading: false, diff --git a/front/src/applications/editor/tools/switchEdition/components.tsx b/front/src/applications/editor/tools/switchEdition/components.tsx index e3547447582..bd7ac34344e 100644 --- a/front/src/applications/editor/tools/switchEdition/components.tsx +++ b/front/src/applications/editor/tools/switchEdition/components.tsx @@ -55,6 +55,7 @@ export const TrackSectionEndpointSelector: FC = ({ onChange, name, }) => { + const dispatch = useDispatch(); const { state, setState } = useContext( EditorContext ) as ExtendedEditorContextType; @@ -101,11 +102,14 @@ export const TrackSectionEndpointSelector: FC = ({ useEffect(() => { if (typeof formData?.track === 'string') { - getEntity(infraID as number, formData.track, 'TrackSection').then( - (track) => { - setTrackSection(track); - } - ); + getEntity( + infraID as number, + formData.track, + 'TrackSection', + dispatch + ).then((track) => { + setTrackSection(track); + }); } else { setTrackSection(null); } @@ -300,6 +304,7 @@ export const SwitchEditionLeftPanel: FC = () => { }; export const SwitchEditionLayers: FC = () => { + const dispatch = useDispatch(); const { t } = useTranslation(); const switchTypes = useSelector(getSwitchTypes); const infraID = useSelector(getInfraID); @@ -348,7 +353,7 @@ export const SwitchEditionLayers: FC = () => { (trackStatus.type === 'loaded' && trackStatus.trackSection.properties.id !== hoveredTrackId) ) { setTrackStatus({ type: 'loading', trackSectionID: hoveredTrackId }); - getEntity(infraID as number, hoveredTrackId, 'TrackSection') + getEntity(infraID as number, hoveredTrackId, 'TrackSection', dispatch) .then((trackSection) => { setTrackStatus({ type: 'loaded', trackSection }); }) @@ -410,25 +415,27 @@ export const SwitchEditionLayers: FC = () => { setGeometryState({ type: 'ready' }); } else { setGeometryState({ type: 'loading' }); - getEntity(infraID as number, port.track, 'TrackSection').then((track) => { - if (!track || !track.geometry.coordinates.length) setGeometryState({ type: 'ready' }); + getEntity(infraID as number, port.track, 'TrackSection', dispatch).then( + (track) => { + if (!track || !track.geometry.coordinates.length) setGeometryState({ type: 'ready' }); - const coordinates = - port.endpoint === 'BEGIN' - ? first(track.geometry.coordinates) - : last(track.geometry.coordinates); - setGeometryState({ - type: 'ready', - entity: { - ...(entity as SwitchEntity), - type: 'Feature', - geometry: { - type: 'Point', - coordinates: coordinates as [number, number], + const coordinates = + port.endpoint === 'BEGIN' + ? first(track.geometry.coordinates) + : last(track.geometry.coordinates); + setGeometryState({ + type: 'ready', + entity: { + ...(entity as SwitchEntity), + type: 'Feature', + geometry: { + type: 'Point', + coordinates: coordinates as [number, number], + }, }, - }, - }); - }); + }); + } + ); } }, [entity?.properties?.ports, infraID]); diff --git a/front/src/applications/editor/tools/trackEdition/components.tsx b/front/src/applications/editor/tools/trackEdition/components.tsx index 2b45e8da8c7..75bb4bba5e1 100644 --- a/front/src/applications/editor/tools/trackEdition/components.tsx +++ b/front/src/applications/editor/tools/trackEdition/components.tsx @@ -6,6 +6,7 @@ import { last } from 'lodash'; import { Position } from 'geojson'; import { BsBoxArrowInRight } from 'react-icons/bs'; +import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; import EditorContext from '../../context'; import GeoJSONs from '../../../../common/Map/Layers/GeoJSONs'; import colors from '../../../../common/Map/Consts/colors'; @@ -22,7 +23,7 @@ import { injectGeometry } from './utils'; import { NEW_ENTITY_ID } from '../../data/utils'; import { getMap } from '../../../../reducers/map/selectors'; import { getInfraID } from '../../../../reducers/osrdconf/selectors'; -import { getAttachedItems, getEntities } from '../../data/api'; +import { getEntities } from '../../data/api'; import { Spinner } from '../../../../common/Loader'; import EntitySumUp from '../../components/EntitySumUp'; import { getEditCatenaryState, getEditSpeedSectionState } from '../rangeEdition/utils'; @@ -43,6 +44,7 @@ export const AttachedRangesItemsList: FC<{ id: string; itemType: 'SpeedSection' id, itemType, }) => { + const dispatch = useDispatch(); const { t } = useTranslation(); const infraID = useSelector(getInfraID); const [itemsState, setItemsState] = useState< @@ -54,13 +56,17 @@ export const AttachedRangesItemsList: FC<{ id: string; itemType: 'SpeedSection' const { switchTool } = useContext(EditorContext) as ExtendedEditorContextType; const [showAll, setShowAll] = useState(false); + const [getAttachedItems] = + osrdEditoastApi.endpoints.getInfraByIdAttachedAndTrackId.useLazyQuery(); + useEffect(() => { - if (itemsState.type === 'idle') { + if (itemsState.type === 'idle' && infraID) { setItemsState({ type: 'loading' }); - getAttachedItems(`${infraID}`, id) + getAttachedItems({ id: infraID, trackId: id }) + .unwrap() .then((res: { [key: string]: string[] }) => { if (res[itemType]?.length) { - getEntities(`${infraID}`, res[itemType], itemType) + getEntities(infraID, res[itemType], itemType, dispatch) .then((entities) => { if (itemType === 'SpeedSection') { setItemsState({ diff --git a/front/src/common/Map/Search/MapSearch.tsx b/front/src/common/Map/Search/MapSearch.tsx index 9cec2b2cbf6..eb77e88178a 100644 --- a/front/src/common/Map/Search/MapSearch.tsx +++ b/front/src/common/Map/Search/MapSearch.tsx @@ -95,12 +95,6 @@ const MapSearch: FC = ({ map, closeMapSearchPopUp }) => { /> ), }, - /* For future implementation - { - label: t('map-search:signalbox'), - content: , - }, - */ ]} /> diff --git a/front/src/common/Map/Search/MapSearchSignalBox.tsx b/front/src/common/Map/Search/MapSearchSignalBox.tsx deleted file mode 100644 index f0cdc06e8ca..00000000000 --- a/front/src/common/Map/Search/MapSearchSignalBox.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React, { FC, useState, useEffect, useCallback } from 'react'; -import nextId from 'react-id-generator'; -import { useTranslation } from 'react-i18next'; - -import InputSNCF from 'common/BootstrapSNCF/InputSNCF'; -import { get } from 'common/requests'; -import { useDebounce } from 'utils/helpers'; - -const searchURI = '/gaia/osrd/signalbox/'; // '/matgaia/search_station'; -type SearchParams = { q?: string; linecode?: string }; -type SearchResult = { - results: Array<{ name: string; linecode: string; stationname: string }>; -}; -const MapSearchSignalBox: FC = () => { - const { t } = useTranslation(['translation', 'map-search']); - const [searchState, setSearch] = useState(''); - const [searchLineState, setSearchLine] = useState(''); - const [dontSearch, setDontSearch] = useState(false); - const [searchResults, setSearchResults] = useState(undefined); - - const debouncedSearchTerm = useDebounce(searchState, 300); - const debouncedSearchLine = useDebounce(searchLineState, 300); - - const updateSearch = useCallback(async (params: SearchParams) => { - const data = await get(searchURI, { params }); - setSearchResults(data); - }, []); - - const onResultClick = useCallback((result: { name: string }) => { - setDontSearch(true); - setSearch(result.name); - setSearchResults(undefined); - }, []); - - useEffect(() => { - if (!dontSearch && (debouncedSearchTerm || debouncedSearchLine)) { - const params: SearchParams = {}; - if (searchState !== '') { - params.q = searchState; - } - if (searchLineState !== '') { - params.linecode = searchLineState; - } - updateSearch(params); - } - }, [debouncedSearchTerm, debouncedSearchLine]); - - const formatSearchResults = () => { - const searchResultsContent = searchResults?.results.sort((a, b) => - a.name.localeCompare(b.name) - ); - return searchResultsContent?.map((result) => ( - - )); - }; - - return ( - <> -
- - { - setSearch(e.target.value); - setDontSearch(false); - }} - onClear={() => { - setSearch(''); - setSearchResults(undefined); - }} - value={searchState} - clearButton - noMargin - sm - /> - { - setSearchLine(e.target.value); - setDontSearch(false); - }} - onClear={() => { - setSearchLine(''); - }} - value={searchLineState} - clearButton - noMargin - sm - /> - -
-
- {searchResults !== undefined && searchResults.results !== undefined ? ( -
{formatSearchResults()}
- ) : ( -

{t('map-search:noresult')}

- )} -
- - ); -}; - -export default MapSearchSignalBox; diff --git a/front/src/common/Map/WarpedMap/SimulationWarpedMap.tsx b/front/src/common/Map/WarpedMap/SimulationWarpedMap.tsx index 8c8c6b1a569..c8e10ff300b 100644 --- a/front/src/common/Map/WarpedMap/SimulationWarpedMap.tsx +++ b/front/src/common/Map/WarpedMap/SimulationWarpedMap.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import React, { FC, useEffect, useMemo, useState } from 'react'; import { clamp, first, isEmpty, isNil, keyBy, last, mapValues, omitBy } from 'lodash'; import { PiLinkBold, PiLinkBreakBold } from 'react-icons/pi'; @@ -62,6 +62,7 @@ function transformDataStatePayload( * then mounts a WarpedMap with all that data: */ const SimulationWarpedMap: FC<{ collapsed?: boolean }> = ({ collapsed }) => { + const dispatch = useDispatch(); const infraID = useSelector(getInfraID); const [state, setState] = useState< | { type: 'idle' } @@ -256,7 +257,7 @@ const SimulationWarpedMap: FC<{ collapsed?: boolean }> = ({ collapsed }) => { useEffect(() => { if (state.type !== 'dataLoaded') return; - getImprovedOSRDData(infraID as number, state.osrd).then((betterFeatures) => { + getImprovedOSRDData(infraID as number, state.osrd, dispatch).then((betterFeatures) => { if (!isEmpty(betterFeatures)) { const betterTransformedFeatures = mapValues(betterFeatures, state.transform); const newTransformedOSRDData = mapValues(state.osrd, (collection: FeatureCollection) => ({ diff --git a/front/src/common/Map/WarpedMap/core/helpers.ts b/front/src/common/Map/WarpedMap/core/helpers.ts index 731eaeb312a..8d9843e1817 100644 --- a/front/src/common/Map/WarpedMap/core/helpers.ts +++ b/front/src/common/Map/WarpedMap/core/helpers.ts @@ -11,6 +11,7 @@ import { EditoastType, LAYER_TO_EDITOAST_DICT, LayerType } from 'applications/ed import { getMixedEntities } from 'applications/editor/data/api'; import { flattenEntity } from 'applications/editor/data/utils'; import vec, { Vec2 } from 'common/Map/WarpedMap/core/vec-lib'; +import { Dispatch } from 'redux'; /* * Useful types: @@ -127,8 +128,9 @@ export function getPointInTriangle( */ const OSRD_BATCH_SIZE = 500; export async function getImprovedOSRDData( - infra: number | string, - data: Partial> + infra: number, + data: Partial>, + dispatch: Dispatch ): Promise> { const queries = _(data) .flatMap((collection: FeatureCollection, layerType: LayerType) => { @@ -149,7 +151,7 @@ export async function getImprovedOSRDData( if (!queries.length) return {}; - return mapValues(await getMixedEntities(infra, queries), (e) => + return mapValues(await getMixedEntities(infra, queries, dispatch), (e) => flattenEntity({ ...e, properties: { diff --git a/front/src/common/api/osrdEditoastApi.ts b/front/src/common/api/osrdEditoastApi.ts index c936761082c..fe7c4f38812 100644 --- a/front/src/common/api/osrdEditoastApi.ts +++ b/front/src/common/api/osrdEditoastApi.ts @@ -829,11 +829,11 @@ export type PostInfraByIdObjectsAndObjectTypeApiArg = { }; export type PostInfraByIdPathfindingApiResponse = /** status 200 Paths, containing track ranges, detectors and switches with their directions. If no path is found, an empty list is returned. */ { - detectors?: string[]; - switches_directions?: { + detectors: string[]; + switches_directions: { [key: string]: string; }; - track_ranges?: DirectionalTrackRange[]; + track_ranges: DirectionalTrackRange[]; }[]; export type PostInfraByIdPathfindingApiArg = { /** Infra ID */ @@ -863,12 +863,12 @@ export type GetInfraByIdRoutesTrackRangesApiResponse = export type GetInfraByIdRoutesTrackRangesApiArg = { /** Infra ID */ id: number; - routes: string; + routes: string[]; }; export type GetInfraByIdRoutesAndWaypointTypeWaypointIdApiResponse = /** status 200 All routes that starting and ending by the given waypoint */ { - ending?: string[]; - starting?: string[]; + ending: string[]; + starting: string[]; }; export type GetInfraByIdRoutesAndWaypointTypeWaypointIdApiArg = { /** Infra ID */ diff --git a/front/src/common/requests.ts b/front/src/common/requests.ts index c98510dee43..148f29786d7 100644 --- a/front/src/common/requests.ts +++ b/front/src/common/requests.ts @@ -75,7 +75,7 @@ export async function get( let newPath = ''; // ULGY HACK https://gateway.dev.dgexsol.fr/osrd if (path.substr(0, 5) === '/gaia') { - newPath = `${mainConfig.proxy?.replace('/osrd', '')}${path}`; + newPath = `${mainConfig.proxy.replace('/osrd', '')}${path}`; } else { newPath = formatPath(path); } diff --git a/front/src/utils/error.ts b/front/src/utils/error.ts new file mode 100644 index 00000000000..783bc35946d --- /dev/null +++ b/front/src/utils/error.ts @@ -0,0 +1,6 @@ +import { SerializedError } from '@reduxjs/toolkit'; +import { ApiError } from 'common/api/emptyApi'; + +// eslint-disable-next-line import/prefer-default-export +export const extractMessageFromError = (error: ApiError | SerializedError) => + `${(error as ApiError)?.data?.message || (error as SerializedError)?.message}`;