import { useTranslation } from "react-i18next"; import type { CategoryDelta } from "../../shared/types"; export interface ComparePeriodTableProps { rows: CategoryDelta[]; /** Label for the "previous" monthly column (e.g. "March 2026" or "2025"). */ previousLabel: string; /** Label for the "current" monthly column (e.g. "April 2026" or "2026"). */ currentLabel: string; /** Optional label for the previous cumulative window (YTD). Falls back to previousLabel. */ cumulativePreviousLabel?: string; /** Optional label for the current cumulative window (YTD). Falls back to currentLabel. */ cumulativeCurrentLabel?: string; } function formatCurrency(amount: number, language: string): string { return new Intl.NumberFormat(language === "fr" ? "fr-CA" : "en-CA", { style: "currency", currency: "CAD", maximumFractionDigits: 0, }).format(amount); } function formatSignedCurrency(amount: number, language: string): string { return new Intl.NumberFormat(language === "fr" ? "fr-CA" : "en-CA", { style: "currency", currency: "CAD", maximumFractionDigits: 0, signDisplay: "always", }).format(amount); } function formatPct(pct: number | null, language: string): string { if (pct === null) return "—"; return new Intl.NumberFormat(language === "fr" ? "fr-CA" : "en-CA", { style: "percent", maximumFractionDigits: 1, signDisplay: "always", }).format(pct / 100); } function variationColor(value: number): string { // Compare report deals with expenses (abs values): spending more is negative // for the user, spending less is positive. Mirror that colour convention // consistently so the eye parses the delta sign at a glance. if (value > 0) return "var(--negative, #ef4444)"; if (value < 0) return "var(--positive, #10b981)"; return ""; } export default function ComparePeriodTable({ rows, previousLabel, currentLabel, cumulativePreviousLabel, cumulativeCurrentLabel, }: ComparePeriodTableProps) { const { t, i18n } = useTranslation(); const monthPrevLabel = previousLabel; const monthCurrLabel = currentLabel; const ytdPrevLabel = cumulativePreviousLabel ?? previousLabel; const ytdCurrLabel = cumulativeCurrentLabel ?? currentLabel; // Totals across all rows (there is no parent/child structure in compare mode). const totals = rows.reduce( (acc, r) => ({ monthCurrent: acc.monthCurrent + r.currentAmount, monthPrevious: acc.monthPrevious + r.previousAmount, monthDelta: acc.monthDelta + r.deltaAbs, ytdCurrent: acc.ytdCurrent + r.cumulativeCurrentAmount, ytdPrevious: acc.ytdPrevious + r.cumulativePreviousAmount, ytdDelta: acc.ytdDelta + r.cumulativeDeltaAbs, }), { monthCurrent: 0, monthPrevious: 0, monthDelta: 0, ytdCurrent: 0, ytdPrevious: 0, ytdDelta: 0 }, ); const totalMonthPct = totals.monthPrevious !== 0 ? (totals.monthDelta / Math.abs(totals.monthPrevious)) * 100 : null; const totalYtdPct = totals.ytdPrevious !== 0 ? (totals.ytdDelta / Math.abs(totals.ytdPrevious)) * 100 : null; return (
{rows.length === 0 ? ( ) : ( <> {rows.map((row) => ( {/* Monthly block */} {/* Cumulative YTD block */} ))} {/* Grand totals row */} )}
{t("reports.highlights.category")} {t("reports.bva.monthly")} {t("reports.bva.ytd")}
{t("reports.compare.currentAmount")}
{monthCurrLabel}
{t("reports.compare.previousAmount")}
{monthPrevLabel}
{t("reports.bva.dollarVar")} {t("reports.bva.pctVar")}
{t("reports.compare.currentAmount")}
{ytdCurrLabel}
{t("reports.compare.previousAmount")}
{ytdPrevLabel}
{t("reports.bva.dollarVar")} {t("reports.bva.pctVar")}
{t("reports.empty.noData")}
{row.categoryName} {formatCurrency(row.currentAmount, i18n.language)} {formatCurrency(row.previousAmount, i18n.language)} {formatSignedCurrency(row.deltaAbs, i18n.language)} {formatPct(row.deltaPct, i18n.language)} {formatCurrency(row.cumulativeCurrentAmount, i18n.language)} {formatCurrency(row.cumulativePreviousAmount, i18n.language)} {formatSignedCurrency(row.cumulativeDeltaAbs, i18n.language)} {formatPct(row.cumulativeDeltaPct, i18n.language)}
{t("reports.compare.totalRow")} {formatCurrency(totals.monthCurrent, i18n.language)} {formatCurrency(totals.monthPrevious, i18n.language)} {formatSignedCurrency(totals.monthDelta, i18n.language)} {formatPct(totalMonthPct, i18n.language)} {formatCurrency(totals.ytdCurrent, i18n.language)} {formatCurrency(totals.ytdPrevious, i18n.language)} {formatSignedCurrency(totals.ytdDelta, i18n.language)} {formatPct(totalYtdPct, i18n.language)}
); }