Improve visual hierarchy of subtotals and totals in all tables
Section subtotals: text-sm font-semibold with more padding. Grand totals: text-sm font-bold with border-t-2 and extra padding. Applied consistently across BudgetTable, BudgetVsActualTable, MonthlyTrendsTable, CategoryOverTimeTable, CategoryTable, and DynamicReportTable. Closes #14 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e1192beca3
commit
861d78eca2
6 changed files with 43 additions and 43 deletions
|
|
@ -387,12 +387,12 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu
|
||||||
</tr>
|
</tr>
|
||||||
{reorderRows(group, subtotalsOnTop).map((row) => renderRow(row))}
|
{reorderRows(group, subtotalsOnTop).map((row) => renderRow(row))}
|
||||||
<tr className="bg-[var(--muted)]/40 border-b border-[var(--border)]">
|
<tr className="bg-[var(--muted)]/40 border-b border-[var(--border)]">
|
||||||
<td className="py-2 px-3 sticky left-0 bg-[var(--muted)]/40 z-10 text-xs font-semibold">
|
<td className="py-2.5 px-3 sticky left-0 bg-[var(--muted)]/40 z-10 text-sm font-semibold">
|
||||||
{t(typeTotalKeys[type])}
|
{t(typeTotalKeys[type])}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-2 px-2 text-right text-xs font-semibold">{formatSigned(sectionAnnualTotal)}</td>
|
<td className="py-2.5 px-2 text-right text-sm font-semibold">{formatSigned(sectionAnnualTotal)}</td>
|
||||||
{sectionMonthTotals.map((total, mIdx) => (
|
{sectionMonthTotals.map((total, mIdx) => (
|
||||||
<td key={mIdx} className="py-2 px-2 text-right text-xs font-semibold">
|
<td key={mIdx} className="py-2.5 px-2 text-right text-sm font-semibold">
|
||||||
{formatSigned(total)}
|
{formatSigned(total)}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
|
|
@ -401,11 +401,11 @@ export default function BudgetTable({ rows, onUpdatePlanned, onSplitEvenly }: Bu
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{/* Totals row */}
|
{/* Totals row */}
|
||||||
<tr className="bg-[var(--muted)] font-semibold">
|
<tr className="bg-[var(--muted)] font-bold border-t-2 border-[var(--border)]">
|
||||||
<td className="py-2.5 px-3 sticky left-0 bg-[var(--muted)] z-10 text-xs">{t("common.total")}</td>
|
<td className="py-3 px-3 sticky left-0 bg-[var(--muted)] z-10 text-sm">{t("common.total")}</td>
|
||||||
<td className="py-2.5 px-2 text-right text-xs">{formatSigned(annualTotal)}</td>
|
<td className="py-3 px-2 text-right text-sm">{formatSigned(annualTotal)}</td>
|
||||||
{monthTotals.map((total, mIdx) => (
|
{monthTotals.map((total, mIdx) => (
|
||||||
<td key={mIdx} className="py-2.5 px-2 text-right text-xs">
|
<td key={mIdx} className="py-3 px-2 text-right text-sm">
|
||||||
{formatSigned(total)}
|
{formatSigned(total)}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -256,26 +256,26 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps)
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<tr className="border-b border-[var(--border)] bg-[var(--muted)]/40 font-semibold">
|
<tr className="border-b border-[var(--border)] bg-[var(--muted)]/40 font-semibold text-sm">
|
||||||
<td className="px-3 py-2 text-xs">{t(typeTotalKeys[section.type])}</td>
|
<td className="px-3 py-2.5">{t(typeTotalKeys[section.type])}</td>
|
||||||
<td className="text-right px-3 py-2 border-l border-[var(--border)]/50">
|
<td className="text-right px-3 py-2.5 border-l border-[var(--border)]/50">
|
||||||
{cadFormatter(sectionTotals.monthActual)}
|
{cadFormatter(sectionTotals.monthActual)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right px-3 py-2">{cadFormatter(sectionTotals.monthBudget)}</td>
|
<td className="text-right px-3 py-2.5">{cadFormatter(sectionTotals.monthBudget)}</td>
|
||||||
<td className={`text-right px-3 py-2 ${variationColor(sectionTotals.monthVariation)}`}>
|
<td className={`text-right px-3 py-2.5 ${variationColor(sectionTotals.monthVariation)}`}>
|
||||||
{cadFormatter(sectionTotals.monthVariation)}
|
{cadFormatter(sectionTotals.monthVariation)}
|
||||||
</td>
|
</td>
|
||||||
<td className={`text-right px-3 py-2 ${variationColor(sectionTotals.monthVariation)}`}>
|
<td className={`text-right px-3 py-2.5 ${variationColor(sectionTotals.monthVariation)}`}>
|
||||||
{pctFormatter(sectionMonthPct)}
|
{pctFormatter(sectionMonthPct)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right px-3 py-2 border-l border-[var(--border)]/50">
|
<td className="text-right px-3 py-2.5 border-l border-[var(--border)]/50">
|
||||||
{cadFormatter(sectionTotals.ytdActual)}
|
{cadFormatter(sectionTotals.ytdActual)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right px-3 py-2">{cadFormatter(sectionTotals.ytdBudget)}</td>
|
<td className="text-right px-3 py-2.5">{cadFormatter(sectionTotals.ytdBudget)}</td>
|
||||||
<td className={`text-right px-3 py-2 ${variationColor(sectionTotals.ytdVariation)}`}>
|
<td className={`text-right px-3 py-2.5 ${variationColor(sectionTotals.ytdVariation)}`}>
|
||||||
{cadFormatter(sectionTotals.ytdVariation)}
|
{cadFormatter(sectionTotals.ytdVariation)}
|
||||||
</td>
|
</td>
|
||||||
<td className={`text-right px-3 py-2 ${variationColor(sectionTotals.ytdVariation)}`}>
|
<td className={`text-right px-3 py-2.5 ${variationColor(sectionTotals.ytdVariation)}`}>
|
||||||
{pctFormatter(sectionYtdPct)}
|
{pctFormatter(sectionYtdPct)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -283,26 +283,26 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps)
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{/* Grand totals */}
|
{/* Grand totals */}
|
||||||
<tr className="border-t-2 border-[var(--border)] font-bold bg-[var(--muted)]/20">
|
<tr className="border-t-2 border-[var(--border)] font-bold text-sm bg-[var(--muted)]/20">
|
||||||
<td className="px-3 py-2">{t("common.total")}</td>
|
<td className="px-3 py-3">{t("common.total")}</td>
|
||||||
<td className="text-right px-3 py-2 border-l border-[var(--border)]/50">
|
<td className="text-right px-3 py-3 border-l border-[var(--border)]/50">
|
||||||
{cadFormatter(totals.monthActual)}
|
{cadFormatter(totals.monthActual)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right px-3 py-2">{cadFormatter(totals.monthBudget)}</td>
|
<td className="text-right px-3 py-3">{cadFormatter(totals.monthBudget)}</td>
|
||||||
<td className={`text-right px-3 py-2 ${variationColor(totals.monthVariation)}`}>
|
<td className={`text-right px-3 py-3 ${variationColor(totals.monthVariation)}`}>
|
||||||
{cadFormatter(totals.monthVariation)}
|
{cadFormatter(totals.monthVariation)}
|
||||||
</td>
|
</td>
|
||||||
<td className={`text-right px-3 py-2 ${variationColor(totals.monthVariation)}`}>
|
<td className={`text-right px-3 py-3 ${variationColor(totals.monthVariation)}`}>
|
||||||
{pctFormatter(totalMonthPct)}
|
{pctFormatter(totalMonthPct)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right px-3 py-2 border-l border-[var(--border)]/50">
|
<td className="text-right px-3 py-3 border-l border-[var(--border)]/50">
|
||||||
{cadFormatter(totals.ytdActual)}
|
{cadFormatter(totals.ytdActual)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right px-3 py-2">{cadFormatter(totals.ytdBudget)}</td>
|
<td className="text-right px-3 py-3">{cadFormatter(totals.ytdBudget)}</td>
|
||||||
<td className={`text-right px-3 py-2 ${variationColor(totals.ytdVariation)}`}>
|
<td className={`text-right px-3 py-3 ${variationColor(totals.ytdVariation)}`}>
|
||||||
{cadFormatter(totals.ytdVariation)}
|
{cadFormatter(totals.ytdVariation)}
|
||||||
</td>
|
</td>
|
||||||
<td className={`text-right px-3 py-2 ${variationColor(totals.ytdVariation)}`}>
|
<td className={`text-right px-3 py-3 ${variationColor(totals.ytdVariation)}`}>
|
||||||
{pctFormatter(totalYtdPct)}
|
{pctFormatter(totalYtdPct)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,8 @@ export default function CategoryOverTimeTable({ data, hiddenCategories }: Catego
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<tr className="border-t-2 border-[var(--border)] font-bold bg-[var(--muted)]/20">
|
<tr className="border-t-2 border-[var(--border)] font-bold text-sm bg-[var(--muted)]/20">
|
||||||
<td className="px-3 py-2 sticky left-0 bg-[var(--muted)]/20 z-10">{t("common.total")}</td>
|
<td className="px-3 py-3 sticky left-0 bg-[var(--muted)]/20 z-10">{t("common.total")}</td>
|
||||||
{months.map((month) => {
|
{months.map((month) => {
|
||||||
const monthData = data.data.find((d) => d.month === month);
|
const monthData = data.data.find((d) => d.month === month);
|
||||||
const monthTotal = visibleCategories.reduce(
|
const monthTotal = visibleCategories.reduce(
|
||||||
|
|
@ -89,12 +89,12 @@ export default function CategoryOverTimeTable({ data, hiddenCategories }: Catego
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<td key={month} className="text-right px-3 py-2">
|
<td key={month} className="text-right px-3 py-3">
|
||||||
{cadFormatter(monthTotal)}
|
{cadFormatter(monthTotal)}
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<td className="text-right px-3 py-2 border-l border-[var(--border)]/50">
|
<td className="text-right px-3 py-3 border-l border-[var(--border)]/50">
|
||||||
{cadFormatter(
|
{cadFormatter(
|
||||||
visibleCategories.reduce(
|
visibleCategories.reduce(
|
||||||
(sum, cat) => sum + data.data.reduce((s, d) => s + ((d as Record<string, unknown>)[cat] as number || 0), 0),
|
(sum, cat) => sum + data.data.reduce((s, d) => s + ((d as Record<string, unknown>)[cat] as number || 0), 0),
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,10 @@ export default function CategoryTable({ data, hiddenCategories }: CategoryTableP
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
<tr className="border-t-2 border-[var(--border)] font-bold bg-[var(--muted)]/20">
|
<tr className="border-t-2 border-[var(--border)] font-bold text-sm bg-[var(--muted)]/20">
|
||||||
<td className="px-3 py-2">{t("common.total")}</td>
|
<td className="px-3 py-3">{t("common.total")}</td>
|
||||||
<td className="text-right px-3 py-2">{cadFormatter(grandTotal)}</td>
|
<td className="text-right px-3 py-3">{cadFormatter(grandTotal)}</td>
|
||||||
<td className="text-right px-3 py-2 text-[var(--muted-foreground)]">100%</td>
|
<td className="text-right px-3 py-3 text-[var(--muted-foreground)]">100%</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -192,13 +192,13 @@ export default function DynamicReportTable({ config, result }: DynamicReportTabl
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
{/* Grand total */}
|
{/* Grand total */}
|
||||||
<tr className="border-t-2 border-[var(--border)] font-bold bg-[var(--muted)]/20">
|
<tr className="border-t-2 border-[var(--border)] font-bold text-sm bg-[var(--muted)]/20">
|
||||||
<td colSpan={rowDims.length || 1} className="px-3 py-2">
|
<td colSpan={rowDims.length || 1} className="px-3 py-3">
|
||||||
{t("reports.pivot.total")}
|
{t("reports.pivot.total")}
|
||||||
</td>
|
</td>
|
||||||
{colValues.map((colVal) =>
|
{colValues.map((colVal) =>
|
||||||
measures.map((m) => (
|
measures.map((m) => (
|
||||||
<td key={`total-${colVal}-${m}`} className="text-right px-3 py-2 border-l border-[var(--border)]/50">
|
<td key={`total-${colVal}-${m}`} className="text-right px-3 py-3 border-l border-[var(--border)]/50">
|
||||||
{cadFormatter(grandTotals[colVal]?.[m] || 0)}
|
{cadFormatter(grandTotals[colVal]?.[m] || 0)}
|
||||||
</td>
|
</td>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,11 @@ export default function MonthlyTrendsTable({ data }: MonthlyTrendsTableProps) {
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
<tr className="border-t-2 border-[var(--border)] font-bold bg-[var(--muted)]/20">
|
<tr className="border-t-2 border-[var(--border)] font-bold text-sm bg-[var(--muted)]/20">
|
||||||
<td className="px-3 py-2">{t("common.total")}</td>
|
<td className="px-3 py-3">{t("common.total")}</td>
|
||||||
<td className="text-right px-3 py-2 text-[var(--positive)]">{cadFormatter(totals.income)}</td>
|
<td className="text-right px-3 py-3 text-[var(--positive)]">{cadFormatter(totals.income)}</td>
|
||||||
<td className="text-right px-3 py-2 text-[var(--negative)]">{cadFormatter(totals.expenses)}</td>
|
<td className="text-right px-3 py-3 text-[var(--negative)]">{cadFormatter(totals.expenses)}</td>
|
||||||
<td className={`text-right px-3 py-2 ${totals.income - totals.expenses >= 0 ? "text-[var(--positive)]" : "text-[var(--negative)]"}`}>
|
<td className={`text-right px-3 py-3 ${totals.income - totals.expenses >= 0 ? "text-[var(--positive)]" : "text-[var(--negative)]"}`}>
|
||||||
{cadFormatter(totals.income - totals.expenses)}
|
{cadFormatter(totals.income - totals.expenses)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue