Skip to content

Commit

Permalink
front: highlight train not keep timetable
Browse files Browse the repository at this point in the history
  • Loading branch information
theocrsb committed Jul 9, 2024
1 parent c390cb4 commit d9e156c
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 43 deletions.
27 changes: 15 additions & 12 deletions front/public/locales/en/operationalStudies/scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,35 @@
"scenarioUpdatedDetails": "Scenario {{ name }} has been updated.",
"timetable": {
"addTrainSchedule": "Add",
"advancedFilterLabel": "Rollingstock",
"choosePath": "Use this path",
"compositionCodes": "Composition codes",
"copy": "copy",
"delete": "Delete",
"deleteSelection": "Delete selected trains",
"downloadSelection": "Download selected trains",
"duplicate": "Clone",
"filterLabel": "Name, label",
"importTrainSchedule": "Import",
"invalidTrains": "The timetable contains invalid trains.",
"noSpeedLimitTags": "Without code",
"rollingStockFilterPlaceholder": "Family, detail, serie",
"scheduledPointsHonoredFilter": "Scheduled points honored",
"showAllTrains": "All",
"showHonoredTrains": "Honored",
"showInvalidTrains": "Invalid",
"showNotHonoredTrains": "Not honored",
"showValidTrains": "Valid",
"stopsCount_one": "1 stop",
"stopsCount_other": "{{ count }} stops",
"toggleFilters": "Toggle filters display",
"toggleMultiSelection": "Toggle multiselection",
"trainAdded": "Train added",
"trainDeleted": "{{ name }} train has been deleted.",
"update": "Edit",
"toggleMultiSelection": "Toggle multiselection",
"trainsSelectionDeletedCount_one": "The train has been deleted.",
"trainsSelectionDeletedCount_other": "The {{count}} trains have been deleted.",
"noSpeedLimitTags": "Without code",
"showValidTrains": "Valid",
"showInvalidTrains": "Invalid",
"showAllTrains": "All",
"compositionCodes": "Composition codes",
"filterLabel": "Name, label",
"advancedFilterLabel": "Rollingstock",
"rollingStockFilterPlaceholder": "Family, detail, serie",
"validityFilter": "Trains validity",
"toggleFilters": "Toggle filters display"
"update": "Edit",
"validityFilter": "Trains validity"
},
"toggleTimetable": "Toggle the timetable",
"trainCount_one": "1 train",
Expand Down
27 changes: 15 additions & 12 deletions front/public/locales/fr/operationalStudies/scenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,35 @@
"scenarioUpdatedDetails": "Le scénario {{ name }} a bien été mis à jour.",
"timetable": {
"addTrainSchedule": "Ajouter",
"advancedFilterLabel": "Engin moteur",
"choosePath": "Utiliser ce chemin",
"compositionCodes": "Codes de composition",
"copy": "copie",
"delete": "Supprimer",
"deleteSelection": "Supprimer les trains sélectionnés",
"downloadSelection": "Télécharger les trains sélectionnés",
"duplicate": "Dupliquer",
"filterLabel": "Nom, étiquette",
"importTrainSchedule": "Importer",
"invalidTrains": "La grille horaires comporte des trains invalides.",
"noSpeedLimitTags": "Sans code",
"rollingStockFilterPlaceholder": "Famille, détail, série",
"scheduledPointsHonoredFilter": "Points horaires honorés",
"showAllTrains": "Tous",
"showHonoredTrains": "Honorés",
"showInvalidTrains": "Invalides",
"showNotHonoredTrains": "Non honorés",
"showValidTrains": "Valides",
"stopsCount_one": "1 arrêt",
"stopsCount_other": "{{count}} arrêts",
"toggleFilters": "Afficher ou masquer les filtres",
"toggleMultiSelection": "Basculer le mode multisélection",
"trainAdded": "Train ajouté",
"trainDeleted": "Le train {{name}} a bien été supprimé.",
"update": "Modifier",
"toggleMultiSelection": "Basculer le mode multisélection",
"trainsSelectionDeletedCount_one": "Le train a bien été supprimé.",
"trainsSelectionDeletedCount_other": "Les {{count}} trains ont bien été supprimés.",
"noSpeedLimitTags": "Sans code",
"showValidTrains": "Valides",
"showInvalidTrains": "Invalides",
"showAllTrains": "Tous",
"compositionCodes": "Codes de composition",
"filterLabel": "Nom, étiquette",
"advancedFilterLabel": "Engin moteur",
"rollingStockFilterPlaceholder": "Famille, détail, série",
"validityFilter": "Validité des trains",
"toggleFilters": "Afficher ou masquer les filtres"
"update": "Modifier",
"validityFilter": "Validité des trains"
},
"toggleTimetable": "Basculer l'affichage de la grille horaires",
"trainCount_one": "1 train",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import type { Margin } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import type { PathStep } from 'reducers/osrdconf/types';

