Skip to content

Commit

Permalink
front: adapt power restriction v2 inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
RomainValls committed Jun 25, 2024
1 parent de1e59c commit 3d839bc
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';

import { compact } from 'lodash';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

Expand All @@ -16,6 +17,7 @@ import { useOsrdConfSelectors } from 'common/osrdContext';
import { useStoreDataForSpeedLimitByTagSelector } from 'common/SpeedLimitByTagSelector/useStoreDataForSpeedLimitByTagSelector';
import Tabs from 'common/Tabs';
import ItineraryV2 from 'modules/pathfinding/components/Itinerary/ItineraryV2';
import { upsertViasInOPs } from 'modules/pathfinding/utils';
import PowerRestrictionsSelectorV2 from 'modules/powerRestriction/components/PowerRestrictionsSelectorV2';
import RollingStock2Img from 'modules/rollingStock/components/RollingStock2Img';
import { RollingStockSelector } from 'modules/rollingStock/components/RollingStockSelector';
Expand Down Expand Up @@ -56,6 +58,19 @@ const ManageTrainScheduleV2 = () => {
value: string;
};

useEffect(() => {
if (pathProperties) {
const allVias = upsertViasInOPs(
pathProperties.suggestedOperationalPoints,
compact(pathSteps)
);
setPathProperties({
...pathProperties,
allVias,
});
}
}, [pathSteps]);

