Skip to content

Commit 131078d

Browse files
Uriel-SautronMath-R
andcommitted
- create TimetableItemAction
Co-authored-by: Math_R_ <mathieu.richard747@gmail.com>
1 parent 7e4aa0e commit 131078d

File tree

6 files changed

+246
-104
lines changed

6 files changed

+246
-104
lines changed

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

+66-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
import React, { useState } from 'react';
1+
import { useState } from 'react';
22

33
import { Checkbox } from '@osrd-project/ui-core';
4-
import { ChevronDown, Manchette } from '@osrd-project/ui-icons';
4+
import { ChevronDown, Clock, Flame, Manchette } from '@osrd-project/ui-icons';
55
import cx from 'classnames';
6+
import dayjs from 'dayjs';
7+
import { useTranslation } from 'react-i18next';
68

9+
import { Duration } from 'utils/duration';
10+
import { ms2min } from 'utils/timeManipulation';
11+
12+
import TimetableItemActions from '../TimetableItemActions';
713
import type { TrainScheduleWithDetails } from '../types';
814

915
export type PacedTrain = TrainScheduleWithDetails & {
@@ -28,10 +34,21 @@ const PacedTrainItem = ({
2834
isOnEdit,
2935
isProjectionPathUsed,
3036
}: PacedTrainItemProps) => {
37+
const { t } = useTranslation(['operationalStudies/scenario']);
38+
3139
const [isOpened, setIsOpened] = useState(false);
3240

3341
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);
3448

49+
const occurencesCount = Math.floor(
50+
(Duration.parse(pacedTrain.paced.duration).ms - 6000) / Duration.parse(pacedTrain.paced.step).ms
51+
);
3552
return (
3653
<div
3754
data-testid="scenario-timetable-train"
@@ -42,7 +59,16 @@ const PacedTrainItem = ({
4259
// invalid: train.invalidReason,
4360
})}
4461
>
45-
<div className="base-info" onClick={toggle} role="button" tabIndex={0}>
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+
>
4672
<div className="checkbox-title">
4773
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
4874
<div onClick={(e) => e.stopPropagation()}>
@@ -53,7 +79,7 @@ const PacedTrainItem = ({
5379
small
5480
/>
5581
</div>
56-
<div className="occurancies-count">1</div>
82+
<div className="occurences-count">{occurencesCount}</div>
5783
<ChevronDown className="toggle-icon" />
5884
</div>
5985
<div title={pacedTrain.trainName} className="checkbox-label">
@@ -66,8 +92,43 @@ const PacedTrainItem = ({
6692
<span className="train-name">{pacedTrain.trainName}</span>
6793
</div>
6894
</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+
)}
69106
</div>
70-
<div className="occurancies" />
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+
)}
71132
</div>
72133
);
73134
};

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

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

20-
import fakePacedTrainData from './PacedTrain/fakeData.json';
2121
import PacedTrainItem, { type PacedTrain } from './PacedTrain/PacedTrainItem';
2222
import TimetableToolbar from './TimetableToolbar';
2323
import TimetableTrainCard from './TimetableTrainCard';
@@ -51,6 +51,7 @@ const Timetable = ({
5151
dtoImport,
5252
}: TimetableProps) => {
5353
const { t } = useTranslation(['operationalStudies/scenario', 'common/itemTypes']);
54+
const showPacedTrains = useSelector(getShowPacedTrains);
5455

5556
const [displayedTrainSchedules, setDisplayedTrainSchedules] = useState<
5657
TrainScheduleWithDetails[]
@@ -152,45 +153,48 @@ const Timetable = ({
152153
isInSelection={selectedTrainIds.length > 0}
153154
/>
154155
<Virtualizer overscan={15}>
155-
{displayedTrainSchedules.map((train: TrainScheduleWithDetails, index) => (
156-
<div key={`timetable-train-card-${train.id}`}>
157-
{showDepartureDates[index] && (
158-
<div className="scenario-timetable-departure-date">
159-
{currentDepartureDates[index]}
160-
</div>
161-
)}
162-
<TimetableTrainCard
163-
isInSelection={selectedTrainIds.includes(train.id)}
164-
handleSelectTrain={handleSelectTrain}
165-
train={train}
166-
isSelected={infraState === 'CACHED' && selectedTrainId === train.id}
167-
isModified={train.id === trainIdToEdit}
168-
setDisplayTrainScheduleManagement={setDisplayTrainScheduleManagement}
169-
upsertTrainSchedules={upsertTrainSchedules}
170-
setTrainIdToEdit={setTrainIdToEdit}
171-
removeTrains={removeAndUnselectTrains}
172-
projectionPathIsUsed={
173-
infraState === 'CACHED' && trainIdUsedForProjection === train.id
156+
{displayedTrainSchedules.map((train: TrainScheduleWithDetails, index) => (
157+
<div key={`timetable-train-card-${train.id}`}>
158+
{showDepartureDates[index] && (
159+
<div className="scenario-timetable-departure-date">
160+
{currentDepartureDates[index]}
161+
</div>
162+
)}
163+
<TimetableTrainCard
164+
isInSelection={selectedTrainIds.includes(train.id)}
165+
handleSelectTrain={handleSelectTrain}
166+
train={train}
167+
isSelected={infraState === 'CACHED' && selectedTrainId === train.id}
168+
isModified={train.id === trainIdToEdit}
169+
setDisplayTrainScheduleManagement={setDisplayTrainScheduleManagement}
170+
upsertTrainSchedules={upsertTrainSchedules}
171+
setTrainIdToEdit={setTrainIdToEdit}
172+
removeTrains={removeAndUnselectTrains}
173+
projectionPathIsUsed={
174+
infraState === 'CACHED' && trainIdUsedForProjection === train.id
175+
}
176+
dtoImport={dtoImport}
177+
/>
178+
</div>
179+
))}
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
174191
}
175-
dtoImport={dtoImport}
192+
isInSelection={selectedTrainIds.includes(12345)}
193+
handleSelectPacedTrain={handleSelectTrain}
194+
isOnEdit={false}
195+
isProjectionPathUsed={false}
176196
/>
177-
</div>
178-
))}
179-
<PacedTrainItem
180-
pacedTrain={
181-
{
182-
...fakePacedTrainData,
183-
startTime: new Date(fakePacedTrainData.startTime),
184-
arrivalTime: fakePacedTrainData.arrivalTime
185-
? new Date(fakePacedTrainData.arrivalTime)
186-
: null,
187-
} as PacedTrain
188-
}
189-
isInSelection={selectedTrainIds.includes(fakePacedTrainData.id)}
190-
handleSelectPacedTrain={handleSelectTrain}
191-
isOnEdit={false}
192-
isProjectionPathUsed={false}
193-
/>
197+
)}
194198
</Virtualizer>
195199
<div
196200
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
};

0 commit comments

Comments
 (0)