diff --git a/front/public/locales/en/stdcm-simulation-report-sheet.json b/front/public/locales/en/stdcm-simulation-report-sheet.json index d8f2c8e3338..700b7163fc6 100644 --- a/front/public/locales/en/stdcm-simulation-report-sheet.json +++ b/front/public/locales/en/stdcm-simulation-report-sheet.json @@ -5,10 +5,14 @@ "conventionalSign": "conv. sign", "convoy": "convoy", "crossedATE": "crossed ATE", + "displayAll": "Display all operational points", + "displayMain": "Display main operational points", + "downloadSimulationSheet": "Download simulation report sheet", "endStop": "end", "for": "for", "formattedDate": "{{year}}/{{month}}/{{day}} at {{hours}}:{{minutes}}", "from": "from", + "gesicoRequest":"and attach this document to your GESICO DSDM request.", "maxLength": "max. length", "maxSpeed": "max. speed", "maxWeight": "max. weight", @@ -21,8 +25,10 @@ "requestedRoute": "requested route", "scheduledArrival": "scheduled arrival at", "scheduledDeparture": "scheduled departure at", + "selectThisSimulation": "Select this simulation", "serviceStop": "Service stop", "simulation": "Simulation", + "simulationSelected": "You have selected this simulation", "speedLimitByTag": "speed limit by tag", "startStop": "start", "stdcm": "ST DCM", diff --git a/front/public/locales/en/stdcm.json b/front/public/locales/en/stdcm.json index 6f32cc4df34..bbec41b837d 100644 --- a/front/public/locales/en/stdcm.json +++ b/front/public/locales/en/stdcm.json @@ -5,11 +5,13 @@ "consist": "Consist", "tractionEngine": "Traction engine" }, + "formattedCreationDate": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}", "loaderImageLegend": "The TGV Nord line", "notificationTitle": "Phase 1: from D-7 to D-1 5pm, on the Perrigny-Miramas axis.", "pathfindingFailed": "No path have been found for these waypoints.", "pleaseWait": "Please wait…", "simulation":{ + "available": "Simulation available", "averageRequestTime": "For your request, the time required is generally 90 seconds.", "calculatingSimulation": "Calculation in progress...", "getSimulation": "Get the simulation", diff --git a/front/public/locales/fr/stdcm-simulation-report-sheet.json b/front/public/locales/fr/stdcm-simulation-report-sheet.json index 619b7e240be..5e71c28ec3e 100644 --- a/front/public/locales/fr/stdcm-simulation-report-sheet.json +++ b/front/public/locales/fr/stdcm-simulation-report-sheet.json @@ -5,10 +5,14 @@ "conventionalSign": "signe conv.", "convoy": "convoi", "crossedATE": "ATE croisé", + "displayAll": "Afficher tous les jalons", + "displayMain": "Afficher les jalons principaux", + "downloadSimulationSheet": "Télécharger la fiche de simulation", "endStop": "arrivée", "for": "pour", "formattedDate": "le {{day}}/{{month}}/{{year}} à {{hours}}:{{minutes}}", "from": "du", + "gesicoRequest":"et joignez ce document à votre demande GESICO DSDM", "maxLength": "longueur max.", "maxSpeed": "vitesse max.", "maxWeight": "tonnage max.", @@ -21,8 +25,10 @@ "requestedRoute": "parcours demandé", "scheduledArrival": "arrivée prévue à", "scheduledDeparture": "départ prévu à", + "selectThisSimulation": "Retenir cette simulation", "serviceStop": "Arrêt de service", "simulation": "Simulation", + "simulationSelected": "Vous avez retenu cette simulation", "speedLimitByTag": "code de composition", "startStop":"départ", "stdcm": "ST DCM", diff --git a/front/public/locales/fr/stdcm.json b/front/public/locales/fr/stdcm.json index 5356fefdffd..641bb079818 100644 --- a/front/public/locales/fr/stdcm.json +++ b/front/public/locales/fr/stdcm.json @@ -5,11 +5,13 @@ "consist": "Convoi", "tractionEngine": "Engin de traction" }, + "formattedCreationDate": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}", "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":{ + "available": "Simulation disponible", "averageRequestTime": "Pour votre demande, le temps nécessaire est généralement de 90 secondes.", "calculatingSimulation": "Calcul en cours...", "getSimulation": "Obtenir la simulation", diff --git a/front/src/applications/stdcmV2/components/StdcmConfig.tsx b/front/src/applications/stdcmV2/components/StdcmConfig.tsx index 206f61c5c12..38f388ef224 100644 --- a/front/src/applications/stdcmV2/components/StdcmConfig.tsx +++ b/front/src/applications/stdcmV2/components/StdcmConfig.tsx @@ -8,6 +8,7 @@ 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 type { StdcmV2SuccessResponse } from 'applications/stdcm/types'; import { useOsrdConfActions } from 'common/osrdContext'; import { usePathfindingV2 } from 'modules/pathfinding/hook/usePathfinding'; import { Map } from 'modules/trainschedule/components/ManageTrainSchedule'; @@ -28,6 +29,8 @@ type StdcmConfigProps = { setCurrentSimulationInputs: React.Dispatch< React.SetStateAction >; + setStdcmSimulationResults: (stdcmSimulationResults: StdcmV2SuccessResponse | undefined) => void; + setSimulationCreationDate: (simulationCreationDate: Date) => void; }; const StdcmConfig = ({ @@ -35,12 +38,16 @@ const StdcmConfig = ({ pathProperties, setPathProperties, setCurrentSimulationInputs, + setStdcmSimulationResults, + setSimulationCreationDate, }: StdcmConfigProps) => { const { t } = useTranslation('stdcm'); const loaderRef = useRef(null); - const { launchStdcmRequest, cancelStdcmRequest, currentStdcmRequestStatus } = useStdcm(); + const { launchStdcmRequest, cancelStdcmRequest, currentStdcmRequestStatus, stdcmV2Results } = + useStdcm(); const isPending = currentStdcmRequestStatus === STDCM_REQUEST_STATUS.pending; + const stdcmResponse = stdcmV2Results?.stdcmResponse; const dispatch = useAppDispatch(); const { updateGridMarginAfter, updateGridMarginBefore, updateStdcmStandardAllowance } = @@ -48,6 +55,15 @@ const StdcmConfig = ({ const { pathfindingState } = usePathfindingV2(setPathProperties, pathProperties); + const clickOnSimulation = () => { + if (pathfindingState.done) { + const currentDateTime = new Date(); + launchStdcmRequest(); + setSimulationCreationDate(currentDateTime); + setCurrentSimulationInputs(undefined); + } + }; + useEffect(() => { if (isPending) { loaderRef?.current?.scrollIntoView({ behavior: 'smooth' }); @@ -61,6 +77,10 @@ const StdcmConfig = ({ dispatch(updateStdcmStandardAllowance({ type: 'time_per_distance', value: 4.5 })); }, []); + useEffect(() => { + setStdcmSimulationResults(stdcmResponse); + }, [stdcmResponse]); + return (
@@ -92,15 +112,7 @@ const StdcmConfig = ({ })} > {currentSimulationInputs && ( -
+
+ {!isSimulationSelected ? ( +
+
+ {isSimulationSelected && ( +
+
+ + } + fileName={`STDCM-${simulationReportSheetNumber}.pdf`} + > +
+
{t('gesicoRequest')}
+
+ )} + + ); +}; + +export default StcdmTableResults; diff --git a/front/src/applications/stdcmV2/components/StdcmVias.tsx b/front/src/applications/stdcmV2/components/StdcmVias.tsx index 9b619479b23..4dad1c84af4 100644 --- a/front/src/applications/stdcmV2/components/StdcmVias.tsx +++ b/front/src/applications/stdcmV2/components/StdcmVias.tsx @@ -13,23 +13,17 @@ import { addElementAtIndex, replaceElementAtIndex } from 'utils/array'; import { formatDurationAsISO8601 } from 'utils/timeManipulation'; import StdcmCard from './StdcmCard'; +import StdcmDefaultCard from './StdcmDefaultCard'; import StdcmInputVia from './StdcmInputVia'; import StdcmOperationalPoint from './StdcmOperationalPoint'; -import type { StdcmSimulationResult } from '../views/StdcmViewV2'; +import type { StdcmConfigCardProps } from '../types'; -const StdcmVias = ({ - disabled = false, - setCurrentSimulationInputs, -}: { - disabled?: boolean; - setCurrentSimulationInputs: React.Dispatch< - React.SetStateAction - >; -}) => { +const StdcmVias = ({ disabled = false, setCurrentSimulationInputs }: StdcmConfigCardProps) => { const { t } = useTranslation('stdcm'); const dispatch = useAppDispatch(); const { getPathSteps } = useOsrdConfSelectors(); - const { updatePathSteps, updateViaStopTimeV2 } = useOsrdConfActions() as StdcmConfSliceActions; + const { deleteViaV2, updatePathSteps, updateViaStopTimeV2 } = + useOsrdConfActions() as StdcmConfSliceActions; const pathSteps = useSelector(getPathSteps); const intermediatePoints = useMemo(() => pathSteps.slice(1, -1), [pathSteps]); @@ -50,10 +44,6 @@ const StdcmVias = ({ ); }; - const deletePathStep = (index: number) => { - dispatch(updatePathSteps(pathSteps.filter((_, i) => i !== index))); - }; - useEffect(() => { setCurrentSimulationInputs((prevState) => ({ ...prevState, @@ -75,11 +65,11 @@ const StdcmVias = ({ intermediate-point - - } // TODO: Remove icon and clear button -> replace them by the "numbered point" icon + } hasTip disabled={disabled} > @@ -99,19 +89,15 @@ const StdcmVias = ({ ); })} - - - + } + onClick={() => { + const newPathSteps = addElementAtIndex(pathSteps, pathSteps.length - 1, null); + dispatch(updatePathSteps(newPathSteps)); + }} + /> ); }; diff --git a/front/src/applications/stdcmV2/views/StdcmViewV2.tsx b/front/src/applications/stdcmV2/views/StdcmViewV2.tsx index fa9bc840951..54080312070 100644 --- a/front/src/applications/stdcmV2/views/StdcmViewV2.tsx +++ b/front/src/applications/stdcmV2/views/StdcmViewV2.tsx @@ -3,20 +3,30 @@ import React, { useState } from 'react'; import { useSelector } from 'react-redux'; import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; -import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; -import type { StdcmConfSliceActions } from 'reducers/osrdconf/stdcmConf'; -import { useAppDispatch } from 'store'; +import type { StdcmV2SuccessResponse } from 'applications/stdcm/types'; +import { useOsrdConfSelectors } from 'common/osrdContext'; +import { useStoreDataForSpeedLimitByTagSelector } from 'common/SpeedLimitByTagSelector/useStoreDataForSpeedLimitByTagSelector'; +import { useStoreDataForRollingStockSelector } from 'modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector'; +import { getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import StdcmConfig from '../components/StdcmConfig'; import StdcmHeader from '../components/StdcmHeader'; +import StdcmResults from '../components/StdcmResults'; import type { StdcmSimulationResult } from '../types'; const StdcmViewV2 = () => { const { getScenarioID } = useOsrdConfSelectors(); const scenarioID = useSelector(getScenarioID); + const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); + const { speedLimitByTag } = useStoreDataForSpeedLimitByTagSelector(); + const { rollingStock } = useStoreDataForRollingStockSelector(); + const [pathProperties, setPathProperties] = useState(); + const [stdcmSimulationResults, setStdcmSimulationResults] = useState(); + const [simulationCreationDate, setSimulationCreationDate] = useState(); + const [currentSimulationInputs, setCurrentSimulationInputs] = useState< StdcmSimulationResult['input'] | undefined >(undefined); @@ -30,6 +40,17 @@ const StdcmViewV2 = () => { pathProperties={pathProperties} setPathProperties={setPathProperties} setCurrentSimulationInputs={setCurrentSimulationInputs} + setStdcmSimulationResults={setStdcmSimulationResults} + setSimulationCreationDate={setSimulationCreationDate} + /> + )} + {trainScheduleV2Activated && rollingStock && stdcmSimulationResults && ( + )} diff --git a/front/src/modules/pathfinding/components/Pathfinding/PathfindingV2.tsx b/front/src/modules/pathfinding/components/Pathfinding/PathfindingV2.tsx index e7acfd54215..52bd3457e6b 100644 --- a/front/src/modules/pathfinding/components/Pathfinding/PathfindingV2.tsx +++ b/front/src/modules/pathfinding/components/Pathfinding/PathfindingV2.tsx @@ -33,10 +33,7 @@ const Pathfinding = ({ pathProperties, setPathProperties }: PathfindingProps) => const { pathfindingState, infraInfos: { infra, reloadCount }, - } = usePathfindingV2({ - pathProperties, - setPathProperties, - }); + } = usePathfindingV2(setPathProperties, pathProperties); const missingElements = conditionalStringConcat([ [!origin, t('origin')], diff --git a/front/src/modules/pathfinding/hook/useInfra.ts b/front/src/modules/pathfinding/hook/useInfra.ts deleted file mode 100644 index f503eac60be..00000000000 --- a/front/src/modules/pathfinding/hook/useInfra.ts +++ /dev/null @@ -1,72 +0,0 @@ -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/styles/scss/applications/stdcmV2/_home.scss b/front/src/styles/scss/applications/stdcmV2/_home.scss index 9d5756e03d0..67f5a56dd21 100644 --- a/front/src/styles/scss/applications/stdcmV2/_home.scss +++ b/front/src/styles/scss/applications/stdcmV2/_home.scss @@ -35,9 +35,28 @@ font-weight: 500; } } + .simulation-availaible { + width: 34.125rem; + height: 5rem; + display: flex; + flex-direction: column; + position: absolute; + margin-top: 2.5rem; + border-radius: 0.5rem; + background-color: rgb(230, 247, 238); + box-shadow: 0px 0px 0px 4px rgba(255, 255, 255, 1) inset, 0px 0px 0px 5px rgb(60, 202, 128) inset, 0px 3px 7px -3px rgba(11, 114, 60, 0.4); + + span { + color: rgb(11, 114, 60); + align-self: center; + padding-top: 1.438rem; + font-size: 1.5rem; + line-height: 2rem; + } + } .warning-box { - border-radius: 8px; + border-radius: 0.5rem; box-shadow: 0px 0px 0px 4px rgba(255, 255, 255, 1) inset, 0px 0px 0px 5px rgba(234, 167, 43, 1) inset, 0px 3px 7px -3px rgba(125, 82, 30, 0.55); background-color: rgba(253, 245, 225, 1); color: #7D521E; @@ -87,5 +106,147 @@ height: calc(100vh - 64px); } } + .stdcm-v2-results { + background-color: rgb(233, 239, 242); + .simuation-banner { + color: rgb(0, 0, 0); + font-weight: 600; + font-size: 1.125rem; + line-height: 1.5rem; + padding-top: 1.813rem; + margin-left: 2rem; + margin-right: 2rem; + border-bottom: 0.063rem solid rgb(182, 178, 175); + .creation-date { + color: rgb(121, 118, 113); + font-weight: 400; + font-size: 0.875rem; + line-height: 1.25rem; + padding-top: 0.25rem; + padding-bottom: 0.688rem; + } + .simulation-validated { + display: flex; + .check-circle{ + color: rgb(60, 202, 128); + padding-left: 0.5rem; + } + } + } + .simuation-results { + display: flex; + justify-content: space-between; + .table-container { + margin-top: 2.125rem; + margin-left: 2rem; + width: 50.25rem; + } + .table-results { + border-radius: 0.375rem; + background-color: rgba(0, 0, 0, 0.05); + } + th { + height: 2rem; + font-size: 0.875rem; + font-weight: 400; + text-transform: capitalize; + color: rgb(121, 118, 113); + padding-top: 0.313rem; + padding-bottom: 0.313rem; + vertical-align: middle; + } + tbody tr:nth-child(odd) { + background-color: rgb(239, 243, 245); + } + tbody tr:nth-child(even) { + background-color: rgb(246, 248, 249); + } + td { + height: 2rem; + font-size: 0.875; + font-weight: 400; + color: rgb(49, 46, 43); + line-height: 1.25rem; + vertical-align: middle; + } + .index { + padding-left: 2rem; + } + .pr { + padding-right: 12.438rem; + } + .ch { + padding-right: 2.25rem; + } + .stop { + font-size: 0.875rem; + font-weight: 600; + color: rgb(0, 0, 0); + line-height: 1.25rem; + width: 4.688rem; + } + .stop-with-duration { + font-size: 0.875rem; + color: rgb(255, 255, 255); + background-color: rgb(33, 100, 130); + border-radius: 0.625rem; + margin-top: 0.375rem; + margin-bottom: 0.375rem; + margin-right: 1.813rem; + text-align: center; + } + .weight { + padding-left: 2.063rem; + } + .semi-bold-output { + font-weight: 600; + } + .display-all { + height: 6.5rem; + background-color: rgba(0, 0, 0, 0.05); + display: flex; + justify-content: space-between; + border-bottom-left-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; + .button-display-all-PR { + padding-top: 2rem; + padding-left: 2.25rem; + } + .button-get-simulation { + padding-top: 2rem; + padding-right: 2.25rem; + } + .selected-simulation{ + padding-top: 0.6rem; + } + } + .get-simulation { + height: 10.75rem; + background-color: rgb(255, 255, 255); + border-bottom-left-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; + .download-simulation { + padding-top: 2.813rem; + padding-left: 16.438rem; + } + .gesico-text { + font-weight: 400; + font-size: 1rem; + color: rgb(49, 46, 43); + padding-top: 1.313rem; + padding-left: 12.938rem; + } + } + } + .map-results { + width: 33.75rem; + height: 33.75rem; + margin-top: 2.313rem; + margin-right: 2rem; + border-radius: 0.5rem; + border: 0.063rem solid rgba(255, 255, 255, 1); + box-shadow: 0rem 0rem 0rem 0.125rem rgba(255, 255, 255, 0.75) inset, 0rem 0rem 0rem 0.063rem rgba(0, 0, 0, 0.25) inset; + } + } }