diff --git a/front/public/locales/en/stdcm.json b/front/public/locales/en/stdcm.json index 611c2d6504b..9d56e8ae61f 100644 --- a/front/public/locales/en/stdcm.json +++ b/front/public/locales/en/stdcm.json @@ -7,6 +7,7 @@ }, "loaderImageLegend": "The TGV Nord line", "notificationTitle": "Phase 1: from D-7 to D-1 5pm, on the Perrigny-Miramas axis.", + "pathfindingFailed": "No paths have been found for these waypoints.", "pleaseWait": "Please wait…", "simulation":{ "averageRequestTime": "For your request, the time required is generally 90 seconds.", diff --git a/front/public/locales/fr/stdcm.json b/front/public/locales/fr/stdcm.json index 25fbdc5de73..5356fefdffd 100644 --- a/front/public/locales/fr/stdcm.json +++ b/front/public/locales/fr/stdcm.json @@ -7,6 +7,7 @@ }, "loaderImageLegend": "La ligne TGV Nord", "notificationTitle": "Phase 1 : de J-7 à J-1 17h, sur l’axe Perrigny—Miramas.", + "pathfindingFailed": "Aucun chemin n'a été trouvé pour ces points de jalonnement.", "pleaseWait": "Veuillez patientez…", "simulation":{ "averageRequestTime": "Pour votre demande, le temps nécessaire est généralement de 90 secondes.", diff --git a/front/src/applications/stdcmV2/components/StdcmConfig.tsx b/front/src/applications/stdcmV2/components/StdcmConfig.tsx index cc1e8e235de..f87fc85e19a 100644 --- a/front/src/applications/stdcmV2/components/StdcmConfig.tsx +++ b/front/src/applications/stdcmV2/components/StdcmConfig.tsx @@ -1,23 +1,52 @@ import React, { useRef, useEffect } from 'react'; import { Button } from '@osrd-project/ui-core'; +import cx from 'classnames'; import { useTranslation } from 'react-i18next'; +import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; import STDCM_REQUEST_STATUS from 'applications/stdcm/consts'; import useStdcm from 'applications/stdcm/hooks/useStdcm'; +import { useOsrdConfActions } from 'common/osrdContext'; +import usePathfindingV2 from 'modules/pathfinding/hook/usePathfinding'; import { Map } from 'modules/trainschedule/components/ManageTrainSchedule'; +import type { StdcmConfSliceActions } from 'reducers/osrdconf/stdcmConf'; +import { useAppDispatch } from 'store'; import StdcmConsist from './StdcmConsist'; import StdcmDestination from './StdcmDestination'; import StdcmLoader from './StdcmLoader'; import StdcmOrigin from './StdcmOrigin'; import StdcmVias from './StdcmVias'; +import type { StdcmSimulationResult } from '../views/StdcmViewV2'; + +const StdcmConfig = ({ + currentSimulationInputs, + pathProperties, + setPathProperties, + setCurrentSimulationInputs, +}: { + currentSimulationInputs: StdcmSimulationResult['input'] | undefined; + pathProperties?: ManageTrainSchedulePathProperties; + setPathProperties: (pathProperties?: ManageTrainSchedulePathProperties) => void; + setCurrentSimulationInputs: React.Dispatch< + React.SetStateAction + >; +}) => { + const { t } = useTranslation('stdcm'); + const loaderRef = useRef(null); -const StdcmConfig = () => { const { launchStdcmRequest, cancelStdcmRequest, currentStdcmRequestStatus } = useStdcm(); const isPending = currentStdcmRequestStatus === STDCM_REQUEST_STATUS.pending; - const loaderRef = useRef(null); - const { t } = useTranslation('stdcm'); + + const dispatch = useAppDispatch(); + const { updateGridMarginAfter, updateGridMarginBefore, updateStdcmStandardAllowance } = + useOsrdConfActions() as StdcmConfSliceActions; + + const { pathfindingState } = usePathfindingV2({ + pathProperties, + setPathProperties, + }); useEffect(() => { if (isPending) { @@ -25,23 +54,54 @@ const StdcmConfig = () => { } }, [isPending]); + useEffect(() => { + dispatch(updateGridMarginAfter(35)); + dispatch(updateGridMarginBefore(35)); + dispatch(updateStdcmStandardAllowance({ type: 'time_per_distance', value: 4.5 })); + }, []); + return (
- +
{/* //TODO: rename StdcmDefaultCard */} {/* } /> */} - - {/* } /> */} - - + + + {/* } /> */} -
-
{isPending && }
diff --git a/front/src/applications/stdcmV2/components/StdcmConsist.tsx b/front/src/applications/stdcmV2/components/StdcmConsist.tsx index 2f95d945e30..77aa4483e2c 100644 --- a/front/src/applications/stdcmV2/components/StdcmConsist.tsx +++ b/front/src/applications/stdcmV2/components/StdcmConsist.tsx @@ -16,6 +16,7 @@ import { useAppDispatch } from 'store'; import StdcmCard from './StdcmCard'; import StdcmSuggestions from './StdcmSuggestions'; +import type { StdcmSimulationResult } from '../views/StdcmViewV2'; interface StdcmSuggestionsConsistOption extends SelectOptionObject, @@ -37,7 +38,15 @@ const ConsistCardTitle = ({ ); }; -const StdcmConsist = ({ disabled = false }: { disabled?: boolean }) => { +const StdcmConsist = ({ + setCurrentSimulationInputs, + disabled = false, +}: { + setCurrentSimulationInputs: React.Dispatch< + React.SetStateAction + >; + disabled?: boolean; +}) => { const { t } = useTranslation('stdcm'); const { speedLimitByTag, speedLimitsByTags, dispatchUpdateSpeedLimitByTag } = useStoreDataForSpeedLimitByTagSelector(); @@ -88,6 +97,12 @@ const StdcmConsist = ({ disabled = false }: { disabled?: boolean }) => { } else { searchMateriel(''); } + setCurrentSimulationInputs((prevState) => ({ + ...prevState, + consist: { + tractionEngine: rollingStock, + }, + })); }, [rollingStock]); return ( diff --git a/front/src/applications/stdcmV2/components/StdcmDestination.tsx b/front/src/applications/stdcmV2/components/StdcmDestination.tsx index bd363b59eee..943e782f8f2 100644 --- a/front/src/applications/stdcmV2/components/StdcmDestination.tsx +++ b/front/src/applications/stdcmV2/components/StdcmDestination.tsx @@ -11,8 +11,17 @@ import { useAppDispatch } from 'store'; import StdcmCard from './StdcmCard'; import StdcmOperationalPoint from './StdcmOperationalPoint'; +import type { StdcmSimulationResult } from '../views/StdcmViewV2'; -const StdcmDestination = ({ disabled = false }: { disabled?: boolean }) => { +const StdcmDestination = ({ + setCurrentSimulationInputs, + disabled = false, +}: { + setCurrentSimulationInputs: React.Dispatch< + React.SetStateAction + >; + disabled?: boolean; +}) => { const { t } = useTranslation('stdcm'); const dispatch = useAppDispatch(); const { getDestinationV2 } = useOsrdConfSelectors(); @@ -21,6 +30,10 @@ const StdcmDestination = ({ disabled = false }: { disabled?: boolean }) => { const updateDestinationV2Point = (pathStep: PathStep | null) => { dispatch(updateDestinationV2(pathStep)); + setCurrentSimulationInputs((prevState) => ({ + ...prevState, + destination: pathStep, + })); }; return ( diff --git a/front/src/applications/stdcmV2/components/StdcmOrigin.tsx b/front/src/applications/stdcmV2/components/StdcmOrigin.tsx index 83d4d17f9ca..5add48db1d2 100644 --- a/front/src/applications/stdcmV2/components/StdcmOrigin.tsx +++ b/front/src/applications/stdcmV2/components/StdcmOrigin.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { MdPinDrop } from 'react-icons/md'; @@ -12,8 +12,17 @@ import { useAppDispatch } from 'store'; import StdcmCard from './StdcmCard'; import StdcmOperationalPoint from './StdcmOperationalPoint'; +import type { StdcmSimulationResult } from '../views/StdcmViewV2'; -const StdcmOrigin = ({ disabled = false }: { disabled?: boolean }) => { +const StdcmOrigin = ({ + setCurrentSimulationInputs, + disabled = false, +}: { + setCurrentSimulationInputs: React.Dispatch< + React.SetStateAction + >; + disabled?: boolean; +}) => { const { t } = useTranslation('stdcm'); const dispatch = useAppDispatch(); const { getOriginV2, getOriginDate, getOriginTime } = useOsrdConfSelectors(); @@ -27,6 +36,15 @@ const StdcmOrigin = ({ disabled = false }: { disabled?: boolean }) => { dispatch(updateOriginV2(pathStep)); }; + useEffect(() => { + setCurrentSimulationInputs((prevState) => ({ + ...prevState, + origin, + departureDate: originDate, + departureTime: originTime, + })); + }, [origin, originDate, originTime]); + return ( { +const StdcmVias = ({ + setCurrentSimulationInputs, + disabled = false, +}: { + setCurrentSimulationInputs: React.Dispatch< + React.SetStateAction + >; + disabled?: boolean; +}) => { const { t } = useTranslation('stdcm'); const dispatch = useAppDispatch(); const { getPathSteps } = useOsrdConfSelectors(); const { updatePathSteps, updateViaStopTimeV2 } = useOsrdConfActions() as StdcmConfSliceActions; const pathSteps = useSelector(getPathSteps); - const intermediatePoints = useMemo( - () => pathSteps.filter((_, index) => index !== 0 && index !== pathSteps.length - 1), - [pathSteps] - ); + const intermediatePoints = useMemo(() => pathSteps.slice(1, -1), [pathSteps]); const updatePathStepsList = (pathStep: PathStep | null, index: number) => { const newPathSteps = replaceElementAtIndex(pathSteps, index, pathStep); @@ -36,11 +42,10 @@ const StdcmVias = ({ disabled = false }: { disabled?: boolean }) => { const updatePathStepStopTime = (stopTime: string, index: number) => { const pathStepToUpdate = pathSteps[index]; if (!pathStepToUpdate) return; - dispatch( updateViaStopTimeV2({ via: pathStepToUpdate, - duration: formatDurationAsISO8601(Number(stopTime || '0') * 60), + duration: formatDurationAsISO8601(Number(stopTime) * 60), }) ); }; @@ -49,49 +54,56 @@ const StdcmVias = ({ disabled = false }: { disabled?: boolean }) => { dispatch(updatePathSteps(pathSteps.filter((_, i) => i !== index))); }; + useEffect(() => { + setCurrentSimulationInputs((prevState) => ({ + ...prevState, + pathSteps, + })); + }, [pathSteps]); + return (
{intermediatePoints.length > 0 && - intermediatePoints.map((pathStep, index) => ( - - {' '} - deletePathStep(index + 1)} />{' '} -
- } // TODO: Remove the clear button -> replace it by the "numbered point" icon - hasTip - disabled={disabled} - > -
- updatePathStepsList(e, index + 1)} - point={pathStep} - disabled={disabled} - /> - {/* TODO: enable this select when the feature is implemented in the backend */} - {/*
- -
*/} -
- {pathStep && ( -
- updatePathStepStopTime(e.target.value, index + 1)} - value={pathStep.stopFor ? `${ISO8601Duration2sec(pathStep.stopFor) / 60}` : ''} - trailingContent="minutes" + intermediatePoints.map((pathStep, index) => { + const pathStepId = index + 1; + return ( + + + deletePathStep(pathStepId)} /> +
+ } // TODO: Remove icon and clear button -> replace them by the "numbered point" icon + hasTip + disabled={disabled} + > +
+ updatePathStepsList(e, pathStepId)} + point={pathStep} + disabled={disabled} />
- )} -
- ))} + {pathStep && ( +
+ { + const value = e.target.value.replace(/[\D.,]/g, ''); + updatePathStepStopTime(value, pathStepId); + }} + value={pathStep.stopFor ? `${ISO8601Duration2sec(pathStep.stopFor) / 60}` : ''} + trailingContent="minutes" + /> +
+ )} + + ); + })}
diff --git a/front/src/applications/stdcmV2/views/StdcmViewV2.tsx b/front/src/applications/stdcmV2/views/StdcmViewV2.tsx index 5db443b8484..916fe933a80 100644 --- a/front/src/applications/stdcmV2/views/StdcmViewV2.tsx +++ b/front/src/applications/stdcmV2/views/StdcmViewV2.tsx @@ -1,35 +1,57 @@ -import React, { useEffect } from 'react'; +import React, { useState } from 'react'; // import { Location, ArrowUp, ArrowDown } from '@osrd-project/ui-icons'; import { useSelector } from 'react-redux'; -import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; -import type { StdcmConfSliceActions } from 'reducers/osrdconf/stdcmConf'; -import { useAppDispatch } from 'store'; +import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; +import type { + PostV2TimetableByIdStdcmApiResponse, + RollingStockWithLiveries, +} from 'common/api/osrdEditoastApi'; +import { useOsrdConfSelectors } from 'common/osrdContext'; +import type { PathStep } from 'reducers/osrdconf/types'; // import StdcmDefaultCard from '../components/StdcmDefaultCard'; import StdcmConfig from '../components/StdcmConfig'; import StdcmHeader from '../components/StdcmHeader'; +export type StdcmSimulationResult = { + createdAt: string; + input: { + origin?: PathStep | null; + departureDate?: string; + departureTime?: string; + destination?: PathStep | null; + pathSteps?: (PathStep | null)[]; + consist?: { + tractionEngine: RollingStockWithLiveries | undefined; + }; + }; + output: PostV2TimetableByIdStdcmApiResponse; +}; + const StdcmViewV2 = () => { const { getScenarioID } = useOsrdConfSelectors(); const scenarioID = useSelector(getScenarioID); - const dispatch = useAppDispatch(); - const { updateGridMarginAfter, updateGridMarginBefore, updateStdcmStandardAllowance } = - useOsrdConfActions() as StdcmConfSliceActions; + const [pathProperties, setPathProperties] = useState(); - useEffect(() => { - dispatch(updateGridMarginAfter(35)); - dispatch(updateGridMarginBefore(35)); - dispatch(updateStdcmStandardAllowance({ type: 'time_per_distance', value: 4.5 })); - }, []); + const [currentSimulationInputs, setCurrentSimulationInputs] = useState< + StdcmSimulationResult['input'] | undefined + >(undefined); return (
- {scenarioID && } + {scenarioID && ( + + )}
); }; diff --git a/front/src/modules/pathfinding/components/Pathfinding/PathfindingV2.tsx b/front/src/modules/pathfinding/components/Pathfinding/PathfindingV2.tsx index c20802ac6cc..9164c9f4e6b 100644 --- a/front/src/modules/pathfinding/components/Pathfinding/PathfindingV2.tsx +++ b/front/src/modules/pathfinding/components/Pathfinding/PathfindingV2.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import { Alert, CheckCircle, Stop } from '@osrd-project/ui-icons'; import cx from 'classnames'; @@ -9,7 +9,6 @@ import { useSelector } from 'react-redux'; import InfraLoadingState from 'applications/operationalStudies/components/Scenario/InfraLoadingState'; import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; import infraLogo from 'assets/pictures/components/tracks.svg'; -import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; import { Spinner } from 'common/Loaders'; import { useOsrdConfSelectors } from 'common/osrdContext'; import usePathfindingV2 from 'modules/pathfinding/hook/usePathfinding'; @@ -26,106 +25,19 @@ type PathfindingProps = { const Pathfinding = ({ pathProperties, setPathProperties }: PathfindingProps) => { const { t } = useTranslation(['operationalStudies/manageTrainSchedule']); - const { getInfraID, getOriginV2, getDestinationV2 } = useOsrdConfSelectors(); - const infraId = useSelector(getInfraID, isEqual); + const { getOriginV2, getDestinationV2 } = useOsrdConfSelectors(); const origin = useSelector(getOriginV2, isEqual); const destination = useSelector(getDestinationV2, isEqual); const { rollingStock } = useStoreDataForRollingStockSelector(); - const [isInfraLoaded, setIsInfraLoaded] = useState(false); - const [reloadCount, setReloadCount] = useState(1); - const [isInfraError, setIsInfraError] = useState(false); - - const { data: infra } = osrdEditoastApi.endpoints.getInfraByInfraId.useQuery( - { infraId: infraId as number }, - { - refetchOnMountOrArgChange: true, - pollingInterval: !isInfraLoaded ? 1000 : undefined, - } - ); - - const { isPathfindingInitialized, pathfindingState, pathfindingDispatch } = usePathfindingV2({ + const { + pathfindingState, + infraInfos: { infra, reloadCount }, + } = usePathfindingV2({ pathProperties, setPathProperties, - infra, }); - const [reloadInfra] = osrdEditoastApi.endpoints.postInfraByInfraIdLoad.useMutation(); - - useEffect(() => { - if (reloadCount <= 5 && infra && infra.state === 'TRANSIENT_ERROR') { - setTimeout(() => { - reloadInfra({ infraId: infraId as number }).unwrap(); - setReloadCount((count) => count + 1); - }, 1000); - } - }, [infra, reloadCount]); - - useEffect(() => { - if (infra) { - switch (infra.state) { - case 'NOT_LOADED': { - reloadInfra({ infraId: infraId as number }).unwrap(); - setIsInfraLoaded(false); - break; - } - case 'ERROR': - case 'TRANSIENT_ERROR': { - setIsInfraLoaded(true); - break; - } - case 'CACHED': { - setIsInfraLoaded(true); - if (isInfraError) setIsInfraError(false); - if (infra.state !== 'CACHED') { - // remplace " !pathfindingAlReadyInitialized " --> const pathfindingAlReadyInitialized = useMemo(() => infra?.state === 'CACHED', []); - //* Ici infra.state ne devrait jamais être autre chose que "CACHED" - pathfindingDispatch({ - type: 'INFRA_CHANGED', - params: { - origin, - destination, - rollingStock, - }, - }); - } - break; - } - default: - break; - } - } - }, [infra]); - - useEffect(() => { - if (isInfraError) { - reloadInfra({ infraId: infraId as number }).unwrap(); - setIsInfraLoaded(false); - } - }, [isInfraError]); - - useEffect(() => { - if ( - pathfindingState.error === 'Infra not loaded' || - pathfindingState.error === 'Invalid version' - ) { - setIsInfraError(true); - } - }, [pathfindingState]); - - useEffect(() => { - if (isPathfindingInitialized) { - pathfindingDispatch({ - type: 'PATHFINDING_PARAM_CHANGED', - params: { - origin, - destination, - rollingStock, - }, - }); - } - }, [origin, destination, rollingStock]); - const missingElements = conditionalStringConcat([ [!origin, t('origin')], [!destination, t('destination')], diff --git a/front/src/modules/pathfinding/hook/useInfra.tsx b/front/src/modules/pathfinding/hook/useInfra.tsx new file mode 100644 index 00000000000..f503eac60be --- /dev/null +++ b/front/src/modules/pathfinding/hook/useInfra.tsx @@ -0,0 +1,72 @@ +import { useState, useEffect } from 'react'; + +import { isEqual } from 'lodash'; +import { useSelector } from 'react-redux'; + +import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; +import { useOsrdConfSelectors } from 'common/osrdContext'; + +export default function useInfra() { + const { getInfraID } = useOsrdConfSelectors(); + const infraId = useSelector(getInfraID, isEqual); + + const [reloadInfra] = osrdEditoastApi.endpoints.postInfraByInfraIdLoad.useMutation(); + + const [isInfraLoaded, setIsInfraLoaded] = useState(false); + const [reloadCount, setReloadCount] = useState(1); + const [isInfraError, setIsInfraError] = useState(false); + + const { data: infra } = osrdEditoastApi.endpoints.getInfraByInfraId.useQuery( + { infraId: infraId as number }, + { + refetchOnMountOrArgChange: true, + pollingInterval: !isInfraLoaded ? 1000 : undefined, + } + ); + + useEffect(() => { + if (reloadCount <= 5 && infra && infra.state === 'TRANSIENT_ERROR') { + setTimeout(() => { + reloadInfra({ infraId: infraId as number }).unwrap(); + setReloadCount((count) => count + 1); + }, 1000); + } + }, [infra, reloadCount]); + + useEffect(() => { + if (infra) { + switch (infra.state) { + case 'NOT_LOADED': { + reloadInfra({ infraId: infraId as number }).unwrap(); + setIsInfraLoaded(false); + break; + } + case 'ERROR': + case 'TRANSIENT_ERROR': { + setIsInfraLoaded(true); + break; + } + case 'CACHED': { + setIsInfraLoaded(true); + if (isInfraError) setIsInfraError(false); + break; + } + default: + break; + } + } + }, [infra]); + + useEffect(() => { + if (isInfraError) { + reloadInfra({ infraId: infraId as number }).unwrap(); + setIsInfraLoaded(false); + } + }, [isInfraError]); + + return { + reloadCount, + setIsInfraError, + infra, + }; +} diff --git a/front/src/modules/pathfinding/hook/usePathfinding.tsx b/front/src/modules/pathfinding/hook/usePathfinding.tsx index 43419f2100e..14ae9685eff 100644 --- a/front/src/modules/pathfinding/hook/usePathfinding.tsx +++ b/front/src/modules/pathfinding/hook/usePathfinding.tsx @@ -5,9 +5,7 @@ import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; -import { enhancedEditoastApi } from 'common/api/enhancedEditoastApi'; import type { - InfraWithState, PostV2InfraByInfraIdPathPropertiesApiArg, PostV2InfraByInfraIdPathfindingBlocksApiArg, RollingStockWithLiveries, @@ -28,6 +26,8 @@ import type { PathStep } from 'reducers/osrdconf/types'; import { useAppDispatch } from 'store'; import { castErrorToFailure } from 'utils/error'; +import useInfra from './useInfra'; + export function reducer(state: PathfindingState, action: PathfindingActionV2): PathfindingState { switch (action.type) { case 'PATHFINDING_STARTED': { @@ -134,16 +134,11 @@ function init({ } type PathfindingProps = { - infra: InfraWithState | undefined; pathProperties?: ManageTrainSchedulePathProperties; setPathProperties: (pathProperties?: ManageTrainSchedulePathProperties) => void; }; -export default function usePathfindingV2({ - infra, - pathProperties, - setPathProperties, -}: PathfindingProps) { +export default function usePathfindingV2({ pathProperties, setPathProperties }: PathfindingProps) { const { t } = useTranslation(['operationalStudies/manageTrainSchedule']); const dispatch = useAppDispatch(); const { @@ -160,6 +155,7 @@ export default function usePathfindingV2({ const destination = useSelector(getDestinationV2, isEqual); const vias = useSelector(getViasV2(), isEqual); const pathSteps = useSelector(getPathSteps); + const { infra, reloadCount, setIsInfraError } = useInfra(); const { rollingStock } = useStoreDataForRollingStockSelector(); // TODO TS2 : update this parts in margins and powerrestriction issues // const powerRestrictions = useSelector(getPowerRestrictionRanges, isEqual); @@ -178,9 +174,7 @@ export default function usePathfindingV2({ const [postPathfindingBlocks] = osrdEditoastApi.endpoints.postV2InfraByInfraIdPathfindingBlocks.useMutation(); const [postPathProperties] = - enhancedEditoastApi.endpoints.postV2InfraByInfraIdPathProperties.useMutation(); - - // const pathfindingAlReadyInitialized = useMemo(() => infra?.state === 'CACHED', []); // !! utilité du useMemo ? + osrdEditoastApi.endpoints.postV2InfraByInfraIdPathProperties.useMutation(); const { updatePathSteps, @@ -206,6 +200,19 @@ export default function usePathfindingV2({ } }, [vias]); + useEffect(() => { + if (isPathfindingInitialized) { + pathfindingDispatch({ + type: 'PATHFINDING_PARAM_CHANGED', + params: { + origin, + destination, + rollingStock, + }, + }); + } + }, [origin, destination, rollingStock]); + useEffect(() => { const startPathFinding = async () => { if (!pathfindingState.running) { @@ -294,6 +301,9 @@ export default function usePathfindingV2({ pathfindingDispatch({ type: 'PATHFINDING_ERROR', message: 'failedRequest' }); } else if ('data' in e && isObject(e.data) && 'message' in e.data) { pathfindingDispatch({ type: 'PATHFINDING_ERROR', message: e.data.message as string }); + if (e.data.message === 'Infra not loaded' || e.data.message === 'Invalid version') { + setIsInfraError(true); + } } } } @@ -310,5 +320,9 @@ export default function usePathfindingV2({ isPathfindingInitialized, pathfindingState, pathfindingDispatch, + infraInfos: { + infra, + reloadCount, + }, }; } diff --git a/front/src/reducers/osrdconf/osrdConfCommon/index.ts b/front/src/reducers/osrdconf/osrdConfCommon/index.ts index 82874e84d27..8fc6bc21203 100644 --- a/front/src/reducers/osrdconf/osrdConfCommon/index.ts +++ b/front/src/reducers/osrdconf/osrdConfCommon/index.ts @@ -272,6 +272,8 @@ export function buildCommonConfReducers(): CommonConfRe return { payload: newVias }; }, }, + // TODO: Change the type of duration to number. It is preferable to keep this value in seconds in the store + //* to avoid multiple conversions between seconds and ISO8601 format across the front. updateViaStopTimeV2( state: Draft, action: PayloadAction<{ via: PathStep; duration: string }> diff --git a/front/src/styles/scss/applications/stdcmV2/_card.scss b/front/src/styles/scss/applications/stdcmV2/_card.scss index 385f6144e8d..acead131338 100644 --- a/front/src/styles/scss/applications/stdcmV2/_card.scss +++ b/front/src/styles/scss/applications/stdcmV2/_card.scss @@ -5,6 +5,11 @@ height: fit-content; position: relative; + .stdcm-v2-via-icon{ + padding-left: 0.125rem; + color: #1844EF; + } + &.has-tip::after { content: ""; position: absolute; @@ -46,7 +51,7 @@ .suggestions.stdcm-v2-ch-selector { label { - margin-bottom: 0.35rem; + margin: 0.2rem 0 0.15rem 0; } select { min-height: 2.55rem; diff --git a/front/src/styles/scss/applications/stdcmV2/_home.scss b/front/src/styles/scss/applications/stdcmV2/_home.scss index 45dd9d09f9a..ac48c50fa2d 100644 --- a/front/src/styles/scss/applications/stdcmV2/_home.scss +++ b/front/src/styles/scss/applications/stdcmV2/_home.scss @@ -27,14 +27,39 @@ } /*TODO Waiting to fix the button in ui-core...*/ - .stdcm-v2-launch-request > button { - justify-content: center; - width: 100%; + .stdcm-v2-launch-request{ + padding-bottom: 0.15rem; + button { + justify-content: center; + width: 100%; + font-weight: 500; + } + } + + .wizz-on-hover { + color: #f6931b; font-weight: 500; } + + .wizz-on-hover > button:active { + animation: tilt-shaking 0.25s 0s; + } + + @keyframes tilt-shaking { + 0% { transform: rotate(0deg); } + 12% { transform: rotate(1deg); } + 24% { transform: rotate(0eg); } + 36% { transform: rotate(-1deg); } + 47% { transform: rotate(0deg); } + 59% { transform: rotate(1deg); } + 70% { transform: rotate(0eg); } + 85% { transform: rotate(-1deg); } + 100% { transform: rotate(0deg); } + } + } } - + .stdcm-v2-map { border-radius: 8px; border: 1px solid rgba(255, 255, 255, 1);