11"use client" ;
22
3+ import { useCallback , useState } from "react" ;
4+ import { useRouter } from "next/navigation" ;
35import {
46 BarChart ,
57 Bar ,
@@ -27,7 +29,15 @@ const TOOLTIP_STYLE = {
2729const TOOLTIP_LABEL_STYLE = { color : "#fafafa" } as const ;
2830const TOOLTIP_ITEM_STYLE = { color : "#fafafa" } as const ;
2931
32+ const BAR_COLOR = "#3b82f6" ;
33+ const BAR_HOVER_COLOR = "#60a5fa" ;
34+ const HIGHLIGHT_COLOR = "#f59e0b" ;
35+ const HIGHLIGHT_HOVER_COLOR = "#fbbf24" ;
36+
3037export function SpendBarChart ( { data, highlightEmail } : SpendBarChartProps ) {
38+ const router = useRouter ( ) ;
39+ const [ hoveredIndex , setHoveredIndex ] = useState < number | null > ( null ) ;
40+
3141 const chartData = data . slice ( 0 , 8 ) . map ( ( d ) => ( {
3242 label : `#${ d . spend_rank } ${ d . name . split ( " " ) [ 0 ] ?? d . email . split ( "@" ) [ 0 ] } ` ,
3343 spend : d . spend_cents / 100 ,
@@ -36,11 +46,30 @@ export function SpendBarChart({ data, highlightEmail }: SpendBarChartProps) {
3646 name : d . name ,
3747 } ) ) ;
3848
49+ const handleBarClick = useCallback (
50+ ( entry : ( typeof chartData ) [ number ] ) => {
51+ router . push ( `/users/${ encodeURIComponent ( entry . email ) } ` ) ;
52+ } ,
53+ [ router ] ,
54+ ) ;
55+
56+ const getFill = ( entry : ( typeof chartData ) [ number ] , index : number ) => {
57+ const isHighlight = entry . email === highlightEmail ;
58+ const isHovered = hoveredIndex === index ;
59+ if ( isHighlight ) return isHovered ? HIGHLIGHT_HOVER_COLOR : HIGHLIGHT_COLOR ;
60+ return isHovered ? BAR_HOVER_COLOR : BAR_COLOR ;
61+ } ;
62+
3963 return (
4064 < div className = "bg-zinc-900 rounded-lg border border-zinc-800 p-4" >
4165 < h3 className = "text-xs font-medium text-zinc-400 mb-2" > Top Spenders (Current Cycle)</ h3 >
4266 < ResponsiveContainer width = "100%" height = { 240 } >
43- < BarChart data = { chartData } layout = "vertical" margin = { { left : 10 , right : 50 } } >
67+ < BarChart
68+ data = { chartData }
69+ layout = "vertical"
70+ margin = { { left : 10 , right : 50 } }
71+ style = { { cursor : "pointer" } }
72+ >
4473 < CartesianGrid strokeDasharray = "3 3" stroke = "#27272a" />
4574 < XAxis
4675 type = "number"
@@ -60,18 +89,29 @@ export function SpendBarChart({ data, highlightEmail }: SpendBarChartProps) {
6089 contentStyle = { TOOLTIP_STYLE }
6190 labelStyle = { TOOLTIP_LABEL_STYLE }
6291 itemStyle = { TOOLTIP_ITEM_STYLE }
63- cursor = { { fill : "rgba(255,255,255,0.05 )" } }
92+ cursor = { { fill : "rgba(255,255,255,0.08 )" } }
6493 formatter = { ( value ) => [ `$${ Number ( value ) . toFixed ( 2 ) } ` , "Spend" ] }
6594 labelFormatter = { ( _label , payload ) => {
6695 const item = payload ?. [ 0 ] ?. payload ;
6796 return item ? `#${ item . rank } ${ item . name } (${ item . email } )` : String ( _label ) ;
6897 } }
6998 />
70- < Bar dataKey = "spend" radius = { [ 0 , 4 , 4 , 0 ] } >
71- { chartData . map ( ( entry ) => (
99+ < Bar
100+ dataKey = "spend"
101+ radius = { [ 0 , 4 , 4 , 0 ] }
102+ onClick = { ( _data , index ) => {
103+ const entry = chartData [ index ] ;
104+ if ( entry ) handleBarClick ( entry ) ;
105+ } }
106+ onMouseEnter = { ( _data , index ) => setHoveredIndex ( index ) }
107+ onMouseLeave = { ( ) => setHoveredIndex ( null ) }
108+ >
109+ { chartData . map ( ( entry , index ) => (
72110 < Cell
73111 key = { entry . email }
74- fill = { entry . email === highlightEmail ? "#f59e0b" : "#3b82f6" }
112+ fill = { getFill ( entry , index ) }
113+ style = { { transition : "fill 0.15s ease, filter 0.15s ease" } }
114+ filter = { hoveredIndex === index ? "brightness(1.15)" : undefined }
75115 />
76116 ) ) }
77117 < LabelList
0 commit comments