@@ -416,14 +416,25 @@ function EngagementTab() {
416416
417417type EventSubTab = 'flow' | 'graph' ;
418418
419- // Custom Sankey node renderer with labels.
420- function SankeyNode ( { x, y, width, height, payload } : { x : number ; y : number ; width : number ; height : number ; payload : { name : string ; value ?: number } } ) {
419+ // Custom Sankey node renderer with labels showing count and flow-through %.
420+ function SankeyNode ( { x, y, width, height, payload } : {
421+ x : number ; y : number ; width : number ; height : number ;
422+ payload : { name : string ; count ?: number ; pct ?: number } ;
423+ } ) {
424+ const count = payload . count ?? 0 ;
425+ const pct = payload . pct ;
426+ const label = pct !== undefined
427+ ? `${ payload . name } ${ formatNum ( count ) } (${ pct . toFixed ( 1 ) } %)`
428+ : `${ payload . name } ${ formatNum ( count ) } ` ;
421429 return (
422430 < g >
423431 < rect x = { x } y = { y } width = { width } height = { height } fill = "#6366f1" stroke = "#818cf8" strokeWidth = { 1 } rx = { 3 } />
424- < text x = { x + width + 8 } y = { y + height / 2 } textAnchor = "start" dominantBaseline = "central" fill = "#e2e8f0" fontSize = { 12 } >
432+ < text x = { x + width + 8 } y = { y + height / 2 } textAnchor = "start" dominantBaseline = "central" fill = "#e2e8f0" fontSize = { 12 } fontWeight = { 500 } >
425433 { payload . name }
426434 </ text >
435+ < text x = { x + width + 8 } y = { y + height / 2 + 16 } textAnchor = "start" dominantBaseline = "central" fill = "#94a3b8" fontSize = { 11 } >
436+ { formatNum ( count ) } { pct !== undefined ? ` (${ pct . toFixed ( 1 ) } %)` : '' }
437+ </ text >
427438 </ g >
428439 ) ;
429440}
@@ -550,22 +561,43 @@ function FlowSubTab({ range, definitions, onEdit, onDelete }: {
550561 return defMap . get ( def . parentId ) ?. name ?? null ;
551562 } ;
552563
564+ // Enrich nodes with percentage flow-through from parent stage.
565+ const enrichedData = sankeyData && sankeyData . hasDependencies ? ( ( ) => {
566+ // Build a map: target node index → source node index (from first incoming link).
567+ const parentOf = new Map < number , number > ( ) ;
568+ for ( const link of sankeyData . links ) {
569+ if ( ! parentOf . has ( link . target ) ) {
570+ parentOf . set ( link . target , link . source ) ;
571+ }
572+ }
573+ const enrichedNodes = sankeyData . nodes . map ( ( node , i ) => {
574+ const parentIdx = parentOf . get ( i ) ;
575+ let pct : number | undefined ;
576+ if ( parentIdx !== undefined ) {
577+ const parentCount = sankeyData . nodes [ parentIdx ] ?. count ?? 0 ;
578+ pct = parentCount > 0 ? ( node . count / parentCount ) * 100 : 0 ;
579+ }
580+ return { ...node , pct } ;
581+ } ) ;
582+ return { ...sankeyData , nodes : enrichedNodes } ;
583+ } ) ( ) : null ;
584+
553585 return (
554586 < div className = "space-y-6" >
555587 { isLoading ? (
556588 < LoadingSpinner size = "lg" className = "py-10" />
557- ) : sankeyData && sankeyData . hasDependencies && sankeyData . nodes . length > 0 && sankeyData . links . length > 0 ? (
589+ ) : enrichedData && enrichedData . nodes . length > 0 && enrichedData . links . length > 0 ? (
558590 < Card className = "p-4" >
559591 < h3 className = "text-sm font-medium text-dark-400 mb-4" > Event Flow</ h3 >
560592 < div className = "overflow-x-auto" >
561593 < Sankey
562594 width = { 900 }
563- height = { Math . max ( 300 , sankeyData . nodes . length * 50 ) }
564- data = { sankeyData }
565- nodePadding = { 40 }
595+ height = { Math . max ( 300 , enrichedData . nodes . length * 60 ) }
596+ data = { enrichedData }
597+ nodePadding = { 50 }
566598 nodeWidth = { 10 }
567599 linkCurvature = { 0.5 }
568- margin = { { top : 20 , right : 160 , bottom : 20 , left : 20 } }
600+ margin = { { top : 20 , right : 200 , bottom : 20 , left : 20 } }
569601 node = { < SankeyNode x = { 0 } y = { 0 } width = { 0 } height = { 0 } payload = { { name : '' } } /> }
570602 >
571603 < Tooltip contentStyle = { tooltipStyle } labelStyle = { tooltipLabelStyle } />
0 commit comments