feat(dashboard): KPI tiles drillable — navigate to /claims with filter
This commit is contained in:
+90
-61
@@ -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 */}
|
||||
|
||||
Reference in New Issue
Block a user