import { auth } from "@/auth"; import { redirect } from "next/navigation"; import Link from "next/link"; import type { Metadata } from "next"; import { ChevronRight } from "lucide-react"; import { hasPermission } from "@/lib/permissions"; import { formatCurrency, formatCompactINR } from "@/lib/utils"; import { getReportDataset, costCentreRows, applyScope, parseScope, parseGranularity, resolveFy, fyLabel, FY_MONTHS, SCOPE_LABELS, } 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: "Cost Centres โ€” Reports" }; const sum = (a: number[]) => a.reduce((x, y) => x + y, 0); export default async function CostCentresReport({ searchParams, }: { searchParams: Promise<{ fy?: string; gran?: string; scope?: 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 gran = parseGranularity(sp.gran); const scope = parseScope(sp.scope); const fy = resolveFy(ds, sp.fy); const yearly = gran === "yearly"; const ranked = costCentreRows(ds, fy); const rankOf = (r: { total: number; fyTotals: number[] }) => (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 totalSpend = shown.reduce((s, r) => s + (yearly ? sum(r.fyTotals) : r.total), 0); const top = shown[0]; // YoY across the shown cost centres (latest two FYs). const n = ds.fys.length; const curT = n >= 1 ? shown.reduce((s, r) => s + r.fyTotals[n - 1], 0) : 0; const prevT = n >= 2 ? shown.reduce((s, r) => s + r.fyTotals[n - 2], 0) : 0; const yoy = prevT ? ((curT - prevT) / prevT) * 100 : 0; // Comparison chart series. let chartData: Record[]; let series: Series[]; const colored = (i: number) => SERIES_COLORS[i % SERIES_COLORS.length]; if (yearly) { chartData = shown.map((r) => { const row: Record = { name: r.name }; 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.name] = r.months[i])); return row; }); series = shown.map((r, i) => ({ key: r.name, color: colored(i) })); } const periodLabel = yearly ? ds.fys.map(fyLabel).join(" ยท ") : fyLabel(fy); const exportHref = `/api/reports/spend?dim=cost-centre&fy=${fy}&gran=${gran}&scope=${scope}`; const detailHref = (id: string) => `/reports/cost-centres/${id}?fy=${fy}&gran=${gran}`; return (
{grand === 0 ? (
No approved spend recorded for {periodLabel} yet.
) : ( <> = 0 ? "+" : ""}${yoy.toFixed(1)}%`} sub="vs prior FY" delta={yoy} />

{yearly ? "Spend by cost centre โ€” year over year" : "Monthly spend by cost centre"}

{periodLabel}
{shown.map((r) => { const value = rankOf(r); const pct = grand ? (value / grand) * 100 : 0; return ( ); })}
Cost Centre Trend Total Spend % of Shown POs
{r.name} {r.code} {formatCurrency(value)}
{pct.toFixed(0)}%
{r.poCount}
)}
); }