Skip to content
Open
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
9 changes: 8 additions & 1 deletion front/public/locales/en/operational-studies.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,15 +344,22 @@
"inverseOD": "Reverse itinerary",
"itineraryModal": {
"addLocationOnMap": "Add a location on the map",
"alertInvalidOP": "At least one of the waypoints could not be recognized",
"focusLocationOnMap": "Focus the map on this operational point",
"frameAll": "Center on all path steps on map",
"invalidOP": "Non recognized point : ",
"moveLocationOnMap": "Move location on the map",
"next": "Next",
"opId": "Operational point identifier",
"opName": "Operational point name",
"openItineraryModal": "New interface for itinerary edition",
"requestedPoint": "Point on map",
"secondaryCode": "Code",
"track": "Track",
"trainName": "Train name"
"trackId": "Track identifier",
"trainName": "Train name",
"trigram": "Trigram",
"uic": "UIC"
},
"launchPathFinding": "Launch pathfinding",
"manageVias": "Steps management",
Expand Down
9 changes: 8 additions & 1 deletion front/public/locales/fr/operational-studies.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,15 +344,22 @@
"inverseOD": "Inverser l'itinéraire",
"itineraryModal": {
"addLocationOnMap": "Ajouter un point remarquable sur la carte",
"alertInvalidOP": "Au moins un des points de passage n'a pas été reconnu",
"focusLocationOnMap": "Centrer la carte sur ce point remarquable",
"frameAll": "Centrer sur tous les points de passage sur la carte",
"invalidOP": "Point non reconnu : ",
"moveLocationOnMap": "Déplacer le point remarquable sur la carte",
"next": "Suivant",
"opId": "Identifiant de point",
"opName": "Nom du point remarquable",
"openItineraryModal": "Nouvelle interface d'édition d'itinéraire",
"requestedPoint": "Point sur la carte",
"secondaryCode": "CH",
"track": "Voie",
"trainName": "Nom du train"
"trackId": "Identifiant de voie",
"trainName": "Nom du train",
"trigram": "Trigramme",
"uic": "UIC"
},
"launchPathFinding": "Lancer la recherche d'itinéraire",
"manageVias": "Gestion des étapes",
Expand Down
5 changes: 5 additions & 0 deletions front/src/applications/operationalStudies/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,8 @@ export type StudyCardDetails = SearchResultItemStudy | StudyWithScenarios;
export type ScenarioCardDetails = SearchResultItemScenario | ScenarioWithDetails;

export type CategoryColors = { normal: string; hovered: string; background: string };

export type ItineraryPathProperties = PathProperties & {
length: number;
incompatibleConstraints?: IncompatibleConstraints;
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,3 @@
max-width: 640px;
}
}

