Skip to content

Commit 92db4e7

Browse files
authored
fix(home/kpi): format Current Coverage / Target with one decimal (#967)
* fix(home/kpi): format Current Coverage / Target with one decimal When the backend returned a fractional `current_coverage` value, the dashboard KPI tile rendered the raw float (e.g. `0.14648736342042862%`) because the template literal `${data.current_coverage || 0}%` did no formatting. Apply `.toFixed(1)` to both `current_coverage` and `target_coverage` so the tile reads as e.g. `0.1%` / `80.0%`, matching the convention already used in inventory.ts. Regression test in dashboard.test.ts asserts the fractional input renders as `0.1%` and never as the raw float. * fix(api/test): repair Session.Role literal and gofmt drift TestHandler_getPurchaseDetails_NotFound_IsClientError set a nonexistent Session.Role field causing go vet to fail: the Session struct carries only UserID/Email; admin is resolved via HasPermissionAPI. Drop the bogus field and confer admin through mockAuth.grantAdmin() so the handler reaches the GetExecutionByID not-found branch it asserts on. Also split multi-statement-per-line entries in handler_purchases_guards_test.go that caused gofmt to fail the pre-commit hook.
1 parent 20ce559 commit 92db4e7

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

‎frontend/src/__tests__/dashboard.test.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,26 @@ describe('Dashboard Module', () => {
177177
expect(summary?.innerHTML).toContain('YTD Savings');
178178
});
179179

180+
test('formats fractional current_coverage with one decimal (not raw float)', async () => {
181+
(api.getDashboardSummary as jest.Mock).mockResolvedValue({
182+
potential_monthly_savings: 0,
183+
total_recommendations: 1,
184+
active_commitments: 1,
185+
committed_monthly: 100,
186+
current_coverage: 0.14648736342042862,
187+
target_coverage: 80,
188+
ytd_savings: 0,
189+
by_service: {}
190+
});
191+
(api.getUpcomingPurchases as jest.Mock).mockResolvedValue({ purchases: [] });
192+
193+
await loadDashboard();
194+
195+
const summary = document.getElementById('summary');
196+
expect(summary?.innerHTML).toContain('0.1%');
197+
expect(summary?.innerHTML).not.toContain('0.14648736342042862');
198+
});
199+
180200
test('renders the merged per-service savings chart', async () => {
181201
(api.getDashboardSummary as jest.Mock).mockResolvedValue({
182202
potential_monthly_savings: 1000,

‎frontend/src/dashboard.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,8 @@ function renderDashboardSummary(data: DashboardSummary, recs: readonly LocalReco
268268
// When no recommendations and no commitments exist, "100% coverage" is
269269
// misleading — nothing is being tracked. Show a dash instead.
270270
const nothingTracked = !data.total_recommendations && !data.active_commitments;
271-
const coverageValue = nothingTracked ? '—' : `${data.current_coverage || 0}%`;
272-
const coverageDetail = nothingTracked ? 'No services tracked' : `Target: ${data.target_coverage || 80}%`;
271+
const coverageValue = nothingTracked ? '—' : `${(data.current_coverage || 0).toFixed(1)}%`;
272+
const coverageDetail = nothingTracked ? 'No services tracked' : `Target: ${(data.target_coverage || 80).toFixed(1)}%`;
273273

274274
// Render KPI tiles via DOM construction (textContent / appendChild)
275275
// rather than an innerHTML template literal, per the issue #340 plan's

‎internal/api/handler_purchases_test.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,10 @@ func TestHandler_getPurchaseDetails_NotFound_IsClientError(t *testing.T) {
16431643
Email: "admin@example.com",
16441644
}
16451645
mockAuth.On("ValidateSession", ctx, "admin-token").Return(adminSession, nil)
1646+
// getPurchaseDetails gates on requirePermission(view, purchases) before
1647+
// the GetExecutionByID lookup this test targets. Confer admin via the auth
1648+
// mock (the Session struct carries no role field; admin is resolved through
1649+
// HasPermissionAPI) so the handler reaches the not-found branch.
16461650
mockAuth.grantAdmin()
16471651

16481652
const missingID = "dddddddd-dddd-dddd-dddd-dddddddddddd"

0 commit comments

Comments
 (0)