diff --git a/front/src/modules/timesStops/TimeColumnComponent.tsx b/front/src/modules/timesStops/TimeInput.tsx similarity index 68% rename from front/src/modules/timesStops/TimeColumnComponent.tsx rename to front/src/modules/timesStops/TimeInput.tsx index fbedf292044..0e6cc4b5f9a 100644 --- a/front/src/modules/timesStops/TimeColumnComponent.tsx +++ b/front/src/modules/timesStops/TimeInput.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useRef, useState, useEffect } from 'react'; -import type { CellComponent, CellProps, Column } from 'react-datasheet-grid/dist/types'; +import type { CellProps } from 'react-datasheet-grid/dist/types'; -const TimeComponent = ({ +const TimeInput = ({ focus, rowData, active, @@ -51,15 +51,6 @@ const TimeComponent = ({ ); }; -TimeComponent.displayName = 'TimeComponent'; +TimeInput.displayName = 'TimeInput'; -const timeColumn: Partial> = { - component: TimeComponent as CellComponent, - deleteValue: () => null, - copyValue: ({ rowData }) => rowData ?? null, - pasteValue: ({ value }) => value, - minWidth: 170, - isCellEmpty: ({ rowData }) => !rowData, -}; - -export default timeColumn; +export default TimeInput; diff --git a/front/src/modules/timesStops/TimeStopsColumns.tsx b/front/src/modules/timesStops/TimeStopsColumns.tsx new file mode 100644 index 00000000000..4cf4201e332 --- /dev/null +++ b/front/src/modules/timesStops/TimeStopsColumns.tsx @@ -0,0 +1,88 @@ +import cx from 'classnames'; +import type { TFunction } from 'i18next'; +import { keyColumn, type Column, checkboxColumn, createTextColumn } from 'react-datasheet-grid'; +import type { CellComponent } from 'react-datasheet-grid/dist/types'; + +import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; +import { marginRegExValidation } from 'utils/physics'; + +import TimeInput from './TimeInput'; +import type { PathWaypointColumn } from './types'; + +const timeColumn: Partial> = { + component: TimeInput as CellComponent, + deleteValue: () => null, + copyValue: ({ rowData }) => rowData ?? null, + pasteValue: ({ value }) => value, + minWidth: 170, + isCellEmpty: ({ rowData }) => !rowData, +}; + +export const inputColumns = ( + t: TFunction<'timesStops', undefined>, + pathProperties: ManageTrainSchedulePathProperties +) => + [ + { + ...keyColumn('name', createTextColumn()), + title: t('name'), + disabled: true, + }, + { + ...keyColumn('ch', createTextColumn()), + title: 'Ch', + disabled: true, + grow: 0.1, + }, + { + ...keyColumn('arrival', timeColumn), + title: t('arrivalTime'), + + // We should not be able to edit the arrival time of the origin + disabled: ({ rowIndex }) => rowIndex === 0, + grow: 0.6, + }, + { + ...keyColumn( + 'stopFor', + createTextColumn({ + continuousUpdates: false, + alignRight: true, + }) + ), + title: `${t('stopTime')}`, + grow: 0.6, + }, + { + ...keyColumn('onStopSignal', checkboxColumn as Partial>), + title: t('receptionOnClosedSignal'), + + // We should not be able to edit the reception on close signal if stopFor is not filled + // except for the destination + grow: 0.6, + disabled: ({ rowData, rowIndex }) => + rowIndex !== pathProperties.allWaypoints?.length - 1 && !rowData.stopFor, + }, + { + ...keyColumn( + 'theoreticalMargin', + createTextColumn({ + continuousUpdates: false, + alignRight: true, + placeholder: t('theoreticalMarginPlaceholder'), + formatBlurredInput: (value) => { + if (!value || value === 'none') return ''; + if (!marginRegExValidation.test(value)) { + return `${value}${t('theoreticalMarginPlaceholder')}`; + } + return value; + }, + }) + ), + cellClassName: ({ rowData }) => cx({ invalidCell: !rowData.isMarginValid }), + title: t('theoreticalMargin'), + disabled: ({ rowIndex }) => rowIndex === pathProperties.allWaypoints.length - 1, + }, + ] as Column[]; + +export default timeColumn; diff --git a/front/src/modules/timesStops/TimesStops.tsx b/front/src/modules/timesStops/TimesStops.tsx index c5ad4657bfa..e2a89b13ee0 100644 --- a/front/src/modules/timesStops/TimesStops.tsx +++ b/front/src/modules/timesStops/TimesStops.tsx @@ -1,12 +1,6 @@ import React, { useMemo, useState, useEffect } from 'react'; -import { - keyColumn, - type Column, - checkboxColumn, - createTextColumn, - DynamicDataSheetGrid, -} from 'react-datasheet-grid'; +import { type Column, DynamicDataSheetGrid } from 'react-datasheet-grid'; import { useTranslation } from 'react-i18next'; import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types'; @@ -16,9 +10,9 @@ import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSc import type { PathStep } from 'reducers/osrdconf/types'; import { useAppDispatch } from 'store'; import { removeElementAtIndex } from 'utils/array'; +import { marginRegExValidation } from 'utils/physics'; -import { marginRegExValidation } from './consts'; -import timeColumn from './TimeColumnComponent'; +import { inputColumns } from './TimeStopsColumns'; import type { PathWaypointColumn } from './types'; import { formatSuggestedViasToRowVias } from './utils'; @@ -30,19 +24,21 @@ type TimesStopsProps = { startTime?: string; }; +type DeleteButtonProps = { + removeVia: () => void; + rowIndex: number; + rowData: PathWaypointColumn; + pathProperties: ManageTrainSchedulePathProperties; + pathSteps: PathStep[]; +}; + const createDeleteViaButton = ({ removeVia, rowIndex, rowData, pathProperties, pathSteps, -}: { - removeVia: () => void; - rowIndex: number; - rowData: PathWaypointColumn; - pathProperties: ManageTrainSchedulePathProperties; - pathSteps: PathStep[]; -}) => { +}: DeleteButtonProps) => { const isRowVia = rowIndex !== 0 && rowIndex !== pathProperties.allWaypoints?.length - 1 && @@ -84,72 +80,8 @@ const TimesStops = ({ pathProperties, pathSteps = [], startTime }: TimesStopsPro setTimesStopsSteps(suggestedOPs); }, [t, pathProperties.allWaypoints, startTime]); - const columns: Column[] = useMemo( - () => [ - { - ...keyColumn('name', createTextColumn()), - title: t('name'), - disabled: true, - }, - { - ...keyColumn('ch', createTextColumn()), - title: 'Ch', - disabled: true, - grow: 0.1, - }, - { - ...keyColumn('arrival', timeColumn), - title: t('arrivalTime'), - - // We should not be able to edit the arrival time of the origin - disabled: ({ rowIndex }) => rowIndex === 0, - grow: 0.6, - }, - { - ...keyColumn( - 'stopFor', - createTextColumn({ - continuousUpdates: false, - alignRight: true, - }) - ), - title: `${t('stopTime')}`, - grow: 0.6, - }, - { - ...keyColumn( - 'onStopSignal', - checkboxColumn as Partial> - ), - title: t('receptionOnClosedSignal'), - - // We should not be able to edit the reception on close signal if stopFor is not filled - // except for the destination - grow: 0.6, - disabled: ({ rowData, rowIndex }) => - rowIndex !== pathProperties.allWaypoints?.length - 1 && !rowData.stopFor, - }, - { - ...keyColumn( - 'theoreticalMargin', - createTextColumn({ - continuousUpdates: false, - alignRight: true, - placeholder: t('theoreticalMarginPlaceholder'), - formatBlurredInput: (value) => { - if (!value || value === 'none') return ''; - if (!marginRegExValidation.test(value)) { - return `${value}${t('theoreticalMarginPlaceholder')}`; - } - return value; - }, - }) - ), - cellClassName: ({ rowData }) => (rowData.isMarginValid ? '' : 'invalidCell'), - title: t('theoreticalMargin'), - disabled: ({ rowIndex }) => rowIndex === pathProperties.allWaypoints.length - 1, - }, - ], + const columns = useMemo[]>( + () => inputColumns(t, pathProperties), [t, pathProperties.allWaypoints.length] ); diff --git a/front/src/modules/timesStops/consts.ts b/front/src/modules/timesStops/consts.ts deleted file mode 100644 index fc5b10c8bb8..00000000000 --- a/front/src/modules/timesStops/consts.ts +++ /dev/null @@ -1,2 +0,0 @@ -// eslint-disable-next-line import/prefer-default-export -export const marginRegExValidation = /^\d+(\.\d+)?%$|^\d+(\.\d+)?min\/100km$/; diff --git a/front/src/modules/timesStops/utils.ts b/front/src/modules/timesStops/utils.ts index 10ffea90d86..c7f6870a580 100644 --- a/front/src/modules/timesStops/utils.ts +++ b/front/src/modules/timesStops/utils.ts @@ -2,8 +2,8 @@ import type { TFunction } from 'i18next'; import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types'; import type { PathStep } from 'reducers/osrdconf/types'; +import { marginRegExValidation } from 'utils/physics'; -import { marginRegExValidation } from './consts'; import type { PathWaypointColumn } from './types'; // eslint-disable-next-line import/prefer-default-export diff --git a/front/src/utils/physics.ts b/front/src/utils/physics.ts index 5ed47fa5147..b195a7e6912 100644 --- a/front/src/utils/physics.ts +++ b/front/src/utils/physics.ts @@ -24,3 +24,5 @@ export function msToKmh(v: number) { export function mToKmOneDecimal(m: number) { return Math.round(m / 100) / 10; } + +export const marginRegExValidation = /^\d+(\.\d+)?%$|^\d+(\.\d+)?min\/100km$/;