.alert-box {
width: 100%;
min-height: 56px;
display: flex;
align-items: center;
padding: 0 24px 0 40px;
border-top: 1px solid;
border-bottom: 1px solid;
margin-top: -1px;
margin-bottom: 16px;

&--warning {
border-color: var(--black10);
background-color: var(--warning5);

.alert-box__icon--warning {
color: var(--warning30);
margin-right: 12px;
width: 24px;
height: 24px;
}

.alert-box__text--warning {
font-family: 'IBM Plex Sans';
color: var(--warning60);
font-size: 16px;
font-weight: 400;
}

.alert-box__close-button {
color: var(--warning60);
margin-left: auto;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useScenarioContext } from 'applications/operationalStudies/hooks/useSce
import AlertBox from 'common/AlertBox';
import type { PathProperties } from 'common/api/osrdEditoastApi';
import { computeBBoxViewport } from 'common/Map/WarpedMap/core/helpers';
import IncompatibleConstraints from 'modules/pathfinding/components/IncompatibleConstraints';
import usePathfindingV2 from 'modules/pathfinding/hooks/usePathfindingV2';
import { useMapSettings, useMapSettingsActions } from 'reducers/commonMap';
import {
Expand Down Expand Up @@ -63,9 +64,12 @@ const ItineraryModal = ({
const [categoryWarning, setCategoryWarning] = useState<string | undefined>(undefined);

const { pathStepsMetadataById } = usePathStepsMetadata(pathSteps);
const { launchPathfindingV2, pathProperties } = usePathfindingV2();
const { launchPathfindingV2, pathProperties, pathfindingError } = usePathfindingV2();

const isMapDisabled = window.matchMedia('(max-width: 1028px)').matches;
const hasInvalidPathStep = Array.from(pathStepsMetadataById.values()).some(
(metadata) => metadata.isInvalid
);

const frameAllPathSteps = () => {
if (pathProperties && pathProperties.geometry) {
Expand Down Expand Up @@ -186,6 +190,10 @@ const ItineraryModal = ({
</div>
<div className="itinerary-modal-form-body">
{categoryWarning && <AlertBox message={categoryWarning} closeable />}
{hasInvalidPathStep && <AlertBox type="error" message={t('alertInvalidOP')} />}
{!hasInvalidPathStep && pathfindingError && (
<AlertBox type="error" message={pathfindingError} />
)}
<div className="path-step-list">
<div className="itinerary-icons">
<button className="frame-all" onClick={frameAllPathSteps}>
Expand All @@ -197,15 +205,24 @@ const ItineraryModal = ({
<span>{t('secondaryCode')}</span>
<span>{t('track')}</span>
</div>
{pathSteps.map((pathStep, i) => (
<PathStepItem
key={pathStep.id}
pathStep={pathStep}
pathStepMetadata={pathStepsMetadataById.get(pathStep.id)}
index={i + 1}
categoryColors={categoryColors}
/>
))}
{pathSteps.map((pathStep, i) => {
const pathStepMetadata = pathStepsMetadataById.get(pathStep.id);
const previousPathStepMetadata = i
? pathStepsMetadataById.get(pathSteps[i - 1].id)
: undefined;
return (
<PathStepItem
key={pathStep.id}
pathStep={pathStep}
pathStepMetadata={pathStepMetadata}
index={i + 1}
categoryColors={categoryColors}
hidePathfindingLine={
i > 0 && (pathStepMetadata?.isInvalid || previousPathStepMetadata?.isInvalid)
}
/>
);
})}
<PathStepItem
hidePathfindingLine={pathSteps.length === 0}
categoryColors={categoryColors}
Expand All @@ -222,7 +239,13 @@ const ItineraryModal = ({
pathSteps={pathSteps}
pathStepsMetadata={pathStepsMetadataById}
pathProperties={pathProperties}
/>
>
<IncompatibleConstraints
geometry={pathProperties?.geometry}
pathLength={pathProperties?.length}
incompatibleConstraints={pathProperties?.incompatibleConstraints}
/>
</ItineraryModalMap>
</div>
)}
</dialog>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useMemo, useRef, useState } from 'react';
import { useCallback, useMemo, useRef, useState, type PropsWithChildren } from 'react';

import type { Position } from 'geojson';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -42,7 +42,8 @@ const ItineraryModalMap = ({
pathSteps,
pathStepsMetadata,
pathProperties,
}: ItineraryModalMapProps) => {
children,
}: PropsWithChildren<ItineraryModalMapProps>) => {
const { t } = useTranslation('operational-studies', {
keyPrefix: 'main',
});
Expand Down Expand Up @@ -222,6 +223,7 @@ const ItineraryModalMap = ({
/>
);
})}
{children}
</BaseMap>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,39 @@ const PathStepItem = ({
const mapSettings = useMapSettings();
const { updateViewport } = useMapSettingsActions();

const getInvalidMessage = () => {
let message = t('invalidOP');
if (!pathStepMetadata?.isInvalid || !pathStep?.location) return message;

const { location } = pathStep;

if ('track' in location) {
return (message += `${t('requestedPoint')}`);
}

const trackInfo = location.track_reference
? 'track_name' in location.track_reference
? `, ${t('track')} ${location.track_reference.track_name}`
: `, ${t('trackId')}`
: '';

if ('operational_point' in location) {
return (message += `${t('opId')}${trackInfo}`);
}

const secondaryCodeInfo = `${location.secondary_code ? `/${location.secondary_code}` : ''}`;

if ('trigram' in location) {
message += `${t('trigram')} ${location.trigram}`;
}

if ('uic' in location) {
message += `${t('uic')} ${location.uic}`;
}

return (message += `${secondaryCodeInfo}${trackInfo}`);
};

const secondaryCodeSuggestions = useMemo(() => {
if (!isOpRefMetadata(pathStepMetadata)) return [];
return [
Expand Down Expand Up @@ -134,20 +167,29 @@ const PathStepItem = ({
>
<div
className={cx('path-step-counter', {
invalid: pathStepMetadata?.isInvalid,
index,
'pathfinding-line': !hidePathfindingLine,
origin: index === 1,
empty: !pathStep,
})}
style={{
borderColor: index ? categoryColors.background : categoryColors.normal,
borderColor: !pathStepMetadata?.isInvalid
? index
? categoryColors.background
: categoryColors.normal
: '',
// @ts-expect-error: variable CSS custom property to be used to style ::before
'--pathBackground': categoryColors.normal,
}}
>
{index}
</div>
<div className="path-step-op-name">
<div
className={cx('path-step-op-name', {
invalid: pathStepMetadata?.isInvalid,
})}
>
<ComboBox
id={`pathStep-name-${pathStep?.id ?? 'empty'}`}
value={isOpRefMetadata(pathStepMetadata) ? pathStepMetadata.name : ''}
Expand All @@ -164,28 +206,40 @@ const PathStepItem = ({
<div className="requested-point-block" />
) : (
<>
<Select
id={`pathStep-type-${pathStep?.id ?? 'empty'}`}
value={selectedSecondaryCodeOption}
options={secondaryCodeSuggestions}
getOptionLabel={(option) => option.label}
getOptionValue={(option) => option.id}
onChange={() => {}}
small
narrow
readOnly
/>
<Select
id={`pathStep-status-${pathStep?.id ?? 'empty'}`}
value={selectedTrackNameOption}
options={trackNameSuggestions}
getOptionLabel={(option) => option.label}
getOptionValue={(option) => option.id}
onChange={() => {}}
small
narrow
readOnly
/>
<div
className={cx('secondary-code', {
invalid: pathStepMetadata?.isInvalid,
})}
>
<Select
id={`pathStep-type-${pathStep?.id ?? 'empty'}`}
value={selectedSecondaryCodeOption}
options={secondaryCodeSuggestions}
getOptionLabel={(option) => option.label}
getOptionValue={(option) => option.id}
onChange={() => {}}
small
narrow
readOnly
/>
</div>
<div
className={cx('track-name', {
invalid: pathStepMetadata?.isInvalid,
})}
>
<Select
id={`pathStep-status-${pathStep?.id ?? 'empty'}`}
value={selectedTrackNameOption}
options={trackNameSuggestions}
getOptionLabel={(option) => option.label}
getOptionValue={(option) => option.id}
onChange={() => {}}
small
narrow
readOnly
/>
</div>
</>
)}
<div className="map-interactions">
Expand Down Expand Up @@ -217,6 +271,9 @@ const PathStepItem = ({
</button>
</div>
</div>
{pathStepMetadata?.isInvalid && (
<span className="invalid-step-message">{getInvalidMessage()}</span>
)}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,22 @@ export const usePathStepsMetadata = (pathSteps: PathStepV2[]) => {
if ('track' in location) {
// TODO : replace the name by the track offset label when provided by backend
const correspondingTrack = trackSectionsById[location.track];
const coordinates = getPointOnTrackCoordinates(
correspondingTrack.geo,
correspondingTrack.length,
location.offset
);

const coordinates = correspondingTrack
? getPointOnTrackCoordinates(
correspondingTrack.geo,
correspondingTrack.length,
location.offset
)
: null;

if (!correspondingTrack || !coordinates) {
// Can happen in case of track offset id does not exist in infra or
// if its offset is greater than the track length
newPathStepsMetadataById.set(pathStep.id, { isInvalid: true });
return;
}

newPathStepsMetadataById.set(pathStep.id, {
type: 'trackOffset',
isInvalid: false,
Expand All @@ -173,8 +184,20 @@ export const usePathStepsMetadata = (pathSteps: PathStepV2[]) => {
);
});

// If no op is found, it means the path step is invalid
if (!matchedOp) {
const trackRef = location.track_reference;
const isValidTrackReference = trackRef
? matchedOp?.parts.some((part) => {
const track = trackSectionsById[part.track];
if (!track) return false;
if ('track_name' in trackRef) {
return trackRef.track_name === track.extensions?.sncf?.track_name;
}
return trackRef.track_id === track.id;
})
: true;

// If no op is found or if its track_reference is invalid, it means the path step is invalid
if (!isValidTrackReference || !matchedOp) {
newPathStepsMetadataById.set(pathStep.id, { isInvalid: true });
return;
}
Expand Down
Loading
Loading