1- import React , { useEffect , useState , useRef } from 'react' ;
1+ import React , { useEffect , useState , useRef , useCallback } from 'react' ;
22import { useDispatch , useStore } from 'react-redux' ;
33import { Box } from '@mui/material' ;
44import { getLayoutManagerInstance } from '@metacell/geppetto-meta-client/common/layout/LayoutManager' ;
@@ -28,6 +28,7 @@ import {
2828 fetchKnowledgeStatements ,
2929 fetchMajorNerves ,
3030 fetchOrderJson ,
31+ fetchEndorgansOrder ,
3132} from './services/fetchService.ts' ;
3233import { getUniqueMajorNerves } from './services/filterValuesService.ts' ;
3334import {
@@ -37,10 +38,10 @@ import {
3738} from './models/explorer.ts' ;
3839import {
3940 getHierarchicalNodes ,
40- getOrgans ,
41- } from './services/hierarchyService.ts ' ;
41+ getOrgansAndTargetSystems ,
42+ } from './services/hierarchyService' ;
4243import ReactGA from 'react-ga4' ;
43- import { Datasnapshot , OrderJson } from './models/json.ts' ;
44+ import { Datasnapshot , OrderJson , NerveResponse } from './models/json.ts' ;
4445import { useDataContext } from './context/DataContext.ts' ;
4546import LoadingOverlay from './components/common/LoadingOverlay.tsx' ;
4647import ErrorModal from './components/common/ErrorModal.tsx' ;
@@ -99,6 +100,12 @@ const AppContent = () => {
99100 Record < string , HierarchicalNode >
100101 > ( { } ) ;
101102 const [ organs , setOrgans ] = useState < Record < string , Organ > > ( { } ) ;
103+ const [ targetSystems , setTargetSystems ] = useState < Record < string , Organ [ ] > > (
104+ { } ,
105+ ) ;
106+ const [ targetSystemNames , setTargetSystemNames ] = useState <
107+ Record < string , string >
108+ > ( { } ) ;
102109 const [ majorNerves , setMajorNerves ] = useState < Set < string > > ( ) ;
103110 const [ knowledgeStatements , setKnowledgeStatements ] = useState <
104111 Record < string , KnowledgeStatement >
@@ -108,6 +115,7 @@ const AppContent = () => {
108115 useState < string > ( '' ) ;
109116 const [ hasValidatedInitialURL , setHasValidatedInitialURL ] = useState ( false ) ;
110117 const [ isDataLoading , setIsDataLoading ] = useState ( false ) ;
118+ const [ hasCriticalError , setHasCriticalError ] = useState ( false ) ;
111119 const [ fetchError , setFetchError ] = useState < {
112120 show : boolean ;
113121 message : string ;
@@ -120,6 +128,9 @@ const AppContent = () => {
120128 } ) ;
121129 const previousDatasnaphshot = useRef < string > ( '' ) ;
122130 const [ orderData , setOrderData ] = useState < OrderJson > ( { } ) ;
131+ const [ endorgansOrder , setEndorgansOrder ] = useState <
132+ Record < string , string [ ] >
133+ > ( { } ) ;
123134 const [ searchParams ] = useSearchParams ( ) ;
124135 const [ urlState , setUrlState ] = useState < URLState > ( {
125136 datasnapshot : null ,
@@ -146,40 +157,106 @@ const AppContent = () => {
146157 dispatch ( addWidget ( connectionsWidget ( ) ) ) ;
147158 } , [ LayoutComponent , dispatch ] ) ;
148159
149- const fetchJSONAndSetHierarchicalNodes = (
150- datasnapshot : Datasnapshot ,
151- orderData : OrderJson ,
152- ) => {
153- fetchJSON ( datasnapshot . a_b_via_c_json_file ) . then ( ( jsonData ) => {
154- setHierarchicalNodes ( getHierarchicalNodes ( jsonData , orderData ) ) ;
155- setOrgans ( getOrgans ( jsonData ) ) ;
156- } ) ;
157- } ;
160+ const fetchJSONAndSetHierarchicalNodes = useCallback (
161+ ( datasnapshot : Datasnapshot , orderData : OrderJson ) => {
162+ fetchJSON ( datasnapshot . a_b_via_c_json_file ) . then ( ( jsonData ) => {
163+ setHierarchicalNodes ( getHierarchicalNodes ( jsonData , orderData ) ) ;
164+ const { organs, targetSystems, targetSystemNames } =
165+ getOrgansAndTargetSystems ( jsonData , endorgansOrder ) ;
166+ setOrgans ( organs ) ;
167+ setTargetSystems ( targetSystems ) ;
168+ setTargetSystemNames ( targetSystemNames ) ;
169+ } ) ;
170+ } ,
171+ [ endorgansOrder ] ,
172+ ) ;
158173
159174 useEffect ( ( ) => {
160175 const fetchInitialData = async ( ) => {
176+ const failedResources : string [ ] = [ ] ;
177+
161178 try {
162- const [ orderDataFetched , majorNervesData , datasnapshots ] =
163- await Promise . all ( [
164- fetchOrderJson ( ) ,
165- fetchMajorNerves ( ) ,
166- fetchDatasnapshots ( ) ,
167- ] ) ;
179+ const results = await Promise . allSettled ( [
180+ fetchOrderJson ( ) . catch ( ( err ) => {
181+ failedResources . push ( 'Organ hierarchy order (GitHub)' ) ;
182+ throw err ;
183+ } ) ,
184+ fetchMajorNerves ( ) . catch ( ( err ) => {
185+ failedResources . push ( 'Major nerves data (GitHub)' ) ;
186+ throw err ;
187+ } ) ,
188+ fetchDatasnapshots ( ) . catch ( ( err ) => {
189+ failedResources . push ( 'Data snapshots (API)' ) ;
190+ throw err ;
191+ } ) ,
192+ fetchEndorgansOrder ( ) . catch ( ( err ) => {
193+ failedResources . push ( 'End organs hierarchy (GitHub)' ) ;
194+ throw err ;
195+ } ) ,
196+ ] ) ;
197+
198+ // Check if any fetch failed
199+ const failures = results . filter (
200+ ( result ) => result . status === 'rejected' ,
201+ ) ;
202+
203+ if ( failures . length > 0 ) {
204+ const errorDetails = failures
205+ . map ( ( failure , index ) => {
206+ if ( failure . status === 'rejected' ) {
207+ const resourceNames = [
208+ 'Organ hierarchy order (GitHub)' ,
209+ 'Major nerves data (GitHub)' ,
210+ 'Data snapshots (API)' ,
211+ 'End organs hierarchy (GitHub)' ,
212+ ] ;
213+ return `• ${ resourceNames [ index ] } : ${ failure . reason instanceof Error ? failure . reason . message : String ( failure . reason ) } ` ;
214+ }
215+ return '' ;
216+ } )
217+ . filter ( Boolean )
218+ . join ( '\n' ) ;
219+
220+ setHasCriticalError ( true ) ;
221+ setFetchError ( {
222+ show : true ,
223+ title : 'Resources Unavailable' ,
224+ message :
225+ 'The resources needed to start the application are not available at the moment. Please try again later or contact support if the problem persists.' ,
226+ details : `Failed to fetch:\n${ errorDetails } ` ,
227+ } ) ;
228+ return ; // Don't proceed with partial data
229+ }
230+
231+ // All fetches succeeded, extract the data
232+ const successfulResults = results . map ( ( result ) =>
233+ result . status === 'fulfilled' ? result . value : null ,
234+ ) ;
235+
236+ const orderDataFetched = successfulResults [ 0 ] as OrderJson ;
237+ const majorNervesData = successfulResults [ 1 ] as NerveResponse ;
238+ const datasnaphotsData = successfulResults [ 2 ] as Datasnapshot [ ] ;
239+ const endorgansOrderFetched = successfulResults [ 3 ] as Record <
240+ string ,
241+ string [ ]
242+ > ;
168243
169244 setOrderData ( orderDataFetched ) ;
170245 setMajorNerves ( getUniqueMajorNerves ( majorNervesData ) ) ;
171- setdatasnapshots ( datasnapshots ) ;
246+ setdatasnapshots ( datasnaphotsData ) ;
247+ setEndorgansOrder ( endorgansOrderFetched ) ;
172248 } catch ( error ) {
173- console . error ( 'Failed to fetch initial data:' , error ) ;
249+ // This catch is a fallback for unexpected errors
250+ console . error ( 'Unexpected error during initial data fetch:' , error ) ;
251+ setHasCriticalError ( true ) ;
174252 setFetchError ( {
175253 show : true ,
176- title : 'Data Loading Error ' ,
254+ title : 'Resources Unavailable ' ,
177255 message :
178- 'Failed to load initial application data . Please refresh the page .' ,
256+ 'The resources needed to start the application are not available at the moment . Please try again later .' ,
179257 details :
180258 error instanceof Error ? error . message : 'Unknown error occurred' ,
181259 } ) ;
182- setMajorNerves ( undefined ) ;
183260 }
184261 } ;
185262
@@ -241,10 +318,20 @@ const AppContent = () => {
241318 const selectedSnapshotObj = datasnapshots . find (
242319 ( ds : Datasnapshot ) => ds . id === parseInt ( selectedDatasnaphshot ) ,
243320 ) ;
244- if ( selectedSnapshotObj && Object . keys ( orderData ) . length > 0 ) {
321+ if (
322+ selectedSnapshotObj &&
323+ Object . keys ( orderData ) . length > 0 &&
324+ Object . keys ( endorgansOrder ) . length > 0
325+ ) {
245326 fetchJSONAndSetHierarchicalNodes ( selectedSnapshotObj , orderData ) ;
246327 }
247- } , [ selectedDatasnaphshot , orderData , datasnapshots ] ) ;
328+ } , [
329+ selectedDatasnaphshot ,
330+ orderData ,
331+ datasnapshots ,
332+ endorgansOrder ,
333+ fetchJSONAndSetHierarchicalNodes ,
334+ ] ) ;
248335
249336 useEffect ( ( ) => {
250337 if ( Object . keys ( hierarchicalNodes ) . length > 0 && selectedDatasnaphshot ) {
@@ -324,7 +411,7 @@ const AppContent = () => {
324411 Object . keys ( knowledgeStatements ) . length == 0 ,
325412 ] ;
326413
327- const isLoading = loadingConditions . some ( Boolean ) ;
414+ const isLoading = ! hasCriticalError && loadingConditions . some ( Boolean ) ;
328415 const loadingProgress =
329416 ( loadingConditions . filter ( ( c ) => ! c ) . length / loadingConditions . length ) *
330417 100 ;
@@ -363,7 +450,10 @@ const AppContent = () => {
363450 majorNerves = { majorNerves ? majorNerves : new Set < string > ( ) }
364451 hierarchicalNodes = { hierarchicalNodes }
365452 organs = { organs }
453+ targetSystems = { targetSystems }
454+ targetSystemNames = { targetSystemNames }
366455 knowledgeStatements = { knowledgeStatements }
456+ endorgansOrder = { endorgansOrder }
367457 >
368458 < Box >
369459 < Header
0 commit comments