feat(dashboard): KPI tiles drillable — navigate to /claims with filter

This commit is contained in:
Tyler
2026-06-21 13:20:16 -06:00
parent 50dc0b2fb3
commit 88da3a6246
3 changed files with 346 additions and 67 deletions
+90 -61
View File
@@ -1,4 +1,5 @@
import { useMemo } from "react";
import { useNavigate } from "react-router-dom";
import {
AlertCircle,
Banknote,
@@ -13,6 +14,7 @@ import { PageHeader } from "@/components/PageHeader";
import { KpiCard } from "@/components/KpiCard";
import { ActivityFeed } from "@/components/ActivityFeed";
import { AnimatedNumber } from "@/components/AnimatedNumber";
import { DrillableCell } from "@/components/drill/DrillableCell";
import { fmt } from "@/lib/format";
import { useAppStore } from "@/store";
@@ -70,6 +72,8 @@ export function Dashboard() {
const providers = useAppStore((s) => s.providers);
const activity = useAppStore((s) => s.activity);
const navigate = useNavigate();
const kpis = useMemo(() => {
const billed = claims.reduce((s, c) => s + c.billedAmount, 0);
const received = claims.reduce((s, c) => s + c.receivedAmount, 0);
@@ -162,67 +166,92 @@ export function Dashboard() {
aria-label="Key performance indicators"
className="grid gap-3.5 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5"
>
<KpiCard
label="Claims"
icon={Receipt}
sparkline={monthly.count}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 0 * kpiStep}ms` }}
value={
<AnimatedNumber value={kpis.count} format={(n) => fmt.num(Math.round(n))} />
}
hint="last 6 months"
/>
<KpiCard
label="Billed"
icon={CircleDollarSign}
accent="accent"
sparkline={monthly.billed}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 1 * kpiStep}ms` }}
value={<AnimatedNumber value={kpis.billed} format={fmt.usd} />}
delta={{ value: "+12.4%", direction: "up", positive: true }}
/>
<KpiCard
label="Received"
icon={Banknote}
accent="success"
sparkline={monthly.received}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 2 * kpiStep}ms` }}
value={<AnimatedNumber value={kpis.received} format={fmt.usd} />}
delta={{ value: "+8.1%", direction: "up", positive: true }}
/>
<KpiCard
label="Pending AR"
icon={Clock}
accent="warning"
sparkline={monthly.ar}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 3 * kpiStep}ms` }}
value={
<AnimatedNumber
value={kpis.outstandingAr}
format={(n) => fmt.usd(Math.max(0, n))}
/>
}
hint={`${kpis.pending} in queue`}
/>
<KpiCard
label="Denial rate"
icon={TrendingDown}
accent="destructive"
sparkline={monthly.denialRate}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 4 * kpiStep}ms` }}
value={
<AnimatedNumber
value={kpis.denialRate}
format={(n) => fmt.pct(n)}
/>
}
delta={{ value: "-1.2 pts", direction: "down", positive: true }}
/>
<DrillableCell
onClick={() => navigate("/claims")}
ariaLabel="View all claims"
>
<KpiCard
label="Claims"
icon={Receipt}
sparkline={monthly.count}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 0 * kpiStep}ms` }}
value={
<AnimatedNumber value={kpis.count} format={(n) => fmt.num(Math.round(n))} />
}
hint="last 6 months"
/>
</DrillableCell>
<DrillableCell
onClick={() => navigate("/claims?sort=-billedAmount")}
ariaLabel="View claims sorted by billed amount"
>
<KpiCard
label="Billed"
icon={CircleDollarSign}
accent="accent"
sparkline={monthly.billed}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 1 * kpiStep}ms` }}
value={<AnimatedNumber value={kpis.billed} format={fmt.usd} />}
delta={{ value: "+12.4%", direction: "up", positive: true }}
/>
</DrillableCell>
<DrillableCell
onClick={() => navigate("/claims?sort=-receivedAmount")}
ariaLabel="View claims sorted by received amount"
>
<KpiCard
label="Received"
icon={Banknote}
accent="success"
sparkline={monthly.received}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 2 * kpiStep}ms` }}
value={<AnimatedNumber value={kpis.received} format={fmt.usd} />}
delta={{ value: "+8.1%", direction: "up", positive: true }}
/>
</DrillableCell>
<DrillableCell
onClick={() => navigate("/claims")}
ariaLabel="View pending accounts receivable claims"
>
<KpiCard
label="Pending AR"
icon={Clock}
accent="warning"
sparkline={monthly.ar}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 3 * kpiStep}ms` }}
value={
<AnimatedNumber
value={kpis.outstandingAr}
format={(n) => fmt.usd(Math.max(0, n))}
/>
}
hint={`${kpis.pending} in queue`}
/>
</DrillableCell>
<DrillableCell
onClick={() => navigate("/claims?status=denied")}
ariaLabel="View denied claims"
>
<KpiCard
label="Denial rate"
icon={TrendingDown}
accent="destructive"
sparkline={monthly.denialRate}
className="animate-fade-in-up"
style={{ animationDelay: `${kpiBase + 4 * kpiStep}ms` }}
value={
<AnimatedNumber
value={kpis.denialRate}
format={(n) => fmt.pct(n)}
/>
}
delta={{ value: "-1.2 pts", direction: "down", positive: true }}
/>
</DrillableCell>
</section>
{/* Activity + Top providers */}