const formatMargin = (pathSteps: PathStep[]): Margin => {
const margins: Margin = {
boundaries: [],
values: [],
};
const formatMargin = (pathSteps: PathStep[]): Margin | undefined => {
const boundaries: string[] = [];
const values: string[] = [];

pathSteps.forEach((step, index) => {
if (index === 0) {
margins.values.push(step.theoreticalMargin || 'none');
} else if (step.theoreticalMargin !== pathSteps[index - 1].theoreticalMargin) {
margins.boundaries.push(step.id);
margins.values.push(step.theoreticalMargin || 'none');
values.push(step.theoreticalMargin || 'none');
} else if (index === pathSteps.length - 1) {
if (values.length === 1 && values[0] !== 'none') {
boundaries.push(step.id);
values.push('none');
}
}

// for the other steps, we add the margin if it's different from the previous one
else if (step.theoreticalMargin && step.theoreticalMargin !== values[values.length - 1]) {
boundaries.push(step.id);
values.push(step.theoreticalMargin);
}
});
return margins;

if (values.length === 1) {
return undefined;
}
return { boundaries, values };
};

export default formatMargin;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
import InputSNCF from 'common/BootstrapSNCF/InputSNCF';
import OptionsSNCF from 'common/BootstrapSNCF/OptionsSNCF';

import type { ValidityFilter } from './types';
import type { ValidityFilter, ScheduledPointsHonoredFilter } from './types';

type FilterPanelProps = {
filter: string;
Expand All @@ -15,6 +15,10 @@ type FilterPanelProps = {
setRollingStockFilter: (rollingStockFilter: string) => void;
validityFilter: ValidityFilter;
setValidityFilter: (validityFilter: ValidityFilter) => void;
scheduledPointsHonoredFilter: ScheduledPointsHonoredFilter;
setScheduledPointsHonoredFilter: (
scheduledPointsHonoredFilter: ScheduledPointsHonoredFilter
) => void;
uniqueTags: string[];
selectedTags: Set<string | null>;
setSelectedTags: React.Dispatch<React.SetStateAction<Set<string | null>>>;
Expand All @@ -27,6 +31,8 @@ const FilterPanel = ({
setRollingStockFilter,
validityFilter,
setValidityFilter,
scheduledPointsHonoredFilter,
setScheduledPointsHonoredFilter,
uniqueTags,
selectedTags,
setSelectedTags,
Expand All @@ -39,6 +45,12 @@ const FilterPanel = ({
{ value: 'invalid', label: t('timetable.showInvalidTrains') },
];

const scheduledPointsHonoredOptions: { value: ScheduledPointsHonoredFilter; label: string }[] = [
{ value: 'both', label: t('timetable.showAllTrains') },
{ value: 'honored', label: t('timetable.showHonoredTrains') },
{ value: 'notHonored', label: t('timetable.showNotHonoredTrains') },
];

const toggleTagSelection = (tag: string | null) => {
setSelectedTags((prevSelectedTags) => {
const newSelectedTags = new Set(prevSelectedTags);
Expand Down Expand Up @@ -97,6 +109,21 @@ const FilterPanel = ({
/>
</div>

<label htmlFor="train-keep-timetable">
{t('timetable.scheduledPointsHonoredFilter')}
</label>
<div className="validity-filter">
<OptionsSNCF
onChange={(event) =>
setScheduledPointsHonoredFilter(event.target.value as ScheduledPointsHonoredFilter)
}
options={scheduledPointsHonoredOptions}
name="schedule-point-honored"
selectedValue={scheduledPointsHonoredFilter}
/>
</div>
</div>
<div className="col-5">
<label htmlFor="composition-tag-filter">{t('timetable.compositionCodes')}</label>
<div className="composition-tag-filter" id="composition-tag-filter">
{uniqueTags.map((tag) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import { useDebounce } from 'utils/helpers';

import FilterPanel from './FilterPanel';
import useTrainSchedulesDetails from './hooks';
import type { TrainScheduleWithDetails, ValidityFilter } from './types';
import type {
ScheduledPointsHonoredFilter,
TrainScheduleWithDetails,
ValidityFilter,
} from './types';

type TimetableToolbarProps = {
trainIds: number[];
Expand Down Expand Up @@ -56,9 +60,12 @@ const TimetableToolbar = ({
const [filter, setFilter] = useState('');
const [rollingStockFilter, setRollingStockFilter] = useState('');
const [validityFilter, setValidityFilter] = useState<ValidityFilter>('both');
const [scheduledPointsHonoredFilter, setScheduledPointsHonoredFilter] =
useState<ScheduledPointsHonoredFilter>('both');
const [selectedTags, setSelectedTags] = useState<Set<string | null>>(new Set());

const debouncedFilter = useDebounce(filter, 500);

const debouncedRollingstockFilter = useDebounce(rollingStockFilter, 500);

const [deleteTrainSchedules] = osrdEditoastApi.endpoints.deleteV2TrainSchedule.useMutation();
Expand All @@ -69,6 +76,7 @@ const TimetableToolbar = ({
debouncedFilter,
debouncedRollingstockFilter,
validityFilter,
scheduledPointsHonoredFilter,
selectedTags
);

Expand Down Expand Up @@ -210,6 +218,8 @@ const TimetableToolbar = ({
setRollingStockFilter={setRollingStockFilter}
validityFilter={validityFilter}
setValidityFilter={setValidityFilter}
scheduledPointsHonoredFilter={scheduledPointsHonoredFilter}
setScheduledPointsHonoredFilter={setScheduledPointsHonoredFilter}
uniqueTags={uniqueTags}
selectedTags={selectedTags}
setSelectedTags={setSelectedTags}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import { Alert, Pencil, Trash } from '@osrd-project/ui-icons';
import { Alert, Pencil, Trash, Clock } from '@osrd-project/ui-icons';
import cx from 'classnames';
import { omit } from 'lodash';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -216,9 +216,20 @@ const TimetableTrainCardV2 = ({
</span>
)}
</div>
<div className="scenario-timetable-train-times V2">
<div className="scenario-timetable-train-departure">{train.startTime}</div>
<div className="scenario-timetable-train-arrival">{train.arrivalTime}</div>
<div
className={cx('scenario-timetable-train-times V2', {
'not-honored': train.scheduledPointsNotHonored,
})}
>
{train.scheduledPointsNotHonored && (
<div className="ml-1">
<Clock size="lg" />
</div>
)}
<div>
<div className="scenario-timetable-train-departure">{train.startTime}</div>
<div className="scenario-timetable-train-arrival">{train.arrivalTime}</div>
</div>
</div>
</div>
<div className="scenario-timetable-train-body">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ const TimetableV2 = ({
{t('timetable.addTrainSchedule')}
</button>
</div>

<TimetableToolbar
trainIds={trainIds}
trainSchedulesDetails={trainSchedulesDetails}
Expand Down
21 changes: 21 additions & 0 deletions front/src/modules/trainschedule/components/TimetableV2/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { extractTagCode, keepTrain } from './utils';
* @param debouncedFilter filter for train name and labels
* @param debouncedRollingstockFilter filter for train's rolling stock metadata
* @param validityFilter filter for valid train or not
* @param scheduledPointsHonoredFilter filter for trains that keep their timetables or not
* @param selectedTags filter for train's speed limit tag
* @returns trainschedules unique speedlimit tags and train schedules
*/
Expand All @@ -29,6 +30,7 @@ const useTrainSchedulesDetails = (
debouncedFilter: string,
debouncedRollingstockFilter: string,
validityFilter: string,
scheduledPointsHonoredFilter: string,
selectedTags: Set<string | null>
) => {
const infraId = useInfraID();
Expand Down Expand Up @@ -80,6 +82,21 @@ const useTrainSchedulesDetails = (
if (validityFilter === 'invalid' && trainSummary.status === 'success') return false;
}

// Apply scheduled points honored filter
if (scheduledPointsHonoredFilter !== 'both') {
if (trainSummary.status === 'success') {
const isHonored = trainSummary.scheduled_points_honored;
if (
(scheduledPointsHonoredFilter === 'honored' && !isHonored) ||
(scheduledPointsHonoredFilter === 'notHonored' && isHonored)
) {
return false;
}
} else {
return false;
}
}

// Apply tag filter
if (
selectedTags.size > 0 &&
Expand Down Expand Up @@ -121,6 +138,7 @@ const useTrainSchedulesDetails = (
// We want to trigger this only if a filter is applied
const filtereredTrainSchedules =
validityFilter !== 'both' ||
scheduledPointsHonoredFilter !== 'both' ||
selectedTags.size > 0 ||
debouncedRollingstockFilter ||
debouncedFilter
Expand All @@ -146,6 +164,7 @@ const useTrainSchedulesDetails = (
duration: trainSummary.time,
pathLength: formatKmValue(trainSummary.length, 'millimeters', 1),
mechanicalEnergyConsumed: jouleToKwh(trainSummary.energy_consumption, true),
scheduledPointsNotHonored: !trainSummary.scheduled_points_honored,
}
: {
arrivalTime: '',
Expand All @@ -166,6 +185,7 @@ const useTrainSchedulesDetails = (
speedLimitTag: trainSchedule.speed_limit_tag ?? null,
labels: trainSchedule.labels ?? [],
rollingStock,

...otherProps,
};
});
Expand All @@ -180,6 +200,7 @@ const useTrainSchedulesDetails = (
debouncedFilter,
debouncedRollingstockFilter,
validityFilter,
scheduledPointsHonoredFilter,
selectedTags,
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type {

export type ValidityFilter = 'both' | 'valid' | 'invalid';

export type ScheduledPointsHonoredFilter = 'both' | 'honored' | 'notHonored';

export type TrainScheduleWithDetails = {
id: number;
trainName: string;
Expand All @@ -21,6 +23,7 @@ export type TrainScheduleWithDetails = {
speedLimitTag: string | null;
labels: string[];
invalidReason?: InvalidReason;
scheduledPointsNotHonored?: boolean;
};

export type InvalidReason = Exclude<SimulationSummaryResult['status'], 'success'>;
Loading

0 comments on commit d9e156c

Please sign in to comment.