Skip to content

fix(purchases): correct Savings History chart axis, tooltip decimals, y-axis ticks - #1253

Merged
cristim merged 3 commits into
mainfrom
fix/qa596-purchases-savings-chart
Jul 17, 2026
Merged

cristim merged 3 commits into
mainfrom
fix/qa596-purchases-savings-chart

Conversation

@cristim

@cristim cristim commented Jun 19, 2026 •

Copy link
Copy Markdown
Member

Closes #1252

Summary

Three visual bugs fixed in the Purchases page Savings History chart (QA session 596).

  • QA 2.2 (x-axis): chart now spans the full selected period from its start, not from the first data point
  • QA 2.3 (tooltip decimals): Period Savings tooltip uses 2 decimal places matching Cumulative and the KPI box
  • QA 2.4/2.5 (y-axis ticks): tick count is capped to prevent instability on legend toggle; float ticks no longer collapse to duplicate integer labels

Root cause and fix per bug

QA 2.2 -- x-axis starts at purchase date, not period start

The chart used a default category x-axis built from a labels array of formatted date strings. Chart.js positions the leftmost tick at the first label, so selecting "Last 30 days" with a recent first purchase showed a chart that started mid-period.

Fix: converted datasets to {x: timestamp_ms, y: value} objects and switched the x-axis to type:'linear' with min set to the period start timestamp and max set to now. Mirrors the Home dashboard chart approach from PR #746. The tick callback uses the shared formatTrendAxisTick helper, extracted to frontend/src/modules/chart-utils.ts and re-exported from dashboard.ts for backward compatibility.

QA 2.3 -- Period Savings tooltip shows 4 decimal places vs Cumulative 2

The tooltip label callback used toFixed(4) for dataset 0 (Period Savings) and toFixed(2) for dataset 1 (Cumulative), while the KPI box also uses 2 decimals.

Fix: changed toFixed(4) to toFixed(2) for Period Savings.

QA 2.4/2.5 -- y-axis tick behavior changes oddly when toggling series

Neither y-axis had a maxTicksLimit, so Chart.js re-autoscaled freely on every legend toggle. The y1 tick formatter used toFixed(0) which collapses nearby float ticks (e.g. 0.1 and 0.2) to the same integer string ("$0", "$0").

Fix: added maxTicksLimit: 6 to both y and y1 axes. Updated the y1 formatter to emit 2 decimal places for non-integer float ticks, preventing label collision.

Testing

Regression tests added to frontend/src/__tests__/savings-history.test.ts:

  • QA 2.2: asserts x-axis.type === 'linear' with numeric min within 1s of the period start; asserts no top-level labels array
  • QA 2.3: asserts Period Savings tooltip matches /\$\d+\.\d{2}(?!\d)/ (exactly 2 decimals) and does not match 3+ decimals
  • QA 2.4: asserts both y-axes have maxTicksLimit set
  • QA 2.5: asserts y1 formatter produces distinct labels for 0.1 vs 0.2, and no trailing decimals for true integers

All 83 tests in savings-history.test.ts pass. npx tsc --noEmit reports no errors. Dashboard tests (96 tests) still pass after the formatTrendAxisTick extraction.

Summary by CodeRabbit

  • Bug Fixes
    • Improved savings chart tooltips to consistently display “Period Savings” and “Cumulative Savings” with exactly 2-decimal precision, including correct $0.00 when values are missing.
    • Updated chart axis behavior for selected periods: the x-axis now uses a linear scale with numeric bounds, and hourly ticks show date+time while daily/weekly show date-only.
    • Stabilized chart tick formatting for the secondary axis to avoid inconsistent trailing decimals.
  • Tests
    • Expanded chart formatting and tooltip regression coverage across intervals to prevent precision/tick regressions.

@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/m Days labels Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d1e41190-644f-490e-9c95-e105c42ac6c7

📥 Commits

Reviewing files that changed from the base of the PR and between f7d6036 and 8be1709.

📒 Files selected for processing (4)
  • frontend/src/__tests__/savings-history.test.ts
  • frontend/src/dashboard.ts
  • frontend/src/modules/chart-utils.ts
  • frontend/src/modules/savings-history.ts
📝 Walkthrough

Walkthrough

