@@ -60,7 +60,7 @@ function ServiceSchedule(props: { timing?: NUSShuttle; title: string }) {
60
60
61
61
function StopServiceDetails ( props : {
62
62
service : ISBService ;
63
- timings ?: NUSShuttle [ ] ;
63
+ timings ?: NUSShuttle [ ] | string ;
64
64
currentStop : ISBStop ;
65
65
selectedService : string | null ;
66
66
setSelectedService : ( service : string | null ) => void ;
@@ -282,10 +282,29 @@ function StopServiceDetails(props: {
282
282
</ div >
283
283
) }
284
284
< div className = { styles . divider } />
285
- < div className = { styles . serviceUpcoming } >
286
- { arriveTiming && < ServiceSchedule timing = { arriveTiming } title = "Arrivals" /> }
287
- { departTiming && < ServiceSchedule timing = { departTiming } title = "Departures" /> }
288
- </ div >
285
+ {
286
+ // if the service is not running, show the next time it will run
287
+ ! departTiming && ! arriveTiming ? (
288
+ < div className = { styles . serviceSchedule } >
289
+ { /* if is error show error msg */ }
290
+ { timings === 'error' ? (
291
+ < div className = { classNames ( styles . upcomingBuses , styles . none ) } >
292
+ Error fetching bus timings
293
+ </ div >
294
+ ) : (
295
+ < div className = { classNames ( styles . upcomingBuses , styles . none ) } >
296
+ No upcoming departures
297
+ </ div >
298
+ ) }
299
+ </ div >
300
+ ) : (
301
+ // if the service is running, show the next few buses
302
+ < div className = { styles . serviceUpcoming } >
303
+ { arriveTiming && < ServiceSchedule timing = { arriveTiming } title = "Arrivals" /> }
304
+ { departTiming && < ServiceSchedule timing = { departTiming } title = "Departures" /> }
305
+ </ div >
306
+ )
307
+ }
289
308
</ div >
290
309
) }
291
310
</ div >
@@ -333,77 +352,125 @@ function StopDetails(props: Props) {
333
352
const { stop } = props ;
334
353
const setSelectedServiceMap = props . setSelectedService ;
335
354
const stopDetails = isbStops . find ( ( s ) => s . name === stop ) ;
336
- const [ selectedStopTiming , setSelectedStopTiming ] = useState < ShuttleServiceResult | null > ( null ) ;
355
+ const [ selectedStopTiming , setSelectedStopTiming ] = useState <
356
+ ShuttleServiceResult | 'error' | 'loading'
357
+ > ( 'loading' ) ;
337
358
const [ selectedService , setSelectedService ] = useState < string | null > ( null ) ;
338
359
339
360
useEffect ( ( ) => {
340
361
if ( ! stop ) return ;
341
- getStopTimings ( stop , setSelectedStopTiming ) ;
362
+ setSelectedStopTiming ( 'loading' ) ;
363
+ getStopTimings (
364
+ stop ,
365
+ ( data ) => {
366
+ setSelectedStopTiming ( data ) ;
367
+ } ,
368
+ ( error ) => {
369
+ console . error ( error ) ;
370
+ setSelectedStopTiming ( 'error' ) ;
371
+ } ,
372
+ ) ;
342
373
} , [ stop ] ) ;
343
374
344
375
useEffect ( ( ) => {
345
376
if ( selectedService ) {
346
- // console.log('selectedService', selectedService);
347
377
setSelectedServiceMap ( isbServices . find ( ( s ) => s . name === selectedService ) || isbServices [ 0 ] ) ;
348
378
}
349
- } , [ selectedService ] ) ;
350
-
351
- if ( ! stopDetails ) return < div > Stop not found</ div > ;
352
-
353
- // console.log(selectedStopTiming);
354
-
355
- const { ShortName, LongName, shuttles } = stopDetails ;
356
- const nusShuttles = shuttles
357
- . filter ( ( shuttle ) => shuttle . routeid )
358
- . filter (
359
- ( shuttle , index , self ) => index === self . findIndex ( ( s ) => s . routeid === shuttle . routeid ) ,
360
- )
361
- . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
362
- // console.log('shuttles', stopDetails);
363
-
364
- const incomingBuses : {
365
- service : ISBService ;
366
- arrivingInSeconds : number ;
367
- plate : string ;
368
- } [ ] = [ ] ;
369
- nusShuttles . forEach ( ( shuttle ) => {
370
- const serviceDetail = isbServices . find ( ( s ) => s . id === shuttle . name . toLocaleLowerCase ( ) ) ;
371
- if ( ! serviceDetail ) return ;
372
- const isEnd = stopDetails . name === serviceDetail . stops [ serviceDetail . stops . length - 1 ] ;
373
- const serviceShuttles = selectedStopTiming ?. shuttles . filter (
374
- ( s ) => s . name === shuttle . name ,
375
- ) as NUSShuttle [ ] ;
376
- const timings = getDepartAndArriveTiming ( serviceShuttles , isEnd ) ;
377
- const timing = timings . departTiming ;
378
-
379
- timing ?. _etas ?. forEach ( ( eta ) => {
380
- incomingBuses . push ( {
381
- service : serviceDetail ,
382
- arrivingInSeconds : eta . eta_s ,
383
- plate : eta . plate ,
379
+ } , [ selectedService , setSelectedServiceMap ] ) ;
380
+
381
+ const incoming = useMemo ( ( ) => {
382
+ if ( stopDetails === undefined ) return null ;
383
+
384
+ const nusShuttles = stopDetails . shuttles
385
+ . filter ( ( shuttle ) => shuttle . routeid )
386
+ . filter (
387
+ ( shuttle , index , self ) => index === self . findIndex ( ( s ) => s . routeid === shuttle . routeid ) ,
388
+ )
389
+ . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
390
+
391
+ if ( selectedStopTiming === 'loading' || selectedStopTiming === 'error' ) {
392
+ return {
393
+ services : nusShuttles ,
394
+ buses : [ ] ,
395
+ buses_grouped : [ ] ,
396
+ } ;
397
+ }
398
+
399
+ const incomingBuses : {
400
+ service : ISBService ;
401
+ arrivingInSeconds : number ;
402
+ plate : string ;
403
+ } [ ] = [ ] ;
404
+ nusShuttles . forEach ( ( shuttle ) => {
405
+ const serviceDetail = isbServices . find ( ( s ) => s . id === shuttle . name . toLocaleLowerCase ( ) ) ;
406
+ if ( ! serviceDetail ) return ;
407
+ const isEnd = stopDetails . name === serviceDetail . stops [ serviceDetail . stops . length - 1 ] ;
408
+ const serviceShuttles = selectedStopTiming ?. shuttles . filter (
409
+ ( s ) => s . name === shuttle . name ,
410
+ ) as NUSShuttle [ ] ;
411
+ const timings = getDepartAndArriveTiming ( serviceShuttles , isEnd ) ;
412
+ const timing = timings . departTiming ;
413
+
414
+ timing ?. _etas ?. forEach ( ( eta ) => {
415
+ incomingBuses . push ( {
416
+ service : serviceDetail ,
417
+ arrivingInSeconds : eta . eta_s ,
418
+ plate : eta . plate ,
419
+ } ) ;
384
420
} ) ;
385
421
} ) ;
386
- } ) ;
387
- incomingBuses . sort ( ( a , b ) => a . arrivingInSeconds - b . arrivingInSeconds ) ;
388
- incomingBuses . splice ( 4 ) ;
389
- const incomingBusGroups = incomingBuses . reduce ( ( acc , bus ) => {
390
- const shownTime = getShownArrivalTime ( bus . arrivingInSeconds ) ;
391
- const newAcc = { ...acc } ;
392
- if ( ! newAcc [ shownTime ] ) newAcc [ shownTime ] = [ ] ;
393
- newAcc [ shownTime ] . push ( bus ) ;
394
- return newAcc ;
395
- } , { } as Record < string , typeof incomingBuses > ) ;
396
-
397
- const publicShuttles = shuttles
398
- . filter ( ( shuttle ) => shuttle . name . startsWith ( 'PUB:' ) )
399
- . map (
400
- ( shuttle ) =>
401
- ( {
402
- ...selectedStopTiming ?. shuttles ?. find ( ( s ) => s . name === shuttle . name ) ,
422
+ incomingBuses . sort ( ( a , b ) => a . arrivingInSeconds - b . arrivingInSeconds ) ;
423
+ incomingBuses . splice ( 4 ) ;
424
+ const incomingBusGroups = incomingBuses . reduce ( ( acc , bus ) => {
425
+ const shownTime = getShownArrivalTime ( bus . arrivingInSeconds ) ;
426
+ const newAcc = { ...acc } ;
427
+ if ( ! newAcc [ shownTime ] ) newAcc [ shownTime ] = [ ] ;
428
+ newAcc [ shownTime ] . push ( bus ) ;
429
+ return newAcc ;
430
+ } , { } as Record < string , typeof incomingBuses > ) ;
431
+
432
+ return {
433
+ services : nusShuttles ,
434
+ buses : incomingBuses ,
435
+ buses_grouped : incomingBusGroups ,
436
+ } ;
437
+ } , [ selectedStopTiming , stopDetails ] ) ;
438
+
439
+ const incomingPublic = useMemo ( ( ) => {
440
+ if ( stopDetails === undefined ) return null ;
441
+ const { shuttles } = stopDetails ;
442
+
443
+ return shuttles
444
+ . filter ( ( shuttle ) => shuttle . name . startsWith ( 'PUB:' ) )
445
+ . map ( ( shuttle ) => {
446
+ let st = {
403
447
number : parseInt ( shuttle . name . replace ( 'PUB:' , '' ) , 10 ) ,
404
- } as PublicShuttle ) ,
405
- )
406
- . sort ( ( a , b ) => a . number - b . number ) ;
448
+ } as PublicShuttle ;
449
+ if ( selectedStopTiming !== 'loading' && selectedStopTiming !== 'error' ) {
450
+ st = {
451
+ ...selectedStopTiming ?. shuttles ?. find ( ( s ) => s . name === shuttle . name ) ,
452
+ number : parseInt ( shuttle . name . replace ( 'PUB:' , '' ) , 10 ) ,
453
+ } as PublicShuttle ;
454
+ }
455
+ return st ;
456
+ } )
457
+ . sort ( ( a , b ) => a . number - b . number ) ;
458
+ } , [ selectedStopTiming , stopDetails ] ) ;
459
+
460
+ if ( ! stopDetails || ! incoming || ! incomingPublic ) return < div > Stop not found</ div > ;
461
+
462
+ const { ShortName, LongName } = stopDetails ;
463
+
464
+ // const publicShuttles = shuttles
465
+ // .filter((shuttle) => shuttle.name.startsWith('PUB:'))
466
+ // .map(
467
+ // (shuttle) =>
468
+ // ({
469
+ // ...selectedStopTiming?.shuttles?.find((s) => s.name === shuttle.name),
470
+ // number: parseInt(shuttle.name.replace('PUB:', ''), 10),
471
+ // } as PublicShuttle),
472
+ // )
473
+ // .sort((a, b) => a.number - b.number);
407
474
408
475
return (
409
476
< div >
@@ -413,8 +480,8 @@ function StopDetails(props: Props) {
413
480
414
481
< div className = { styles . incomingBusesWrapper } >
415
482
< ol className = { styles . incomingBuses } >
416
- { Object . entries ( incomingBusGroups ) . length ? (
417
- Object . entries ( incomingBusGroups ) . map ( ( [ time , buses ] , i ) => (
483
+ { Object . entries ( incoming . buses_grouped ) . length ? (
484
+ Object . entries ( incoming . buses_grouped ) . map ( ( [ time , buses ] , i ) => (
418
485
< Fragment key = { `${ time } ${ stopDetails . name } ` } >
419
486
{ i > 0 && < ChevronRight className = { styles . chevron } /> }
420
487
< li className = { styles . serviceWithChevron } >
@@ -440,17 +507,28 @@ function StopDetails(props: Props) {
440
507
) )
441
508
) : (
442
509
< span className = { classNames ( styles . noIncoming , 'text-muted' ) } >
443
- No upcoming buses today
510
+ { /* No upcoming buses today */ }
511
+ { /* if there is an error fetching, show an error msg */ }
512
+ { selectedStopTiming === 'error'
513
+ ? 'Error fetching bus timings'
514
+ : 'No upcoming buses today' }
444
515
</ span >
445
516
) }
446
517
</ ol >
447
518
</ div >
448
519
449
- { nusShuttles . map ( ( shuttle ) => {
520
+ { incoming . services . map ( ( shuttle ) => {
450
521
const service = isbServices . find ( ( s ) => s . id === shuttle . name . toLocaleLowerCase ( ) ) ;
451
- const timings = selectedStopTiming ?. shuttles . filter (
452
- ( s ) => s . name === shuttle . name ,
453
- ) as NUSShuttle [ ] ;
522
+ let timings ;
523
+ if ( selectedStopTiming === 'loading' ) {
524
+ timings = 'loading' ;
525
+ } else if ( selectedStopTiming === 'error' ) {
526
+ timings = 'error' ;
527
+ } else {
528
+ timings = selectedStopTiming ?. shuttles . filter (
529
+ ( s ) => s . name === shuttle . name ,
530
+ ) as NUSShuttle [ ] ;
531
+ }
454
532
if ( ! service ) return < Fragment key = { shuttle . name } /> ;
455
533
return (
456
534
< StopServiceDetails
@@ -463,7 +541,7 @@ function StopDetails(props: Props) {
463
541
/>
464
542
) ;
465
543
} ) }
466
- { publicShuttles . map ( ( shuttle ) => (
544
+ { incomingPublic . map ( ( shuttle ) => (
467
545
< PublicBusDetails service = { shuttle } />
468
546
) ) }
469
547
</ div >
0 commit comments