From ad6d53188f8635d2cbb3bd7c4a3c60cb37307cb1 Mon Sep 17 00:00:00 2001 From: Uriel-Sautron Date: Thu, 20 Feb 2025 13:10:29 +0100 Subject: [PATCH] fix comments --- .../en/operationalStudies/scenario.json | 12 +- .../fr/operationalStudies/scenario.json | 12 +- .../Timetable/PacedTrain/PacedTrainItem.tsx | 10 +- .../components/Timetable/Timetable.tsx | 48 ++++--- .../components/Timetable/TimetableToolbar.tsx | 132 +++++++++++++----- .../components/Timetable/types.ts | 7 +- .../operationalStudies/_pacedTrain.scss | 20 ++- .../operationalStudies/_scenario.scss | 6 +- 8 files changed, 173 insertions(+), 74 deletions(-) diff --git a/front/public/locales/en/operationalStudies/scenario.json b/front/public/locales/en/operationalStudies/scenario.json index 4dbfd2b65fa..6f8c399706c 100644 --- a/front/public/locales/en/operationalStudies/scenario.json +++ b/front/public/locales/en/operationalStudies/scenario.json @@ -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", @@ -72,6 +79,7 @@ "simulation_failed": "Simulation failed" }, "invalidTrains": "Some trains are invalid", + "noItem": "No item", "noSpeedLimitTags": "Without code", "noSpeedLimitTagsShort": "None", "noTrain": "No train", @@ -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", diff --git a/front/public/locales/fr/operationalStudies/scenario.json b/front/public/locales/fr/operationalStudies/scenario.json index 3027d5b56d3..5491cb316b4 100644 --- a/front/public/locales/fr/operationalStudies/scenario.json +++ b/front/public/locales/fr/operationalStudies/scenario.json @@ -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", @@ -71,6 +78,7 @@ "simulation_failed": "Simulation impossible" }, "invalidTrains": "Certains trains sont invalides", + "noItem": "Aucun item", "noSpeedLimitTags": "Sans code", "noSpeedLimitTagsShort": "Aucun", "noTrain": "Aucun train", @@ -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", diff --git a/front/src/modules/trainschedule/components/Timetable/PacedTrain/PacedTrainItem.tsx b/front/src/modules/trainschedule/components/Timetable/PacedTrain/PacedTrainItem.tsx index e9c58aac0c6..91a93dd9b9b 100644 --- a/front/src/modules/trainschedule/components/Timetable/PacedTrain/PacedTrainItem.tsx +++ b/front/src/modules/trainschedule/components/Timetable/PacedTrain/PacedTrainItem.tsx @@ -90,9 +90,9 @@ const PacedTrainItem = ({ - {!pacedTrain.invalidReason && ( + {!pacedTrain.invalidReason ? (
- {pacedTrain.isValid &&
—{` ${ms2min(pacedTrainCadence.ms)}min`}
} + {pacedTrain.isValid &&
— {`${ms2min(pacedTrainCadence.ms)}min`}
}
+ ) : ( +
+ + {t(`timetable.invalid.${pacedTrain.invalidReason}`)} + +
)}
void; @@ -68,10 +73,11 @@ const Timetable = ({ const [displayedTimetableItems, setDisplayedTimetableItems] = useState([]); const [conflictsListExpanded, setConflictsListExpanded] = useState(false); - const [selectedTimetableItemIds, setSelectedTimetableItemIds] = useState<{ - trainScheduleIds: TrainScheduleId[]; - pacedTrainIds: PacedTrainId[]; - }>({ trainScheduleIds: [], pacedTrainIds: [] }); + const [selectedTimetableItemIdsByType, setSelectedTimetableItemIdsByType] = + useState({ + selectedTrainScheduleIds: [], + selectedPacedTrainIds: [], + }); const [showTrainDetails, setShowTrainDetails] = useState(false); const [timetableItems, setTimetableItems] = useState([]); const selectedTrainId = useSelector(getSelectedTrainId); @@ -84,7 +90,7 @@ const Timetable = ({ const removeAndUnselectTrains = useCallback((trainIds: TimetableItemId[]) => { removeTrains(trainIds); - setSelectedTimetableItemIds({ trainScheduleIds: [], pacedTrainIds: [] }); + setSelectedTimetableItemIdsByType({ selectedTrainScheduleIds: [], selectedPacedTrainIds: [] }); dtoImport(); }, []); @@ -94,8 +100,8 @@ const Timetable = ({ const handleSelectTimetableItem = useCallback( (id: TimetableItemId) => { - const itemType = isTrainSchedule(id) ? 'trainScheduleIds' : 'pacedTrainIds'; - const currentSelectedTrainIds: TimetableItemId[] = selectedTimetableItemIds[itemType]; + const itemType = isTrainSchedule(id) ? 'selectedTrainScheduleIds' : 'selectedPacedTrainIds'; + const currentSelectedTrainIds: TimetableItemId[] = selectedTimetableItemIdsByType[itemType]; const index = currentSelectedTrainIds.indexOf(id as TrainScheduleId); if (index === -1) { @@ -104,12 +110,12 @@ const Timetable = ({ currentSelectedTrainIds.splice(index, 1); } - setSelectedTimetableItemIds({ - ...selectedTimetableItemIds, + setSelectedTimetableItemIdsByType({ + ...selectedTimetableItemIdsByType, [itemType]: currentSelectedTrainIds, }); }, - [selectedTimetableItemIds] + [selectedTimetableItemIdsByType] ); const handleConflictClick = (conflict: Conflict) => { @@ -136,7 +142,7 @@ const Timetable = ({ }); }, [currentDepartureDates]); - const selectTimeTableItemToEdit = (itemToEdit: TimetableItemResult) => { + const selectTimetableItemToEdit = (itemToEdit: TimetableItemResult) => { dispatch(selectTrainToEdit(itemToEdit)); // TODO Paced train : Adapt this to handle paced trains in issue https://github.com/OpenRailAssociation/osrd/issues/10615 setItemIdToEdit(itemToEdit.id); @@ -194,14 +200,14 @@ const Timetable = ({ timetableItems={timetableItems} displayedTimetableItems={displayedTimetableItems} setDisplayedTimetableItems={setDisplayedTimetableItems} - selectedTimetableItemIds={selectedTimetableItemIds} - setSelectedTimetableItemIds={setSelectedTimetableItemIds} + selectedTimetableItemIdsByType={selectedTimetableItemIdsByType} + setSelectedTimetableItemIdsByType={setSelectedTimetableItemIdsByType} removeTrains={removeAndUnselectTrains} trainSchedules={trainSchedules} isInSelection={ [ - ...selectedTimetableItemIds.pacedTrainIds, - ...selectedTimetableItemIds.trainScheduleIds, + ...selectedTimetableItemIdsByType.selectedPacedTrainIds, + ...selectedTimetableItemIdsByType.selectedTrainScheduleIds, ].length > 0 } /> @@ -217,7 +223,7 @@ const Timetable = ({ https://github.com/OpenRailAssociation/osrd/issues/10615 */} {isTrainSchedule(timetableItem.id) ? ( void; - selectedTimetableItemIds: { - trainScheduleIds: TrainScheduleId[]; - pacedTrainIds: PacedTrainId[]; - }; - setSelectedTimetableItemIds: (selectedTimetableIds: { - trainScheduleIds: TrainScheduleId[]; - pacedTrainIds: PacedTrainId[]; - }) => void; + selectedTimetableItemIdsByType: SelectedTimetableIdsByType; + setSelectedTimetableItemIdsByType: (selectedTimetableIds: SelectedTimetableIdsByType) => void; removeTrains: (trainIds: TimetableItemId[]) => void; trainSchedules: TrainScheduleResultWithTrainId[]; isInSelection: boolean; @@ -53,13 +52,13 @@ const TimetableToolbar = ({ timetableItems, displayedTimetableItems, setDisplayedTimetableItems, - selectedTimetableItemIds, - setSelectedTimetableItemIds, + selectedTimetableItemIdsByType, + setSelectedTimetableItemIdsByType, removeTrains, trainSchedules, isInSelection, }: TimetableToolbarProps) => { - const { t } = useTranslation(['operationalStudies/scenario', 'common/itemTypes']); + const { t } = useTranslation(['operationalStudies/scenario', 'common/itemTypes', 'translation']); const dispatch = useAppDispatch(); const { openModal } = useContext(ModalContext); @@ -74,14 +73,30 @@ const TimetableToolbar = ({ useState('both'); const [selectedTags, setSelectedTags] = useState>(new Set()); + const { totalPacedTrainCount, totalTrainScheduleCount } = useMemo( + () => + timetableItems.reduce( + (acc, { id }) => { + if (isTrainSchedule(id)) { + acc.totalTrainScheduleCount += 1; + } else { + acc.totalPacedTrainCount += 1; + } + return acc; + }, + { totalPacedTrainCount: 0, totalTrainScheduleCount: 0 } + ), + [timetableItems] + ); + const debouncedFilter = useDebounce(filter, 500); const debouncedRollingstockFilter = useDebounce(rollingStockFilter, 500); const [deleteTrainSchedules] = osrdEditoastApi.endpoints.deleteTrainSchedule.useMutation(); - const { trainScheduleIds, pacedTrainIds } = selectedTimetableItemIds; - const timetableItemIds = [...trainScheduleIds, ...pacedTrainIds]; + const { selectedTrainScheduleIds, selectedPacedTrainIds } = selectedTimetableItemIdsByType; + const selectedTimetableItemIds = [...selectedTrainScheduleIds, ...selectedPacedTrainIds]; // TODO: move this hook in Timetable const { uniqueTags } = useFilterTrainSchedules( @@ -99,43 +114,49 @@ const TimetableToolbar = ({ }; const toggleAllTrainsSelecton = () => { - if (displayedTimetableItems.length === [...trainScheduleIds, ...pacedTrainIds].length) { - setSelectedTimetableItemIds({ trainScheduleIds: [], pacedTrainIds: [] }); + if (displayedTimetableItems.length === selectedTimetableItemIds.length) { + setSelectedTimetableItemIdsByType({ + selectedTrainScheduleIds: [], + selectedPacedTrainIds: [], + }); } else { const timetableItemsDisplayed = displayedTimetableItems.reduce( (acc, { id }) => { if (isTrainSchedule(id)) { - acc.trainScheduleIds.push(id); + acc.selectedTrainScheduleIds.push(id); } else { - acc.pacedTrainIds.push(id); + acc.selectedPacedTrainIds.push(id); } return acc; }, - { trainScheduleIds: [] as TrainScheduleId[], pacedTrainIds: [] as PacedTrainId[] } + { + selectedTrainScheduleIds: [] as TrainScheduleId[], + selectedPacedTrainIds: [] as PacedTrainId[], + } ); - setSelectedTimetableItemIds(timetableItemsDisplayed); + setSelectedTimetableItemIdsByType(timetableItemsDisplayed); } }; const handleTrainsDelete = async () => { - const itemsCount = timetableItemIds.length; + const itemsCount = selectedTimetableItemIds.length; // TODO Paced train : Adapt this to handle delete paced trains in issue https://github.com/OpenRailAssociation/osrd/issues/10615 - if (selectedTrainId && timetableItemIds.includes(selectedTrainId as TrainScheduleId)) { + if (selectedTrainId && selectedTimetableItemIds.includes(selectedTrainId as TrainScheduleId)) { // we need to set selectedTrainId to undefined, otherwise just after the delete, // some unvalid rtk calls are dispatched (see rollingstock request in SimulationResults) dispatch(updateSelectedTrainId(undefined)); } // TODO Paced train : Adapt this to handle delete paced trains in issue https://github.com/OpenRailAssociation/osrd/issues/10615 - const editoastSelectedTrainScheduleIds = timetableItemIds + const editoastSelectedTrainScheduleIds = selectedTimetableItemIds .filter(isTrainSchedule) .map((id) => formatTrainScheduleIdToEditoastTrainId(id)); await deleteTrainSchedules({ body: { ids: editoastSelectedTrainScheduleIds } }) .unwrap() .then(() => { - removeTrains(trainScheduleIds); + removeTrains(selectedTrainScheduleIds); dispatch( setSuccess({ title: t('timetable.trainsSelectionDeletedCount', { count: itemsCount }), @@ -145,7 +166,10 @@ const TimetableToolbar = ({ }) .catch((e) => { // TODO Paced train : Adapt this to handle delete paced trains in issue https://github.com/OpenRailAssociation/osrd/issues/10615 - if (selectedTrainId && timetableItemIds.includes(selectedTrainId as TrainScheduleId)) { + if ( + selectedTrainId && + selectedTimetableItemIds.includes(selectedTrainId as TrainScheduleId) + ) { dispatch(updateSelectedTrainId(selectedTrainId)); } else { dispatch(setFailure(castErrorToFailure(e))); @@ -171,6 +195,45 @@ const TimetableToolbar = ({ a.click(); }; + const computedItemLabel = (trainSchedulesCount: number, pacedTrainCount: number) => { + if (trainSchedulesCount === 0 && pacedTrainCount === 0) return t('timetable.noItem'); + + const pacedTrainLabel = t('pacedTrainCount', { + count: selectedPacedTrainIds.length, + totalCount: totalPacedTrainCount, + }); + + const trainScheduleLabel = t('trainCount', { + count: selectedTrainScheduleIds.length, + totalCount: totalTrainScheduleCount, + }); + + if ( + trainSchedulesCount === 0 || + (selectedPacedTrainIds.length > 0 && selectedTrainScheduleIds.length === 0) + ) { + return pacedTrainLabel; + } + + if ( + pacedTrainCount === 0 || + (selectedTrainScheduleIds.length > 0 && selectedPacedTrainIds.length === 0) + ) { + return trainScheduleLabel; + } + + if (selectedTrainScheduleIds.length > 0 && selectedPacedTrainIds.length > 0) { + return t('pacedTrainAndTrainCount', { + pacedTrainCount: selectedPacedTrainIds.length, + totalPacedTrainCount, + trainCount: selectedTrainScheduleIds.length, + totalTrainScheduleCount, + }); + } + + return `${pacedTrainLabel}\u00A0${t('translation:common.and')}\u00A0${trainScheduleLabel}`; + }; + return ( <>
0 + selectedTimetableItemIds.length === timetableItems.length && + selectedTimetableItemIds.length > 0 } isIndeterminate={ - timetableItemIds.length !== timetableItems.length && timetableItemIds.length > 0 + selectedTimetableItemIds.length !== timetableItems.length && + selectedTimetableItemIds.length > 0 } onChange={() => toggleAllTrainsSelecton()} /> @@ -218,7 +280,7 @@ const TimetableToolbar = ({ )}
- {timetableItemIds.length > 0 && ( + {selectedTimetableItemIds.length > 0 && (
)} diff --git a/front/src/modules/trainschedule/components/Timetable/types.ts b/front/src/modules/trainschedule/components/Timetable/types.ts index 98facd0c2f9..c62d6c3f70b 100644 --- a/front/src/modules/trainschedule/components/Timetable/types.ts +++ b/front/src/modules/trainschedule/components/Timetable/types.ts @@ -14,7 +14,7 @@ export type ScheduledPointsHonoredFilter = 'both' | 'honored' | 'notHonored'; type SimulationSummaryResultSuccess = Extract; -export type TimetableItemWithDetails = Omit< +type TimetableItemWithDetails = Omit< TrainScheduleResult, 'id' | 'train_name' | 'rolling_stock_name' | 'timetable_id' | 'start_time' > & { @@ -57,3 +57,8 @@ export type PacedTrainWithResult = TimetableItemWithDetails & { }; export type TimetableItemResult = TrainScheduleWithDetails | PacedTrainWithResult; + +export type SelectedTimetableIdsByType = { + selectedTrainScheduleIds: TrainScheduleId[]; + selectedPacedTrainIds: PacedTrainId[]; +}; diff --git a/front/src/styles/scss/applications/operationalStudies/_pacedTrain.scss b/front/src/styles/scss/applications/operationalStudies/_pacedTrain.scss index a4e0c597084..410450998bb 100644 --- a/front/src/styles/scss/applications/operationalStudies/_pacedTrain.scss +++ b/front/src/styles/scss/applications/operationalStudies/_pacedTrain.scss @@ -20,7 +20,7 @@ } } - &:has(.paced-train-right-zone:hover, .action-buttons:hover):not(.modified) { + &:hover:not(.modified) { .action-buttons { display: flex !important; z-index: 1; @@ -35,12 +35,6 @@ z-index: 3; } - &.invalid { - .checkbox-title { - min-width: auto !important; - } - } - .paced-train-main-info { display: flex; margin-inline: 9px 24px; @@ -144,4 +138,16 @@ height: 0; } } + + .invalid-reason { + margin-right: 18px; + font-size: 16px; + color: var(--error80); + font-weight: 600; + min-width: 155px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 24px; + } } diff --git a/front/src/styles/scss/applications/operationalStudies/_scenario.scss b/front/src/styles/scss/applications/operationalStudies/_scenario.scss index 3ba87465a1e..1340dd2a521 100644 --- a/front/src/styles/scss/applications/operationalStudies/_scenario.scss +++ b/front/src/styles/scss/applications/operationalStudies/_scenario.scss @@ -655,15 +655,11 @@ background-image: url('data:image/svg+xml,%3Csvg version="1.1" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"%3E%3Ctitle%3EAssets/TrainList/InvalidTrain%3C/title%3E%3Cg fill-rule="evenodd"%3E%3Crect width="48" height="48" fill="%23FFEEED"/%3E%3Cpath d="m48 26v22h-22l22-22zm0-26-48 48v-22l26-26h22z" fill="%23FF6868" opacity=".1"/%3E%3C/g%3E%3C/svg%3E'); background-repeat: repeat; box-shadow: inset 0 -1px 0 0 rgba(0, 0, 0, 0.25); - .checkbox-title { - min-width: 0; - } + .checkbox-title::after { display: none; } .rolling-stock { - min-width: max-content; - white-space: nowrap; font-weight: 600; color: var(--error80); }