The savings chart now uses timestamped {x, y} datasets on a period-bounded linear x-axis. Tick formatting is shared, tooltip values use two decimals, y-axis tick counts are limited, and regression tests cover the updated behavior.

Changes

Savings History chart behavior

Layer / File(s) Summary
Extract and re-export chart tick formatter
frontend/src/modules/chart-utils.ts, frontend/src/dashboard.ts
Moves formatTrendAxisTick into a shared module while preserving its dashboard export.
Refactor chart data and axes
frontend/src/modules/savings-history.ts
Passes unit and period bounds into chart rendering, builds {x, y} datasets, uses a linear x-axis with numeric bounds, limits y-axis ticks, and standardizes tooltip and y1 tick formatting.
Validate updated chart behavior
frontend/src/__tests__/savings-history.test.ts
Updates axis and tooltip assertions and adds QA regression coverage for axis bounds, tick limits, decimal precision, and float formatting.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main Savings History chart fixes for axis, tooltip decimals, and y-axis ticks.
Linked Issues check ✅ Passed The changes address #1252 QA 2.2-2.5 by using a linear period-spanning x-axis, 2-decimal tooltips, stable y-axis ticks, and distinct y1 labels.
Out of Scope Changes check ✅ Passed No material changes appear unrelated to the linked Savings History chart fixes; the shared helper export and tests are in scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/qa596-purchases-savings-chart

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

@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 force-pushed the fix/qa596-purchases-savings-chart branch from f8fd2d0 to 3d01037 Compare June 19, 2026 14:46
@cristim
cristim changed the base branch from main to fix/1222-precommit-repair June 19, 2026 14:46
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

This PR is now stacked on #1254 (branch fix/1222-precommit-repair).

The earlier .trivyignore DS-0002 suppression I had added to unblock pre-commit has been dropped from this branch. #1254 fixes Dockerfile.test to run as a non-root user instead, which is the proper fix and replaces the suppression.

This PR has been rebased onto fix/1222-precommit-repair and its base retargeted accordingly. It now contains ONLY the Savings History chart changes (savings-history.ts, chart-utils.ts, dashboard.ts re-export, and the test files). It will be retargeted back to main once #1254 merges.

@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 force-pushed the fix/qa596-purchases-savings-chart branch from 3d01037 to 51c8e63 Compare June 19, 2026 22:01
@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

Rate Limit Exceeded

@cristim have exceeded the limit for the number of chat messages per hour. Please wait 2 minutes and 6 seconds before sending another message.

@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 triggered.

@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.

@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.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@cristim

cristim commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

