11import { ChartLineIcon } from '@phosphor-icons/react' ;
2- import { useCallback , useMemo } from 'react' ;
32import {
43 Area ,
54 AreaChart ,
@@ -20,27 +19,13 @@ import {
2019import { cn } from '@/lib/utils' ;
2120import {
2221 type ChartDataRow ,
23- METRIC_COLORS ,
2422 METRICS ,
2523 type MetricConfig ,
2624} from './metrics-constants' ;
2725import { SkeletonChart } from './skeleton-chart' ;
2826
29- const CustomTooltip = ( {
30- active,
31- payload,
32- label,
33- } : {
34- active ?: boolean ;
35- payload ?: Array < {
36- name : string ;
37- value : number ;
38- color : string ;
39- payload : ChartDataRow ;
40- } > ;
41- label ?: string ;
42- } ) => {
43- if ( ! ( active && payload && payload . length ) ) {
27+ const CustomTooltip = ( { active, payload, label } : any ) => {
28+ if ( ! ( active && payload ?. length ) ) {
4429 return null ;
4530 }
4631
@@ -51,38 +36,31 @@ const CustomTooltip = ({
5136 < p className = "font-semibold text-foreground text-sm" > { label } </ p >
5237 </ div >
5338 < div className = "space-y-2.5" >
54- { payload . map ( ( entry ) => {
55- const dataPoint = entry . payload ;
56- const metric = METRICS . find (
57- ( m ) => m . label === entry . name || m . key === entry . name
58- ) ;
39+ { payload . map ( ( entry : any ) => {
40+ const metric = METRICS . find ( ( m ) => m . key === entry . dataKey ) ;
5941 if ( ! metric ) {
6042 return null ;
6143 }
6244
63- const Icon = metric . icon ;
6445 const displayValue = metric . formatValue
65- ? metric . formatValue ( entry . value , dataPoint )
46+ ? metric . formatValue ( entry . value , entry . payload )
6647 : entry . value . toLocaleString ( ) ;
6748
6849 return (
6950 < div
70- className = "group flex items-center justify-between gap-3"
71- key = { `item- ${ metric . key } ` }
51+ className = "flex items-center justify-between gap-3"
52+ key = { metric . key }
7253 >
7354 < div className = "flex items-center gap-2.5" >
7455 < div
75- className = "h-3 w-3 rounded-full shadow-sm ring-2 ring-background "
56+ className = "h-3 w-3 rounded-full"
7657 style = { { backgroundColor : entry . color } }
7758 />
78- < div className = "flex items-center gap-1.5" >
79- < Icon className = "h-3 w-3" />
80- < span className = "font-medium text-muted-foreground text-xs" >
81- { metric . label }
82- </ span >
83- </ div >
59+ < span className = "text-muted-foreground text-xs" >
60+ { metric . label }
61+ </ span >
8462 </ div >
85- < span className = "font-bold text-foreground text-sm group-hover:text-primary " >
63+ < span className = "font-bold text-foreground text-sm" >
8664 { displayValue }
8765 </ span >
8866 </ div >
@@ -118,17 +96,17 @@ export function MetricsChart({
11896 metricsFilter,
11997 showLegend = true ,
12098} : MetricsChartProps ) {
121- const chartData = useMemo ( ( ) => data || [ ] , [ data ] ) ;
99+ const chartData = data || [ ] ;
122100
123- const valueFormatter = useCallback ( ( value : number ) : string => {
101+ const valueFormatter = ( value : number ) : string => {
124102 if ( value >= 1_000_000 ) {
125103 return `${ ( value / 1_000_000 ) . toFixed ( 1 ) } M` ;
126104 }
127105 if ( value >= 1000 ) {
128106 return `${ ( value / 1000 ) . toFixed ( 1 ) } k` ;
129107 }
130108 return value . toString ( ) ;
131- } , [ ] ) ;
109+ } ;
132110
133111 const yAxisConfig = {
134112 yAxisId : 'left' ,
@@ -185,15 +163,15 @@ export function MetricsChart({
185163
186164 const displayMetrics = metricsFilter
187165 ? METRICS . filter ( metricsFilter )
188- : METRICS . filter ( ( metric ) =>
189- [
166+ : METRICS . filter ( ( metric ) => {
167+ return [
190168 'pageviews' ,
191169 'visitors' ,
192170 'sessions' ,
193171 'bounce_rate' ,
194172 'avg_session_duration' ,
195- ] . includes ( metric . key )
196- ) ;
173+ ] . includes ( metric . key ) ;
174+ } ) ;
197175
198176 return (
199177 < Card className = { cn ( 'w-full overflow-hidden rounded-none p-0' , className ) } >
@@ -202,8 +180,6 @@ export function MetricsChart({
202180 className = "relative"
203181 style = { { width : '100%' , height : height + 20 } }
204182 >
205- < div className = "pointer-events-none absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-muted/5" />
206-
207183 < ResponsiveContainer height = "100%" width = "100%" >
208184 < AreaChart
209185 data = { chartData }
@@ -215,41 +191,27 @@ export function MetricsChart({
215191 } }
216192 >
217193 < defs >
218- { Object . entries ( METRIC_COLORS ) . map ( ( [ key , colors ] ) => (
194+ { displayMetrics . map ( ( metric ) => (
219195 < linearGradient
220- id = { `gradient-${ key } ` }
221- key = { key }
196+ id = { `gradient-${ metric . gradient } ` }
197+ key = { metric . key }
222198 x1 = "0"
223199 x2 = "0"
224200 y1 = "0"
225201 y2 = "1"
226202 >
227203 < stop
228204 offset = "0%"
229- stopColor = { colors . primary }
205+ stopColor = { metric . color }
230206 stopOpacity = { 0.3 }
231207 />
232- < stop
233- offset = "50%"
234- stopColor = { colors . primary }
235- stopOpacity = { 0.1 }
236- />
237208 < stop
238209 offset = "100%"
239- stopColor = { colors . primary }
210+ stopColor = { metric . color }
240211 stopOpacity = { 0.02 }
241212 />
242213 </ linearGradient >
243214 ) ) }
244- { Object . entries ( METRIC_COLORS ) . map ( ( [ key ] ) => (
245- < filter id = { `glow-${ key } ` } key = { `glow-${ key } ` } >
246- < feGaussianBlur result = "coloredBlur" stdDeviation = "3" />
247- < feMerge >
248- < feMergeNode in = "coloredBlur" />
249- < feMergeNode in = "SourceGraphic" />
250- </ feMerge >
251- </ filter >
252- ) ) }
253215 </ defs >
254216 < CartesianGrid
255217 stroke = "var(--border)"
@@ -284,104 +246,42 @@ export function MetricsChart({
284246 align = "center"
285247 formatter = { ( value ) => {
286248 const metric = displayMetrics . find (
287- ( m ) => m . label === value || m . key === value
249+ ( m ) => m . label === value
288250 ) ;
289- if ( ! metric ) {
290- // Fallback for unknown metrics
291- return (
292- < span className = "inline-flex cursor-pointer select-none items-center font-medium text-muted-foreground text-xs capitalize leading-none opacity-100 transition-all duration-200 hover:text-foreground" >
293- { String ( value ) . replace ( / _ / g, ' ' ) }
294- </ span >
295- ) ;
296- }
297-
298- const hasData = chartData . some (
299- ( item ) =>
300- metric . key in item &&
301- item [ metric . key ] !== undefined &&
302- item [ metric . key ] !== null
303- ) ;
304- const isHidden = hiddenMetrics [ metric . key ] ;
305-
251+ const isHidden = metric && hiddenMetrics [ metric . key ] ;
306252 return (
307253 < span
308- className = { `inline-flex cursor-pointer select-none items-center font-medium text-xs capitalize leading-none transition-all duration-200 ${
254+ className = { `cursor-pointer text-xs ${
309255 isHidden
310- ? 'text-slate-600 line-through decoration-1 opacity-40'
311- : hasData
312- ? 'text-muted-foreground opacity-100 hover:text-foreground'
313- : 'text-muted-foreground/60 opacity-60 hover:text-foreground'
256+ ? 'text-muted-foreground/50 line-through'
257+ : 'text-muted-foreground hover:text-foreground'
314258 } `}
315259 >
316- { metric . label }
260+ { value }
317261 </ span >
318262 ) ;
319263 } }
320- iconSize = { 10 }
321- iconType = "circle"
322- layout = "horizontal"
323- onClick = { ( payload ) => {
324- const anyPayload = payload as unknown as {
325- dataKey ?: string | number ;
326- value ?: string | number ;
327- id ?: string | number ;
328- } ;
329- const raw =
330- anyPayload ?. dataKey ??
331- anyPayload ?. value ??
332- anyPayload ?. id ;
333- if ( raw == null || ! onToggleMetric ) {
334- return ;
335- }
336- const key = String ( raw ) ;
264+ onClick = { ( payload : any ) => {
337265 const metric = displayMetrics . find (
338- ( m ) => m . label === key || m . key === key
266+ ( m ) => m . label === payload . value
339267 ) ;
340- if ( metric ) {
268+ if ( metric && onToggleMetric ) {
341269 onToggleMetric ( metric . key ) ;
342270 }
343271 } }
344- payload = { displayMetrics . map ( ( metric ) => ( {
345- value : metric . label ,
346- type : 'circle' ,
347- color : metric . color ,
348- id : metric . key ,
349- dataKey : metric . key ,
350- } ) ) }
351272 verticalAlign = "bottom"
352- wrapperStyle = { {
353- display : 'flex' ,
354- justifyContent : 'center' ,
355- gap : 12 ,
356- fontSize : '12px' ,
357- paddingTop : '20px' ,
358- bottom : chartData . length > 5 ? 35 : 5 ,
359- fontWeight : 500 ,
360- cursor : 'pointer' ,
361- } }
362273 />
363274 ) }
364275 { displayMetrics . map ( ( metric ) => {
365276 const hasData = chartData . some (
366- ( item ) =>
367- metric . key in item &&
368- item [ metric . key ] !== undefined &&
369- item [ metric . key ] !== null
277+ ( item ) => item [ metric . key ] != null
370278 ) ;
371279 const isHidden = hiddenMetrics [ metric . key ] ;
372280 return (
373281 < Area
374- activeDot = { {
375- r : 6 ,
376- strokeWidth : 3 ,
377- stroke : metric . color ,
378- fill : 'var(--background)' ,
379- filter : `url(#glow-${ metric . gradient } )` ,
380- } }
282+ activeDot = { { r : 4 , stroke : metric . color , strokeWidth : 2 } }
381283 dataKey = { metric . key }
382- dot = { { r : 0 } }
383284 fill = { `url(#gradient-${ metric . gradient } )` }
384- fillOpacity = { 1 }
385285 hide = { ! hasData || isHidden }
386286 key = { metric . key }
387287 name = { metric . label }
0 commit comments