1
- import { useMemo , useState , useCallback } from 'react' ;
1
+ import { useMemo , useState , useCallback , useEffect } from 'react' ;
2
2
3
- import { compact , groupBy } from 'lodash' ;
3
+ import { compact } from 'lodash' ;
4
4
import { useSelector } from 'react-redux' ;
5
5
6
- import type { PathItem , SearchResultItemTrainSchedule } from 'common/api/osrdEditoastApi' ;
6
+ import type {
7
+ PathItem ,
8
+ SearchQuery ,
9
+ SearchResultItemOperationalPoint ,
10
+ SearchResultItemTrainSchedule ,
11
+ } from 'common/api/osrdEditoastApi' ;
7
12
import { osrdEditoastApi } from 'common/api/osrdEditoastApi' ;
8
- import { useOsrdConfSelectors } from 'common/osrdContext' ;
9
- import { isEqualDate } from 'utils/date' ;
13
+ import { useInfraID , useOsrdConfSelectors } from 'common/osrdContext' ;
14
+ import { isArrivalDateInSearchTimeWindow , isEqualDate } from 'utils/date' ;
10
15
11
- import type { StdcmLinkedPathResult , StdcmLinkedPathStep } from '../types' ;
16
+ import type { StdcmLinkedPathResult } from '../types' ;
12
17
import computeOpSchedules from '../utils/computeOpSchedules' ;
13
18
14
19
const useLinkedPathSearch = ( ) => {
15
20
const [ postSearch ] = osrdEditoastApi . endpoints . postSearch . useMutation ( ) ;
21
+ const [ postTrainScheduleSimulationSummary ] =
22
+ osrdEditoastApi . endpoints . postTrainScheduleSimulationSummary . useLazyQuery ( ) ;
16
23
17
24
const { getTimetableID, getSearchDatetimeWindow } = useOsrdConfSelectors ( ) ;
18
25
26
+ const infraId = useInfraID ( ) ;
19
27
const timetableId = useSelector ( getTimetableID ) ;
20
28
const searchDatetimeWindow = useSelector ( getSearchDatetimeWindow ) ;
21
29
@@ -29,42 +37,60 @@ const useLinkedPathSearch = () => {
29
37
} , [ searchDatetimeWindow ] ) ;
30
38
31
39
const [ displaySearchButton , setDisplaySearchButton ] = useState ( true ) ;
32
- const [ hasSearchBeenLaunched , setHasSearchBeenLaunched ] = useState ( false ) ;
33
40
const [ trainNameInput , setTrainNameInput ] = useState ( '' ) ;
34
41
const [ linkedPathDate , setLinkedPathDate ] = useState ( selectableSlot . start ) ;
35
- const [ linkedPathResults , setLinkedPathResults ] = useState < StdcmLinkedPathResult [ ] > ( [ ] ) ;
42
+ const [ linkedPathResults , setLinkedPathResults ] = useState < StdcmLinkedPathResult [ ] > ( ) ;
36
43
37
- const getExtremitiesDetails = useCallback (
38
- async ( pathItemList : PathItem [ ] ) => {
39
- const origin = pathItemList . at ( 0 ) ! ;
40
- const destination = pathItemList . at ( - 1 ) ! ;
41
- if ( ! ( 'operational_point' in origin ) || ! ( 'operational_point' in destination ) )
42
- return undefined ;
43
- const originId = origin . operational_point ;
44
- const destinationId = destination . operational_point ;
44
+ const getExtremityDetails = useCallback (
45
+ async ( pathItem : PathItem ) => {
46
+ if ( ! ( 'operational_point' in pathItem ) && ! ( 'uic' in pathItem ) ) return undefined ;
47
+
48
+ const pathItemQuery =
49
+ 'operational_point' in pathItem
50
+ ? [ '=' , [ 'obj_id' ] , pathItem . operational_point ]
51
+ : ( [
52
+ 'and' ,
53
+ [ '=' , [ 'uic' ] , pathItem . uic ] ,
54
+ [ '=' , [ 'ch' ] , pathItem . secondary_code ] ,
55
+ ] as SearchQuery ) ;
45
56
46
57
try {
47
58
const payloadOP = {
48
59
object : 'operationalpoint' ,
49
- query : [ 'or' , [ '=' , [ 'obj_id' ] , originId ] , [ '=' , [ 'obj_id' ] , destinationId ] ] ,
50
- } ;
51
- const resultsOP = await postSearch ( { searchPayload : payloadOP , pageSize : 25 } ) . unwrap ( ) ;
52
- const groupedResults = groupBy ( resultsOP , 'obj_id' ) ;
53
- return {
54
- origin : groupedResults [ originId ] [ 0 ] ,
55
- destination : groupedResults [ destinationId ] [ 0 ] ,
60
+ query : pathItemQuery ,
56
61
} ;
62
+ const opDetails = ( await postSearch ( {
63
+ searchPayload : payloadOP ,
64
+ pageSize : 25 ,
65
+ } ) . unwrap ( ) ) as SearchResultItemOperationalPoint [ ] ;
66
+ return opDetails [ 0 ] ;
57
67
} catch ( error ) {
58
- console . error ( 'Failed to fetch operational points :' , error ) ;
68
+ console . error ( 'Failed to fetch operational point :' , error ) ;
59
69
return undefined ;
60
70
}
61
71
} ,
62
72
[ postSearch ]
63
73
) ;
64
74
75
+ const getTrainsSummaries = useCallback (
76
+ async ( trainsIds : number [ ] ) => {
77
+ if ( ! infraId ) return undefined ;
78
+ const trainsSummaries = await postTrainScheduleSimulationSummary ( {
79
+ body : {
80
+ infra_id : infraId ,
81
+ ids : trainsIds ,
82
+ } ,
83
+ } ) . unwrap ( ) ;
84
+ return trainsSummaries ;
85
+ } ,
86
+ [ postTrainScheduleSimulationSummary , infraId ]
87
+ ) ;
88
+
65
89
const launchTrainScheduleSearch = useCallback ( async ( ) => {
90
+ setLinkedPathResults ( undefined ) ;
91
+ if ( ! trainNameInput ) return ;
92
+
66
93
setDisplaySearchButton ( false ) ;
67
- setLinkedPathResults ( [ ] ) ;
68
94
try {
69
95
const results = ( await postSearch ( {
70
96
searchPayload : {
@@ -86,41 +112,59 @@ const useLinkedPathSearch = () => {
86
112
return ;
87
113
}
88
114
115
+ const filteredResultsSummaries = await getTrainsSummaries ( filteredResults . map ( ( r ) => r . id ) ) ;
116
+
89
117
const newLinkedPathResults = await Promise . all (
90
118
filteredResults . map ( async ( result ) => {
91
- const opDetails = await getExtremitiesDetails ( result . path ) ;
92
- const computedOpSchedules = computeOpSchedules (
93
- result . start_time ,
94
- result . schedule . at ( - 1 ) ! . arrival !
95
- ) ;
96
- if ( opDetails === undefined ) return undefined ;
119
+ const resultSummary = filteredResultsSummaries && filteredResultsSummaries [ result . id ] ;
120
+ if ( ! resultSummary || resultSummary . status !== 'success' ) return undefined ;
121
+ const msFromStartTime = resultSummary . path_item_times_final . at ( - 1 ) ! ;
122
+
123
+ const originDetails = await getExtremityDetails ( result . path . at ( 0 ) ! ) ;
124
+ const destinationDetails = await getExtremityDetails ( result . path . at ( - 1 ) ! ) ;
125
+ const computedOpSchedules = computeOpSchedules ( result . start_time , msFromStartTime ) ;
126
+
127
+ if ( ! originDetails || ! destinationDetails ) return undefined ;
97
128
return {
98
129
trainName : result . train_name ,
99
- origin : { ...opDetails . origin , ...computedOpSchedules . origin } as StdcmLinkedPathStep ,
130
+ origin : { ...originDetails , ...computedOpSchedules . origin } ,
100
131
destination : {
101
- ...opDetails . destination ,
132
+ ...destinationDetails ,
102
133
...computedOpSchedules . destination ,
103
- } as StdcmLinkedPathStep ,
134
+ } ,
104
135
} ;
105
136
} )
106
137
) ;
107
138
setLinkedPathResults ( compact ( newLinkedPathResults ) ) ;
108
- setHasSearchBeenLaunched ( true ) ;
109
139
} catch ( error ) {
110
140
console . error ( 'Train schedule search failed:' , error ) ;
111
141
setDisplaySearchButton ( true ) ;
112
142
}
113
- } , [ postSearch , trainNameInput , timetableId , linkedPathDate , getExtremitiesDetails ] ) ;
143
+ } , [ postSearch , trainNameInput , timetableId , linkedPathDate , getExtremityDetails ] ) ;
144
+
145
+ const resetLinkedPathSearch = ( ) => {
146
+ setDisplaySearchButton ( true ) ;
147
+ setLinkedPathResults ( undefined ) ;
148
+ setTrainNameInput ( '' ) ;
149
+ } ;
150
+
151
+ useEffect ( ( ) => {
152
+ if ( ! isArrivalDateInSearchTimeWindow ( linkedPathDate , searchDatetimeWindow ) ) {
153
+ setLinkedPathDate ( selectableSlot . start ) ;
154
+ resetLinkedPathSearch ( ) ;
155
+ }
156
+ } , [ selectableSlot ] ) ;
114
157
115
158
return {
116
159
displaySearchButton,
117
- hasSearchBeenLaunched,
118
160
launchTrainScheduleSearch,
119
161
linkedPathDate,
120
162
linkedPathResults,
163
+ resetLinkedPathSearch,
121
164
selectableSlot,
122
165
setDisplaySearchButton,
123
166
setLinkedPathDate,
167
+ setLinkedPathResults,
124
168
setTrainNameInput,
125
169
trainNameInput,
126
170
} ;
0 commit comments