"use client"; import { RiInformationLine } from "@remixicon/react"; import Link from "next/link"; import { useMemo } from "react"; import { formatPeriodLabel } from "@/features/reports/lib/utils"; import { CategoryIconBadge } from "@/shared/components/entity-avatar"; import StatusDot from "@/shared/components/feedback/status-dot"; import { Card } from "@/shared/components/ui/card"; import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from "@/shared/components/ui/table"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/shared/components/ui/tooltip"; import type { CategoryReportItem } from "@/shared/lib/types/reports"; import { formatCurrency } from "@/shared/utils/currency"; import { formatPeriodForUrl } from "@/shared/utils/period"; import { CategoryCell } from "./category-cell"; interface CategoryTableProps { title: string; categories: CategoryReportItem[]; periods: string[]; } export function CategoryTable({ title, categories, periods, }: CategoryTableProps) { // Calculate section totals const sectionTotals = useMemo(() => { const totalsMap = new Map(); let grandTotal = 0; for (const category of categories) { grandTotal += category.total; for (const period of periods) { const monthData = category.monthlyData.get(period); const current = totalsMap.get(period) ?? 0; totalsMap.set(period, current + (monthData?.amount ?? 0)); } } const nonZeroPeriodCount = periods.filter( (p) => (totalsMap.get(p) ?? 0) > 0, ).length; return { totalsMap, grandTotal, averageMonthlyTotal: nonZeroPeriodCount > 0 ? grandTotal / nonZeroPeriodCount : 0, }; }, [categories, periods]); if (categories.length === 0) { return null; } return ( Categoria {periods.map((period) => ( {formatPeriodLabel(period)} ))}
Média A média considera apenas os meses com gastos registrados (valores maiores que zero). Meses sem movimentação não entram no cálculo.
Total
{categories.map((category, index) => { const periodParam = formatPeriodForUrl(periods[periods.length - 1]); return (
{category.name}
{periods.map((period, periodIndex) => { const monthData = category.monthlyData.get(period); const isFirstMonth = periodIndex === 0; return ( ); })} {(() => { const nonZeroCount = periods.filter( (p) => (category.monthlyData.get(p)?.amount ?? 0) > 0, ).length; return formatCurrency( nonZeroCount > 0 ? category.total / nonZeroCount : 0, ); })()} {formatCurrency(category.total)}
); })}
Total {periods.map((period) => { const periodTotal = sectionTotals.totalsMap.get(period) ?? 0; return ( {formatCurrency(periodTotal)} ); })} {formatCurrency(sectionTotals.averageMonthlyTotal)} {formatCurrency(sectionTotals.grandTotal)}
); }