@@ -9,6 +9,7 @@ import useSWR from "swr";
99import { GearIcon , PauseIcon , PlayIcon } from "@components/icons" ;
1010import { NetworkGraph2d } from "@components/network-graph/network-graph-2d" ;
1111import { NetworkGraph3d } from "@components/network-graph/network-graph-3d" ;
12+ import { NetworkGlobe } from "@components/network-graph/network-globe" ;
1213
1314import { FieldReportsOverlay } from "./FieldReportsOverlay" ;
1415import { InfoPanel , type SelectedInfo } from "./InfoPanel" ;
@@ -19,7 +20,9 @@ import { TimelineBar } from "./TimelineBar";
1920import { useFieldReports , useImagePreloader } from "@/hooks/dashboard" ;
2021import type { DataResponse } from "@/pages/api/data" ;
2122import type { FieldReportsResponse , Pool , PoolsResponse } from "@/types" ;
23+ import type { GlobeDataResponse , GlobePoint , GlobeArc } from "@/types/globe" ;
2224import type { Voucher } from "@/types/voucher" ;
25+ import type { GraphType } from "./sections/DisplaySection" ;
2326
2427// Fetcher function for SWR
2528const fetcher = ( url : string ) => fetch ( url ) . then ( ( res ) => res . json ( ) ) ;
@@ -63,7 +66,17 @@ export function Dashboard() {
6366
6467 // Panel states
6568 const [ optionsOpen , setOptionsOpen ] = React . useState ( false ) ;
66- const [ graphType , setGraphType ] = React . useState < "2D" | "3D" > ( "2D" ) ;
69+ const [ graphType , setGraphType ] = React . useState < GraphType > ( "2D" ) ;
70+
71+ // Fetch globe geo data (only when Globe view is active)
72+ const { data : geoData } = useSWR < GlobeDataResponse > (
73+ graphType === "Globe" ? "/api/geo" : null ,
74+ fetcher ,
75+ {
76+ refreshInterval : 5 * 60 * 1000 ,
77+ revalidateOnFocus : false ,
78+ }
79+ ) ;
6780 const [ showTimelineBar , setShowTimelineBar ] = React . useState ( true ) ;
6881
6982 // Pool filtering
@@ -391,6 +404,36 @@ export function Dashboard() {
391404 } ) ;
392405 } , [ ] ) ;
393406
407+ const handleGlobePointClick = React . useCallback ( ( point : GlobePoint ) => {
408+ if ( point . type === "account" ) {
409+ setSelectedInfo ( {
410+ type : "node" ,
411+ data : {
412+ id : point . id ,
413+ value : point . value ,
414+ usedVouchers : { } ,
415+ } ,
416+ } ) ;
417+ }
418+ } , [ ] ) ;
419+
420+ const handleGlobeArcClick = React . useCallback ( ( arc : GlobeArc ) => {
421+ setSelectedInfo ( {
422+ type : "link" ,
423+ data : {
424+ source : arc . sourceId ,
425+ target : arc . targetId ,
426+ token_name : arc . tokenName ,
427+ token_symbol : arc . tokenSymbol ,
428+ contract_address : arc . contractAddress ,
429+ txCount : arc . txCount ,
430+ value : arc . value ,
431+ date : arc . date ,
432+ dateFirst : arc . dateFirst ,
433+ } ,
434+ } ) ;
435+ } , [ ] ) ;
436+
394437 const copyToClipboard = React . useCallback ( ( text : string , field : string ) => {
395438 navigator . clipboard . writeText ( text ) ;
396439 setCopiedField ( field ) ;
@@ -522,8 +565,25 @@ export function Dashboard() {
522565 />
523566 ) }
524567
525- { /* Graph */ }
526- { graphData && graphType === "2D" ? (
568+ { /* Graph / Globe */ }
569+ { graphType === "Globe" ? (
570+ < NetworkGlobe
571+ globeData = {
572+ geoData ?. globeData ?? {
573+ points : [ ] ,
574+ arcs : [ ] ,
575+ unmappedAccountCount : 0 ,
576+ totalAccountCount : 0 ,
577+ }
578+ }
579+ animate = { animate }
580+ selectedVoucherAddresses = { selectedVoucherAddresses }
581+ currentDate = { date }
582+ showRecentOnly = { showRecentOnly }
583+ onPointClick = { handleGlobePointClick }
584+ onArcClick = { handleGlobeArcClick }
585+ />
586+ ) : graphType === "2D" ? (
527587 < NetworkGraph2d
528588 animate = { animate }
529589 graphData = { graphData }
0 commit comments