Skip to content

Commit 6a1f99e

Browse files
Uriel-SautronMath-R
andcommitted
front: create paced train item
- create PacedTrainItem component - create TimetableItemAction Co-authored-by: Math_R_ <mathieu.richard747@gmail.com> Signed-off-by: Uriel-Sautron <uriel.sautron@gmail.com>
1 parent 4ddefb0 commit 6a1f99e

File tree

8 files changed

+334
-51
lines changed

8 files changed

+334
-51
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { useState } from 'react';
2+
3+
import { Checkbox } from '@osrd-project/ui-core';
4+
import { ChevronDown, Clock, Flame, Manchette } from '@osrd-project/ui-icons';
5+
import cx from 'classnames';
6+
import dayjs from 'dayjs';
7+
import { useTranslation } from 'react-i18next';
8+
9+
import { Duration } from 'utils/duration';
10+
import { ms2min } from 'utils/timeManipulation';
11+
12+
import TimetableItemActions from '../TimetableItemActions';
13+
import type { TrainScheduleWithDetails } from '../types';
14+
15+
export type PacedTrain = TrainScheduleWithDetails & {
16+
paced: {
17+
duration: string;
18+
step: string;
19+
};
20+
};
21+
22+
type PacedTrainItemProps = {
23+
isInSelection: boolean;
24+
handleSelectPacedTrain: (pacedTrainId: number) => void;
25+
pacedTrain: PacedTrain;
26+
isOnEdit: boolean;
27+
isProjectionPathUsed: boolean;
28+
};
29+
30+
const PacedTrainItem = ({
31+
isInSelection,
32+
handleSelectPacedTrain,
33+
pacedTrain,
34+
isOnEdit,
35+
isProjectionPathUsed,
36+
}: PacedTrainItemProps) => {
37+
const { t } = useTranslation(['operationalStudies/scenario']);
38+
39+
const [isOpened, setIsOpened] = useState(false);
40+
41+
const toggle = () => setIsOpened((open) => !open);
42+
const selectPathProjection = async () => {};
43+
const duplicatePacedTrain = async () => {};
44+
const editPacedTrain = () => {};
45+
const deletePacedTrain = async () => {};
46+
47+
const stepDuration = Duration.parse(pacedTrain.paced.step);
48+
49+
const occurencesCount = Math.floor(
50+
(Duration.parse(pacedTrain.paced.duration).ms - 6000) / Duration.parse(pacedTrain.paced.step).ms
51+
);
52+
return (
53+
<div
54+
data-testid="scenario-timetable-train"
55+
className={cx('scenario-timetable-train paced-train', {
56+
modified: isOnEdit,
57+
'in-selection': isInSelection,
58+
closed: !isOpened,
59+
// invalid: train.invalidReason,
60+
})}
61+
>
62+
<div
63+
className={cx('base-info', {
64+
warning: pacedTrain.invalidReason || pacedTrain.notHonoredReason,
65+
invalid: pacedTrain.invalidReason,
66+
'not-honored': pacedTrain.notHonoredReason,
67+
})}
68+
onClick={toggle}
69+
role="button"
70+
tabIndex={0}
71+
>
72+
<div className="checkbox-title">
73+
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
74+
<div onClick={(e) => e.stopPropagation()}>
75+
<Checkbox
76+
label=""
77+
checked={isInSelection}
78+
onChange={() => handleSelectPacedTrain(pacedTrain.id)}
79+
small
80+
/>
81+
</div>
82+
<div className="occurences-count">{occurencesCount}</div>
83+
<ChevronDown className="toggle-icon" />
84+
</div>
85+
<div title={pacedTrain.trainName} className="checkbox-label">
86+
<div className="train-info">
87+
{isProjectionPathUsed && (
88+
<div className="train-projected">
89+
<Manchette iconColor="var(--white100)" />
90+
</div>
91+
)}
92+
<span className="train-name">{pacedTrain.trainName}</span>
93+
</div>
94+
</div>
95+
{!pacedTrain.invalidReason && (
96+
<div className="mission-time">
97+
{pacedTrain.isValid && (
98+
<div className="frequency">&mdash;{` ${ms2min(stepDuration.ms)}min`}</div>
99+
)}
100+
<div className="status-icon not-honored-or-too-fast">
101+
{pacedTrain.notHonoredReason &&
102+
(pacedTrain.notHonoredReason === 'scheduleNotHonored' ? <Clock /> : <Flame />)}
103+
</div>
104+
</div>
105+
)}
106+
</div>
107+
<TimetableItemActions
108+
selectPathProjection={selectPathProjection}
109+
duplicateTimetableItem={duplicatePacedTrain}
110+
editTimetableItem={editPacedTrain}
111+
deleteTimetableItem={deletePacedTrain}
112+
/>
113+
<div className="occurences" />
114+
{pacedTrain.isValid && (
115+
<div className="more-info">
116+
<div className="more-info-left">
117+
<span className="more-info-item">
118+
{t('timetable.stopsCount', { count: pacedTrain.stopsCount })}
119+
</span>
120+
<span className="more-info-item">{pacedTrain.pathLength}</span>
121+
<span className="more-info-item m-0" data-testid="allowance-energy-consumed">
122+
{pacedTrain.mechanicalEnergyConsumed}&nbsp;kWh
123+
</span>
124+
</div>
125+
<div className="duration-time">
126+
<span data-testid="train-duration">
127+
{dayjs.duration(pacedTrain.duration).format('HH[h]mm')}
128+
</span>
129+
</div>
130+
</div>
131+
)}
132+
</div>
133+
);
134+
};
135+
136+
export default PacedTrainItem;

