Skip to content

fix(dashboard): fix Home savings 'All' range and label chart tooltip date - #1257

Merged
cristim merged 2 commits into
mainfrom
fix/qa403-home-savings-all-tooltip
Jul 10, 2026
Merged

fix(dashboard): fix Home savings 'All' range and label chart tooltip date#1257
cristim merged 2 commits into
mainfrom
fix/qa403-home-savings-all-tooltip

Conversation

@cristim

@cristim cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member

Closes #1248

Symptoms

  • QA 3.1: Clicking the "All" timeframe button on the Home page Savings-over-time chart shows "Savings history is not available yet." while 7d/30d/90d render correctly.
  • QA 3.2: Hovering over a data point shows an unlabeled 13-digit millisecond timestamp as the tooltip header instead of a human-readable date.

Root cause

QA 3.1: loadSavingsTrendChart sent start=1970-01-01T00:00:00Z (an epoch sentinel) when the "All" range was selected. The backend analytics handler (internal/api/handler_analytics.go) caps the date range at 366 days and returns HTTP 400 "date range too large" for a 1970 start. The catch block renders the generic empty-state message for all errors, so the 400 surfaced as "Savings history is not available yet."

QA 3.2: The chart x-axis is type: 'linear' with data points {x: timestampMs, y: value}. The tooltip had a label callback but no title callback, so Chart.js rendered the raw x value (milliseconds) as the tooltip header.

Fix

  • QA 3.1: Replace the 1970 epoch sentinel with a rolling ~365-day window (now - 365 * 86400_000) that fits inside the backend's 366-day cap. The existing axisMinMs anchor-to-earliest-point logic is preserved so sparse data still fills the chart width correctly. The backend 366-day cap (a deliberate DoS guard) is unchanged.
  • QA 3.2: Add a title callback to the tooltip that formats the x timestamp using the existing formatTrendAxisTick helper (already used for x-axis tick labels).

Testing

  • Replaced the old "epoch sentinel" test (which asserted the broken behavior) with a test asserting the "All" range sends a start within ~365 days of now (not 1970), and that the chart renders rather than showing the empty-state when the API returns data.
  • Added a test asserting the tooltip title callback returns a formatted date string (not a bare 13-digit number) for a given x timestamp.
  • Both new tests confirmed to FAIL on pre-fix code and PASS after the fix.
  • npx jest src/__tests__/dashboard.test.ts: 97 pass, 0 fail, 1 skipped.
  • npx tsc --noEmit: no errors.

Stacked on #1254 (pre-commit repair); retarget to main when #1254 merges.

Summary by CodeRabbit

  • Bug Fixes
    • Updated the savings trend chart’s “All” range to query analytics using a rolling ~365-day window instead of an unbounded epoch-based start.
    • Improved the savings trend chart tooltip header to display human-readable dates rather than raw timestamps.
  • Tests
    • Updated dashboard chart tests to match the new “All” range start behavior and added coverage for tooltip date formatting.

…date

QA 3.1: The 'All' timeframe sent '1970-01-01T00:00:00Z' as the start
param, triggering HTTP 400 "date range too large" from the analytics
handler (which caps at 366 days). The catch block rendered all errors as
"Savings history is not available yet." Replace the epoch sentinel with a
rolling ~365-day window (now - 365d) that fits the backend cap. The
existing axisMinMs anchor-to-earliest-point logic is preserved.

QA 3.2: The tooltip had no title callback, so Chart.js rendered the raw
13-digit millisecond x value as the tooltip header. Add a title callback
that formats the x timestamp via the existing formatTrendAxisTick helper.

Closes #1248
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6449b38a-0ae9-499f-88e3-4ad232e8f1c1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/qa403-home-savings-all-tooltip

Comment @coderabbitai help to get the list of available commands.

@cristim cristim added triaged Item has been triaged type/bug Defect priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/this-quarter Within the quarter impact/many Affects most users effort/s Hours labels Jun 19, 2026
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
cristim changed the base branch from fix/1222-precommit-repair to main June 19, 2026 14:51
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@cristim

cristim commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

…test mock

Adversarial-review follow-ups to PR #1257:

- frontend/src/dashboard.ts: the loadSavingsTrendChart docstring still
  described the pre-QA-2.3 "empty windows render labelled axes" behaviour;
  the shipped code (lines 1083-1098) shows a filter-aware empty-state
  banner instead. Reconciled the docstring to describe the actual three
  branches (data / empty / fetch-error).

- frontend/src/__tests__/dashboard.test.ts: the new "All range sends ~365d
  start" test set mockResolvedValue twice with a mockClear() in between.
  mockClear() resets call history but NOT mock return values, so the first
  set was dead code. Collapsed to a single mockResolvedValue + mockClear
  with a clarifying comment.

97 dashboard tests pass, tsc clean. No behaviour change.
@cristim

cristim commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

Adversarial-review pass

Ran the playbook against the QA 3.1 / QA 3.2 fixes plus the explicit risk surfaces (All semantics, stale docstring memory hit, tooltip date format, empty/zero state, timezone, range cross-product, tests).

In-scope fixes pushed (8008bc0)

  1. Stale docstring on loadSavingsTrendChart (frontend/src/dashboard.ts:1004-1010). The docstring still described the pre-QA-2.3 behaviour ("Empty windows render labelled axes rather than a 'no data' stub"); the shipped code (lines 1083-1098) shows a filter-aware empty-state banner instead. This was a tracked TODO in the project memory garden (memory todo_dashboard_trend_docstring), held until the next edit to the file. This PR is that edit -- updated the docstring to describe the actual three branches (data / empty / fetch-error). The memory entry is now stale and can be deleted.
  2. Redundant mock setup in the new All-range test (__tests__/dashboard.test.ts:887-893). The test called mockResolvedValue twice with a mockClear() between -- mockClear() resets call history but NOT mock return values, so the first set was dead code. Collapsed to one mockResolvedValue + mockClear() with a clarifying comment. No behaviour change; 97 dashboard tests still pass.

Confirmed clean

  • 365-day window fits the backend 366-day cap: windowStartMs = now - 365 * 86400_000, backend cap is < 366 * 24h (handler_analytics.go:309-315). 1-day cushion.
  • Tooltip title callback: reuses formatTrendAxisTick, has nullish-guard on raw?.x so missing-x renders '' rather than "NaN" or the raw timestamp.
  • Empty-state path on 'All': already covered by the generic data_points: [] test (line 920); the same points.length === 0 branch handles all ranges.
  • Fetch-error path: existing test at line 978 still covers the catch block.
  • Tests confirmed pre-fix-FAIL / post-fix-PASS: 97/97 dashboard tests pass on this branch; tsc --noEmit clean.

Out-of-scope follow-ups filed

Risk surfaces from the brief, checked

Re-pinging CR.

@cristim

cristim commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
cristim merged commit d484c9d into main Jul 10, 2026
13 of 17 checks passed
@cristim
cristim deleted the fix/qa403-home-savings-all-tooltip branch July 27, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/many Affects most users priority/p3 Polish / idea / may never ship severity/low Minor harm triaged Item has been triaged type/bug Defect urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(dashboard): Home Savings-over-time 'All' range shows empty-state + tooltip shows unlabeled timestamp (QA 3.1/3.2)

1 participant