diff --git a/front/public/locales/en/rollingstock.json b/front/public/locales/en/rollingstock.json index 0646ace3eb6..2f41b19e169 100644 --- a/front/public/locales/en/rollingstock.json +++ b/front/public/locales/en/rollingstock.json @@ -11,6 +11,7 @@ "comfortAcceleration": "Comfort acceleration", "comfortTypes": { "AC": "Air conditioning", + "AIR_CONDITIONING": "Air conditioning", "HEATING": "Heating", "STANDARD": "Standard" }, diff --git a/front/public/locales/fr/rollingstock.json b/front/public/locales/fr/rollingstock.json index d56d682b31c..944faeebfd5 100644 --- a/front/public/locales/fr/rollingstock.json +++ b/front/public/locales/fr/rollingstock.json @@ -11,6 +11,7 @@ "comfortAcceleration": "Accélération «\u202Fconfort\u202F»", "comfortTypes": { "AC": "Climatisation", + "AIR_CONDITIONING": "Climatisation", "HEATING": "Chauffage", "STANDARD": "Standard" }, diff --git a/front/src/applications/operationalStudies/views/Scenario.tsx b/front/src/applications/operationalStudies/views/Scenario.tsx deleted file mode 100644 index 5cd8ed1fa14..00000000000 --- a/front/src/applications/operationalStudies/views/Scenario.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import { useSelector } from 'react-redux'; - -import { getStdcmV2Activated, getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; - -import ScenarioV1 from './ScenarioV1'; -import ScenarioV2 from './v2/ScenarioV2'; - -export default function Scenario() { - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); - const stdcmV2Activated = useSelector(getStdcmV2Activated); - const useTrainScheduleV2 = trainScheduleV2Activated || stdcmV2Activated; - return useTrainScheduleV2 ? : ; -} diff --git a/front/src/applications/operationalStudies/views/SimulationResults.tsx b/front/src/applications/operationalStudies/views/SimulationResults.tsx index 730fa72085e..fca81af0c61 100644 --- a/front/src/applications/operationalStudies/views/SimulationResults.tsx +++ b/front/src/applications/operationalStudies/views/SimulationResults.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { Rnd } from 'react-rnd'; -import { osrdEditoastApi, type SimulationReport } from 'common/api/osrdEditoastApi'; +import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; import SimulationWarpedMap from 'common/Map/WarpedMap/SimulationWarpedMap'; import getScaleDomainFromValues from 'modules/simulationResult/components/ChartHelpers/getScaleDomainFromValues'; import SimulationResultsMap from 'modules/simulationResult/components/SimulationResultsMap'; @@ -143,9 +143,7 @@ export default function SimulationResults({ })} >
-
- {selectedTrain && } -
+
{selectedTrain && }
diff --git a/front/src/applications/operationalStudies/views/Study.tsx b/front/src/applications/operationalStudies/views/Study.tsx index a9ed7c30cb6..bbbb62f66ab 100644 --- a/front/src/applications/operationalStudies/views/Study.tsx +++ b/front/src/applications/operationalStudies/views/Study.tsx @@ -3,7 +3,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import { Pencil } from '@osrd-project/ui-icons'; import { useTranslation } from 'react-i18next'; import nextId from 'react-id-generator'; -import { useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; import BreadCrumbs from 'applications/operationalStudies/components/BreadCrumbs'; @@ -23,7 +22,6 @@ import { Loader, Spinner } from 'common/Loaders'; import ScenarioCard from 'modules/scenario/components/ScenarioCard'; import ScenarioCardEmpty from 'modules/scenario/components/ScenarioCardEmpty'; import AddOrEditStudyModal from 'modules/study/components/AddOrEditStudyModal'; -import { getStdcmV2Activated, getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import { budgetFormat } from 'utils/numbers'; type SortOptions = @@ -43,9 +41,6 @@ export default function Study() { const { t } = useTranslation('operationalStudies/study'); const { openModal } = useModal(); const { projectId: urlProjectId, studyId: urlStudyId } = useParams() as studyParams; - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); - const stdcmV2Activated = useSelector(getStdcmV2Activated); - const useTrainScheduleV2 = trainScheduleV2Activated || stdcmV2Activated; const [scenariosList, setScenariosList] = useState([]); const [filter, setFilter] = useState(''); @@ -77,17 +72,6 @@ export default function Study() { const [postSearch] = osrdEditoastApi.endpoints.postSearch.useMutation(); - const { data: scenariosV1 } = - osrdEditoastApi.endpoints.getProjectsByProjectIdStudiesAndStudyIdScenarios.useQuery( - { - projectId: projectId!, - studyId: studyId!, - ordering: sortOption, - pageSize: 1000, - }, - { skip: !projectId || !studyId } - ); - const { data: scenariosV2 } = osrdEditoastApi.endpoints.getV2ProjectsByProjectIdStudiesAndStudyIdScenarios.useQuery( { @@ -156,8 +140,7 @@ export default function Study() { console.error(error); } } else { - const scenarios = useTrainScheduleV2 ? scenariosV2?.results : scenariosV1?.results; - setScenariosList(scenarios || []); + setScenariosList(scenariosV2?.results || []); } setIsLoading(false); } @@ -188,7 +171,7 @@ export default function Study() { useEffect(() => { getScenarioList(); - }, [sortOption, filter, scenariosV1, scenariosV2, useTrainScheduleV2]); + }, [sortOption, filter, scenariosV2]); return ( <> diff --git a/front/src/applications/stdcm/hooks/useStdcm.ts b/front/src/applications/stdcm/hooks/useStdcm.ts index 538f8511c23..db6df618626 100644 --- a/front/src/applications/stdcm/hooks/useStdcm.ts +++ b/front/src/applications/stdcm/hooks/useStdcm.ts @@ -1,21 +1,15 @@ import { useState } from 'react'; -import { cloneDeep } from 'lodash'; import { useTranslation } from 'react-i18next'; import nextId from 'react-id-generator'; import { useSelector } from 'react-redux'; import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; import { STDCM_REQUEST_STATUS, STDCM_TRAIN_ID } from 'applications/stdcm/consts'; -import formatStdcmConf from 'applications/stdcm/formatStdcmConf'; import type { StdcmRequestStatus, StdcmV2SuccessResponse } from 'applications/stdcm/types'; import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; -import type { - SimulationReport, - PostStdcmApiResponse, - TrainScheduleResult, -} from 'common/api/osrdEditoastApi'; -import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; +import type { TrainScheduleResult } from 'common/api/osrdEditoastApi'; +import { 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'; @@ -25,10 +19,7 @@ import { updateConsolidatedSimulation, updateSelectedTrainId, updateSimulation, - updateSelectedProjection, } from 'reducers/osrdsimulation/actions'; -import type { Train } from 'reducers/osrdsimulation/types'; -import { getStdcmV2Activated, getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; import { castErrorToFailure } from 'utils/error'; @@ -37,7 +28,6 @@ import { checkStdcmConf, formatStdcmPayload } from '../utils/formatStdcmConfV2'; const useStdcm = () => { const [stdcmTrainResult, setStdcmTrainResult] = useState(); - const [stdcmResults, setStdcmResults] = useState(); const [stdcmV2Response, setStdcmV2Response] = useState(); const [currentStdcmRequestStatus, setCurrentStdcmRequestStatus] = useState( STDCM_REQUEST_STATUS.idle @@ -48,107 +38,29 @@ const useStdcm = () => { const { t } = useTranslation(['translation', 'stdcm']); const { getConf, getTimetableID } = useOsrdConfSelectors(); const osrdconf = useSelector(getConf); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); - const stdcmV2Activated = useSelector(getStdcmV2Activated); const timetableId = useSelector(getTimetableID); const stdcmV2Results = useStdcmResults(stdcmV2Response, stdcmTrainResult, setPathProperties); - const [postStdcm] = osrdEditoastApi.endpoints.postStdcm.useMutation(); const [postV2TimetableByIdStdcm] = osrdEditoastApi.endpoints.postV2TimetableByIdStdcm.useMutation(); - const [postTrainScheduleResults] = - osrdEditoastApi.endpoints.postTrainScheduleResults.useMutation(); - - const [getTimetable] = osrdEditoastApi.endpoints.getTimetableById.useLazyQuery(); const { data: stdcmRollingStock } = osrdEditoastApi.endpoints.getLightRollingStockByRollingStockId.useQuery( { rollingStockId: osrdconf.rollingStockID as number, }, - { skip: !osrdconf.rollingStockID && !trainScheduleV2Activated } + { skip: !osrdconf.rollingStockID } ); - const { updateItinerary } = useOsrdConfActions(); - // https://developer.mozilla.org/en-US/docs/Web/API/AbortController const controller = new AbortController(); - const { timetableID } = osrdconf; - const { speedLimitByTag } = useStoreDataForSpeedLimitByTagSelector(); - const resetResults = () => { - dispatch(updateSelectedTrainId(undefined)); - dispatch(updateConsolidatedSimulation([])); - dispatch(updateSimulation({ trains: [] })); - setStdcmResults(undefined); - }; - - const launchStdcmRequestV1 = async () => { - const payload = formatStdcmConf(dispatch, t, osrdconf as OsrdStdcmConfState); - if (payload && timetableID) { - resetResults(); - postStdcm(payload) - .unwrap() - .then((result) => { - setCurrentStdcmRequestStatus(STDCM_REQUEST_STATUS.success); - setStdcmResults(result); - dispatch(updateItinerary(result.path)); - - const fakedNewTrain = { - ...cloneDeep(result.simulation), - id: STDCM_TRAIN_ID, - isStdcm: true, - }; - getTimetable({ id: timetableID }).then(({ data: timetable }) => { - const trainIdsToFetch = - timetable?.train_schedule_summaries.map((train) => train.id) ?? []; - postTrainScheduleResults({ - body: { - path_id: result.path.id, - train_ids: trainIdsToFetch, - }, - }) - .unwrap() - .then((timetableTrains) => { - const trains: SimulationReport[] = [...timetableTrains.simulations, fakedNewTrain]; - const consolidatedSimulation = createTrain( - CHART_AXES.SPACE_TIME, - trains as Train[] // TODO: remove Train interface - ); - dispatch(updateConsolidatedSimulation(consolidatedSimulation)); - dispatch(updateSimulation({ trains })); - dispatch(updateSelectedTrainId(fakedNewTrain.id)); - - dispatch( - updateSelectedProjection({ - id: fakedNewTrain.id, - path: result.path.id, - }) - ); - }) - .catch((e) => { - dispatch( - setFailure( - castErrorToFailure(e, { - name: t('stdcm:stdcmError'), - message: t('translation:common.error'), - }) - ) - ); - }); - }); - }) - .catch((e) => { - setCurrentStdcmRequestStatus(STDCM_REQUEST_STATUS.rejected); - dispatch(setFailure(castErrorToFailure(e, { name: t('stdcm:stdcmError') }))); - }); - } - }; + const launchStdcmRequest = async () => { + setCurrentStdcmRequestStatus(STDCM_REQUEST_STATUS.pending); - const launchStdcmRequestV2 = async () => { const validConfig = checkStdcmConf(dispatch, t, osrdconf as OsrdStdcmConfState); if (validConfig) { const payload = formatStdcmPayload(validConfig); @@ -197,15 +109,6 @@ const useStdcm = () => { } }; - const launchStdcmRequest = async () => { - setCurrentStdcmRequestStatus(STDCM_REQUEST_STATUS.pending); - if (trainScheduleV2Activated || stdcmV2Activated) { - launchStdcmRequestV2(); - } else { - launchStdcmRequestV1(); - } - }; - const cancelStdcmRequest = () => { // when http ready https://axios-http.com/docs/cancellation @@ -221,7 +124,6 @@ const useStdcm = () => { const isPending = currentStdcmRequestStatus === STDCM_REQUEST_STATUS.pending; return { - stdcmResults, stdcmV2Results, launchStdcmRequest, currentStdcmRequestStatus, diff --git a/front/src/applications/stdcm/views/StdcmConfig.tsx b/front/src/applications/stdcm/views/StdcmConfig.tsx index 6dc8d366738..f42c7bd9219 100644 --- a/front/src/applications/stdcm/views/StdcmConfig.tsx +++ b/front/src/applications/stdcm/views/StdcmConfig.tsx @@ -8,12 +8,10 @@ import type { ManageTrainSchedulePathProperties } from 'applications/operational import RunningTime from 'applications/stdcm/components/RunningTime'; import { STDCM_REQUEST_STATUS } from 'applications/stdcm/consts'; import type { StdcmV2Results } from 'applications/stdcm/types'; -import StdcmResults from 'applications/stdcm/views/StdcmResults'; -import { osrdEditoastApi, type PostStdcmApiResponse } from 'common/api/osrdEditoastApi'; +import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; import { useInfraID, useOsrdConfSelectors } from 'common/osrdContext'; import SpeedLimitByTagSelector from 'common/SpeedLimitByTagSelector/SpeedLimitByTagSelector'; import { useStoreDataForSpeedLimitByTagSelector } from 'common/SpeedLimitByTagSelector/useStoreDataForSpeedLimitByTagSelector'; -import Itinerary from 'modules/pathfinding/components/Itinerary/Itinerary'; import ItineraryV2 from 'modules/pathfinding/components/Itinerary/ItineraryV2'; import { RollingStockSelector } from 'modules/rollingStock/components/RollingStockSelector'; import { useStoreDataForRollingStockSelector } from 'modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector'; @@ -21,14 +19,12 @@ import ScenarioExplorer from 'modules/scenario/components/ScenarioExplorer'; import StdcmAllowances from 'modules/stdcmAllowances/components/StdcmAllowances'; import { Map } from 'modules/trainschedule/components/ManageTrainSchedule'; import type { OsrdStdcmConfState } from 'reducers/osrdconf/types'; -import { getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import StdcmResultsV2 from './StdcmResultsV2'; type OSRDStdcmConfigProps = { currentStdcmRequestStatus: string; launchStdcmRequest: () => Promise; - stdcmResults?: PostStdcmApiResponse; stdcmV2Results?: StdcmV2Results | null; pathProperties?: ManageTrainSchedulePathProperties; setPathProperties: (pathProperties?: ManageTrainSchedulePathProperties) => void; @@ -37,7 +33,6 @@ type OSRDStdcmConfigProps = { const StdcmConfig = ({ currentStdcmRequestStatus, launchStdcmRequest, - stdcmResults, stdcmV2Results, pathProperties, setPathProperties, @@ -55,7 +50,6 @@ const StdcmConfig = ({ const studyID = useSelector(getStudyID); const scenarioID = useSelector(getScenarioID); const timetableID = useSelector(getTimetableID); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); const originV2 = useSelector(getOriginV2); const destinationV2 = useSelector(getDestinationV2); const infraID = useInfraID(); @@ -83,12 +77,9 @@ const StdcmConfig = ({ ); const disabledApplyButton = useMemo(() => { - if (trainScheduleV2Activated) { - if (!originV2 || !destinationV2 || !osrdconf.originDate || !osrdconf.destinationDate) - return true; - } else if (!osrdconf.origin || !osrdconf.destination) { + if (!originV2 || !destinationV2 || !osrdconf.originDate || !osrdconf.destinationDate) return true; - } + return ( infra?.state !== 'CACHED' || !(osrdconf.originTime || osrdconf.destinationTime) || @@ -138,15 +129,13 @@ const StdcmConfig = ({ speedLimitsByTags={speedLimitsByTags} dispatchUpdateSpeedLimitByTag={dispatchUpdateSpeedLimitByTag} /> - {trainScheduleV2Activated ? ( - - ) : ( - - )} + + +
@@ -195,15 +184,8 @@ const StdcmConfig = ({ )}
- {!trainScheduleV2Activated && rollingStock && stdcmResults && ( - - )} - {trainScheduleV2Activated && rollingStock && stdcmV2Results && ( + + {rollingStock && stdcmV2Results && ( { const dispatch = useAppDispatch(); const { - stdcmResults, stdcmV2Results, launchStdcmRequest, currentStdcmRequestStatus, @@ -34,7 +33,6 @@ const StdcmViewV1 = () => { ({ type: 'idle' }); - const pathfindingID = useSelector(getSelectedProjection)?.path as number; - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); - const [getPath] = osrdEditoastApi.endpoints.getPathfindingByPathfindingId.useLazyQuery(); const layers = useMemo(() => new Set(['track_sections']), []); const [mode, setMode] = useState<'manual' | 'auto'>('auto'); @@ -268,25 +260,8 @@ const SimulationWarpedMap = ({ */ useEffect(() => { setState({ type: 'loading' }); - - // V2 - if (trainScheduleV2Activated) { - if (pathProperties) updateWarpedMapState(pathProperties.geometry.coordinates); - } else { - // TODO DROP V1: remove this condition - getPath({ pathfindingId: pathfindingID }) - .then(({ data, isError, error }) => { - if (isError) { - setState({ type: 'error', message: error as string }); - } else if (!data?.geographic?.coordinates) { - setState({ type: 'error', message: 'No coordinates' }); - } else { - updateWarpedMapState(data?.geographic?.coordinates); - } - }) - .catch((error) => setState({ type: 'error', message: error })); - } - }, [pathfindingID]); + if (pathProperties) updateWarpedMapState(pathProperties.geometry.coordinates); + }, [pathProperties]); /** * This effect tries to gradually improve the quality of the OSRD data. diff --git a/front/src/common/UserSettings.tsx b/front/src/common/UserSettings.tsx index b574e865847..7a4a3ad7aa5 100644 --- a/front/src/common/UserSettings.tsx +++ b/front/src/common/UserSettings.tsx @@ -8,51 +8,19 @@ import { useSelector } from 'react-redux'; import InputSNCF from 'common/BootstrapSNCF/InputSNCF'; import { ModalBodySNCF, ModalHeaderSNCF } from 'common/BootstrapSNCF/ModalSNCF'; import SwitchSNCF, { SWITCH_TYPES } from 'common/BootstrapSNCF/SwitchSNCF/SwitchSNCF'; -import { operationalStudiesConfSliceActions } from 'reducers/osrdconf/operationalStudiesConf'; -import { stdcmConfSliceActions } from 'reducers/osrdconf/stdcmConf'; -import { - updateUserPreferences, - switchTrainScheduleV2Activated, - switchStdcmV2Activated, -} from 'reducers/user'; -import { - getTrainScheduleV2Activated, - getUserPreferences, - getStdcmV2Activated, -} from 'reducers/user/userSelectors'; +import { updateUserPreferences, switchStdcmV2Activated } from 'reducers/user'; +import { getUserPreferences, getStdcmV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; import { useDebounce } from 'utils/helpers'; const UserSettings = () => { const userPreferences = useSelector(getUserPreferences); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); const stdcmV2Activated = useSelector(getStdcmV2Activated); const [safeWordText, setSafeWordText] = useState(userPreferences.safeWord); const dispatch = useAppDispatch(); const debouncedSafeWord = useDebounce(safeWordText, 500); - const { - updateScenarioID: updateEexScenarioId, - updateTimetableID: updateEexTimetableId, - updateInfraID: updateEexInfraId, - } = operationalStudiesConfSliceActions; - const { - updateScenarioID: updateStdcmScenarioId, - updateTimetableID: updateStdcmTimetableId, - updateInfraID: updateStdcmInfraId, - } = stdcmConfSliceActions; - - const resetStore = () => { - dispatch(updateEexScenarioId(undefined)); - dispatch(updateEexTimetableId(undefined)); - dispatch(updateEexInfraId(undefined)); - - dispatch(updateStdcmScenarioId(undefined)); - dispatch(updateStdcmTimetableId(undefined)); - dispatch(updateStdcmInfraId(undefined)); - }; - useEffect(() => { dispatch(updateUserPreferences({ ...userPreferences, safeWord: debouncedSafeWord })); }, [debouncedSafeWord]); @@ -89,21 +57,7 @@ const UserSettings = () => { {t('safeWordHelp')} -
-
- { - dispatch(switchTrainScheduleV2Activated()); - resetStore(); - }} - checked={trainScheduleV2Activated} - /> -

TrainSchedule V2

-
-
+
{ name="stdcm-version-switch" onChange={() => { dispatch(switchStdcmV2Activated()); - resetStore(); }} checked={stdcmV2Activated} /> diff --git a/front/src/main/app.jsx b/front/src/main/app.jsx index b9dfe7630a6..3479de0368a 100644 --- a/front/src/main/app.jsx +++ b/front/src/main/app.jsx @@ -6,8 +6,8 @@ import 'i18n'; import HomeEditor from 'applications/editor/Home'; import HomeOperationalStudies from 'applications/operationalStudies/Home'; import Project from 'applications/operationalStudies/views/Project'; -import Scenario from 'applications/operationalStudies/views/Scenario'; import Study from 'applications/operationalStudies/views/Study'; +import ScenarioV2 from 'applications/operationalStudies/views/v2/ScenarioV2'; import HomeMap from 'applications/referenceMap/Home'; import HomeRollingStockEditor from 'applications/rollingStockEditor/Home'; import HomeStdcm from 'applications/stdcm/Home'; @@ -101,7 +101,7 @@ const router = createBrowserRouter([ }, { path: 'projects/:projectId/studies/:studyId/scenarios/:scenarioId', - element: , + element: , }, ], }, diff --git a/front/src/modules/rollingStock/components/RollingStockCard/RollingStockCardButtons.tsx b/front/src/modules/rollingStock/components/RollingStockCard/RollingStockCardButtons.tsx index c7ed181357c..367a9d841a4 100644 --- a/front/src/modules/rollingStock/components/RollingStockCard/RollingStockCardButtons.tsx +++ b/front/src/modules/rollingStock/components/RollingStockCard/RollingStockCardButtons.tsx @@ -3,13 +3,12 @@ import React, { useContext, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; -import type { RollingStockComfortType, TrainScheduleBase } from 'common/api/osrdEditoastApi'; +import type { Comfort } from 'common/api/osrdEditoastApi'; import { ModalContext } from 'common/BootstrapSNCF/ModalSNCF/ModalProvider'; import OptionsSNCF from 'common/BootstrapSNCF/OptionsSNCF'; import type { Option } from 'common/BootstrapSNCF/OptionsSNCF'; import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; import { comfort2pictogram } from 'modules/rollingStock/components/RollingStockSelector/RollingStockHelpers'; -import { getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; interface RollingStockCardButtonsProps { @@ -27,31 +26,16 @@ const RollingStockCardButtons = ({ const { t } = useTranslation(['rollingstock']); const { closeModal } = useContext(ModalContext); - const { getRollingStockComfort } = useOsrdConfSelectors(); - const currentComfortInStore = useSelector(getRollingStockComfort); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); - const [comfort, setComfort] = useState('STANDARD'); + const { getRollingStockComfortV2 } = useOsrdConfSelectors(); + const currentComfortInStore = useSelector(getRollingStockComfortV2); + const [comfort, setComfort] = useState(currentComfortInStore as string); - const { - updatePathfindingID, - updateRollingStockComfort, - updateRollingStockComfortV2, - updateRollingStockID, - } = useOsrdConfActions(); + const { updateRollingStockComfortV2, updateRollingStockID } = useOsrdConfActions(); const selectRollingStock = () => { setOpenedRollingStockCardId(undefined); - dispatch(updateRollingStockComfort(comfort as RollingStockComfortType)); dispatch(updateRollingStockID(id)); - dispatch(updatePathfindingID(undefined)); - // TODO TS2 : when dropping v1, change comfort type to TrainScheduleBase['comfort'] - // and verify that i18n keys are properly set for air conditioning - if (trainScheduleV2Activated) - dispatch( - updateRollingStockComfortV2( - comfort !== 'AC' ? (comfort as TrainScheduleBase['comfort']) : 'AIR_CONDITIONING' - ) - ); + dispatch(updateRollingStockComfortV2(comfort as Comfort)); closeModal(); }; @@ -72,10 +56,10 @@ const RollingStockCardButtons = ({ } if (curvesComfortList.includes('AC')) { options.push({ - value: 'AC', + value: 'AIR_CONDITIONING', label: ( - {comfort2pictogram('AC')} {t('comfortTypes.AC')} + {comfort2pictogram('AIR_CONDITIONING')} {t('comfortTypes.AIR_CONDITIONING')} ), }); diff --git a/front/src/modules/rollingStock/components/RollingStockCurve.tsx b/front/src/modules/rollingStock/components/RollingStockCurve.tsx index 6d1d9681b2b..fdc6fb421c3 100644 --- a/front/src/modules/rollingStock/components/RollingStockCurve.tsx +++ b/front/src/modules/rollingStock/components/RollingStockCurve.tsx @@ -50,9 +50,15 @@ function LegendComfortSwitches(props: { }) { const { curvesComfortList, comfortsStates, onComfortsStatesChange } = props; - return curvesComfortList.length > 1 ? ( + // TODO: remove this condition when getRollingStock endpoint returns comfort + // with type Comfort instead of RollingStockComfortType */ + const curvesComfortListV2 = curvesComfortList.map((comfort) => + comfort === 'AC' ? 'AIR_CONDITIONING' : comfort + ); + + return curvesComfortListV2.length > 1 ? ( - {curvesComfortList.map((comfort) => ( + {curvesComfortListV2.map((comfort) => ( ) : ( - {comfort2pictogram(curvesComfortList[0])} + {comfort2pictogram(curvesComfortListV2[0])} ); } @@ -102,9 +108,11 @@ function Legend(props: { {isOnEditionMode && showPowerRestriction && curve.power_restriction} {isOnEditionMode && !showPowerRestriction && curve.electrical_profile_level} {!isOnEditionMode && !showPowerRestriction && curve.mode} + {/* TODO: remove this condition when getRollingStock endpoint returns comfort + with type Comfort instead of RollingStockComfortType */} {curve.comfort !== STANDARD_COMFORT_LEVEL && !isOnEditionMode && - comfort2pictogram(curve.comfort)} + comfort2pictogram(curve.comfort === 'AC' ? 'AIR_CONDITIONING' : curve.comfort)} ))} @@ -216,13 +224,14 @@ export default function RollingStockCurve({ const [curvesVisibility, setCurvesVisibility] = useState(setupCurvesVisibility(transformedData)); const formatTooltip = (tooltip: PointTooltipProps) => { + const transformedCurve = transformedData[tooltip.point.serieId]; const editionModeTooltipLabel = isOnEditionMode && showPowerRestriction - ? geti18nKeyForNull(transformedData[tooltip.point.serieId]?.powerRestriction) - : geti18nKeyForNull(transformedData[tooltip.point.serieId]?.electricalProfile); + ? geti18nKeyForNull(transformedCurve?.powerRestriction) + : geti18nKeyForNull(transformedCurve?.electricalProfile); return (
- {transformedData[tooltip.point.serieId] && ( + {transformedCurve && (
- {isOnEditionMode - ? editionModeTooltipLabel - : transformedData[tooltip.point.serieId].mode} + {isOnEditionMode ? editionModeTooltipLabel : transformedCurve.mode} - {transformedData[tooltip.point.serieId].comfort !== STANDARD_COMFORT_LEVEL && ( + {transformedCurve.comfort !== STANDARD_COMFORT_LEVEL && ( - {comfort2pictogram(transformedData[tooltip.point.serieId].comfort)} + {/* TODO: remove this condition when getRollingStock endpoint returns comfort + with type Comfort instead of RollingStockComfortType */} + {comfort2pictogram( + transformedCurve.comfort === 'AC' ? 'AIR_CONDITIONING' : transformedCurve.comfort + )} )}
diff --git a/front/src/modules/rollingStock/components/RollingStockSelector/RollingStockHelpers.tsx b/front/src/modules/rollingStock/components/RollingStockSelector/RollingStockHelpers.tsx index 5272d1599b1..b6416973065 100644 --- a/front/src/modules/rollingStock/components/RollingStockSelector/RollingStockHelpers.tsx +++ b/front/src/modules/rollingStock/components/RollingStockSelector/RollingStockHelpers.tsx @@ -4,11 +4,7 @@ import { BiLockAlt } from 'react-icons/bi'; import { ImFire } from 'react-icons/im'; import { IoIosSnow } from 'react-icons/io'; -import type { - LightRollingStock, - RollingStockComfortType, - RollingStock, -} from 'common/api/osrdEditoastApi'; +import type { LightRollingStock, RollingStock, Comfort } from 'common/api/osrdEditoastApi'; const RollingStockUnit = ({ unit, detail }: { unit: string; detail: string }) => { if (unit && unit !== 'US') { @@ -73,9 +69,9 @@ export const RollingStockInfo = ({ ); }; -export function comfort2pictogram(comfort: RollingStockComfortType | undefined) { +export function comfort2pictogram(comfort: Comfort | undefined) { switch (comfort) { - case 'AC': + case 'AIR_CONDITIONING': return ( diff --git a/front/src/modules/rollingStock/components/RollingStockSelector/RollingStockSelector.tsx b/front/src/modules/rollingStock/components/RollingStockSelector/RollingStockSelector.tsx index 40180f99de9..97d502500d6 100644 --- a/front/src/modules/rollingStock/components/RollingStockSelector/RollingStockSelector.tsx +++ b/front/src/modules/rollingStock/components/RollingStockSelector/RollingStockSelector.tsx @@ -3,7 +3,7 @@ import React, { useRef } from 'react'; import { useTranslation } from 'react-i18next'; import icon from 'assets/pictures/components/train.svg'; -import type { RollingStockComfortType, RollingStockWithLiveries } from 'common/api/osrdEditoastApi'; +import type { Comfort, RollingStockWithLiveries } from 'common/api/osrdEditoastApi'; import { useModal } from 'common/BootstrapSNCF/ModalSNCF'; import RollingStock2Img from 'modules/rollingStock/components/RollingStock2Img'; import { @@ -15,7 +15,7 @@ import RollingStockModal from 'modules/rollingStock/components/RollingStockSelec type RollingStockProps = { condensed?: boolean; rollingStockSelected?: RollingStockWithLiveries; - rollingStockComfort: RollingStockComfortType; + rollingStockComfort: Comfort; image?: JSX.Element; }; diff --git a/front/src/modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector.ts b/front/src/modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector.ts index 3f5ce31c687..367132eed9e 100644 --- a/front/src/modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector.ts +++ b/front/src/modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector.ts @@ -4,9 +4,9 @@ import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; import { useOsrdConfSelectors } from 'common/osrdContext'; export const useStoreDataForRollingStockSelector = () => { - const { getRollingStockID, getRollingStockComfort } = useOsrdConfSelectors(); + const { getRollingStockID, getRollingStockComfortV2 } = useOsrdConfSelectors(); const rollingStockId = useSelector(getRollingStockID); - const rollingStockComfort = useSelector(getRollingStockComfort); + const rollingStockComfort = useSelector(getRollingStockComfortV2); const { data: rollingStock } = osrdEditoastApi.endpoints.getRollingStockByRollingStockId.useQuery( { diff --git a/front/src/modules/rollingStock/consts.ts b/front/src/modules/rollingStock/consts.ts index 5f788290d83..e23ee301423 100644 --- a/front/src/modules/rollingStock/consts.ts +++ b/front/src/modules/rollingStock/consts.ts @@ -1,4 +1,4 @@ -import type { RollingStockComfortType } from 'common/api/osrdEditoastApi'; +import type { Comfort, RollingStockComfortType } from 'common/api/osrdEditoastApi'; import { isElectric } from 'modules/rollingStock/helpers/electric'; import type { ElectricalProfileByMode, @@ -303,9 +303,9 @@ export const CONVERSION_FACTORS_SCHEMA: Partial< 'kN/(km/h)²': { 'N/(m/s)²': 1000 * 3.6 ** 2, 'N/(km/h)²': 1000 }, }; -const ComfortLevels = { +export const ComfortLevels: Record = { STANDARD: 'STANDARD', - AC: 'AC', + AIR_CONDITIONING: 'AIR_CONDITIONING', HEATING: 'HEATING', }; diff --git a/front/src/modules/scenario/components/AddOrEditScenarioModal.tsx b/front/src/modules/scenario/components/AddOrEditScenarioModal.tsx index 3e009edf9dc..7e2c37aece9 100644 --- a/front/src/modules/scenario/components/AddOrEditScenarioModal.tsx +++ b/front/src/modules/scenario/components/AddOrEditScenarioModal.tsx @@ -7,14 +7,9 @@ import { useTranslation } from 'react-i18next'; import { FaPlus } from 'react-icons/fa'; import { GiElectric } from 'react-icons/gi'; import { MdDescription, MdTitle } from 'react-icons/md'; -import { useSelector } from 'react-redux'; import { useNavigate, useParams } from 'react-router-dom'; -import { - type ScenarioCreateForm, - type ScenarioPatchForm, - osrdEditoastApi, -} from 'common/api/osrdEditoastApi'; +import { type ScenarioPatchForm, osrdEditoastApi } from 'common/api/osrdEditoastApi'; import ChipsSNCF from 'common/BootstrapSNCF/ChipsSNCF'; import InputSNCF from 'common/BootstrapSNCF/InputSNCF'; import { ConfirmModal } from 'common/BootstrapSNCF/ModalSNCF'; @@ -27,7 +22,6 @@ import TextareaSNCF from 'common/BootstrapSNCF/TextareaSNCF'; import { useInfraID, useOsrdConfActions } from 'common/osrdContext'; import { InfraSelectorModal } from 'modules/infra/components/InfraSelector'; import { setFailure, setSuccess } from 'reducers/main'; -import { getStdcmV2Activated, getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; import { castErrorToFailure } from 'utils/error'; import useInputChange from 'utils/hooks/useInputChange'; @@ -73,9 +67,6 @@ export default function AddOrEditScenarioModal({ const navigate = useNavigate(); const infraID = useInfraID(); const { updateScenarioID } = useOsrdConfActions(); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); - const stdcmV2Activated = useSelector(getStdcmV2Activated); - const useTrainScheduleV2 = trainScheduleV2Activated || stdcmV2Activated; const [currentScenario, setCurrentScenario] = useState(scenario || emptyScenario); @@ -93,19 +84,6 @@ export default function AddOrEditScenarioModal({ [urlStudyId, urlProjectId] ); - // V1 endpoints - const [deleteScenarioV1] = - osrdEditoastApi.endpoints.deleteProjectsByProjectIdStudiesAndStudyIdScenariosScenarioId.useMutation( - {} - ); - const [patchScenarioV1] = - osrdEditoastApi.endpoints.patchProjectsByProjectIdStudiesAndStudyIdScenariosScenarioId.useMutation( - {} - ); - const [postScenarioV1] = - osrdEditoastApi.endpoints.postProjectsByProjectIdStudiesAndStudyIdScenarios.useMutation({}); - - // v2 endpoints const [postTimetableV2] = osrdEditoastApi.endpoints.postV2Timetable.useMutation({}); const [postScenarioV2] = osrdEditoastApi.endpoints.postV2ProjectsByProjectIdStudiesAndStudyIdScenarios.useMutation({}); @@ -181,30 +159,21 @@ export default function AddOrEditScenarioModal({ if (!currentScenario.infra_id || hasErrors) { setDisplayErrors(true); } else if (projectId && studyId && currentScenario && currentScenario.name) { - let postScenarioRequest; const ids = { projectId, studyId }; - if (useTrainScheduleV2) { - const timetable = await postTimetableV2({ - timetableForm: { electrical_profile_set_id: currentScenario.electrical_profile_set_id }, - }).unwrap(); - postScenarioRequest = postScenarioV2({ - ...ids, - scenarioCreateFormV2: { - description: currentScenario.description || '', - infra_id: currentScenario.infra_id, - name: currentScenario.name, - tags: currentScenario.tags || [], - timetable_id: timetable.id, - }, - }); - } else { - postScenarioRequest = postScenarioV1({ - ...ids, - scenarioCreateForm: currentScenario as ScenarioCreateForm, - }); - } - - postScenarioRequest + const timetable = await postTimetableV2({ + timetableForm: { electrical_profile_set_id: currentScenario.electrical_profile_set_id }, + }).unwrap(); + + postScenarioV2({ + ...ids, + scenarioCreateFormV2: { + description: currentScenario.description || '', + infra_id: currentScenario.infra_id, + name: currentScenario.name, + tags: currentScenario.tags || [], + timetable_id: timetable.id, + }, + }) .unwrap() .then(({ id }) => { dispatch(updateScenarioID(id)); @@ -224,22 +193,15 @@ export default function AddOrEditScenarioModal({ } else if (scenario && projectId && studyId && scenario.id) { const ids = { projectId, studyId, scenarioId: scenario.id }; - const patchScenarioRequest = useTrainScheduleV2 - ? patchScenarioV2({ - ...ids, - scenarioPatchFormV2: { - description: currentScenario.description, - infra_id: currentScenario.infra_id, - name: currentScenario.name, - tags: currentScenario.tags, - }, - }) - : patchScenarioV1({ - ...ids, - scenarioPatchForm: currentScenario, - }); - - patchScenarioRequest + patchScenarioV2({ + ...ids, + scenarioPatchFormV2: { + description: currentScenario.description, + infra_id: currentScenario.infra_id, + name: currentScenario.name, + tags: currentScenario.tags, + }, + }) .unwrap() .then(() => { dispatch( @@ -257,9 +219,8 @@ export default function AddOrEditScenarioModal({ }; const removeScenario = () => { - const deleteScenario = useTrainScheduleV2 ? deleteScenarioV2 : deleteScenarioV1; if (projectId && studyId && scenario?.id) { - deleteScenario({ projectId, studyId, scenarioId: scenario.id }) + deleteScenarioV2({ projectId, studyId, scenarioId: scenario.id }) .unwrap() .then(() => { dispatch(updateScenarioID(undefined)); diff --git a/front/src/modules/scenario/components/ScenarioExplorer/ScenarioExplorer.tsx b/front/src/modules/scenario/components/ScenarioExplorer/ScenarioExplorer.tsx index f0377726715..387ed6d7a74 100644 --- a/front/src/modules/scenario/components/ScenarioExplorer/ScenarioExplorer.tsx +++ b/front/src/modules/scenario/components/ScenarioExplorer/ScenarioExplorer.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import cx from 'classnames'; import { useTranslation } from 'react-i18next'; @@ -11,10 +11,9 @@ import projectIcon from 'assets/pictures/views/projects.svg'; import studyIcon from 'assets/pictures/views/study.svg'; import { getDocument } from 'common/api/documentApi'; import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; -import type { TrainScheduleSummary } from 'common/api/osrdEditoastApi'; import { useModal } from 'common/BootstrapSNCF/ModalSNCF'; import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; -import { getStdcmV2Activated, getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; +import { getStdcmV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; import ScenarioExplorerModal, { type ScenarioExplorerProps } from './ScenarioExplorerModal'; @@ -31,9 +30,7 @@ const ScenarioExplorer = ({ const { getTimetableID } = useOsrdConfSelectors(); const timetableID = useSelector(getTimetableID); const [imageUrl, setImageUrl] = useState(); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); const stdcmV2Activated = useSelector(getStdcmV2Activated); - const useTrainScheduleV2 = trainScheduleV2Activated || stdcmV2Activated; const { updateInfraID, updateTimetableID, updateScenarioID } = useOsrdConfActions(); const { data: projectDetails } = osrdEditoastApi.endpoints.getProjectsByProjectId.useQuery( @@ -47,18 +44,6 @@ const ScenarioExplorer = ({ { skip: !globalProjectId && !globalStudyId } ); - const { data: scenarioV1 } = - osrdEditoastApi.endpoints.getProjectsByProjectIdStudiesAndStudyIdScenariosScenarioId.useQuery( - { - projectId: globalProjectId as number, - studyId: globalStudyId as number, - scenarioId: globalScenarioId as number, - }, - { - skip: !globalProjectId || !globalStudyId || !globalScenarioId || useTrainScheduleV2, - } - ); - const { currentData: scenarioV2, isSuccess: isScenarioSuccess } = osrdEditoastApi.endpoints.getV2ProjectsByProjectIdStudiesAndStudyIdScenariosScenarioId.useQuery( { @@ -67,19 +52,14 @@ const ScenarioExplorer = ({ scenarioId: globalScenarioId as number, }, { - skip: !useTrainScheduleV2 || !globalProjectId || !globalStudyId || !globalScenarioId, + skip: !globalProjectId || !globalStudyId || !globalScenarioId, refetchOnMountOrArgChange: true, } ); - const { data: timetable } = osrdEditoastApi.endpoints.getTimetableById.useQuery( - { id: timetableID as number }, - { skip: !timetableID || useTrainScheduleV2 } - ); - const { data: timetableV2 } = osrdEditoastApi.endpoints.getV2TimetableById.useQuery( { id: timetableID as number }, - { skip: !useTrainScheduleV2 || !timetableID } + { skip: !timetableID } ); const getProjectImage = async (imageId: number) => { @@ -91,26 +71,14 @@ const ScenarioExplorer = ({ } }; - const validTrainCount = (trains: TrainScheduleSummary[]): number => { - const validTrains = trains.filter( - (train) => train.invalid_reasons && train.invalid_reasons.length === 0 - ); - return validTrains.length; - }; - const v2TrainCount = (trainIds: number[]) => trainIds.length; - const scenario = useMemo( - () => (useTrainScheduleV2 ? scenarioV2 : scenarioV1), - [useTrainScheduleV2, scenarioV2, scenarioV1] - ); - useEffect(() => { - if (scenario) { - dispatch(updateTimetableID(scenario.timetable_id)); - dispatch(updateInfraID(scenario.infra_id)); + if (scenarioV2) { + dispatch(updateTimetableID(scenarioV2.timetable_id)); + dispatch(updateInfraID(scenarioV2.infra_id)); } - }, [scenario]); + }, [scenarioV2]); useEffect(() => { if (projectDetails?.image) { @@ -124,7 +92,7 @@ const ScenarioExplorer = ({ if (!isScenarioSuccess && stdcmV2Activated) { dispatch(updateScenarioID(undefined)); } - }, [scenario, scenarioV2, isScenarioSuccess]); + }, [scenarioV2, isScenarioSuccess]); return (
- {globalProjectId && projectDetails && studyDetails && scenario ? ( + {globalProjectId && projectDetails && studyDetails && scenarioV2 ? (
{displayImgProject && imageUrl && (
@@ -178,28 +146,20 @@ const ScenarioExplorer = ({ scenario icon {t('scenarioLegend')}
- - {scenario.name} + + {scenarioV2.name} + + + + {timetableV2 && v2TrainCount(timetableV2.train_ids)} + - {useTrainScheduleV2 ? ( - - {timetableV2 && v2TrainCount(timetableV2.train_ids)} - - - ) : ( - timetable && ( - - {validTrainCount(timetable.train_schedule_summaries)} - - - ) - )}
infra icon {t('infraLegend')} -
{scenario.infra_name}
+
{scenarioV2.infra_name}
diff --git a/front/src/modules/scenario/components/ScenarioExplorer/ScenarioExplorerModal.tsx b/front/src/modules/scenario/components/ScenarioExplorer/ScenarioExplorerModal.tsx index 1df20071628..39063e7fe45 100644 --- a/front/src/modules/scenario/components/ScenarioExplorer/ScenarioExplorerModal.tsx +++ b/front/src/modules/scenario/components/ScenarioExplorer/ScenarioExplorerModal.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { MdArrowRight } from 'react-icons/md'; -import { useSelector } from 'react-redux'; import projectsLogo from 'assets/pictures/views/projects.svg'; import scenarioExploratorLogo from 'assets/pictures/views/scenarioExplorator.svg'; @@ -16,7 +15,6 @@ import { import ModalBodySNCF from 'common/BootstrapSNCF/ModalSNCF/ModalBodySNCF'; import ModalHeaderSNCF from 'common/BootstrapSNCF/ModalSNCF/ModalHeaderSNCF'; import { setFailure } from 'reducers/main'; -import { getStdcmV2Activated, getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; import { castErrorToFailure } from 'utils/error'; @@ -41,9 +39,6 @@ const ScenarioExplorerModal = ({ const [scenarioID, setScenarioID] = useState(globalScenarioId); const [studiesList, setStudiesList] = useState(); const [scenariosList, setScenariosList] = useState(); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); - const stdcmV2Activated = useSelector(getStdcmV2Activated); - const useTrainScheduleV2 = trainScheduleV2Activated || stdcmV2Activated; const { projectsList, @@ -60,8 +55,6 @@ const ScenarioExplorerModal = ({ ); const [getStudiesList] = osrdEditoastApi.endpoints.getProjectsByProjectIdStudies.useLazyQuery(); - const [getScenariosList] = - osrdEditoastApi.endpoints.getProjectsByProjectIdStudiesAndStudyIdScenarios.useLazyQuery(); const [getV2ScenariosList] = osrdEditoastApi.endpoints.getV2ProjectsByProjectIdStudiesAndStudyIdScenarios.useLazyQuery(); @@ -87,29 +80,17 @@ const ScenarioExplorerModal = ({ useEffect(() => { if (projectID && studyID && !isProjectsError) { - if (useTrainScheduleV2) { - getV2ScenariosList({ - projectId: projectID, - studyId: studyID, - ordering: 'LastModifiedDesc', - pageSize: 1000, - }) - .unwrap() - .then(({ results }) => setScenariosList(results)) - .catch((error) => console.error(error)); - } else { - getScenariosList({ - projectId: projectID, - studyId: studyID, - ordering: 'LastModifiedDesc', - pageSize: 1000, - }) - .unwrap() - .then(({ results }) => setScenariosList(results)) - .catch((error) => console.error(error)); - } + getV2ScenariosList({ + projectId: projectID, + studyId: studyID, + ordering: 'LastModifiedDesc', + pageSize: 1000, + }) + .unwrap() + .then(({ results }) => setScenariosList(results)) + .catch((error) => console.error(error)); } - }, [projectID, studyID, useTrainScheduleV2, isProjectsError]); + }, [projectID, studyID, isProjectsError]); return (
diff --git a/front/src/modules/simulationResult/components/TimeButtons.tsx b/front/src/modules/simulationResult/components/TimeButtons.tsx index 87c0d9b99be..1bac6907362 100644 --- a/front/src/modules/simulationResult/components/TimeButtons.tsx +++ b/front/src/modules/simulationResult/components/TimeButtons.tsx @@ -2,13 +2,10 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FaBackward, FaPause, FaPlay, FaStop } from 'react-icons/fa'; -import { useSelector } from 'react-redux'; import { convertDepartureTimeIntoSec } from 'applications/operationalStudies/utils'; -import type { SimulationReport } from 'common/api/osrdEditoastApi'; import InputSNCF from 'common/BootstrapSNCF/InputSNCF'; import { updateIsPlaying } from 'reducers/osrdsimulation/actions'; -import { getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; import { datetime2time, sec2datetime, time2datetime } from 'utils/timeManipulation'; @@ -22,14 +19,12 @@ const factor2ms = (factor: number) => { }; type TimeButtonsProps = { - selectedTrain?: SimulationReport; departureTime?: string; }; -const TimeButtons = ({ selectedTrain, departureTime }: TimeButtonsProps) => { +const TimeButtons = ({ departureTime }: TimeButtonsProps) => { const dispatch = useAppDispatch(); const { t } = useTranslation('simulation'); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); const [playInterval, setPlayInterval] = useState(undefined); const [playReverse, setPlayReverse] = useState(false); @@ -57,12 +52,8 @@ const TimeButtons = ({ selectedTrain, departureTime }: TimeButtonsProps) => { const stop = () => { clearInterval(playInterval); setPlayInterval(undefined); - if (trainScheduleV2Activated) { - if (departureTime) - updateTimePositionV2(sec2datetime(convertDepartureTimeIntoSec(departureTime))); - } else if (selectedTrain) { - updateTimePosition(sec2datetime(selectedTrain.base.stops[0].time)); - } + if (departureTime) + updateTimePositionV2(sec2datetime(convertDepartureTimeIntoSec(departureTime))); dispatch(updateIsPlaying(false)); }; @@ -84,11 +75,7 @@ const TimeButtons = ({ selectedTrain, departureTime }: TimeButtonsProps) => { } else { i += factor.steps; } - if (trainScheduleV2Activated) { - updateTimePositionV2(new Date(i * 1000)); - } else { - updateTimePosition(new Date(i * 1000)); - } + updateTimePositionV2(new Date(i * 1000)); }, factor.ms); setPlayInterval(playIntervalLocal); dispatch(updateIsPlaying(true)); diff --git a/front/src/modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup.tsx b/front/src/modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup.tsx index b48e8172f58..8440c4e8e6d 100644 --- a/front/src/modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup.tsx +++ b/front/src/modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup.tsx @@ -15,12 +15,8 @@ import { calculateDistanceAlongTrack } from 'applications/editor/tools/utils'; import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; import { osrdEditoastApi } from 'common/api/osrdEditoastApi'; import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; -import { - setPointIti, - setPointItiV2, -} from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/setPointIti'; +import { setPointItiV2 } from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/setPointIti'; import type { PathStep } from 'reducers/osrdconf/types'; -import { getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; type FeatureInfoClickType = { displayPopup: boolean; @@ -37,7 +33,6 @@ function RenderPopup({ pathProperties }: RenderPopupProps) { const osrdConfActions = useOsrdConfActions(); const { t } = useTranslation(['operationalStudies/manageTrainSchedule']); const featureInfoClick: FeatureInfoClickType = useSelector(getFeatureInfoClick); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); const infraId = useSelector(getInfraID); const [trackOffset, setTrackOffset] = useState(0); @@ -74,7 +69,7 @@ function RenderPopup({ pathProperties }: RenderPopupProps) { setTrackOffset(offset); }; - if (trainScheduleV2Activated && featureInfoClick.displayPopup) { + if (featureInfoClick.displayPopup) { calculateOffset(); } }, [featureInfoClick]); @@ -87,13 +82,6 @@ function RenderPopup({ pathProperties }: RenderPopupProps) { ) return null; - const properties = { - ...featureInfoClick.feature.properties, - coordinates: featureInfoClick.coordinates.slice(0, 2), - }; - - // TS2 section - // TODO TS2: if !pathProperties, return null const { properties: trackProperties } = featureInfoClick.feature; const coordinates = featureInfoClick.coordinates.slice(0, 2); @@ -128,67 +116,35 @@ function RenderPopup({ pathProperties }: RenderPopupProps) { {featureInfoClick.feature.properties.extensions_sncf_line_name}
- {trainScheduleV2Activated ? ( -
- - - -
- ) : ( -
- - - -
- )} + +
+ + + +
); } diff --git a/front/src/modules/trainschedule/components/ManageTrainSchedule/Map.tsx b/front/src/modules/trainschedule/components/ManageTrainSchedule/Map.tsx index 1bac1d0d176..660c0e0b84c 100644 --- a/front/src/modules/trainschedule/components/ManageTrainSchedule/Map.tsx +++ b/front/src/modules/trainschedule/components/ManageTrainSchedule/Map.tsx @@ -43,13 +43,10 @@ import { computeBBoxViewport } from 'common/Map/WarpedMap/core/helpers'; import { useInfraID, useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; import { LAYER_GROUPS_ORDER, LAYERS } from 'config/layerOrder'; import VirtualLayers from 'modules/simulationResult/components/SimulationResultsMap/VirtualLayers'; -import Itinerary from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/Itinerary'; -import ItineraryMarkers from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/ItineraryMarkers'; import RenderPopup from 'modules/trainschedule/components/ManageTrainSchedule/ManageTrainScheduleMap/RenderPopup'; import { updateViewport } from 'reducers/map'; import type { Viewport } from 'reducers/map'; import { getMap, getTerrain3DExaggeration } from 'reducers/map/selectors'; -import { getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; import { getMapMouseEventNearestFeature } from 'utils/mapHelper'; @@ -74,7 +71,6 @@ const Map = ({ const infraID = useInfraID(); const terrain3DExaggeration = useSelector(getTerrain3DExaggeration); const { viewport, mapSearchMarker, mapStyle, showOSM, layersSettings } = useSelector(getMap); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); const [mapIsLoaded, setMapIsLoaded] = useState(false); const [showLayers, setShowLayers] = useState(true); @@ -376,22 +372,16 @@ const Map = ({ )} - {mapIsLoaded && - (trainScheduleV2Activated ? ( - <> - - {mapRef.current && } - - ) : ( - <> - - {mapRef.current && } - - ))} + {mapIsLoaded && ( + <> + + {mapRef.current && } + + )} {mapSearchMarker && } {snappedPoint !== undefined && } diff --git a/front/src/modules/trainschedule/components/ManageTrainSchedule/TrainSettings.tsx b/front/src/modules/trainschedule/components/ManageTrainSchedule/TrainSettings.tsx index 20ff3004546..2226b505d53 100644 --- a/front/src/modules/trainschedule/components/ManageTrainSchedule/TrainSettings.tsx +++ b/front/src/modules/trainschedule/components/ManageTrainSchedule/TrainSettings.tsx @@ -10,7 +10,6 @@ import { isInvalidName } from 'applications/operationalStudies/utils'; import ChipsSNCF from 'common/BootstrapSNCF/ChipsSNCF'; import InputSNCF from 'common/BootstrapSNCF/InputSNCF'; import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext'; -import { getTrainScheduleV2Activated } from 'reducers/user/userSelectors'; import { useAppDispatch } from 'store'; import { dateTimeToIso } from 'utils/date'; import { useDebounce } from 'utils/helpers'; @@ -28,7 +27,6 @@ export default function TrainSettings() { const initialSpeedFromStore = useSelector(getInitialSpeed); const departureTimeFromStore = useSelector(getDepartureTime); const startTimeFromStore = useSelector(getStartTime); - const trainScheduleV2Activated = useSelector(getTrainScheduleV2Activated); const [name, setName] = useState(nameFromStore); // TODO TS2 : remove departureTime when drop v1 @@ -102,40 +100,22 @@ export default function TrainSettings() { />
- {trainScheduleV2Activated ? ( - - - {/* TOSO TS2 : rename trainScheduleDepartureTime key to trainScheduleStartTime everywhere */} - {t('trainScheduleDepartureTime')} - - } - id="trainSchedule-startTime" - onChange={(e: React.ChangeEvent) => setStartTime(e.target.value)} - value={startTime} - isInvalid={!startTime} - errorMsg={t('errorMessages.mandatoryField')} - noMargin - /> - ) : ( - - - {t('trainScheduleDepartureTime')} - - } - id="trainSchedule-departureTime" - onChange={(e: React.ChangeEvent) => setDepartureTime(e.target.value)} - value={departureTime} - isInvalid={!departureTime} - errorMsg={t('errorMessages.mandatoryField')} - noMargin - /> - )} + + + {/* TODO TS2 : rename trainScheduleDepartureTime key to trainScheduleStartTime everywhere */} + {t('trainScheduleDepartureTime')} + + } + id="trainSchedule-startTime" + onChange={(e: React.ChangeEvent) => setStartTime(e.target.value)} + value={startTime} + isInvalid={!startTime} + errorMsg={t('errorMessages.mandatoryField')} + noMargin + />
; - trainScheduleV2Activated: boolean; stdcmV2Activated: boolean; } @@ -18,7 +17,6 @@ export const userInitialState: UserState = { username: '', userPreferences: { safeWord: '' }, account: {}, - trainScheduleV2Activated: false, stdcmV2Activated: false, }; @@ -46,9 +44,6 @@ export const userSlice = createSlice({ updateUserPreferences(state, action: PayloadAction<{ safeWord: string }>) { state.userPreferences = action.payload; }, - switchTrainScheduleV2Activated(state) { - state.trainScheduleV2Activated = !state.trainScheduleV2Activated; - }, switchStdcmV2Activated(state) { state.stdcmV2Activated = !state.stdcmV2Activated; }, @@ -60,7 +55,6 @@ export const { loginError, logoutSuccess, updateUserPreferences, - switchTrainScheduleV2Activated, switchStdcmV2Activated, } = userSlice.actions; diff --git a/front/src/reducers/user/userReducer.spec.ts b/front/src/reducers/user/userReducer.spec.ts index b4ddb5487dd..bff2e1c504e 100644 --- a/front/src/reducers/user/userReducer.spec.ts +++ b/front/src/reducers/user/userReducer.spec.ts @@ -7,7 +7,6 @@ import { logoutSuccess, type UserState, updateUserPreferences, - switchTrainScheduleV2Activated, switchStdcmV2Activated, } from 'reducers/user'; import { createStoreWithoutMiddleware } from 'store'; @@ -76,13 +75,6 @@ describe('userReducer', () => { }); }); - it('should handle switchTrainScheduleV2Activated', () => { - const store = createStore(userInitialState); - store.dispatch(switchTrainScheduleV2Activated()); - const userState = store.getState().user; - expect(userState.trainScheduleV2Activated).toBe(true); - }); - it('should handle switchStdcmV2Activated', () => { const store = createStore(userInitialState); store.dispatch(switchStdcmV2Activated()); diff --git a/front/src/reducers/user/userSelectors.ts b/front/src/reducers/user/userSelectors.ts index eb1c6435afc..0a47b056473 100644 --- a/front/src/reducers/user/userSelectors.ts +++ b/front/src/reducers/user/userSelectors.ts @@ -5,7 +5,6 @@ import { makeSubSelector } from 'utils/selectors'; export const getUser = (state: RootState) => state.user; const makeUserSelector = makeSubSelector(getUser); export const getUserPreferences = makeUserSelector('userPreferences'); -export const getTrainScheduleV2Activated = makeUserSelector('trainScheduleV2Activated'); export const getStdcmV2Activated = makeUserSelector('stdcmV2Activated'); const makeUserPreferencesSelector = makeSubSelector(getUserPreferences); diff --git a/front/tests/004-scenario-management.spec.ts b/front/tests/004-scenario-management.spec.ts index 85998a3d447..e7aea3d13da 100644 --- a/front/tests/004-scenario-management.spec.ts +++ b/front/tests/004-scenario-management.spec.ts @@ -1,7 +1,7 @@ import { test, expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import type { Infra, Project, Scenario, Study } from 'common/api/osrdEditoastApi'; +import type { Infra, Project, Scenario, Study, Timetable } from 'common/api/osrdEditoastApi'; import scenarioData from './assets/operationStudies/scenario.json'; import CommonPage from './pages/common-page-model'; @@ -13,6 +13,7 @@ let smallInfra: Infra; let project: Project; let study: Study; let scenario: Scenario; +let timetable: Timetable; test.beforeAll(async () => { smallInfra = (await getInfra()) as Infra; @@ -21,11 +22,15 @@ test.beforeAll(async () => { }); test.beforeEach(async () => { - scenario = await postApiRequest(`/api/projects/${project.id}/studies/${study.id}/scenarios`, { + timetable = await postApiRequest(`/api/v2/timetable/`, { + electrical_profile_set_id: null, + }); + scenario = await postApiRequest(`/api/v2/projects/${project.id}/studies/${study.id}/scenarios`, { ...scenarioData, name: `${scenarioData.name} ${uuidv4()}`, study_id: study.id, infra_id: smallInfra.id, + timetable_id: timetable.id, }); }); diff --git a/front/tests/005-operational-studies.spec.ts b/front/tests/005-operational-studies.spec.ts index 2504d87448a..a168c7b2535 100644 --- a/front/tests/005-operational-studies.spec.ts +++ b/front/tests/005-operational-studies.spec.ts @@ -1,7 +1,14 @@ import { test, expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import type { Infra, Project, RollingStock, Scenario, Study } from 'common/api/osrdEditoastApi'; +import type { + Infra, + Project, + RollingStock, + Scenario, + Study, + Timetable, +} from 'common/api/osrdEditoastApi'; import scenarioData from './assets/operationStudies/scenario.json'; import HomePage from './pages/home-page-model'; @@ -14,6 +21,7 @@ let project: Project; let study: Study; let scenario: Scenario; let rollingStock: RollingStock; +let timetable: Timetable; test.beforeAll(async () => { smallInfra = (await getInfra()) as Infra; @@ -23,11 +31,15 @@ test.beforeAll(async () => { }); test.beforeEach(async () => { - scenario = await postApiRequest(`/api/projects/${project.id}/studies/${study.id}/scenarios`, { + timetable = await postApiRequest(`/api/v2/timetable/`, { + electrical_profile_set_id: null, + }); + scenario = await postApiRequest(`/api/v2/projects/${project.id}/studies/${study.id}/scenarios`, { ...scenarioData, name: `${scenarioData.name} ${uuidv4()}`, study_id: study.id, infra_id: smallInfra.id, + timetable_id: timetable.id, }); }); diff --git a/front/tests/006-stdcm-page.spec.ts b/front/tests/006-stdcm-page.spec.ts index a257b115906..cdc4925ab00 100644 --- a/front/tests/006-stdcm-page.spec.ts +++ b/front/tests/006-stdcm-page.spec.ts @@ -12,7 +12,7 @@ const studyName = study.name; const scenarioName = scenario.name; const rollingStockName = 'rollingstock_1500_25000_test_e2e'; -const rollingStockTranslation = manageTrainScheduleTranslation.rollingstock; +const emptyRouteTranslation = manageTrainScheduleTranslation.pathfindingNoState; test.describe('STDCM page', () => { test('should configure and launch a stdcm', async ({ page }) => { @@ -33,17 +33,16 @@ test.describe('STDCM page', () => { await stdcmPage.selectMiniCard(studyName); await stdcmPage.selectMiniCard(scenarioName); - // Check no rollingstock is selected and "rollingstock" is in the missing information - await expect(stdcmPage.missingParams).toContainText(rollingStockTranslation); + // Check no route is selected + await expect(stdcmPage.pathfindingNoState).toContainText(emptyRouteTranslation); // Select a rolling stock await stdcmPage.openRollingstockModal(); await expect(stdcmPage.rollingStockSelectorModal).toBeVisible(); await stdcmPage.selectRollingStock(rollingStockName); - // Check that the rollingstock is selected and "rollingstock" is not in the missing information anymore + // Check that the rollingstock is selected await expect(stdcmPage.rollingStockSelectorModal).not.toBeVisible(); - await expect(stdcmPage.missingParams).not.toContainText(rollingStockTranslation); await stdcmPage.selectPathByTrigram('MWS', 'NES'); await stdcmPage.checkPathfindingDistance('34.000 km'); diff --git a/front/tests/007-op-rollingstock-tab.spec.ts b/front/tests/007-op-rollingstock-tab.spec.ts index 91aa5414dd5..fb385ec9ee4 100644 --- a/front/tests/007-op-rollingstock-tab.spec.ts +++ b/front/tests/007-op-rollingstock-tab.spec.ts @@ -11,7 +11,6 @@ import type { } from 'common/api/osrdEditoastApi'; import scenarioData from './assets/operationStudies/scenario.json'; -import HomePage from './pages/home-page-model'; import OperationalStudiesPage from './pages/operational-studies-page-model'; import RollingStockSelectorPage from './pages/rollingstock-selector-page'; import ScenarioPage from './pages/scenario-page-model'; @@ -52,11 +51,6 @@ test.describe('Verifying that all elements in the rolling stock tab are loaded c const scenarioPage = new ScenarioPage(page); const rollingStockSelector = new RollingStockSelectorPage(page); - // TODO: DROP TSV1: remove this part - const homePage = new HomePage(page); - await homePage.goToHomePage(); - await homePage.toggleTSV2(); - // Navigate to the created scenario page await page.goto( `/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}` @@ -102,11 +96,6 @@ test.describe('Verifying that all elements in the rolling stock tab are loaded c const scenarioPage = new ScenarioPage(page); const rollingStockSelector = new RollingStockSelectorPage(page); - // TODO: DROP TSV1: remove this part - const homePage = new HomePage(page); - await homePage.goToHomePage(); - await homePage.toggleTSV2(); - // Navigate to the created scenario page await page.goto( `/operational-studies/projects/${project.id}/studies/${study.id}/scenarios/${scenario.id}` diff --git a/front/tests/008-allowances.spec.ts b/front/tests/008-allowances.spec.ts deleted file mode 100644 index 45dbd87e6c1..00000000000 --- a/front/tests/008-allowances.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { test } from '@playwright/test'; -import { v4 as uuidv4 } from 'uuid'; - -import ScenarioPage from './pages/scenario-page-model'; -import createCompleteScenario, { allowancesManagement } from './utils/scenario-utils'; - -let scenarioName: string; -let scenarioPage: ScenarioPage; - -test.describe('Testing if all mandatory elements simulation configuration are loaded in operationnal studies app', () => { - test.beforeEach(async ({ page }) => { - scenarioPage = new ScenarioPage(page); - scenarioName = `Train_Schedule_${uuidv4().slice(0, 5)}`; - await createCompleteScenario(page, scenarioName, '1', '15'); - }); - - // ***************** Test apply allowances ***************** - - test('Testing standard allowances', async () => { - await allowancesManagement(scenarioPage, scenarioName, 'standard'); - }); - - test('Testing engineering allowances', async () => { - await allowancesManagement(scenarioPage, scenarioName, 'engineering'); - }); -}); diff --git a/front/tests/010-op-route-tab.spec.ts b/front/tests/010-op-route-tab.spec.ts index 99986287f2f..0e3840d2e10 100644 --- a/front/tests/010-op-route-tab.spec.ts +++ b/front/tests/010-op-route-tab.spec.ts @@ -40,7 +40,6 @@ test.beforeEach(async ({ page }) => { const homePage = new HomePage(page); await homePage.goToHomePage(); selectedLanguage = await homePage.getOSRDLanguage(); - await homePage.toggleTSV2(); // Navigate to the created scenario page await page.goto( diff --git a/front/tests/global-setup.ts b/front/tests/global-setup.ts index 4fd96ce026b..10ebe2ccdc5 100644 --- a/front/tests/global-setup.ts +++ b/front/tests/global-setup.ts @@ -1,6 +1,7 @@ import fs from 'fs'; import { test as setup } from '@playwright/test'; +import { v4 as uuidv4 } from 'uuid'; import type { Infra, @@ -60,9 +61,16 @@ async function createDataForTests() { budget: 1234567890, } as StudyCreateForm); - await postApiRequest(`/api/projects/${project.id}/studies/${study.id}/scenarios`, { + const timetable = await postApiRequest(`/api/v2/timetable/`, { + electrical_profile_set_id: null, + }); + + await postApiRequest(`/api/v2/projects/${project.id}/studies/${study.id}/scenarios`, { ...scenarioData, + name: `${scenarioData.name} ${uuidv4()}`, + study_id: study.id, infra_id: smallInfra.id, + timetable_id: timetable.id, }); } diff --git a/front/tests/pages/home-page-model.ts b/front/tests/pages/home-page-model.ts index 13251fb5319..0a3930e3cf4 100644 --- a/front/tests/pages/home-page-model.ts +++ b/front/tests/pages/home-page-model.ts @@ -116,16 +116,6 @@ class HomePage { return this.translation[key]; } - // TODO : Delete after drop V1 - // Check TS Version - async toggleTSV2() { - await this.dropDown.click(); - await this.userSettings.click(); - if (!(await this.TSV2Switch.isChecked()) && (await this.TSV2Switch.isVisible())) { - await this.TSV2Switch.click(); - } - } - // Check Stdcm Version async toggleStdcmV1() { await this.dropDown.click(); diff --git a/front/tests/pages/stdcm-page-model.ts b/front/tests/pages/stdcm-page-model.ts index d3d77c47d18..34020fc22ef 100644 --- a/front/tests/pages/stdcm-page-model.ts +++ b/front/tests/pages/stdcm-page-model.ts @@ -3,7 +3,7 @@ import { expect, type Locator, type Page } from '@playwright/test'; import SimulationConfPage from './simulation-conf-page'; class StdcmPage extends SimulationConfPage { - readonly missingParams: Locator; + readonly pathfindingNoState: Locator; // Scenario Explorator private scenarioExplorerButton: Locator; @@ -18,7 +18,7 @@ class StdcmPage extends SimulationConfPage { constructor(page: Page) { super(page); - this.missingParams = page.locator('.missing-params'); + this.pathfindingNoState = page.getByTestId('pathfinding-no-state'); // Scenario Explorator this.scenarioExplorerButton = page.getByTestId('scenario-explorator'); diff --git a/front/tests/utils/scenario-utils.ts b/front/tests/utils/scenario-utils.ts index 92d85d2aa09..da86c13034b 100644 --- a/front/tests/utils/scenario-utils.ts +++ b/front/tests/utils/scenario-utils.ts @@ -7,6 +7,7 @@ import HomePage from '../pages/home-page-model'; import RollingStockSelectorPage from '../pages/rollingstock-selector-page'; import ScenarioPage from '../pages/scenario-page-model'; +// TODO : Check if this util can be reutilized in other tests // Scenario creation export default async function createCompleteScenario( page: Page, @@ -20,7 +21,7 @@ export default async function createCompleteScenario( const rollingStock = await getRollingStock(); const scenario = await postApiRequest( - `/api/projects/${project.id}/studies/${study.id}/scenarios`, + `/api/v2/projects/${project.id}/studies/${study.id}/scenarios`, { ...scenarioData, name: `${scenarioData.name} ${uuidv4()}`,