import { auth } from "@/auth"; import { redirect } from "next/navigation"; import Link from "next/link"; import type { Metadata } from "next"; import { ChevronRight, BarChart3 } from "lucide-react"; import { hasPermission } from "@/lib/permissions"; import { formatCurrency, formatCompactINR } from "@/lib/utils"; import { getReportDataset, buildAccountIndex, accountLevelRows, applyScope, parseScope, parseGranularity, resolveFy, fyLabel, FY_MONTHS, SCOPE_LABELS, type NodeSpend, } from "@/lib/reports"; import { ReportsToolbar } from "@/components/reports/reports-toolbar"; import { ComparisonChart, Sparkline, SERIES_COLORS, type Series } from "@/components/reports/charts"; import { Kpi, KpiStrip } from "@/components/reports/kpi"; import { ReportBreadcrumb, ReportTitle } from "@/components/reports/report-header"; export const metadata: Metadata = { title: "Accounting Codes — Reports" }; const sum = (a: number[]) => a.reduce((x, y) => x + y, 0); const tierBadgeCls: Record = { Heading: "bg-primary-50 text-primary-700", "Sub-heading": "bg-violet-50 text-violet-700", Leaf: "bg-neutral-100 text-neutral-600", }; export default async function AccountingCodesReport({ searchParams, }: { searchParams: Promise<{ fy?: string; gran?: string; scope?: string; parent?: string }>; }) { const session = await auth(); if (!session?.user) return null; if (!hasPermission(session.user.role, "view_analytics")) redirect("/dashboard"); const sp = await searchParams; const ds = await getReportDataset(); const idx = buildAccountIndex(ds.accounts); const gran = parseGranularity(sp.gran); const scope = parseScope(sp.scope); const fy = resolveFy(ds, sp.fy); const yearly = gran === "yearly"; const parent = sp.parent && idx.byId.has(sp.parent) ? sp.parent : null; const parentNode = parent ? idx.byId.get(parent)! : null; const ranked = accountLevelRows(ds, idx, parent, fy); const rankOf = (r: NodeSpend) => (yearly ? sum(r.fyTotals) : r.total); ranked.sort((a, b) => rankOf(b) - rankOf(a)); const shown = applyScope(ranked, scope); const grand = shown.reduce((s, r) => s + rankOf(r), 0); const childTier = shown[0]?.node.tier ?? "Heading"; const top = shown[0]; const nf = ds.fys.length; const curT = nf >= 1 ? shown.reduce((s, r) => s + r.fyTotals[nf - 1], 0) : 0; const prevT = nf >= 2 ? shown.reduce((s, r) => s + r.fyTotals[nf - 2], 0) : 0; const yoy = prevT ? ((curT - prevT) / prevT) * 100 : 0; const colored = (i: number) => SERIES_COLORS[i % SERIES_COLORS.length]; let chartData: Record[]; let series: Series[]; if (yearly) { chartData = shown.map((r) => { const row: Record = { name: r.node.code }; ds.fys.forEach((y, i) => (row[fyLabel(y)] = r.fyTotals[i])); return row; }); series = ds.fys.map((y, i) => ({ key: fyLabel(y), color: colored(i) })); } else { chartData = FY_MONTHS.map((m, i) => { const row: Record = { month: m }; shown.forEach((r) => (row[r.node.code] = r.months[i])); return row; }); series = shown.map((r, i) => ({ key: r.node.code, color: colored(i) })); } const periodLabel = yearly ? ds.fys.map(fyLabel).join(" · ") : fyLabel(fy); const linkWith = (parentId: string | null) => { const p = new URLSearchParams({ fy: String(fy), gran, scope }); if (parentId) p.set("parent", parentId); return `/reports/accounting-codes?${p.toString()}`; }; const detailHref = (id: string) => `/reports/accounting-codes/${id}?fy=${fy}&gran=${gran}`; const rowHref = (r: NodeSpend) => (idx.isLeaf(r.node.id) ? detailHref(r.node.id) : linkWith(r.node.id)); const exportHref = `/api/reports/spend?dim=accounting-code&fy=${fy}&gran=${gran}&scope=${scope}${parent ? `&parent=${parent}` : ""}`; // Breadcrumb: Reports / Accounting Codes / …ancestors… / current parent. const trail = [{ label: "Accounting Codes", href: parent ? linkWith(null) : undefined }]; if (parentNode) { const path = idx.pathTo(parentNode.id); path.forEach((a, i) => trail.push({ label: `${a.code} · ${a.name}`, href: i < path.length - 1 ? linkWith(a.id) : undefined }) ); } return (
{parentNode && ( ← Back to {parentNode.parentId ? idx.byId.get(parentNode.parentId)!.name : "Accounting Codes"} )} {grand === 0 ? (
No approved spend recorded for {periodLabel} yet.
) : ( <> s + (yearly ? sum(r.fyTotals) : r.total), 0))} sub={periodLabel} /> = 0 ? "+" : ""}${yoy.toFixed(1)}%`} sub="vs prior FY" delta={yoy} />

{yearly ? `Spend by ${childTier.toLowerCase()} — year over year` : `Monthly spend by ${childTier.toLowerCase()}`}

{periodLabel}
{shown.map((r) => { const value = rankOf(r); const pct = grand ? (value / grand) * 100 : 0; const leaf = idx.isLeaf(r.node.id); return ( {r.node.code} {r.node.name} {r.node.tier} {formatCurrency(value)}
{pct.toFixed(0)}%
{leaf ? ( ) : ( )} ); })}
)}
); }