front/src/modules/trainschedule/components/Timetable/Timetable.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ import {
1515
getSelectedTrainId,
1616
getTrainIdUsedForProjection,
1717
} from 'reducers/simulationResults/selectors';
18+
import { getShowPacedTrains } from 'reducers/user/userSelectors';
1819
import { useAppDispatch } from 'store';
1920

21+
import PacedTrainItem, { type PacedTrain } from './PacedTrain/PacedTrainItem';
2022
import TimetableToolbar from './TimetableToolbar';
2123
import TimetableTrainCard from './TimetableTrainCard';
2224
import type { TrainScheduleWithDetails } from './types';
@@ -49,6 +51,7 @@ const Timetable = ({
4951
dtoImport,
5052
}: TimetableProps) => {
5153
const { t } = useTranslation(['operationalStudies/scenario', 'common/itemTypes']);
54+
const showPacedTrains = useSelector(getShowPacedTrains);
5255

5356
const [displayedTrainSchedules, setDisplayedTrainSchedules] = useState<
5457
TrainScheduleWithDetails[]
@@ -174,6 +177,24 @@ const Timetable = ({
174177
/>
175178
</div>
176179
))}
180+
{showPacedTrains && (
181+
<PacedTrainItem
182+
pacedTrain={
183+
{
184+
...displayedTrainSchedules[0],
185+
id: 12345,
186+
paced: {
187+
duration: 'PT2H',
188+
step: 'PT20M',
189+
},
190+
} as PacedTrain
191+
}
192+
isInSelection={selectedTrainIds.includes(12345)}
193+
handleSelectPacedTrain={handleSelectTrain}
194+
isOnEdit={false}
195+
isProjectionPathUsed={false}
196+
/>
197+
)}
177198
</Virtualizer>
178199
<div
179200
className={cx('bottom-timetables-trains', {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// import React from 'react';
2+
3+
import { Duplicate, Pencil, Trash } from '@osrd-project/ui-icons';
4+
import { useTranslation } from 'react-i18next';
5+
import { GiPathDistance } from 'react-icons/gi';
6+
7+
type TimetableItemActionsProps = {
8+
selectPathProjection: () => Promise<void>;
9+
duplicateTimetableItem: () => Promise<void>;
10+
editTimetableItem: () => void;
11+
deleteTimetableItem: () => Promise<void>;
12+
};
13+
14+
const TimetableItemActions = ({
15+
selectPathProjection,
16+
duplicateTimetableItem,
17+
editTimetableItem,
18+
deleteTimetableItem,
19+
}: TimetableItemActionsProps) => {
20+
const { t } = useTranslation(['operationalStudies/scenario']);
21+
return (
22+
<div className="action-buttons">
23+
<button
24+
type="button"
25+
aria-label={t('timetable.choosePath')}
26+
title={t('timetable.choosePath')}
27+
onClick={selectPathProjection}
28+
>
29+
<GiPathDistance />
30+
</button>
31+
<button
32+
type="button"
33+
aria-label={t('timetable.duplicate')}
34+
title={t('timetable.duplicate')}
35+
onClick={duplicateTimetableItem}
36+
>
37+
<Duplicate />
38+
</button>
39+
<button
40+
type="button"
41+
aria-label={t('timetable.update')}
42+
title={t('timetable.update')}
43+
onClick={editTimetableItem}
44+
data-testid="edit-train"
45+
>
46+
<Pencil />
47+
</button>
48+
<button
49+
type="button"
50+
aria-label={t('timetable.delete')}
51+
title={t('timetable.delete')}
52+
onClick={deleteTimetableItem}
53+
>
54+
<Trash />
55+
</button>
56+
</div>
57+
);
58+
};
59+
60+
export default TimetableItemActions;

front/src/modules/trainschedule/components/Timetable/TimetableTrainCard.tsx

+10-39
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import React from 'react';
22

33
import { Checkbox } from '@osrd-project/ui-core';
4-
import { Duplicate, Pencil, Trash, Clock, Flame, Moon, Manchette } from '@osrd-project/ui-icons';
4+
import { Clock, Flame, Moon, Manchette } from '@osrd-project/ui-icons';
55
import cx from 'classnames';
66
import dayjs from 'dayjs';
77
import { omit } from 'lodash';
88
import { useTranslation } from 'react-i18next';
9-
import { GiPathDistance } from 'react-icons/gi';
109

1110
import { MANAGE_TRAIN_SCHEDULE_TYPES } from 'applications/operationalStudies/consts';
1211
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
@@ -20,6 +19,7 @@ import { useAppDispatch } from 'store';
2019
import { formatToIsoDate, isoDateToMs } from 'utils/date';
2120
import { castErrorToFailure } from 'utils/error';
2221

22+
import TimetableItemActions from './TimetableItemActions';
2323
import type { TrainScheduleWithDetails } from './types';
2424

2525
type TimetableTrainCardProps = {
@@ -205,7 +205,7 @@ const TimetableTrainCard = ({
205205
<div className="status-icon after-midnight">{isAfterMidnight && <Moon />}</div>
206206
{train.isValid && (
207207
<div
208-
className="scenario-timetable-train-departure"
208+
className="scenario-timetable-train-times"
209209
title={formatFullDate(train.startTime)}
210210
>
211211
{formatDateHours(train.startTime)}
@@ -218,7 +218,7 @@ const TimetableTrainCard = ({
218218
{train.arrivalTime && (
219219
<div
220220
data-testid="train-arrival-time"
221-
className="scenario-timetable-train-arrival"
221+
className="scenario-timetable-train-times"
222222
title={formatFullDate(train.arrivalTime)}
223223
>
224224
{formatDateHours(train.arrivalTime)}
@@ -254,41 +254,12 @@ const TimetableTrainCard = ({
254254
</div>
255255
)}
256256
</div>
257-
<div className="action-buttons">
258-
<button
259-
type="button"
260-
aria-label={t('timetable.choosePath')}
261-
title={t('timetable.choosePath')}
262-
onClick={selectPathProjection}
263-
>
264-
<GiPathDistance />
265-
</button>
266-
<button
267-
type="button"
268-
aria-label={t('timetable.duplicate')}
269-
title={t('timetable.duplicate')}
270-
onClick={duplicateTrain}
271-
>
272-
<Duplicate />
273-
</button>
274-
<button
275-
type="button"
276-
aria-label={t('timetable.update')}
277-
title={t('timetable.update')}
278-
onClick={editTrainSchedule}
279-
data-testid="edit-train"
280-
>
281-
<Pencil />
282-
</button>
283-
<button
284-
type="button"
285-
aria-label={t('timetable.delete')}
286-
title={t('timetable.delete')}
287-
onClick={deleteTrain}
288-
>
289-
<Trash />
290-
</button>
291-
</div>
257+
<TimetableItemActions
258+
selectPathProjection={selectPathProjection}
259+
duplicateTimetableItem={duplicateTrain}
260+
editTimetableItem={editTrainSchedule}
261+
deleteTimetableItem={deleteTrain}
262+
/>
292263
</div>
293264
);
294265
};

front/src/styles/scss/_variables.scss

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ $colors: (
5454
'grey40': #94918e,
5555
'grey50': #797671,
5656
'grey60': #5c5955,
57+
'grey70': #494641,
5758
'grey80': #312e2b,
5859
'grey90': #1f1b17,
5960
'info5': #e6f7ff,

0 commit comments

Comments
 (0)