From 82d973061b8eed8a3a10643f20f8d261d89ce37a Mon Sep 17 00:00:00 2001 From: Theo Macron Date: Thu, 13 Jun 2024 16:20:59 +0200 Subject: [PATCH] front: display simulation results in stdcm add simulation table add creation date in simuation results reset selected simulation when new one is created hide results when simulation is on pending automatically format css file --- front/public/locales/en/stdcm.json | 10 + front/public/locales/fr/stdcm.json | 10 + .../components/SimulationReportSheetV2.tsx | 59 ++- .../src/applications/stdcm/hooks/useStdcm.ts | 8 + front/src/applications/stdcm/types.ts | 23 +- front/src/applications/stdcm/utils.ts | 31 +- .../applications/stdcm/views/StdcmConfig.tsx | 7 - .../stdcm/views/StdcmResultsV2.tsx | 32 +- .../stdcmV2/components/StdcmConfig.tsx | 32 +- .../components/StdcmResults/StdcmResults.tsx | 96 +++++ .../StdcmResults/StdcmResultsTable.tsx | 149 ++++++++ .../stdcmV2/components/StdcmResults/index.ts | 3 + .../stdcmV2/views/StdcmViewV2.tsx | 18 +- .../pathfinding/hook/usePathfinding.ts | 6 +- .../scss/applications/stdcmV2/_home.scss | 344 ++++++++++++++---- front/src/utils/date.ts | 18 +- 16 files changed, 660 insertions(+), 186 deletions(-) create mode 100644 front/src/applications/stdcmV2/components/StdcmResults/StdcmResults.tsx create mode 100644 front/src/applications/stdcmV2/components/StdcmResults/StdcmResultsTable.tsx create mode 100644 front/src/applications/stdcmV2/components/StdcmResults/index.ts diff --git a/front/public/locales/en/stdcm.json b/front/public/locales/en/stdcm.json index 6f32cc4df34..0a0397f9bca 100644 --- a/front/public/locales/en/stdcm.json +++ b/front/public/locales/en/stdcm.json @@ -10,10 +10,20 @@ "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", "pendingSimulation": "Simulation in progress", + "results":{ + "displayAll": "Display all operational points", + "displayMain": "Display main operational points", + "downloadSimulationSheet": "Download simulation report sheet", + "gesicoRequest":"and attach this document to your GESICO DSDM request.", + "selectThisSimulation": "Select this simulation", + "simulationNumber": "Simulation n°1", + "simulationSelected": "You have selected this simulation" + }, "stopCalculation" : "Stop calculation" }, "spaceSpeedGraphic": "Space-Velocity graph", diff --git a/front/public/locales/fr/stdcm.json b/front/public/locales/fr/stdcm.json index 5356fefdffd..ca32d0295ac 100644 --- a/front/public/locales/fr/stdcm.json +++ b/front/public/locales/fr/stdcm.json @@ -10,10 +10,20 @@ "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", "pendingSimulation": "simulation en cours", + "results":{ + "displayAll": "Afficher tous les jalons", + "displayMain": "Afficher les jalons principaux", + "downloadSimulationSheet": "Télécharger la fiche de simulation", + "gesicoRequest":"et joignez ce document à votre demande GESICO DSDM", + "selectThisSimulation": "Retenir cette simulation", + "simulationNumber": "Simulation n°1", + "simulationSelected": "Vous avez retenu cette simulation" + }, "stopCalculation" : "Arrêter le calcul" }, "spaceSpeedGraphic": "Graphique Espace-Vitesse", diff --git a/front/src/applications/stdcm/components/SimulationReportSheetV2.tsx b/front/src/applications/stdcm/components/SimulationReportSheetV2.tsx index f3124e71218..81dea51180d 100644 --- a/front/src/applications/stdcm/components/SimulationReportSheetV2.tsx +++ b/front/src/applications/stdcm/components/SimulationReportSheetV2.tsx @@ -10,21 +10,18 @@ import { formatDateToString, formatDayV2 } from 'utils/date'; import styles from './SimulationReportStyleSheet'; import type { SimulationReportSheetProps } from '../types'; -import { extractSpeedLimit, getStopDurationTime, getOperationalPointsWithTimes } from '../utils'; +import { extractSpeedLimit, getStopDurationTime } from '../utils'; const SimulationReportSheetV2 = ({ stdcmData, - pathProperties, - rollingStockData, - speedLimitByTag, simulationReportSheetNumber, mapCanvas, - creationDate, + operationalPointsList, }: SimulationReportSheetProps) => { const { t } = useTranslation('stdcm-simulation-report-sheet'); let renderedIndex = 0; - const date = creationDate && t('formattedDate', formatDateToString(creationDate)); + const { rollingStock, speedLimitByTag, departure_time: departureTime, creationDate } = stdcmData; // TODO: Add RC information when it becomes avalaible, until that, we use fake ones const fakeInformation = { @@ -36,18 +33,6 @@ const SimulationReportSheetV2 = ({ path_number2: 'n°YYYYYY', }; - const simulationReport: SimulationReportSheetProps = { - stdcmData, - pathProperties, - rollingStockData, - speedLimitByTag, - simulationReportSheetNumber, - mapCanvas, - creationDate, - }; - - const opList = getOperationalPointsWithTimes(simulationReport); - return ( @@ -68,7 +53,9 @@ const SimulationReportSheetV2 = ({ n° {simulationReportSheetNumber} - {date} + + {t('formattedDate', formatDateToString(creationDate))} + @@ -85,7 +72,7 @@ const SimulationReportSheetV2 = ({ {t('applicationDate')} - {formatDayV2(stdcmData.departure_time)} + {formatDayV2(departureTime)} {t('referencePath')} {fakeInformation.path_number1} @@ -104,22 +91,22 @@ const SimulationReportSheetV2 = ({ - {t('maxSpeed')} - {`${Math.floor(rollingStockData.max_speed * 3.6)} km/h`} + {`${Math.floor(rollingStock.max_speed * 3.6)} km/h`} {t('maxWeight')} - {`${Math.floor(rollingStockData.mass / 1000)} t`} + {`${Math.floor(rollingStock.mass / 1000)} t`} {t('referenceEngine')} - {rollingStockData.metadata?.reference || '-'} + {rollingStock.metadata?.reference || '-'} {t('maxLength')} {`${rollingStockData.length} m`} + >{`${rollingStock.length} m`} @@ -155,9 +142,9 @@ const SimulationReportSheetV2 = ({ {t('motif')} - {opList.map((step, index) => { + {operationalPointsList.map((step, index) => { const isFirstStep = index === 0; - const isLastStep = index === opList.length - 1; + const isLastStep = index === operationalPointsList.length - 1; const shouldRenderRow = isFirstStep || step.duration > 0 || isLastStep; if (shouldRenderRow) { renderedIndex += 1; @@ -183,7 +170,7 @@ const SimulationReportSheetV2 = ({ - {isFirstStep ? step.departureTime : ''} + {isFirstStep ? step.stopEndTime : ''} @@ -259,10 +246,10 @@ const SimulationReportSheetV2 = ({ {t('crossedATE')} - {opList.map((step, index) => { + {operationalPointsList.map((step, index) => { const isFirstStep = index === 0; - const isLastStep = index === opList.length - 1; - const prevStep = opList[index - 1]; + const isLastStep = index === operationalPointsList.length - 1; + const prevStep = operationalPointsList[index - 1]; return ( - {!isFirstStep ? '=' : `${Math.floor(rollingStockData.mass / 1000)} t`} + {!isFirstStep ? '=' : `${Math.floor(rollingStock.mass / 1000)} t`} - {!isFirstStep ? '=' : rollingStockData.metadata?.reference} + {!isFirstStep ? '=' : rollingStock.metadata?.reference} @@ -349,9 +336,11 @@ const SimulationReportSheetV2 = ({ - - - + {mapCanvas && ( + + + + )} {t('withoutWarranty')} diff --git a/front/src/applications/stdcm/hooks/useStdcm.ts b/front/src/applications/stdcm/hooks/useStdcm.ts index e7ee0c74121..538f8511c23 100644 --- a/front/src/applications/stdcm/hooks/useStdcm.ts +++ b/front/src/applications/stdcm/hooks/useStdcm.ts @@ -16,6 +16,7 @@ import type { TrainScheduleResult, } from 'common/api/osrdEditoastApi'; import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; +import { useStoreDataForSpeedLimitByTagSelector } from 'common/SpeedLimitByTagSelector/useStoreDataForSpeedLimitByTagSelector'; import createTrain from 'modules/simulationResult/components/SpaceTimeChart/createTrain'; import { CHART_AXES } from 'modules/simulationResult/consts'; import { setFailure } from 'reducers/main'; @@ -76,6 +77,8 @@ const useStdcm = () => { const { timetableID } = osrdconf; + const { speedLimitByTag } = useStoreDataForSpeedLimitByTagSelector(); + const resetResults = () => { dispatch(updateSelectedTrainId(undefined)); dispatch(updateConsolidatedSimulation([])); @@ -160,6 +163,8 @@ const useStdcm = () => { setStdcmV2Response({ ...response, rollingStock: stdcmRollingStock, + creationDate: new Date(), + speedLimitByTag, } as StdcmV2SuccessResponse); const stdcmTrain: TrainScheduleResult = { @@ -213,6 +218,8 @@ const useStdcm = () => { dispatch(updateSimulation(emptySimulation)); }; + const isPending = currentStdcmRequestStatus === STDCM_REQUEST_STATUS.pending; + return { stdcmResults, stdcmV2Results, @@ -221,6 +228,7 @@ const useStdcm = () => { cancelStdcmRequest, pathProperties, setPathProperties, + isPending, }; }; diff --git a/front/src/applications/stdcm/types.ts b/front/src/applications/stdcm/types.ts index 690d8771700..e9bb5f7d37e 100644 --- a/front/src/applications/stdcm/types.ts +++ b/front/src/applications/stdcm/types.ts @@ -1,5 +1,4 @@ import type { - ManageTrainSchedulePathProperties, PathPropertiesFormatted, TrainSpaceTimeData, } from 'applications/operationalStudies/types'; @@ -22,16 +21,30 @@ export type StdcmV2SuccessResponse = Omit< > & { simulation: Extract; rollingStock: LightRollingStock; + creationDate: Date; + speedLimitByTag?: string; }; export type SimulationReportSheetProps = { stdcmData: StdcmV2SuccessResponse; - pathProperties?: ManageTrainSchedulePathProperties; - rollingStockData: RollingStockWithLiveries; - speedLimitByTag?: string; simulationReportSheetNumber: string; mapCanvas?: string; - creationDate?: Date; + operationalPointsList: StdcmResultsOperationalPointsList; +}; + +export type StdcmResultsOperationalPointsList = StdcmResultsOperationalPoint[]; + +type StdcmResultsOperationalPoint = { + opId: string; + positionOnPath: number; + time: string | null; + name?: string; + ch?: string; + stop?: string | null; + duration: number; + departureTime: string; + stopEndTime: string; + trackName?: string; }; export type StdcmV2Results = { diff --git a/front/src/applications/stdcm/utils.ts b/front/src/applications/stdcm/utils.ts index 6ba4ac320b3..aea538f50d4 100644 --- a/front/src/applications/stdcm/utils.ts +++ b/front/src/applications/stdcm/utils.ts @@ -1,4 +1,7 @@ -import type { SimulationReportSheetProps } from './types'; +import type { SimulationResponse } from 'common/api/generatedEditoastApi'; +import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types'; + +import type { StdcmResultsOperationalPointsList } from './types'; function generateRandomString(length: number): string { return Array.from({ length }, () => Math.floor(Math.random() * 10)).join(''); @@ -112,28 +115,16 @@ const getStopDurationBetweenToPositions = ( return null; }; -export function getOperationalPointsWithTimes(simulationReport: SimulationReportSheetProps): { - opId: string; - positionOnPath: number; - time: string | null; - name: string | undefined; - ch: string | undefined; - stop: string | null | undefined; - duration: number; - departureTime: string; - stopEndTime: string; - trackName: string | undefined; -}[] { - const operationalPoints = simulationReport.pathProperties?.suggestedOperationalPoints || []; - const { simulation } = simulationReport.stdcmData; - +export function getOperationalPointsWithTimes( + operationalPoints: SuggestedOP[], + simulation: Extract, + departureTime: string +): StdcmResultsOperationalPointsList { const { positions, times } = simulation.final_output; - const departureTime = new Date(simulationReport.stdcmData.departure_time) - .toLocaleTimeString() - .substring(0, 5); + const pathDepartureTime = new Date(departureTime).toLocaleTimeString().substring(0, 5); // Parse departure time into hours and minutes - const [departureHour, departureMinute] = departureTime.split(':').map(Number); + const [departureHour, departureMinute] = pathDepartureTime.split(':').map(Number); // Map operational points with their positions, times, and stop durations const opResults = operationalPoints.map((op) => { diff --git a/front/src/applications/stdcm/views/StdcmConfig.tsx b/front/src/applications/stdcm/views/StdcmConfig.tsx index 646936af409..6dc8d366738 100644 --- a/front/src/applications/stdcm/views/StdcmConfig.tsx +++ b/front/src/applications/stdcm/views/StdcmConfig.tsx @@ -99,11 +99,7 @@ const StdcmConfig = ({ ); }, [infra, osrdconf, originV2, destinationV2]); - const [creationDate, setCreationDate] = useState(); - const handleClick = () => { - const currentDateTime = new Date(); - setCreationDate(currentDateTime); launchStdcmRequest(); }; @@ -212,9 +208,6 @@ const StdcmConfig = ({ mapCanvas={mapCanvas} stdcmV2Results={stdcmV2Results} pathProperties={pathProperties} - rollingStockData={rollingStock} - speedLimitByTag={speedLimitByTag} - creationDate={creationDate} /> )} diff --git a/front/src/applications/stdcm/views/StdcmResultsV2.tsx b/front/src/applications/stdcm/views/StdcmResultsV2.tsx index 752a88699e7..401d178d405 100644 --- a/front/src/applications/stdcm/views/StdcmResultsV2.tsx +++ b/front/src/applications/stdcm/views/StdcmResultsV2.tsx @@ -1,10 +1,9 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { PDFDownloadLink } from '@react-pdf/renderer'; import { useTranslation } from 'react-i18next'; import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; -import type { RollingStockWithLiveries } from 'common/api/osrdEditoastApi'; import { LoaderFill } from 'common/Loaders'; import SpaceTimeChartV2 from 'modules/simulationResult/components/SpaceTimeChart/SpaceTimeChartV2'; import SpeedSpaceChartV2 from 'modules/simulationResult/components/SpeedSpaceChart/SpeedSpaceChartV2'; @@ -13,29 +12,19 @@ import type { TimeScaleDomain } from 'modules/simulationResult/types'; import SimulationReportSheetV2 from '../components/SimulationReportSheetV2'; import { STDCM_TRAIN_ID } from '../consts'; import type { StdcmV2Results } from '../types'; -import { generateCodeNumber } from '../utils'; +import { generateCodeNumber, getOperationalPointsWithTimes } from '../utils'; type StcdmResultsProps = { mapCanvas?: string; stdcmV2Results: StdcmV2Results; pathProperties?: ManageTrainSchedulePathProperties; - rollingStockData: RollingStockWithLiveries; - speedLimitByTag?: string; - creationDate?: Date; }; const codeNumber = generateCodeNumber(); // TODO TS2 : Adapt StdcmResult to trainSchedule v2 (SpaceTimeChart and SpeedSpaceChart) -const StcdmResultsV2 = ({ - mapCanvas, - stdcmV2Results, - pathProperties, - rollingStockData, - speedLimitByTag, - creationDate, -}: StcdmResultsProps) => { +const StcdmResultsV2 = ({ mapCanvas, stdcmV2Results, pathProperties }: StcdmResultsProps) => { const { t } = useTranslation(['stdcm']); const [spaceTimeChartHeight, setSpaceTimeChartHeight] = useState(450); const [speedSpaceChartHeight, setSpeedSpaceChartHeight] = useState(250); @@ -55,6 +44,16 @@ const StcdmResultsV2 = ({ setSpaceTimeData, } = stdcmV2Results; + const operationalPointsList = useMemo( + () => + getOperationalPointsWithTimes( + pathProperties?.suggestedOperationalPoints || [], + stdcmV2Results.stdcmResponse.simulation, + stdcmV2Results.stdcmResponse.departure_time + ), + [pathProperties, stdcmV2Results] + ); + return (
@@ -104,12 +103,9 @@ const StcdmResultsV2 = ({ document={ } fileName={`STDCM-${codeNumber}.pdf`} diff --git a/front/src/applications/stdcmV2/components/StdcmConfig.tsx b/front/src/applications/stdcmV2/components/StdcmConfig.tsx index bca2ad1cbdc..5bd24241bb8 100644 --- a/front/src/applications/stdcmV2/components/StdcmConfig.tsx +++ b/front/src/applications/stdcmV2/components/StdcmConfig.tsx @@ -6,8 +6,6 @@ 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'; @@ -24,7 +22,9 @@ import type { StdcmSimulationResult } from '../types'; type StdcmConfigProps = { currentSimulationInputs: StdcmSimulationResult['input'] | undefined; pathProperties?: ManageTrainSchedulePathProperties; - setPathProperties: (pathProperties?: ManageTrainSchedulePathProperties) => void; + isPending: boolean; + launchStdcmRequest: () => Promise; + cancelStdcmRequest: () => void; setCurrentSimulationInputs: React.Dispatch< React.SetStateAction >; @@ -33,20 +33,26 @@ type StdcmConfigProps = { const StdcmConfig = ({ currentSimulationInputs, pathProperties, - setPathProperties, + isPending, + launchStdcmRequest, + cancelStdcmRequest, setCurrentSimulationInputs, }: StdcmConfigProps) => { const { t } = useTranslation('stdcm'); const loaderRef = useRef(null); - const { launchStdcmRequest, cancelStdcmRequest, currentStdcmRequestStatus } = useStdcm(); - const isPending = currentStdcmRequestStatus === STDCM_REQUEST_STATUS.pending; - const dispatch = useAppDispatch(); const { updateGridMarginAfter, updateGridMarginBefore, updateStdcmStandardAllowance } = useOsrdConfActions() as StdcmConfSliceActions; - const { pathfindingState } = usePathfindingV2(setPathProperties, pathProperties); + const { pathfindingState } = usePathfindingV2(undefined, pathProperties); + + const clickOnSimulation = () => { + if (pathfindingState.done) { + launchStdcmRequest(); + setCurrentSimulationInputs(undefined); + } + }; useEffect(() => { if (isPending) { @@ -92,15 +98,7 @@ const StdcmConfig = ({ })} > {currentSimulationInputs && ( -
+
{t('simulation.results.gesicoRequest')}
+ + )} + +
+ +
+ +
+ ); +}; + +export default StcdmResults; diff --git a/front/src/applications/stdcmV2/components/StdcmResults/StdcmResultsTable.tsx b/front/src/applications/stdcmV2/components/StdcmResults/StdcmResultsTable.tsx new file mode 100644 index 00000000000..7058a56affa --- /dev/null +++ b/front/src/applications/stdcmV2/components/StdcmResults/StdcmResultsTable.tsx @@ -0,0 +1,149 @@ +import React, { useEffect, useState } from 'react'; + +import { Button } from '@osrd-project/ui-core'; +import { useTranslation } from 'react-i18next'; + +import type { + StdcmResultsOperationalPointsList, + StdcmV2SuccessResponse, +} from 'applications/stdcm/types'; +import { getStopDurationTime } from 'applications/stdcm/utils'; + +type SimulationTableProps = { + stdcmData: StdcmV2SuccessResponse; + setIsSimulationSelected: (simulationSelected: boolean) => void; + isSimulationSelected: boolean; + operationalPointsList: StdcmResultsOperationalPointsList; +}; + +const StcdmResultsTable = ({ + stdcmData, + setIsSimulationSelected, + isSimulationSelected, + operationalPointsList, +}: SimulationTableProps) => { + const { t } = useTranslation(['stdcm-simulation-report-sheet', 'stdcm']); + + const [showAllOP, setShowAllOP] = useState(false); + + const selectSimulation = () => { + setIsSimulationSelected(true); + }; + + useEffect(() => { + setIsSimulationSelected(false); + }, [stdcmData]); + + const handleShowAllClick = () => { + setShowAllOP((prevState) => !prevState); + }; + + return ( +
+ + + + + + + + + + + + {operationalPointsList.map((step, index) => { + const isFirstStep = index === 0; + const isLastStep = index === operationalPointsList.length - 1; + const prevStep = operationalPointsList[index - 1]; + const shouldRenderRow = isFirstStep || step.duration > 0 || isLastStep; + if (showAllOP || shouldRenderRow) { + return ( + + + + + + + + + + + ); + } + return null; + })} + +
+ {t('operationalPoint')}{t('code')}{t('endStop')}{t('passageStop')}{t('startStop')}{t('weight')}{t('refEngine')}
+ {index + 1} + + {!isFirstStep && + !isLastStep && + step.name === prevStep.name && + step.duration === 0 + ? '=' + : step.name || 'Unknown'} + {step.ch}{isLastStep || step.duration > 0 ? step.time : ''} +
= 60 ? 55 : 65}px`, + }} + > + { + // eslint-disable-next-line no-nested-ternary + !isFirstStep && !isLastStep + ? step.duration !== 0 + ? getStopDurationTime(step.duration) + : step.time + : '' + } +
+
+ {isFirstStep || step.duration > 0 ? step.stopEndTime : ''} + + {!isFirstStep && !isLastStep + ? '=' + : `${Math.floor(stdcmData.rollingStock.mass / 1000)} t`} + + {!isFirstStep && !isLastStep ? '=' : stdcmData.rollingStock.metadata?.reference} +
+
+
+
+
+ {!isSimulationSelected ? ( +
+
+
+ ); +}; + +export default StcdmResultsTable; diff --git a/front/src/applications/stdcmV2/components/StdcmResults/index.ts b/front/src/applications/stdcmV2/components/StdcmResults/index.ts new file mode 100644 index 00000000000..9df53c47daa --- /dev/null +++ b/front/src/applications/stdcmV2/components/StdcmResults/index.ts @@ -0,0 +1,3 @@ +import StdcmResults from './StdcmResults'; + +export default StdcmResults; diff --git a/front/src/applications/stdcmV2/views/StdcmViewV2.tsx b/front/src/applications/stdcmV2/views/StdcmViewV2.tsx index 15c0b3e641d..a67cb48c182 100644 --- a/front/src/applications/stdcmV2/views/StdcmViewV2.tsx +++ b/front/src/applications/stdcmV2/views/StdcmViewV2.tsx @@ -2,19 +2,20 @@ import React, { useState } from 'react'; import { useSelector } from 'react-redux'; -import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; +import useStdcm from 'applications/stdcm/hooks/useStdcm'; import { useOsrdConfSelectors } from 'common/osrdContext'; import StdcmConfig from '../components/StdcmConfig'; import StdcmHeader from '../components/StdcmHeader'; +import StdcmResults from '../components/StdcmResults'; import type { StdcmSimulationResult } from '../types'; const StdcmViewV2 = () => { + const { launchStdcmRequest, cancelStdcmRequest, isPending, stdcmV2Results, pathProperties } = + useStdcm(); const { getScenarioID } = useOsrdConfSelectors(); const scenarioID = useSelector(getScenarioID); - const [pathProperties, setPathProperties] = useState(); - const [currentSimulationInputs, setCurrentSimulationInputs] = useState< StdcmSimulationResult['input'] | undefined >(undefined); @@ -26,10 +27,19 @@ const StdcmViewV2 = () => { )} + {stdcmV2Results?.stdcmResponse && !isPending && ( + + )} ); }; diff --git a/front/src/modules/pathfinding/hook/usePathfinding.ts b/front/src/modules/pathfinding/hook/usePathfinding.ts index 737862b4276..7d8af08df19 100644 --- a/front/src/modules/pathfinding/hook/usePathfinding.ts +++ b/front/src/modules/pathfinding/hook/usePathfinding.ts @@ -133,7 +133,7 @@ function init({ } export const usePathfindingV2 = ( - setPathProperties: (pathProperties?: ManageTrainSchedulePathProperties) => void, + setPathProperties?: (pathProperties?: ManageTrainSchedulePathProperties) => void | null, pathProperties?: ManageTrainSchedulePathProperties ) => { const { t } = useTranslation(['operationalStudies/manageTrainSchedule']); @@ -167,7 +167,7 @@ export const usePathfindingV2 = ( const { updatePathSteps } = useOsrdConfActions(); const generatePathfindingParams = (): PostV2InfraByInfraIdPathfindingBlocksApiArg | null => { - setPathProperties(undefined); + if (setPathProperties) setPathProperties(undefined); return getPathfindingQuery({ infraId, rollingStock, origin, destination, pathSteps }); }; @@ -268,7 +268,7 @@ export const usePathfindingV2 = ( compact(updatedPathSteps) ); - setPathProperties({ + setPathProperties?.({ electrifications, geometry, suggestedOperationalPoints, diff --git a/front/src/styles/scss/applications/stdcmV2/_home.scss b/front/src/styles/scss/applications/stdcmV2/_home.scss index ac7b4a05b1d..ed78935419f 100644 --- a/front/src/styles/scss/applications/stdcmV2/_home.scss +++ b/front/src/styles/scss/applications/stdcmV2/_home.scss @@ -1,90 +1,284 @@ .stdcm-v2 { - .stdcm-v2__body { - padding: 32px; - background-color: rgb(239, 243, 245); + .stdcm-v2__body { + padding: 32px; + background-color: rgb(239, 243, 245); + display: flex; + + .stdcm-v2-simulation-settings { + display: flex; + gap: 13px; + .stdcm-v2__separator { + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.05); + width: 6px; + height: 280px; + margin: 12px 0; + } + .stdcm-v2-simulation-itinerary { display: flex; - - .stdcm-v2-simulation-settings { - display: flex; - gap: 13px; - .stdcm-v2__separator { - border-radius: 4px; - background-color: rgba(0, 0, 0, 0.05); - width: 6px; - height: 280px; - margin: 12px 0; - } - .stdcm-v2-simulation-itinerary { - display: flex; - flex-direction: column; - gap: 32px; - width: 450px; + flex-direction: column; + gap: 32px; + width: 450px; - .stdcm-v2-vias-list { - display: flex; - flex-direction: column; - gap: 2rem; - } + .stdcm-v2-vias-list { + display: flex; + flex-direction: column; + gap: 2rem; + } - /*TODO Waiting to fix the button in ui-core...*/ - .stdcm-v2-launch-request{ - padding-bottom: 0.15rem; - button { - justify-content: center; - width: 100%; - font-weight: 500; - } - } + /*TODO Waiting to fix the button in ui-core...*/ + .stdcm-v2-launch-request { + padding-bottom: 0.15rem; - .warning-box { - border-radius: 8px; - 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; - display: flex; - flex-direction: column; - padding: 1rem 1.8125rem 2rem 1.8125rem; - margin-top: 0.75rem; + button { + justify-content: center; + width: 100%; + 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: #EAA72B; - align-self: center; - margin-bottom: 6px; - } + span { + color: rgb(11, 114, 60); + align-self: center; + padding-top: 1.438rem; + font-size: 1.5rem; + line-height: 2rem; + } + } - } + .warning-box { + 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; + display: flex; + flex-direction: column; + padding: 1rem 1.8125rem 2rem 1.8125rem; + margin-top: 0.75rem; - .wizz-effect:active { - .warning-box { - animation: tilt-shaking 0.25s 0s; - } - } + span { + color: #eaa72b; + align-self: center; + margin-bottom: 6px; + } + } - @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); } - } + .wizz-effect:active { + .warning-box { + 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); - box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.75) inset, 0px 0px 0px 1px rgba(0, 0, 0, 0.25) inset; - height: auto; - min-height: 540px; - margin-left: 0.85rem; - width: 100%; - height: calc(100vh - 64px); + } + } + + .stdcm-v2-map { + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 1); + box-shadow: + 0px 0px 0px 2px rgba(255, 255, 255, 0.75) inset, + 0px 0px 0px 1px rgba(0, 0, 0, 0.25) inset; + height: auto; + min-height: 540px; + margin-left: 0.85rem; + width: 100%; + 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; + .results-and-sheet { + display: flex; + flex-direction: column; + } + .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 { + margin-left: 2rem; + 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; + } + } } diff --git a/front/src/utils/date.ts b/front/src/utils/date.ts index 6a58f029384..d477f775887 100644 --- a/front/src/utils/date.ts +++ b/front/src/utils/date.ts @@ -17,7 +17,11 @@ export function formatIsoDate(date: Date) { return date.toISOString().substring(0, 10); } -export function dateTimeFormatting(date: Date, withoutTime: boolean = false) { +export function dateTimeFormatting( + date: Date, + withoutTime: boolean = false, + formatType: string = 'default' +) { let locale; switch (i18n.language) { case 'fr': @@ -28,7 +32,17 @@ export function dateTimeFormatting(date: Date, withoutTime: boolean = false) { } // Force interpreting the date in UTC const dateToUTC = dayjs(date).utc(true); - const dateFormat = withoutTime ? 'D MMM YYYY' : 'D MMM YYYY HH:mm'; + let dateFormat; + switch (formatType) { + case 'default': + dateFormat = withoutTime ? 'D MMM YYYY' : 'D MMM YYYY HH:mm'; + break; + case 'alternate': + dateFormat = withoutTime ? 'DD/MM/YY' : 'DD/MM/YY HH:mm'; + break; + default: + throw new Error('Invalid format type'); + } const tz = dayjs.tz.guess(); return dateToUTC.locale(locale).tz(tz).format(dateFormat).replace(/\./gi, ''); }