Adversarial review (PR #1253)

Reviewed the four QA fixes against the actual data path (API → reducer → frontend chart options) and probed the edge cases the task brief asked about. No actionable findings to block merge.

Verified

  • QA 2.2 (x-axis): bug was wrong axis projection, not just label - the prior category axis positioned the leftmost tick at the first data point regardless of period. Fix switches to type:'linear' with min/max from periodStart/periodEnd, mirroring loadSavingsTrendChart in dashboard.ts:1126-1141. Verified the data is now {x: ms, y: value} objects. ✓
  • QA 2.3 (tooltip decimals): tooltip Period Savings now toFixed(2), matching Cumulative tooltip, the KPI box (formatCurrency in savings-history.ts:332-340), and the y-axis tick formatter. No drift between the four sites. ✓
  • QA 2.4/2.5 (y-axis ticks): maxTicksLimit:6 on both y and y1; y1 conditionally shows 2 decimals for non-integer floats. Test y1Cb(0.1) !== y1Cb(0.2) correctly catches the original collision bug.
  • Empty state: loadSavingsHistory early-returns to showEmptyState when data_points.length === 0 (lines 111-114) - renderSavingsChart never sees empty data, so the linear-axis change does not affect empty-state handling. The todo_dashboard_trend_docstring.md memory entry references dashboard.ts:~914 (Home chart), not this purchases chart.
  • Currency formatting: hardcoded $ prefix consistent with the rest of the app (dashboard.ts:1138, formatCurrency in utils).
  • Tests: 83 tests in savings-history.test.ts cover positive (12-point linear axis with min within 1s of period start) + negative (no labels array, not.toMatch(/\$\d+\.\d{3}/) for 3+ decimals) cases.

Minor observations (non-blocking, not worth a follow-up)

  1. y vs y1 decimal asymmetry: y always $X.XX, y1 shows $10 for integers and $10.50 for floats. Author's choice keeps cumulative-totals axis visually clean while still avoiding the 0.1/0.2 collision; both are dollar values rendered slightly differently. Pre-existing y-axis behavior, not a regression.
  2. Hourly tooltip precision: toFixed(2) will show $0.00/hr for sub-cent hourly values (e.g. $7.30/mo → $0.01/hr; smaller values floor to $0.00). This is intentional alignment with the KPI box which already had this floor via formatCurrency. No regression in practice.
  3. 'monthly' dead branch in tickIntervalHint mapping (line 376-377): getPeriodDates never returns 'monthly', but the fallback to 'daily' is defensive coding.

UNSTABLE state

Failing checks are pre-existing on main (verified: 5 most recent main CI - Build & Test runs are all failure):

  • Lint Code: 2875 issues in internal/server/, internal/api/, internal/email/ - none in files this PR touches.
  • Integration Tests: PostgreSQL fixture failure (role "testuser" does not exist).
  • Security Scanning: 44 npm audit vulnerabilities in transitive deps (ws, webpack).

None are caused by or related to this PR's frontend-only changes. Not blocking.

Verdict

LGTM. CR has already reviewed multiple times with no actionable comments. No fixes pushed.

@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 force-pushed the fix/qa596-purchases-savings-chart branch from 9f47ee4 to 4384654 Compare July 17, 2026 07:56
@cristim

cristim commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

Rebased onto origin/main (27fdb06). Clean 3-commit rebase, no conflicts. Gates: go build/vet exit 0; npm test exit 0 (78 suites, 2629 tests).

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

@cristim: Thanks for the rebase and validation results. I’ll review the updated PR state.

✅ 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 force-pushed the fix/qa596-purchases-savings-chart branch from 4384654 to f7d6036 Compare July 17, 2026 10:59
@cristim

cristim commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

Rebased onto origin/main (89658a3 -> a945344) which includes #1437 (tflint plugin cache + retry) and #1438 (trivy \*\*/.terraform skip). Workflow files confirmed to match main. Gates: npm test 2642 passed. Pre-commit should be fully green.

cristim added 3 commits July 17, 2026 17:03
…dule

Move formatTrendAxisTick from dashboard.ts into
frontend/src/modules/chart-utils.ts so it can be reused by the
Purchases Savings History chart. Re-export from dashboard.ts for
backward compatibility with existing tests and callers.
… y-axis ticks

QA 2.2 -- x-axis starts at period start, not first data point:
Convert datasets from scalar arrays to {x: timestamp_ms, y: value}
objects and switch the x-axis to type:'linear' with min/max anchored
to the selected period window. Mirrors the Home dashboard approach
from PR #746. Uses the shared formatTrendAxisTick helper from
chart-utils.ts for tick labels.

QA 2.3 -- Period Savings tooltip precision matches Cumulative:
Change toFixed(4) to toFixed(2) for the Period Savings tooltip label
so both series and the KPI box above the chart show 2 decimal places.

QA 2.4/2.5 -- y-axis ticks stable when toggling series:
Add maxTicksLimit:6 to both y and y1 axis tick configs to cap
re-autoscaling on legend toggle. Fix the y1 formatter to emit 2
decimal places for non-integer float ticks, preventing distinct float
values from collapsing to the same integer label string.

Regression tests added in savings-history.test.ts:
- QA 2.2: x-axis type:linear with numeric min equal to period start
- QA 2.3: Period Savings tooltip has exactly 2 decimal places
- QA 2.4: both y-axes have maxTicksLimit
- QA 2.5: y1 formatter does not collapse distinct floats to same label

Closes #1252
…etry

Minor readability cleanup: the Period Savings dataset array is now named
periodSavingsData, matching its sibling cumulativeSavingsData. No behavior
change.
@cristim
cristim force-pushed the fix/qa596-purchases-savings-chart branch from f7d6036 to 8be1709 Compare July 17, 2026 14:04
@cristim
cristim merged commit 5663254 into main Jul 17, 2026
20 checks passed
@cristim
cristim deleted the fix/qa596-purchases-savings-chart 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/m Days 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(purchases): Savings History chart x-axis start, tooltip decimals, y-axis tick instability (QA 2.2/2.3/2.4)

1 participant