"use client"; import { useMemo, useState } from "react"; import { Dialog } from "@/components/ui"; import { Bar, Figures, Panel } from "@/components/portal"; import { csvEsc, csvOf, fmtDate, money, monthLabel, prevMonth } from "@/lib/compute"; import { downloadCsv } from "@/lib/print"; import { HeadActions, PanelEmpty, TableWrap, TOTAL, plural } from "./bits"; import type { ReportData } from "./useReportData"; export const JOURNAL_ID = "tc-rep-journal"; export default function SpendTab({ d, onMonth }: { d: ReportData; onMonth: (m: string) => void }) { const { R, month, mLbl, csv, print, jnTotItems, jnTot } = d; // Only the keys are held: the lines are recounted from the snapshot on every render, so the // dialog still agrees with the row that opened it if stock moves while it is open. const [drill, setDrill] = useState<{ cc: string; dept: string; keys: string[] } | null>(null); const drillRows = useMemo(() => (drill ? drill.keys.flatMap((k) => R.ccLines[k] || []).sort((a, b) => a.date.localeCompare(b.date) || a.who.localeCompare(b.who) || a.item.localeCompare(b.item)) : []), [drill, R]); const drillQty = drillRows.reduce((t, r) => t + r.qty, 0), drillAmt = drillRows.reduce((t, r) => t + r.amt, 0); const csvDrill = () => drill && downloadCsv(`threadcount-cost-centre-${drill.cc.replace(/[^A-Za-z0-9]+/g, "-").toLowerCase()}-${month}.csv`, `Issues behind cost centre,${csvEsc(drill.cc)},${month}\n\n` + csvOf(["Date", "Staff", "Item", "Size", "Qty", "Unit cost", "Value"], [...drillRows.map((r) => [r.date, r.who, r.item, r.size, r.qty, r.unit.toFixed(2), r.amt.toFixed(2)] as (string | number)[]), ["TOTAL", "", "", "", drillQty, "", drillAmt.toFixed(2)]])); const open = (cc: string, dept: string, keys: string[]) => setDrill({ cc: cc === "—" ? "UNALLOCATED" : cc, dept, keys }); const drillBtn = (cc: string, dept: string, keys: string[], items: number, amt: number) => { const name = cc === "—" ? "UNALLOCATED" : cc; return ( ); }; const pm = prevMonth(month); const pct = R.totPrev > 0 ? Math.round(((R.totAmt - R.totPrev) / R.totPrev) * 100) : null; const pctText = pct === null ? "—" : (pct > 0 ? "+" : pct < 0 ? "−" : "") + Math.abs(pct) + "%"; const ccShown = R.ccRows.filter((r) => r.items > 0 || r.amt > 0); const ccMax = Math.max(0, ...ccShown.map((r) => r.amt)); const monthOnly = monthLabel(month, { month: "long" }); return (