Skip to content

Commit 7e66514

Browse files
committed
front: add power restriction warning banner and incompatible cell styling
Show a warning banner above the table when power restrictions are incompatible with the electrification, and highlight the affected cells with warning colors. Signed-off-by: nncluzu <ngamenichaka@yahoo.fr>
1 parent cd89cce commit 7e66514

File tree

6 files changed

+123
-8
lines changed

6 files changed

+123
-8
lines changed

front/public/locales/en/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,8 @@
842842
"timeFromPreviousOp": "time from above waypoint",
843843
"totalTravelTime": "total travel time",
844844
"trackName": "track",
845-
"waypoint": "Waypoint {{id}}"
845+
"waypoint": "Waypoint {{id}}",
846+
"powerRestrictionIncompatibility_one": "{{count}} power restriction code isn't compatible with the electrification.",
847+
"powerRestrictionIncompatibility_other": "{{count}} power restriction codes aren't compatible with the electrification."
846848
}
847849
}

front/public/locales/fr/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,8 @@
842842
"timeFromPreviousOp": "temps depuis le point précédent",
843843
"totalTravelTime": "temps total du trajet",
844844
"trackName": "voie",
845-
"waypoint": "Via {{id}}"
845+
"waypoint": "Via {{id}}",
846+
"powerRestrictionIncompatibility_one": "{{count}} code de restriction de puissance n'est pas compatible avec l'électrification.",
847+
"powerRestrictionIncompatibility_other": "{{count}} codes de restriction de puissance ne sont pas compatibles avec l'électrification."
846848
}
847849
}

