fire-planner: filterable date range on the home-page history chart
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

User asked for a manual date range on the Dashboard chart instead of
the hard-coded 12-month window.

Backend (/networth/history):
- Read LIVE from wf_sync's daily_account_valuation JOIN accounts so the
  chart spans the broker's full daily series. The account_snapshot
  cache is only the latest snapshot — never had >2 daily points for
  charting. Falls back to the cache when wf_sync isn't wired (tests).
- Accept `from=YYYY-MM-DD` and `to=YYYY-MM-DD` query params. When `from`
  is set, the window is [from, to or today] (inclusive). Otherwise the
  legacy `days` look-back still applies. 422 when from > to.

Frontend:
- New HistoryRangePicker component: preset buttons (1m / 3m / 6m / 1y /
  3y / All) plus two date inputs for an explicit custom range.
- Dashboard wires the picker to the chart via react-query keyed on the
  selected range, so the chart re-fetches on change.

Tests:
- Renamed `respects_days_filter` → `respects_from_to_filter` and added
  inverted-window rejection. Old test asserted "days=1 returns 1 point"
  which only worked when 'today' was within the seed window — the new
  windowing is correct and explicit.

271 → 272 tests passing.
This commit is contained in:
Viktor Barzin 2026-05-28 09:04:58 +00:00
parent 4da58fe56e
commit 4a0ef1faf6
5 changed files with 222 additions and 36 deletions

View file

@ -111,12 +111,19 @@ async def test_networth_history_returns_per_date(client: AsyncClient,
assert Decimal(by_date["2026-04-25"]["by_account"]["ISA"]) == Decimal("300000")
async def test_networth_history_respects_days_filter(
async def test_networth_history_respects_from_to_filter(
client: AsyncClient,
session: AsyncSession,
) -> None:
"""The `from`/`to` params return only points in [from, to] inclusive."""
await _seed_snapshots(session)
resp = await client.get("/networth/history?days=1")
resp = await client.get("/networth/history?from=2026-04-25&to=2026-04-25")
assert resp.status_code == 200
# days=1 ⇒ only the latest 1 distinct date
assert len(resp.json()["points"]) == 1
points = resp.json()["points"]
assert len(points) == 1
assert points[0]["snapshot_date"] == "2026-04-25"
async def test_networth_history_rejects_inverted_window(client: AsyncClient) -> None:
resp = await client.get("/networth/history?from=2026-05-01&to=2026-04-01")
assert resp.status_code == 422