const pathElectrificationRanges = (): IntervalItem[] => {
if (!pathProperties || !pathProperties.electrifications) return [];

Expand Down
11 changes: 7 additions & 4 deletions front/src/common/IntervalsEditor/IntervalsEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type IntervalsEditorProps = {
/** Total length of the path */
totalLength: number;
disableDrag?: boolean;
onResizeFromInput?: (intervalIndex: number, newEnd: number, context: 'begin' | 'end') => void;
} & (
| {
intervalType: INTERVAL_TYPES.NUMBER;
Expand Down Expand Up @@ -99,6 +100,7 @@ const IntervalsEditor = (props: IntervalsEditorProps) => {
mergeTool: true,
},
disableDrag = false,
onResizeFromInput,
} = props;

// Which segment areas are visible
Expand All @@ -113,13 +115,13 @@ const IntervalsEditor = (props: IntervalsEditorProps) => {
// Data to display
const [resizingData, setResizingData] = useState<IntervalItem[]>(data);

useEffect(() => {
setResizingData(data);
}, [data]);

// Which segment is selected
const [selected, setSelected] = useState<number | null>(null);

useEffect(() => {
setResizingData(data);
}, [data, selected]);

// For mouse click / doubleClick
const [clickTimeout, setClickTimeout] = useState<number | null>(null);
const [clickPrevent, setClickPrevent] = useState<boolean>(false);
Expand Down Expand Up @@ -333,6 +335,7 @@ const IntervalsEditor = (props: IntervalsEditorProps) => {
interval={data[selected]}
selectedIntervalIndex={selected}
setData={setData}
onInputChange={onResizeFromInput}
setSelectedIntervalIndex={setSelected}
totalLength={totalLength}
defaultValue={defaultValue}
Expand Down
12 changes: 9 additions & 3 deletions front/src/common/IntervalsEditor/IntervalsEditorCommonForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ type IntervalsEditorFormProps = {
data: IntervalItem[];
interval: IntervalItem;
selectedIntervalIndex: number;
setData: (newData: IntervalItem[]) => void;
setData: (newData: IntervalItem[], selectedIntervalIndex?: number) => void;
onInputChange?: (intervalIndex: number, newBegin: number, context: 'begin' | 'end') => void;
setSelectedIntervalIndex: (selectedIntervalIndex: number) => void;
totalLength: number;
defaultValue: string | number;
Expand All @@ -26,11 +27,12 @@ const IntervalsEditorCommonForm = ({
setSelectedIntervalIndex,
totalLength,
defaultValue,
onInputChange,
}: IntervalsEditorFormProps) => {
const { t } = useTranslation('common/common');

const [begin, setBegin] = useState<number>(Math.round(interval.begin));
const [end, setEnd] = useState(Math.round(interval.end));
const [end, setEnd] = useState<number>(Math.round(interval.end));

useEffect(() => {
setBegin(Math.round(interval.begin));
Expand All @@ -53,7 +55,11 @@ const IntervalsEditorCommonForm = ({
fieldName: 'value',
defaultValue,
});
setData(fixedResults);
if (onInputChange) {
onInputChange(selectedIntervalIndex, newPosition, context);
} else {
setData(fixedResults, selectedIntervalIndex);
}

// update the selected interval if needed
// corner case: if we create a new empty first segment
Expand Down
8 changes: 5 additions & 3 deletions front/src/common/IntervalsEditor/IntervalsEditorTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from 'react';

import { useTranslation } from 'react-i18next';

import { formatMValue } from 'utils/strings';

import type { IntervalItem } from './types';

interface IntervalsEditorTooltip {
Expand All @@ -13,15 +15,15 @@ const IntervalsEditorTooltip = ({ item, point }: IntervalsEditorTooltip) => {
const { t } = useTranslation('common/common');
return (
<div className="linear-metadata-tooltip">
<div className="header">{point && <span>{Math.round(point)}</span>}</div>
<div className="header">{point && <span>{Math.round(formatMValue(point))}</span>}</div>
<div className="content">
<div>
<span className="mr-3">{t('begin')}</span>
{Math.round(item.begin)}
{Math.round(formatMValue(item.begin))}
</div>
<div>
<span className="mr-3">{t('end')}</span>
{Math.round(item.end)}
{Math.round(formatMValue(item.end))}
</div>
<div>
<span className="mr-3">{t('value')}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ const PowerRestrictionsSelectorV2 = ({
resetPowerRestrictionRangesV2,
cutPowerRestrictionRangesV2,
deletePowerRestrictionRangesV2,
resizePowerRestrictionRangeV2,
} = useOsrdConfActions();
const powerRestrictionRanges = useSelector(getPowerRestrictionV2);
const pathSteps = compact(useSelector(getPathSteps));

// pathSteps + points de coupure d'électricité
// const [updatedPathSteps, setUpdatedPathSteps] = useState<PathStep[]>([]);
const [cutPositions, setCutPositions] = useState<number[]>([]);
const [intervalsEditorData, setIntervalsEditorData] = useState<IntervalItem[]>([]);

Expand Down Expand Up @@ -198,6 +198,53 @@ const PowerRestrictionsSelectorV2 = ({
dispatch(deletePowerRestrictionRangesV2({ from: fromPathStep, to: toPathStep }));
}
};

const resizeSegmentByInput = (
selectedSegmentIndex: number,
newEnd: number,
context: 'begin' | 'end'
) => {
const firstIndex = context === 'end' ? selectedSegmentIndex : selectedSegmentIndex - 1;

let firstRestriction: PowerRestrictionV2 | undefined;
let secondRestriction: PowerRestrictionV2 | undefined;
if (firstIndex >= 0) {
const firstRangeData = intervalsEditorData[firstIndex];
if (firstRangeData.value !== NO_POWER_RESTRICTION) {
const fromPathStep = pathSteps.find((step) => step.positionOnPath === firstRangeData.begin);
const toPathStep = pathSteps.find((step) => step.positionOnPath === firstRangeData.end);
if (fromPathStep && toPathStep) {
firstRestriction = powerRestrictionRanges.find(
(restriction) =>
restriction.from === fromPathStep.id && restriction.to === toPathStep.id
);
}
}
}

const secondRangeData = intervalsEditorData[firstIndex + 1];
if (secondRangeData.value !== NO_POWER_RESTRICTION) {
const fromPathStep = pathSteps.find((step) => step.positionOnPath === secondRangeData.begin);
const toPathStep = pathSteps.find((step) => step.positionOnPath === secondRangeData.end);
if (fromPathStep && toPathStep) {
secondRestriction = powerRestrictionRanges.find(
(restriction) => restriction.from === fromPathStep.id && restriction.to === toPathStep.id
);
}
}

let newEndPathStep = pathSteps.find((pathStep) => pathStep.positionOnPath === newEnd);
if (!newEndPathStep) {
newEndPathStep = createPathStep(newEnd, cumulativeSums, pathProperties, pathSteps);
}

if (firstRestriction && newEndPathStep) {
dispatch(
resizePowerRestrictionRangeV2({ firstRestriction, secondRestriction, newEndPathStep })
);
}
};

const formatElectricalRanges = (
ranges: PowerRestrictionV2[]
): { begin: number; end: number; value: string }[] => {
Expand Down Expand Up @@ -324,6 +371,7 @@ const PowerRestrictionsSelectorV2 = ({
deleteTool: true,
}}
disableDrag
onResizeFromInput={resizeSegmentByInput}
/>
<button type="button" onClick={() => dispatch(resetPowerRestrictionRangesV2())}>
RESET
Expand Down
140 changes: 133 additions & 7 deletions front/src/reducers/osrdconf/osrdConfCommon/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CaseReducer, PayloadAction, PrepareAction } from '@reduxjs/toolkit';
import type { Draft } from 'immer';
import { compact, keyBy, omit, sortBy } from 'lodash';
import { compact, isEqual, keyBy, omit, sortBy } from 'lodash';
import nextId from 'react-id-generator';

import type { PointOnMap, PowerRestrictionV2 } from 'applications/operationalStudies/consts';
Expand All @@ -22,6 +22,8 @@ import { addElementAtIndex, removeElementAtIndex, replaceElementAtIndex } from '
import { formatIsoDate } from 'utils/date';
import type { ArrayElement } from 'utils/types';

import { checkValidPathStep } from './utils';

export const defaultCommonConf: OsrdConfState = {
constraintDistribution: 'MARECO',
name: '',
Expand Down Expand Up @@ -122,8 +124,15 @@ interface CommonConfReducers<S extends OsrdConfState> extends InfraStateReducers
S,
PayloadAction<{ from: PathStep; to: PathStep }>
>;
['resizePowerRestrictionRangeV2']: CaseReducer<
S,
PayloadAction<{
firstRestriction: PowerRestrictionV2;
secondRestriction?: PowerRestrictionV2;
newEndPathStep: PathStep;
}>
>;
['resetPowerRestrictionRangesV2']: CaseReducer<S>;
// ajouter une nouvelle action updatePowerRestrionRangesV2
['updateTrainScheduleIDsToModify']: CaseReducer<S, PayloadAction<S['trainScheduleIDsToModify']>>;
['updateFeatureInfoClick']: CaseReducer<S, PayloadAction<S['featureInfoClick']>>;
['updatePathSteps']: CaseReducer<S, PayloadAction<S['pathSteps']>>;
Expand Down Expand Up @@ -450,11 +459,6 @@ export function buildCommonConfReducers<S extends OsrdConfState>(): CommonConfRe
(restriction) => restriction.from !== from.id && restriction.to !== to.id
);

const checkValidPathStep = (pathStep: PathStep) => {
if (!pathStep || pathStep.locked || pathStep.arrival || pathStep.stopFor) return false;
return true;
};

// TO DO: retirer les path steps inutilisés
// / ! \ verifier le champ des marges après rebase
const fromIsUsed = newPowerRestrictionRangesV2.some(
Expand Down Expand Up @@ -486,6 +490,128 @@ export function buildCommonConfReducers<S extends OsrdConfState>(): CommonConfRe
state.pathSteps = newPathSteps;
state.powerRestrictionV2 = newPowerRestrictionRangesV2;
},
resizePowerRestrictionRangeV2(
state: Draft<S>,
action: PayloadAction<{
firstRestriction: PowerRestrictionV2;
secondRestriction?: PowerRestrictionV2;
newEndPathStep: PathStep;
}>
) {
const { firstRestriction, secondRestriction, newEndPathStep } = action.payload;
let newPathSteps = [...state.pathSteps];
let newPowerRestrictionRanges = state.powerRestrictionV2.filter(
(restriction) =>
!isEqual(restriction, firstRestriction) || !isEqual(restriction, secondRestriction)
);

// remove the previous end pathStep of the first restriction if needed
const oldPathStepEndId = firstRestriction.to;
const oldPathStepEnd = newPathSteps.find(
(pathStep) => pathStep && pathStep.id === oldPathStepEndId
);
if (oldPathStepEnd) {
const oldPathStepEndIsUsed = newPowerRestrictionRanges.some(
(restriction) =>
restriction.from === oldPathStepEndId || restriction.to === oldPathStepEndId
);
if (oldPathStepEndIsUsed && checkValidPathStep(oldPathStepEnd)) {
newPathSteps = newPathSteps.filter(
(pathStep) => pathStep?.positionOnPath !== oldPathStepEnd.positionOnPath
);
}
}

// create the new to pathStep if it does not exist
const newEndPathStepExists = newPathSteps.some(
(pathStep) => pathStep && pathStep.id === newEndPathStep.id
);
if (!newEndPathStepExists) {
const index = newPathSteps.findIndex(
(step) => step?.positionOnPath && step.positionOnPath > newEndPathStep.positionOnPath!
);

newPathSteps = addElementAtIndex(newPathSteps, index, newEndPathStep);
}

// remove the previous from pathStep of the second restriction if needed
if (secondRestriction) {
const oldBeginPathStepId = secondRestriction.from;
const oldBeginPathStep = newPathSteps.find(
(pathStep) => pathStep && pathStep.id === oldBeginPathStepId
);

if (oldBeginPathStep) {
const oldBeginPathStepIsUsed = newPowerRestrictionRanges.some(
(restriction) =>
restriction.from === oldBeginPathStepId || restriction.to === oldBeginPathStepId
);

if (!oldBeginPathStepIsUsed && checkValidPathStep(oldBeginPathStep)) {
newPathSteps = newPathSteps.filter(
(pathStep) => pathStep?.positionOnPath !== oldBeginPathStep.positionOnPath
);
}
}
}

// update the powerRestrictions
newPowerRestrictionRanges = newPowerRestrictionRanges.map((restriction) => {
if (restriction.to === firstRestriction.to) {
return { ...restriction, to: newEndPathStep.id };
}
if (restriction.from === secondRestriction?.from) {
return { ...restriction, from: newEndPathStep.id };
}
return restriction;
});
// retirer les ranges qui sont complètement recouvertes
if (secondRestriction) {
const secondRestrictionToPathStep = newPathSteps.find(
(pathStep) => pathStep && pathStep.id === secondRestriction.to
);
if (secondRestrictionToPathStep) {
const isRangeCovered = (range: PowerRestrictionV2) => {
const fromPathStep = newPathSteps.find(
(pathStep) => pathStep && pathStep.id === range.from
);
const toPathStep = newPathSteps.find(
(pathStep) => pathStep && pathStep.id === range.to
);

if (
fromPathStep?.positionOnPath === undefined ||
toPathStep?.positionOnPath === undefined
) {
return false;
}
return (
fromPathStep?.positionOnPath >= newEndPathStep.positionOnPath! &&
toPathStep?.positionOnPath <= secondRestrictionToPathStep.positionOnPath!
);
};
newPowerRestrictionRanges = newPowerRestrictionRanges.filter(
(restriction) => !isRangeCovered(restriction)
);
}
if (secondRestrictionToPathStep) {
const secondRestrictionToPathStepIsUsed = newPowerRestrictionRanges.some(
(restriction) =>
restriction.from === secondRestrictionToPathStep.id ||
restriction.to === secondRestrictionToPathStep.id
);

if (!secondRestrictionToPathStepIsUsed && newPathSteps) {
newPathSteps = newPathSteps.filter(
(pathStep) => pathStep && pathStep.id !== secondRestrictionToPathStep!.id
);
}
}
}

state.pathSteps = newPathSteps;
state.powerRestrictionV2 = newPowerRestrictionRanges;
},
// TODO Remove this
resetPowerRestrictionRangesV2(state: Draft<S>) {
state.powerRestrictionV2 = [];
Expand Down
Loading

0 comments on commit 3d839bc

Please sign in to comment.