whisper-money/resources/js/components/ui
Víctor Falcón 50d0fccd17
fix(dashboard): render negative net worth as a downward bar (#722)
## Problem

On the dashboard **Net Worth Evolution** monthly bar chart, users with a
large negative net worth (lots of debt) saw an **empty chart** — only
the x-axis month labels rendered, no bars.

Root cause: the chart scales each asset segment so the stacked bar
height equals net worth. For negative net worth the scale factor was
clamped to zero:

```ts
const scaleFactor = hasLiabs && totalAssets > 0
    ? Math.max(0, netWorth / totalAssets) // negative net worth -> 0 -> every bar collapses
    : 1;
```

A stack of positive asset segments simply can't represent a negative
total, so everything collapsed to zero height.

## Fix

- Extract the scaling into a tested pure helper
`computeNetWorthBarScaling`.
- When net worth is **negative**, hide the (meaningless) scaled asset
segments and draw the deficit as a **single downward bar** from a **zero
baseline** (dashed reference line).
- **Positive** periods still stack assets upward. Both directions round
only at their far end and meet the zero line flush, so upward and
downward bars look symmetric.
- Extend the same handling to the **daily area chart**, which shared the
bug through the same scaled dataset.
- Share a single `NetWorthMode` type across the bar/area charts and
tooltip (was duplicated in three places).
- Translate the tooltip **Net Worth / Total** labels (were hardcoded
English; added `Net Worth` to `lang/es.json`).
- Render the chart for **liability-only** users (a debtor with just a
loan) instead of the "No account data available" empty state.

## Tests

- Unit tests for `computeNetWorthBarScaling`: no-liabilities, positive,
negative-with-assets, liabilities-only, net-worth-exactly-zero, and
no-assets cases.
- `bun run test`, `prettier --check`, `eslint` all green on the touched
files.

## QA

Verified in the running app (Playwright) with two seeded users:

- **Heavily indebted** (net worth −56.250,00 €): the chart now renders
red downward bars instead of an empty area.
- **Net worth crossing zero**: negative months draw downward, positive
months stack upward, sharing the zero baseline; tooltip shows
per-account balances + the (now translated) "Patrimonio Neto" total.

## Demo


https://github.com/user-attachments/assets/7a9b167f-c037-4c82-8b68-f32932be99fe

## Screenshots

**Before** — empty chart, only x-axis labels:

<!-- PLACEHOLDER: before screenshot -->
<img width="1440" height="900" alt="net-worth-before"
src="https://github.com/user-attachments/assets/3bf9b3e3-ae75-4230-bca7-e7246a9a35d9"
/>


**After** — negative net worth as downward bars:
<img width="1440" height="900" alt="net-worth-after-tooltip"
src="https://github.com/user-attachments/assets/85302f0a-e427-4cbf-baba-5b1414049e9f"
/>
<img width="1440" height="900" alt="net-worth-after-mixed"
src="https://github.com/user-attachments/assets/41f08331-f055-4245-beeb-8d071c0d5d6d"
/>
<img width="1440" height="900" alt="net-worth-after-negative"
src="https://github.com/user-attachments/assets/c8610b03-dcff-46de-8987-d47c477ddd85"
/>
2026-07-22 09:32:18 +00:00
..
ai-sparkle-icon.tsx
…
alert-dialog.tsx
…
alert.tsx
…
amount-display.tsx
…
amount-input.test.tsx
…
amount-input.tsx
…
animated-collapse.tsx
…
avatar.tsx
…
badge.tsx
…
breadcrumb.tsx
…
button-group.tsx
…
button.tsx
…
card.tsx
…
chart-tooltip-portal.test.tsx
…
chart.tsx
…
checkbox.tsx
…
collapsible.tsx
…
command.tsx
…
context-menu.tsx
…
create-button.tsx
…
data-table-pagination.tsx
…
data-table-view-options.tsx
…
data-table.tsx
…
dialog.tsx
…
drawer.tsx
…
dropdown-menu.tsx
…
glowing-effect.tsx
…
input-otp.tsx
…
input.tsx
…
kbd.tsx
…
label.tsx
…
multi-select.tsx
…
password-input.test.tsx
…
password-input.tsx
…
popover.tsx
…
progress.tsx
…
radio-group.tsx
…
select.tsx
…
separator.tsx
…
sheet.tsx
…
sidebar.tsx
…
skeleton.tsx
…
spinner.tsx
…
stacked-area-chart.tsx
…
stacked-bar-chart.test.tsx
…
stacked-bar-chart.tsx
…
table.tsx
…
textarea.tsx
…
toggle-group.tsx
…
toggle.tsx
…
tooltip.tsx
…