Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

front: create paced train item #10698

Merged
merged 3 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions front/public/locales/en/operationalStudies/scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
"pacedTrain": {
"pacedTrain": "Paced train"
},
"pacedTrain_one": "1 service",
"pacedTrain_other": "{{count}} services",
"pacedTrain_zero": "0 service",
"pacedTrainCount_one": "1/$t(pacedTrain, { 'count': {{totalCount}} }) selected",
"pacedTrainCount_other": "{{count}}/$t(pacedTrain, { 'count': {{totalCount}} }) selected",
"pacedTrainCount_zero": "$t(pacedTrain, { 'count': {{totalCount}} })",
"pacedTrainAndTrainCount": "{{pacedTrainCount}}/$t(pacedTrain, { 'count': {{totalPacedTrainCount}} }) and {{trainCount}}/$t(train, { 'count': {{totalTrainScheduleCount}} }) selected",
"scenarioCancel": "Cancel",
"scenarioCreateButton": "Create a scenario",
"scenarioCreationTitle": "Create a scenario",
Expand Down Expand Up @@ -72,6 +79,7 @@
"simulation_failed": "Simulation failed"
},
"invalidTrains": "Some trains are invalid",
"noItem": "No item",
"noSpeedLimitTags": "Without code",
"noSpeedLimitTagsShort": "None",
"noTrain": "No train",
Expand All @@ -95,8 +103,8 @@
"validityFilter": "Trains validity"
},
"toggleTimetable": "Toggle the timetable",
"trainCount_one": "1 out of $t(train, { 'count': {{totalCount}} }) selected",
"trainCount_other": "{{count}} out of $t(train, { 'count': {{totalCount}} }) selected",
"trainCount_one": "1/$t(train, { 'count': {{totalCount}} }) selected",
"trainCount_other": "{{count}}/$t(train, { 'count': {{totalCount}} }) selected",
"trainCount_zero": "$t(train, { 'count': {{totalCount}} })",
"train_one": "1 train",
"train_other": "{{count}} trains",
Expand Down
12 changes: 10 additions & 2 deletions front/public/locales/fr/operationalStudies/scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
"pacedTrain": {
"pacedTrain": "Mission"
},
"pacedTrain_one": "1 mission",
"pacedTrain_other": "{{count}} missions",
"pacedTrain_zero": "0 mission",
"pacedTrainCount_one": "1/$t(pacedTrain, { 'count': {{totalCount}} }) sélectionnée",
"pacedTrainCount_other": "{{count}}/$t(pacedTrain, { 'count': {{totalCount}} }) sélectionnées",
"pacedTrainCount_zero": "$t(pacedTrain, { 'count': {{totalCount}} })",
"pacedTrainAndTrainCount": "{{pacedTrainCount}}/$t(pacedTrain, { 'count': {{totalPacedTrainCount}} }) et {{trainCount}}/$t(train, { 'count': {{totalTrainScheduleCount}} }) sélectionnés",
"scenarioCancel": "Annuler",
"scenarioCreateButton": "Créer le scénario",
"scenarioCreationTitle": "Créer un scénario",
Expand Down Expand Up @@ -71,6 +78,7 @@
"simulation_failed": "Simulation impossible"
},
"invalidTrains": "Certains trains sont invalides",
"noItem": "Aucun item",
"noSpeedLimitTags": "Sans code",
"noSpeedLimitTagsShort": "Aucun",
"noTrain": "Aucun train",
Expand All @@ -94,8 +102,8 @@
"validityFilter": "Validité des trains"
},
"toggleTimetable": "Basculer l'affichage de la grille horaire",
"trainCount_one": "1 sélectionné sur $t(train, { 'count': {{totalCount}} })",
"trainCount_other": "{{count}} sélectionnés sur $t(train, { 'count': {{totalCount}} })",
"trainCount_one": "1/$t(train, { 'count': {{totalCount}} }) sélectionné",
"trainCount_other": "{{count}}/$t(train, { 'count': {{totalCount}} }) sélectionnés",
"trainCount_zero": "$t(train, { 'count': {{totalCount}} })",
"train_one": "1 train",
"train_other": "{{count}} trains",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const ScenarioContent = ({
MANAGE_TRAIN_SCHEDULE_TYPES.none
);
const [collapsedTimetable, setCollapsedTimetable] = useState(false);
const [trainIdToEdit, setTrainIdToEdit] = useState<TimetableItemId>();
const [itemIdToEdit, setItemIdToEdit] = useState<TimetableItemId>();
const [isMacro, setIsMacro] = useState(false);
const {
trainScheduleSummaries,
Expand Down Expand Up @@ -138,8 +138,8 @@ const ScenarioContent = ({
displayTrainScheduleManagement={displayTrainScheduleManagement}
setDisplayTrainScheduleManagement={setDisplayTrainScheduleManagement}
upsertTrainSchedules={upsertTrainSchedules}
trainIdToEdit={trainIdToEdit}
setTrainIdToEdit={setTrainIdToEdit}
itemIdToEdit={itemIdToEdit}
setItemIdToEdit={setItemIdToEdit}
infraState={infra.state}
dtoImport={dtoImport}
/>
Expand All @@ -150,8 +150,8 @@ const ScenarioContent = ({
conflicts={conflicts}
upsertTrainSchedules={upsertTrainSchedules}
removeTrains={removeTrains}
setTrainIdToEdit={setTrainIdToEdit}
trainIdToEdit={trainIdToEdit}
setItemIdToEdit={setItemIdToEdit}
itemIdToEdit={itemIdToEdit}
trainSchedules={trainSchedules}
trainSchedulesWithDetails={trainScheduleSummaries}
dtoImport={dtoImport}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import SimulationResultExport from 'modules/simulationResult/SimulationResultExp
import type { ProjectionData } from 'modules/simulationResult/types';
import TimesStopsOutput from 'modules/timesStops/TimesStopsOutput';
import type { TrainScheduleWithDetails } from 'modules/trainschedule/components/Timetable/types';
import type { TrainId, TrainScheduleId } from 'reducers/osrdconf/types';
import type { TrainId } from 'reducers/osrdconf/types';
import { updateSelectedTrainId } from 'reducers/simulationResults';
import { getTrainIdUsedForProjection } from 'reducers/simulationResults/selectors';
import { useAppDispatch } from 'store';
Expand Down Expand Up @@ -118,9 +118,7 @@ const SimulationResults = ({
const selectedTrainSummary = useMemo(
() =>
trainScheduleSummaries?.find(
(train) =>
formatTrainScheduleIdToEditoastTrainId(train.id as TrainScheduleId) ===
selectedTrainSchedule?.id
(train) => formatTrainScheduleIdToEditoastTrainId(train.id) === selectedTrainSchedule?.id
),
[trainScheduleSummaries, selectedTrainSchedule]
);
Expand Down
3 changes: 1 addition & 2 deletions front/src/modules/conflict/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Conflict } from 'common/api/osrdEditoastApi';
import type { TrainScheduleWithDetails } from 'modules/trainschedule/components/Timetable/types';
import type { TrainScheduleId } from 'reducers/osrdconf/types';
import { formatTrainScheduleIdToEditoastTrainId } from 'utils/trainId';

import type { ConflictWithTrainNames } from './types';
Expand All @@ -13,7 +12,7 @@ export default function addTrainNamesToConflicts(

trainSchedulesDetails.forEach(({ id, trainName }) => {
// TODO Paced train : Adapt this to handle paced trains in issue https://github.com/OpenRailAssociation/osrd/issues/10615
const editoastTrainId = formatTrainScheduleIdToEditoastTrainId(id as TrainScheduleId);
const editoastTrainId = formatTrainScheduleIdToEditoastTrainId(id);
trainNameMap[editoastTrainId] = trainName;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import useUpdateTrainSchedule from './hooks/useUpdateTrainSchedule';

type TimetableManageTrainScheduleProps = {
displayTrainScheduleManagement: string;
trainIdToEdit?: TimetableItemId;
itemIdToEdit?: TimetableItemId;
setDisplayTrainScheduleManagement: (type: string) => void;
upsertTrainSchedules: (trainSchedules: TrainScheduleResultWithTrainId[]) => void;
infraState?: InfraState;
setTrainIdToEdit: (trainIdToEdit?: TimetableItemId) => void;
setItemIdToEdit: (itemIdToEdit?: TimetableItemId) => void;
dtoImport: () => void;
};

Expand All @@ -27,25 +27,25 @@ const TimetableManageTrainSchedule = ({
setDisplayTrainScheduleManagement,
upsertTrainSchedules,
infraState,
trainIdToEdit,
setTrainIdToEdit,
itemIdToEdit,
setItemIdToEdit,
dtoImport,
}: TimetableManageTrainScheduleProps) => {
const { t } = useTranslation('operationalStudies/manageTrainSchedule');
const [isWorking, setIsWorking] = useState(false);

const leaveManageTrainSchedule = () => {
setDisplayTrainScheduleManagement(MANAGE_TRAIN_SCHEDULE_TYPES.none);
setTrainIdToEdit(undefined);
setItemIdToEdit(undefined);
};

const updateTrainSchedule = useUpdateTrainSchedule(
setIsWorking,
setDisplayTrainScheduleManagement,
upsertTrainSchedules,
setTrainIdToEdit,
setItemIdToEdit,
dtoImport,
trainIdToEdit
itemIdToEdit
);
return (
<div className="scenario-timetable-managetrainschedule">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { useState } from 'react';

import { Checkbox } from '@osrd-project/ui-core';
import { ChevronDown, ChevronRight, Clock, Flame, Manchette } from '@osrd-project/ui-icons';
import cx from 'classnames';
import dayjs from 'dayjs';
import { useTranslation } from 'react-i18next';

import type { PacedTrainId } from 'reducers/osrdconf/types';
import { ms2min } from 'utils/timeManipulation';

import TimetableItemActions from '../TimetableItemActions';
import type { PacedTrainWithResult } from '../types';

type PacedTrainItemProps = {
isInSelection: boolean;
handleSelectPacedTrain: (pacedTrainId: PacedTrainId) => void;
pacedTrain: PacedTrainWithResult;
isOnEdit: boolean;
isProjectionPathUsed: boolean;
selectPacedTrainToEdit: (pacedTrain: PacedTrainWithResult) => void;
};

const PacedTrainItem = ({
isInSelection,
handleSelectPacedTrain,
pacedTrain,
isOnEdit,
isProjectionPathUsed,
selectPacedTrainToEdit,
}: PacedTrainItemProps) => {
const { t } = useTranslation(['operationalStudies/scenario']);

const [isOccurrencesListOpen, setIsOccurrencesListOpen] = useState(false);

const toggleOccurrencesList = () => setIsOccurrencesListOpen((open) => !open);
const selectPathProjection = async () => {};
const duplicatePacedTrain = async () => {};
const deletePacedTrain = async () => {};

const pacedTrainCadence = pacedTrain.paced.step;

const occurrencesCount = Math.ceil(pacedTrain.paced.duration.ms / pacedTrain.paced.step.ms);
return (
<div
data-testid="scenario-timetable-train"
className={cx('scenario-timetable-train paced-train', {
modified: isOnEdit,
'in-selection': isInSelection,
closed: !isOccurrencesListOpen,
invalid: pacedTrain.invalidReason,
})}
>
<div
className={cx('base-info', {
warning: pacedTrain.invalidReason || pacedTrain.notHonoredReason,
invalid: pacedTrain.invalidReason,
'not-honored': pacedTrain.notHonoredReason,
})}
>
<div className="checkbox-title">
<Checkbox
label=""
checked={isInSelection}
onChange={() => handleSelectPacedTrain(pacedTrain.id)}
small
/>
</div>

<div
title={pacedTrain.trainName}
className="paced-train-main-info"
onClick={toggleOccurrencesList}
role="button"
tabIndex={0}
>
{isProjectionPathUsed && (
<div className="train-projected">
<Manchette iconColor="var(--white100)" />
</div>
)}
<div className="occurrences-count">{occurrencesCount}</div>
{isOccurrencesListOpen ? (
<ChevronDown className="toggle-icon center-icon" />
) : (
<ChevronRight className="toggle-icon center-icon" />
)}
<div className="train-info">
<span className="train-name">{pacedTrain.trainName}</span>
</div>
</div>

{!pacedTrain.invalidReason ? (
<div className="paced-train-right-zone">
{pacedTrain.isValid && <div>&mdash;&nbsp;{`${ms2min(pacedTrainCadence.ms)}min`}</div>}
<div
className={cx('status-icon', {
'not-honored-or-too-fast': pacedTrain.notHonoredReason,
})}
>
{pacedTrain.notHonoredReason &&
(pacedTrain.notHonoredReason === 'scheduleNotHonored' ? (
<Clock className="center-icon" />
) : (
<Flame className="center-icon" />
))}
</div>
</div>
) : (
<div className="invalid-reason">
<span title={t(`timetable.invalid.${pacedTrain.invalidReason}`)}>
{t(`timetable.invalid.${pacedTrain.invalidReason}`)}
</span>
</div>
)}
</div>
<TimetableItemActions
selectPathProjection={selectPathProjection}
duplicateTimetableItem={duplicatePacedTrain}
editTimetableItem={() => selectPacedTrainToEdit(pacedTrain)}
deleteTimetableItem={deletePacedTrain}
/>
<div className="occurrences" />
{pacedTrain.isValid && (
<div className="more-info">
<div className="more-info-left">
<span className="more-info-item">
{t('timetable.stopsCount', { count: pacedTrain.stopsCount })}
</span>
<span className="more-info-item">{pacedTrain.pathLength}</span>
<span className="more-info-item m-0" data-testid="allowance-energy-consumed">
{pacedTrain.mechanicalEnergyConsumed}&nbsp;kWh
</span>
</div>
<div className="duration-time">
<span data-testid="train-duration">
{dayjs.duration(pacedTrain.duration!.ms).format('HH[h]mm')}
</span>
</div>
</div>
)}
</div>
);
};

export default PacedTrainItem;
Loading
Loading