chore(balance): post-merge cleanup of #182-#185 reviews (#187) #195

Merged
maximus merged 7 commits from issue-187-balance-cleanup-post-184-185 into main 2026-05-03 23:42:15 +00:00
Showing only changes of commit 372a785834 - Show all commits

View file

@ -173,96 +173,104 @@ export default function BalancePage() {
</div>
)}
<div className="space-y-6">
{(() => {
// Issue #178 — show a 2-step onboarding card while the user has no
// accounts or no snapshots yet. We probe accountsLatest for ANY
// snapshot date so the empty-state guard is independent of the
// active period filter (state.period).
const accountsCount = state.accountsLatest.length;
const hasAnySnapshot = state.accountsLatest.some(
(a) => a.latest_snapshot_date != null
);
if (accountsCount === 0 || !hasAnySnapshot) {
return (
{/* Issue #178 empty-state guard. We probe accountsLatest for ANY
snapshot date so the guard is independent of the active period
filter (state.period). When empty, we render only the onboarding
card period selector, chart and accounts table would all show
empty states stacked under it (S2 from #187). */}
{(() => {
const accountsCount = state.accountsLatest.length;
const hasAnySnapshot = state.accountsLatest.some(
(a) => a.latest_snapshot_date != null
);
const isEmpty = accountsCount === 0 || !hasAnySnapshot;
if (isEmpty) {
return (
<div className="space-y-6">
<BalanceOnboardingCard
accountsCount={accountsCount}
snapshotsCount={hasAnySnapshot ? 1 : 0}
/>
);
}
return <BalanceOverviewCard totals={state.evolutionTotals} />;
})()}
</div>
);
}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
{/* Period selector */}
<div
role="group"
aria-label={t("balance.period.legend")}
className="inline-flex rounded-lg border border-[var(--border)] overflow-hidden"
>
{PERIOD_OPTIONS.map((p) => (
<button
key={p}
type="button"
onClick={() => setPeriod(p)}
className={`px-3 py-1.5 text-sm font-medium ${
state.period === p
? "bg-[var(--primary)] text-white"
: "bg-[var(--card)] text-[var(--foreground)] hover:bg-[var(--muted)]/40"
}`}
aria-pressed={state.period === p}
return (
<div className="space-y-6">
<BalanceOverviewCard totals={state.evolutionTotals} />
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
{/* Period selector */}
<div
role="group"
aria-label={t("balance.period.legend")}
className="inline-flex rounded-lg border border-[var(--border)] overflow-hidden"
>
{t(`balance.period.${p}`)}
</button>
))}
</div>
{PERIOD_OPTIONS.map((p) => (
<button
key={p}
type="button"
onClick={() => setPeriod(p)}
className={`px-3 py-1.5 text-sm font-medium ${
state.period === p
? "bg-[var(--primary)] text-white"
: "bg-[var(--card)] text-[var(--foreground)] hover:bg-[var(--muted)]/40"
}`}
aria-pressed={state.period === p}
>
{t(`balance.period.${p}`)}
</button>
))}
</div>
{/* Chart mode toggle */}
<div
role="group"
aria-label={t("balance.chart.modeLegend")}
className="inline-flex rounded-lg border border-[var(--border)] overflow-hidden"
>
{(["line", "stacked"] as BalanceChartMode[]).map((mode) => (
<button
key={mode}
type="button"
onClick={() => setChartMode(mode)}
className={`px-3 py-1.5 text-sm font-medium ${
state.chartMode === mode
? "bg-[var(--primary)] text-white"
: "bg-[var(--card)] text-[var(--foreground)] hover:bg-[var(--muted)]/40"
}`}
aria-pressed={state.chartMode === mode}
{/* Chart mode toggle */}
<div
role="group"
aria-label={t("balance.chart.modeLegend")}
className="inline-flex rounded-lg border border-[var(--border)] overflow-hidden"
>
{t(`balance.chart.mode.${mode}`)}
</button>
))}
{(["line", "stacked"] as BalanceChartMode[]).map((mode) => (
<button
key={mode}
type="button"
onClick={() => setChartMode(mode)}
className={`px-3 py-1.5 text-sm font-medium ${
state.chartMode === mode
? "bg-[var(--primary)] text-white"
: "bg-[var(--card)] text-[var(--foreground)] hover:bg-[var(--muted)]/40"
}`}
aria-pressed={state.chartMode === mode}
>
{t(`balance.chart.mode.${mode}`)}
</button>
))}
</div>
</div>
<BalanceEvolutionChart
mode={state.chartMode}
totals={state.evolutionTotals}
byCategory={state.evolutionByCategory}
categoryLabels={categoryLabels}
transferMarkers={allTransferMarkers}
/>
<div>
<h2 className="text-lg font-semibold mb-3">
{t("balance.overview.accountsTitle")}
</h2>
<BalanceAccountsTable
accounts={state.accountsLatest}
periodAnchor={state.accountsPeriodAnchor}
sinceCreationDate={earliestSnapshotDate}
onArchiveAccount={(acc) => handleArchiveAccount(acc.account_id)}
onLinkTransfers={(acc) => setLinkTarget(acc)}
/>
</div>
</div>
</div>
<BalanceEvolutionChart
mode={state.chartMode}
totals={state.evolutionTotals}
byCategory={state.evolutionByCategory}
categoryLabels={categoryLabels}
transferMarkers={allTransferMarkers}
/>
<div>
<h2 className="text-lg font-semibold mb-3">
{t("balance.overview.accountsTitle")}
</h2>
<BalanceAccountsTable
accounts={state.accountsLatest}
periodAnchor={state.accountsPeriodAnchor}
sinceCreationDate={earliestSnapshotDate}
onArchiveAccount={(acc) => handleArchiveAccount(acc.account_id)}
onLinkTransfers={(acc) => setLinkTarget(acc)}
/>
</div>
</div>
);
})()}
<StarterAccountsModal
isOpen={showStarterModal}