Skip to content

Commit

Permalink
front: add scenario content context with cached tracks and infraid
Browse files Browse the repository at this point in the history
Signed-off-by: Alice Khoudli <[email protected]>
  • Loading branch information
Synar committed Nov 27, 2024
1 parent be2d4f1 commit 378c835
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import MicroMacroSwitch from 'applications/operationalStudies/components/MicroMa
import NGE from 'applications/operationalStudies/components/NGE/NGE';
import type { NetzgrafikDto, NGEEvent } from 'applications/operationalStudies/components/NGE/types';
import { MANAGE_TRAIN_SCHEDULE_TYPES } from 'applications/operationalStudies/consts';
import { ScenarioContextProvider } from 'applications/operationalStudies/hooks/useScenarioContext';
import useScenarioData from 'applications/operationalStudies/hooks/useScenarioData';
import ImportTrainSchedule from 'applications/operationalStudies/views/ImportTrainSchedule';
import ManageTrainSchedule from 'applications/operationalStudies/views/ManageTrainSchedule';
Expand Down Expand Up @@ -120,123 +121,125 @@ const ScenarioContent = ({
});

return (
<main className="mastcontainer mastcontainer-no-mastnav">
<div className="scenario">
<div className="row scenario-container">
<div
data-testid="scenario-sidemenu"
className={`scenario-sidemenu ${collapsedTimetable ? 'd-none' : 'col-hdp-3 col-xl-4 col-lg-5 col-md-6'}`}
>
<div className="scenario-sidemenu">
<ScenarioDescription
scenario={scenario}
infra={infra}
infraReloadCount={reloadCount}
collapseTimetable={() => setCollapsedTimetable(true)}
/>
<MicroMacroSwitch isMacro={isMacro} setIsMacro={toggleMicroMacroButton} />
{!isMacro && infra && (
<>
{displayTrainScheduleManagement !== MANAGE_TRAIN_SCHEDULE_TYPES.none && (
<TimetableManageTrainSchedule
displayTrainScheduleManagement={displayTrainScheduleManagement}
<ScenarioContextProvider infraId={infra.id}>
<main className="mastcontainer mastcontainer-no-mastnav">
<div className="scenario">
<div className="row scenario-container">
<div
data-testid="scenario-sidemenu"
className={`scenario-sidemenu ${collapsedTimetable ? 'd-none' : 'col-hdp-3 col-xl-4 col-lg-5 col-md-6'}`}
>
<div className="scenario-sidemenu">
<ScenarioDescription
scenario={scenario}
infra={infra}
infraReloadCount={reloadCount}
collapseTimetable={() => setCollapsedTimetable(true)}
/>
<MicroMacroSwitch isMacro={isMacro} setIsMacro={toggleMicroMacroButton} />
{!isMacro && infra && (
<>
{displayTrainScheduleManagement !== MANAGE_TRAIN_SCHEDULE_TYPES.none && (
<TimetableManageTrainSchedule
displayTrainScheduleManagement={displayTrainScheduleManagement}
setDisplayTrainScheduleManagement={setDisplayTrainScheduleManagement}
upsertTrainSchedules={upsertTrainSchedules}
trainIdToEdit={trainIdToEdit}
setTrainIdToEdit={setTrainIdToEdit}
infraState={infra.state}
/>
)}
<Timetable
setDisplayTrainScheduleManagement={setDisplayTrainScheduleManagement}
infraState={infra.state}
selectedTrainId={selectedTrainId}
conflicts={conflicts}
upsertTrainSchedules={upsertTrainSchedules}
trainIdToEdit={trainIdToEdit}
removeTrains={removeTrains}
setTrainIdToEdit={setTrainIdToEdit}
infraState={infra.state}
trainIdToEdit={trainIdToEdit}
trainSchedules={trainSchedules}
trainSchedulesWithDetails={trainScheduleSummaries}
/>
)}
<Timetable
setDisplayTrainScheduleManagement={setDisplayTrainScheduleManagement}
infraState={infra.state}
selectedTrainId={selectedTrainId}
conflicts={conflicts}
upsertTrainSchedules={upsertTrainSchedules}
removeTrains={removeTrains}
setTrainIdToEdit={setTrainIdToEdit}
trainIdToEdit={trainIdToEdit}
trainSchedules={trainSchedules}
trainSchedulesWithDetails={trainScheduleSummaries}
/>
</>
)}
</>
)}
</div>
</div>
</div>

<div className={collapsedTimetable ? 'col-12' : 'col-hdp-9 col-xl-8 col-lg-7 col-md-6'}>
{!isInfraLoaded &&
!isMacro &&
displayTrainScheduleManagement !== MANAGE_TRAIN_SCHEDULE_TYPES.add &&
displayTrainScheduleManagement !== MANAGE_TRAIN_SCHEDULE_TYPES.edit && (
<ScenarioLoaderMessage infraState={infra?.state} />
)}
{(displayTrainScheduleManagement === MANAGE_TRAIN_SCHEDULE_TYPES.add ||
displayTrainScheduleManagement === MANAGE_TRAIN_SCHEDULE_TYPES.edit) && (
<div className="scenario-managetrainschedule">
<ManageTrainSchedule trainIdToEdit={trainIdToEdit} />
</div>
)}
{displayTrainScheduleManagement === MANAGE_TRAIN_SCHEDULE_TYPES.import && (
<div className="scenario-managetrainschedule">
<ImportTrainSchedule
timetableId={scenario.timetable_id}
upsertTrainSchedules={upsertTrainSchedules}
/>
</div>
)}
<div className="scenario-results">
{collapsedTimetable && (
<>
<div className="scenario-timetable-collapsed">
<button
data-testid="timetable-collapse-button"
className="timetable-collapse-button"
type="button"
aria-label={t('toggleTimetable')}
onClick={() => setCollapsedTimetable(false)}
>
<ChevronRight />
</button>
<div className="lead ml-2">{scenario.name}</div>
<div className="d-flex align-items-center ml-auto">
<img src={infraLogo} alt="Infra logo" className="infra-logo mr-2" />
{scenario.infra_name}
</div>
<div className="d-flex align-items-center ml-4">
<span className="mr-1">
<GiElectric />
</span>
{scenario.electrical_profile_set_id
? scenario.electrical_profile_set_id
: t('noElectricalProfileSet')}
</div>
</div>
<MicroMacroSwitch isMacro={isMacro} setIsMacro={toggleMicroMacroButton} />
</>
)}
{isMacro ? (
<div className={cx(collapsedTimetable ? 'macro-container' : 'h-100')}>
<NGE dto={ngeDto} onOperation={handleNGEOperation} />
<div className={collapsedTimetable ? 'col-12' : 'col-hdp-9 col-xl-8 col-lg-7 col-md-6'}>
{!isInfraLoaded &&
!isMacro &&
displayTrainScheduleManagement !== MANAGE_TRAIN_SCHEDULE_TYPES.add &&
displayTrainScheduleManagement !== MANAGE_TRAIN_SCHEDULE_TYPES.edit && (
<ScenarioLoaderMessage infraState={infra?.state} />
)}
{(displayTrainScheduleManagement === MANAGE_TRAIN_SCHEDULE_TYPES.add ||
displayTrainScheduleManagement === MANAGE_TRAIN_SCHEDULE_TYPES.edit) && (
<div className="scenario-managetrainschedule">
<ManageTrainSchedule trainIdToEdit={trainIdToEdit} />
</div>
) : (
isInfraLoaded &&
infra && (
<SimulationResults
scenarioData={{ name: scenario.name, infraName: scenario.infra_name }}
collapsedTimetable={collapsedTimetable}
projectionData={projectionData}
simulationResults={simulationResults}
infraId={infra.id}
conflicts={conflicts}
)}
{displayTrainScheduleManagement === MANAGE_TRAIN_SCHEDULE_TYPES.import && (
<div className="scenario-managetrainschedule">
<ImportTrainSchedule
timetableId={scenario.timetable_id}
upsertTrainSchedules={upsertTrainSchedules}
/>
)
</div>
)}
<div className="scenario-results">
{collapsedTimetable && (
<>
<div className="scenario-timetable-collapsed">
<button
data-testid="timetable-collapse-button"
className="timetable-collapse-button"
type="button"
aria-label={t('toggleTimetable')}
onClick={() => setCollapsedTimetable(false)}
>
<ChevronRight />
</button>
<div className="lead ml-2">{scenario.name}</div>
<div className="d-flex align-items-center ml-auto">
<img src={infraLogo} alt="Infra logo" className="infra-logo mr-2" />
{scenario.infra_name}
</div>
<div className="d-flex align-items-center ml-4">
<span className="mr-1">
<GiElectric />
</span>
{scenario.electrical_profile_set_id
? scenario.electrical_profile_set_id
: t('noElectricalProfileSet')}
</div>
</div>
<MicroMacroSwitch isMacro={isMacro} setIsMacro={toggleMicroMacroButton} />
</>
)}
{isMacro ? (
<div className={cx(collapsedTimetable ? 'macro-container' : 'h-100')}>
<NGE dto={ngeDto} onOperation={handleNGEOperation} />
</div>
) : (
isInfraLoaded &&
infra && (
<SimulationResults
scenarioData={{ name: scenario.name, infraName: scenario.infra_name }}
collapsedTimetable={collapsedTimetable}
projectionData={projectionData}
simulationResults={simulationResults}
infraId={infra.id}
conflicts={conflicts}
/>
)
)}
</div>
</div>
</div>
</div>
</div>
</main>
</main>
</ScenarioContextProvider>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useRef, useCallback } from 'react';

import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
import type { InfraObjectWithGeometry } from 'common/api/osrdEditoastApi';

export default function useCachedTrackSections(infraId: number) {
const trackIdsRef = useRef<Set<string>>(new Set());
const trackSectionsRef = useRef<InfraObjectWithGeometry[]>([]);
const [loadInfraObject, { isLoading }] =
osrdEditoastApi.endpoints.postInfraByInfraIdObjectsAndObjectType.useMutation();

const getTrackSectionsByIds = useCallback(
async (requestedTrackIds: string[]) => {
let fetchedSections: InfraObjectWithGeometry[] = [];
const uniqueNewIds = requestedTrackIds.filter((id) => !trackIdsRef.current.has(id));
if (uniqueNewIds.length !== 0) {
try {
fetchedSections = await loadInfraObject({
infraId,
objectType: 'TrackSection',
body: uniqueNewIds,
}).unwrap();

uniqueNewIds.forEach((id) => trackIdsRef.current.add(id));
trackSectionsRef.current = [...trackSectionsRef.current, ...fetchedSections];
} catch (error) {
console.error('Failed to fetch track sections:', error);
}
}

return trackSectionsRef.current.filter((section) =>
requestedTrackIds.includes(section.obj_id)
);
},
[infraId]
);

return { getTrackSectionsByIds, isLoading };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createContext, useContext, useMemo, type ReactNode } from 'react';

import useCachedTrackSections from 'applications/operationalStudies/hooks/useCachedTrackSections';
import type { InfraObjectWithGeometry } from 'common/api/osrdEditoastApi';

type ScenarioContextType = {
getTrackSectionsByIds: (requestedTrackIds: string[]) => Promise<InfraObjectWithGeometry[]>;
infraId: number;
trackSectionsLoading: boolean;
} | null;
const ScenarioContext = createContext<ScenarioContextType>(null);

type ScenarioContextProviderProps = { infraId: number; children: ReactNode };

export const ScenarioContextProvider = ({ infraId, children }: ScenarioContextProviderProps) => {
const { getTrackSectionsByIds, isLoading: trackSectionsLoading } =
useCachedTrackSections(infraId);
const providedContext = useMemo(
() => ({
getTrackSectionsByIds,
infraId,
trackSectionsLoading,
}),
[getTrackSectionsByIds, infraId, trackSectionsLoading]
);
return <ScenarioContext.Provider value={providedContext}>{children}</ScenarioContext.Provider>;
};

export const useScenarioContext = () => {
const context = useContext(ScenarioContext);
if (!context) {
throw new Error('useScenarioContext must be used within a TrackSectionsProvider');
}
return context;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useEffect } from 'react';

import { type Position } from '@turf/helpers';
import { omit } from 'lodash';
import { useSelector } from 'react-redux';

import {
osrdEditoastApi,
Expand All @@ -13,9 +12,9 @@ import {
type TrainScheduleResult,
type PathfindingResult,
} from 'common/api/osrdEditoastApi';
import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext';
import { useOsrdConfActions } from 'common/osrdContext';
import {
formatSuggestedOperationalPointsWithTrackName,
formatSuggestedOperationalPoints,
matchPathStepAndOp,
upsertPathStepsInOPs,
} from 'modules/pathfinding/utils';
Expand All @@ -29,6 +28,7 @@ import { castErrorToFailure } from 'utils/error';
import { getPointCoordinates } from 'utils/geometry';

import type { ManageTrainSchedulePathProperties } from '../types';
import { useScenarioContext } from './useScenarioContext';

type ItineraryForTrainUpdate = {
pathSteps: (PathStep | null)[];
Expand Down Expand Up @@ -64,8 +64,6 @@ const useSetupItineraryForTrainUpdate = (
setPathProperties: (pathProperties: ManageTrainSchedulePathProperties) => void,
trainIdToEdit: number
) => {
const { getInfraID } = useOsrdConfSelectors();
const infraId = useSelector(getInfraID);
const dispatch = useAppDispatch();

const { updatePathSteps } = useOsrdConfActions();
Expand All @@ -77,6 +75,7 @@ const useSetupItineraryForTrainUpdate = (
osrdEditoastApi.endpoints.postInfraByInfraIdPathfindingBlocks.useMutation();
const [postPathProperties] =
osrdEditoastApi.endpoints.postInfraByInfraIdPathProperties.useMutation();
const { infraId } = useScenarioContext();

useEffect(() => {
const computeItineraryForTrainUpdate = async (
Expand Down Expand Up @@ -124,14 +123,11 @@ const useSetupItineraryForTrainUpdate = (
const stepsCoordinates = pathfindingResult.path_item_positions.map((position) =>
getPointCoordinates(geometry, pathfindingResult.length, position)
);
const suggestedOperationalPoints: SuggestedOP[] =
await formatSuggestedOperationalPointsWithTrackName(
operational_points,
geometry,
pathfindingResult.length,
infraId,
dispatch
);
const suggestedOperationalPoints: SuggestedOP[] = formatSuggestedOperationalPoints(
operational_points,
geometry,
pathfindingResult.length
);

const computedpathSteps = computeBasePathSteps(trainSchedule);
const updatedPathSteps: PathStep[] = updatePathStepsFromOperationalPoints(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ const SimulationResults = ({
const { operationalPoints, loading: formattedOpPointsLoading } = useFormattedOperationalPoints(
selectedTrainSchedule,
trainSimulation,
pathProperties,
infraId
pathProperties
);

// Compute path items coordinates in order to place them on the map
Expand Down
Loading

0 comments on commit 378c835

Please sign in to comment.