front/src/applications/operationalStudies/views/Scenario/components/SimulationResults/SimulationResults.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ const SimulationResults = ({
373373
upsertTimetableItems={upsertTimetableItems}
374374
isSimulationDataLoading={isSimulationDataLoading}
375375
operationalPointsOnPath={simulationResults.pathProperties?.operationalPoints}
376+
voltages={simulationResults.pathProperties?.voltages}
376377
rollingStock={simulationResults.rollingStock}
377378
{...(simulationResults?.isValid &&
378379
simulationSummary?.isValid && {

front/src/modules/timesStops/TimesStopsOutput.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,25 @@ import cx from 'classnames';
44
import { useSelector } from 'react-redux';
55

66
import type { PathPropertiesFormatted } from 'applications/operationalStudies/types';
7+
import {
8+
getPowerRestrictionsWarnings,
9+
countWarnings,
10+
} from 'applications/operationalStudies/views/Scenario/components/ManageTimetableItem/PowerRestrictionsSelector/helpers/powerRestrictionWarnings';
711
import type {
812
CorePathfindingResultSuccess,
913
ReceptionSignal,
1014
RollingStock,
1115
SimulationResponseSuccess,
1216
} from 'common/api/osrdEditoastApi';
17+
import { matchPathStepAndOp } from 'modules/pathfinding/utils';
1318
import type { SimulationSummary, TimetableItemWithDetails } from 'modules/timetableItem/types';
1419
import type { TimetableItem, Train } from 'reducers/osrdconf/types';
1520
import { getUseNewTimesStopsTable } from 'reducers/user/userSelectors';
1621
import { formatLocalTime } from 'utils/date';
1722
import { Duration } from 'utils/duration';
1823

1924
import { computeOptimisticRow } from './helpers/cellUpdate';
25+
import { buildOpMatchParams } from './helpers/utils';
2026
import useOutputTableData from './hooks/useOutputTableData';
2127
import useTimesStopsTableData from './hooks/useTimesStopsTableData';
2228
import useUpdateTimesStopsTable from './hooks/useUpdateTimesStopsTable';
@@ -35,6 +41,7 @@ type TimesStopsOutputProps = {
3541
simulatedPathItemTimes?: Extract<SimulationSummary, { isValid: true }>['pathItemTimes'];
3642
simulatedPathItemRespect?: Extract<SimulationSummary, { isValid: true }>['pathItemRespect'];
3743
operationalPointsOnPath?: PathPropertiesFormatted['operationalPoints'];
44+
voltages?: PathPropertiesFormatted['voltages'];
3845
isSimulationDataLoading?: boolean;
3946
rollingStock?: RollingStock;
4047
};
@@ -49,6 +56,7 @@ const TimesStopsOutput = ({
4956
simulatedPathItemTimes,
5057
simulatedPathItemRespect,
5158
operationalPointsOnPath,
59+
voltages,
5260
isSimulationDataLoading = false,
5361
rollingStock,
5462
}: TimesStopsOutputProps) => {
@@ -115,6 +123,61 @@ const TimesStopsOutput = ({
115123
[rollingStock]
116124
);
117125

126+
const { powerRestrictionWarningCount, incompatiblePowerRestrictionIds } = useMemo(() => {
127+
const empty = {
128+
powerRestrictionWarningCount: 0,
129+
incompatiblePowerRestrictionIds: new Set<string>(),
130+
};
131+
if (!voltages?.length || !rollingStock || !selectedTrain.power_restrictions?.length)
132+
return empty;
133+
134+
const pathStepPositions = new Map<string, number>();
135+
selectedTrain.path.forEach((pathStep) => {
136+
const matchingOp = operationalPointsOnPath?.find((op) =>
137+
matchPathStepAndOp(pathStep.location, buildOpMatchParams(op))
138+
);
139+
if (matchingOp) pathStepPositions.set(pathStep.id, matchingOp.position);
140+
});
141+
142+
const rangesWithId = selectedTrain.power_restrictions.flatMap((pr) => {
143+
const begin = pathStepPositions.get(pr.from);
144+
const end = pathStepPositions.get(pr.to);
145+
if (begin === undefined || end === undefined) return [];
146+
return [{ begin, end, value: pr.value, fromId: pr.from }];
147+
});
148+
149+
if (!rangesWithId.length) return empty;
150+
151+
const warnings = getPowerRestrictionsWarnings(
152+
rangesWithId,
153+
voltages,
154+
rollingStock.effort_curves.modes
155+
);
156+
157+
const warningRanges = [
158+
...warnings.invalidCombinationWarnings,
159+
...warnings.modeNotSupportedWarnings,
160+
...warnings.missingPowerRestrictionWarnings,
161+
];
162+
163+
const incompatibleIds = new Set<string>(
164+
rangesWithId
165+
.filter((pr) => warningRanges.some((w) => w.end > pr.begin && w.begin < pr.end))
166+
.map((pr) => pr.fromId)
167+
);
168+
169+
return {
170+
powerRestrictionWarningCount: countWarnings(warnings),
171+
incompatiblePowerRestrictionIds: incompatibleIds,
172+
};
173+
}, [
174+
voltages,
175+
selectedTrain.power_restrictions,
176+
selectedTrain.path,
177+
operationalPointsOnPath,
178+
rollingStock,
179+
]);
180+
118181
const {
119182
updateArrival,
120183
updateStopDuration,
@@ -202,6 +265,8 @@ const TimesStopsOutput = ({
202265
isValid={stableIsValid}
203266
isComputedDataPending={isAwaitingSimulation}
204267
availablePowerRestrictions={availablePowerRestrictions}
268+
powerRestrictionWarningCount={powerRestrictionWarningCount}
269+
incompatiblePowerRestrictionIds={incompatiblePowerRestrictionIds}
205270
onArrivalChange={handleArrivalChange}
206271
onStopDurationChange={handleStopDurationChange}
207272
onDepartureChange={handleDepartureChange}

front/src/modules/timesStops/TimesStopsTable.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useCallback, Fragment, useMemo, useRef } from 'react';
22

33
import { Checkbox } from '@osrd-project/ui-core';
4-
import { Moon, TriangleDown } from '@osrd-project/ui-icons';
4+
import { Alert, Moon, TriangleDown } from '@osrd-project/ui-icons';
55
import {
66
createColumnHelper,
77
flexRender,
@@ -35,6 +35,8 @@ declare module '@tanstack/react-table' {
3535
allRows: TimesStopsRowNew[];
3636
isComputedDataPending?: boolean;
3737
availablePowerRestrictions: string[];
38+
powerRestrictionWarningCount: number;
39+
incompatiblePowerRestrictionIds: Set<string>;
3840
onArrivalChange: (row: TimesStopsRowNew, arrival: Date | null) => void;
3941
onStopDurationChange: (row: TimesStopsRowNew, durationSeconds: number | null) => void;
4042
onDepartureChange: (row: TimesStopsRowNew, departure: Date | null) => void;
@@ -84,6 +86,8 @@ type TimesStopsTableProps = {
8486
isValid: boolean;
8587
isComputedDataPending?: boolean;
8688
availablePowerRestrictions: string[];
89+
powerRestrictionWarningCount?: number;
90+
incompatiblePowerRestrictionIds?: Set<string>;
8791
onArrivalChange: (row: TimesStopsRowNew, arrival: Date | null) => void;
8892
onStopDurationChange: (row: TimesStopsRowNew, durationSeconds: number | null) => void;
8993
onDepartureChange: (row: TimesStopsRowNew, departure: Date | null) => void;
@@ -106,6 +110,8 @@ const TimesStopsTable = ({
106110
isValid,
107111
isComputedDataPending,
108112
availablePowerRestrictions,
113+
powerRestrictionWarningCount = 0,
114+
incompatiblePowerRestrictionIds,
109115
onArrivalChange,
110116
onStopDurationChange,
111117
onDepartureChange,
@@ -463,6 +469,8 @@ const TimesStopsTable = ({
463469
allRows: rows,
464470
isComputedDataPending,
465471
availablePowerRestrictions,
472+
powerRestrictionWarningCount,
473+
incompatiblePowerRestrictionIds: incompatiblePowerRestrictionIds ?? new Set(),
466474
onArrivalChange,
467475
onStopDurationChange,
468476
onDepartureChange,
@@ -507,6 +515,14 @@ const TimesStopsTable = ({
507515
<div
508516
className={cx('times-stops-table-new', { 'computed-data-pending': isComputedDataPending })}
509517
>
518+
{powerRestrictionWarningCount > 0 && (
519+
<div className="power-restriction-warning">
520+
<Alert variant="fill" />
521+
<span>
522+
{t('powerRestrictionIncompatibility', { count: powerRestrictionWarningCount })}
523+
</span>
524+
</div>
525+
)}
510526
<table className="table-container">
511527
<thead>
512528
{table.getHeaderGroups().map((headerGroup) => (
@@ -560,7 +576,14 @@ const TimesStopsTable = ({
560576
})}
561577
>
562578
{row.getVisibleCells().map((cell) => (
563-
<td key={cell.id} className={cell.column.columnDef.meta?.className}>
579+
<td
580+
key={cell.id}
581+
className={cx(cell.column.columnDef.meta?.className, {
582+
'power-restriction-incompatible':
583+
cell.column.id === 'powerRestriction' &&
584+
table.options.meta!.incompatiblePowerRestrictionIds.has(row.original.id),
585+
})}
586+
>
564587
{flexRender(cell.column.columnDef.cell, cell.getContext())}
565588
</td>
566589
))}

front/src/modules/timesStops/styles/_timesStopsTable.scss

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@
88
pointer-events: none;
99
}
1010

11+
.power-restriction-warning {
12+
display: flex;
13+
align-items: center;
14+
gap: 8px;
15+
height: 47px;
16+
padding-inline: 16px;
17+
background: var(--warning5);
18+
color: var(--warning60);
19+
font-size: 0.875rem;
20+
border-bottom: 1px solid var(--black25);
21+
22+
svg {
23+
flex-shrink: 0;
24+
color: var(--warning30);
25+
}
26+
}
27+
1128
.table-container {
1229
position: relative;
1330
thead {
@@ -219,10 +236,6 @@
219236

220237
option {
221238
color: initial;
222-
223-
&:checked {
224-
color: var(--primary60);
225-
}
226239
}
227240
}
228241

@@ -237,8 +250,17 @@
237250

238251
}
239252

253+
&.power-restriction-incompatible {
254+
background: var(--warning5);
255+
box-shadow: inset 0 0 0 1px var(--warning30);
256+
257+
select {
258+
color: var(--warning60);
259+
}
260+
}
240261
}
241262

263+
242264
/* comfortable padding */
243265
@media (min-width: 784px) {
244266
th[class^='col-'] {

0 commit comments

Comments
 (0)