fix: custom tooltip with inline fill color, dynamic subtitle

- Replace ChartTooltipContent with custom tooltip that reads fill
  directly from payload — guaranteed to show the slice color
- Subtitle adapts: shows 'current and previous period' only when
  previous period data exists, otherwise 'rage vs dead breakdown'
- Filter out zero-count slices from chart data
This commit is contained in:
Usman Baig
2026-03-12 18:29:43 +01:00
parent 3bf832af92
commit 540c774100

View File

@@ -1,12 +1,10 @@
'use client'
import { TrendUp } from '@phosphor-icons/react'
import { Pie, PieChart } from 'recharts'
import { Pie, PieChart, Tooltip } from 'recharts'
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
} from '@/components/charts'
import type { FrustrationSummary } from '@/lib/api/stats'
@@ -30,6 +28,13 @@ function SkeletonCard() {
)
}
const LABELS: Record<string, string> = {
rage_clicks: 'Rage Clicks',
dead_clicks: 'Dead Clicks',
prev_rage_clicks: 'Prev Rage Clicks',
prev_dead_clicks: 'Prev Dead Clicks',
}
const COLORS = {
rage_clicks: 'rgba(253, 94, 15, 0.7)',
dead_clicks: 'rgba(180, 83, 9, 0.7)',
@@ -38,27 +43,32 @@ const COLORS = {
} as const
const chartConfig = {
count: {
label: 'Count',
},
rage_clicks: {
label: 'Rage Clicks',
color: COLORS.rage_clicks,
},
dead_clicks: {
label: 'Dead Clicks',
color: COLORS.dead_clicks,
},
prev_rage_clicks: {
label: 'Prev Rage Clicks',
color: COLORS.prev_rage_clicks,
},
prev_dead_clicks: {
label: 'Prev Dead Clicks',
color: COLORS.prev_dead_clicks,
},
count: { label: 'Count' },
rage_clicks: { label: 'Rage Clicks', color: COLORS.rage_clicks },
dead_clicks: { label: 'Dead Clicks', color: COLORS.dead_clicks },
prev_rage_clicks: { label: 'Prev Rage Clicks', color: COLORS.prev_rage_clicks },
prev_dead_clicks: { label: 'Prev Dead Clicks', color: COLORS.prev_dead_clicks },
} satisfies ChartConfig
function CustomTooltip({ active, payload }: { active?: boolean; payload?: Array<{ payload: { type: string; count: number; fill: string } }> }) {
if (!active || !payload?.length) return null
const item = payload[0].payload
return (
<div className="flex items-center gap-2 rounded-lg border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-800 px-2.5 py-1.5 text-xs shadow-xl">
<div
className="h-2.5 w-2.5 shrink-0 rounded-full"
style={{ backgroundColor: item.fill }}
/>
<span className="text-neutral-500 dark:text-neutral-400">
{LABELS[item.type] ?? item.type}
</span>
<span className="font-mono font-medium tabular-nums text-neutral-900 dark:text-neutral-50">
{item.count.toLocaleString()}
</span>
</div>
)
}
export default function FrustrationTrend({ summary, loading }: FrustrationTrendProps) {
if (loading || !summary) return <SkeletonCard />
@@ -70,13 +80,14 @@ export default function FrustrationTrend({ summary, loading }: FrustrationTrendP
const totalChange = totalPrevious > 0
? Math.round(((totalCurrent - totalPrevious) / totalPrevious) * 100)
: null
const hasPrevious = totalPrevious > 0
const chartData = [
{ type: 'rage_clicks', count: summary.rage_clicks, fill: COLORS.rage_clicks },
{ type: 'dead_clicks', count: summary.dead_clicks, fill: COLORS.dead_clicks },
{ type: 'prev_rage_clicks', count: summary.prev_rage_clicks, fill: COLORS.prev_rage_clicks },
{ type: 'prev_dead_clicks', count: summary.prev_dead_clicks, fill: COLORS.prev_dead_clicks },
]
].filter(d => d.count > 0)
if (!hasData) {
return (
@@ -87,7 +98,7 @@ export default function FrustrationTrend({ summary, loading }: FrustrationTrendP
</h3>
</div>
<p className="text-sm text-neutral-500 dark:text-neutral-400 mb-4">
Current vs. previous period comparison
Rage vs. dead click breakdown
</p>
<div className="flex-1 min-h-[270px] flex flex-col items-center justify-center text-center px-6 py-8 gap-4">
<div className="rounded-full bg-neutral-100 dark:bg-neutral-800 p-4">
@@ -97,7 +108,7 @@ export default function FrustrationTrend({ summary, loading }: FrustrationTrendP
No trend data yet
</h4>
<p className="text-sm text-neutral-500 dark:text-neutral-400 max-w-md">
Frustration trend data will appear here once rage clicks or dead clicks are detected across periods.
Frustration trend data will appear here once rage clicks or dead clicks are detected on your site.
</p>
</div>
</div>
@@ -112,7 +123,9 @@ export default function FrustrationTrend({ summary, loading }: FrustrationTrendP
</h3>
</div>
<p className="text-sm text-neutral-500 dark:text-neutral-400 mb-4">
Rage and dead clicks split across current and previous period
{hasPrevious
? 'Rage and dead clicks split across current and previous period'
: 'Rage vs. dead click breakdown'}
</p>
<div className="flex-1">
@@ -121,9 +134,9 @@ export default function FrustrationTrend({ summary, loading }: FrustrationTrendP
className="mx-auto aspect-square max-h-[250px]"
>
<PieChart>
<ChartTooltip
<Tooltip
cursor={false}
content={<ChartTooltipContent hideLabel />}
content={<CustomTooltip />}
/>
<Pie
data={chartData}