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: highlight train not keep timetable #7959

Merged
merged 1 commit into from
Jul 10, 2024
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
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
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'>;
Original file line number Diff line number Diff line change
Expand Up @@ -270,14 +270,15 @@
.scenario-timetable-train-header {
display: flex;
align-items: center;
padding: 0 0.25rem;
background-color: var(--coolgray3);
transition: background-color 0.2s;
.scenario-timetable-train-name {
padding-right: 20px;
position: relative;
display: flex;
align-items: center;
overflow: hidden;
padding-left: 0.25rem;
.scenario-timetable-train-idx {
display: flex;
align-items: center;
Expand Down Expand Up @@ -318,10 +319,12 @@
}
}
.scenario-timetable-train-times {
display: flex;
justify-content: space-between;
min-width: 20%;
text-align: right;
height: 1.5rem;
line-height: 0.8rem;
padding-right: 0.25rem;
.scenario-timetable-train-departure {
font-weight: 600;
font-size: 0.9rem;
Expand All @@ -334,6 +337,11 @@
.V2 {
min-width: 40%;
}
.not-honored{
background-color: var(--warning);
border-top: solid 2px var(--warning);
border-bottom: solid 2px var(--warning);
}
}
.scenario-timetable-train-body {
display: flex;
Expand Down
Loading