From c1a88a39e2c6e89da2cff665bad32841d3998ede Mon Sep 17 00:00:00 2001 From: Saravana Kumar Date: Sat, 30 May 2026 20:23:14 +0000 Subject: [PATCH] feat(tracker-web): add dashboard stats retry state --- dashboards/tracker-web/e2e/tracker.spec.ts | 32 ++++++++++++ .../tracker-web/src/app/dashboard/page.tsx | 50 ++++++++++++++----- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/dashboards/tracker-web/e2e/tracker.spec.ts b/dashboards/tracker-web/e2e/tracker.spec.ts index 10bf54d6..01a6b2df 100644 --- a/dashboards/tracker-web/e2e/tracker.spec.ts +++ b/dashboards/tracker-web/e2e/tracker.spec.ts @@ -278,6 +278,38 @@ test.describe('Tracker — Authenticated dashboard', () => { await expect(page.getByText('admin@example.com')).toBeVisible(); }); + test('shows a retry path when dashboard stats fail', async ({ page }) => { + await page.route('**/api/auth/me', (route: Route) => + route.fulfill({ + json: { id: 'u1', email: 'admin@example.com', role: 'admin', displayName: 'Admin' }, + }) + ); + let allowRecovery = false; + await page.route('**/api/tracker/**', (route: Route) => { + if (route.request().url().includes('/items/stats')) { + return !allowRecovery + ? route.fulfill({ status: 502, json: { error: 'upstream unavailable' } }) + : route.fulfill({ + json: { + productId: 'tracker-e2e', + total: 7, + byType: { bug: 1, feature: 5, task: 1 }, + byStatus: { open: 4, in_progress: 2, done: 1 }, + byPriority: { critical: 0, high: 2, medium: 4, low: 1 }, + }, + }); + } + return route.fulfill({ json: {} }); + }); + + await page.addInitScript(() => localStorage.setItem('tracker_token', 'fake-e2e-token')); + await page.goto('/dashboard'); + await expect(page.getByRole('heading', { name: /could not load dashboard/i })).toBeVisible(); + allowRecovery = true; + await page.getByRole('button', { name: /retry/i }).click(); + await expect(page.getByTestId('bl-number-flow').filter({ hasText: '7' }).first()).toBeVisible(); + }); + test('renders settings for admin configuration', async ({ page }) => { await page.route('**/api/auth/me', (route: Route) => route.fulfill({ diff --git a/dashboards/tracker-web/src/app/dashboard/page.tsx b/dashboards/tracker-web/src/app/dashboard/page.tsx index 7087f1a8..0732731f 100644 --- a/dashboards/tracker-web/src/app/dashboard/page.tsx +++ b/dashboards/tracker-web/src/app/dashboard/page.tsx @@ -1,12 +1,13 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import dynamic from 'next/dynamic'; import { PageHeader, LoadingSpinner } from '@bytelyst/dashboard-components'; import { KpiCard } from '@bytelyst/data-viz'; import { Reveal, NumberFlow } from '@bytelyst/motion'; -import { Skeleton, toast } from '@/components/ui/Primitives'; +import { Button, Skeleton, toast } from '@/components/ui/Primitives'; import { useAuth } from '@/lib/auth-context'; +import { useProduct } from '@/lib/product-context'; import { getStats, type TrackerStats } from '@/lib/tracker-client'; import { overviewKpis } from '@/lib/overview-charts'; @@ -18,16 +19,31 @@ const OverviewCharts = dynamic(() => import('@/components/overview-charts'), { export default function DashboardOverview() { const { token } = useAuth(); + const { productId } = useProduct(); const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const loadStats = useCallback(async () => { + if (!token) return; + setLoading(true); + setError(null); + try { + const data = await getStats(); + setStats(data); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : 'Unknown error'; + setStats(null); + setError(message); + toast({ type: 'error', title: 'Failed to load stats', description: message }); + } finally { + setLoading(false); + } + }, [token]); useEffect(() => { - if (!token) return; - getStats() - .then(setStats) - .catch(err => - toast({ type: 'error', title: 'Failed to load stats', description: err.message }) - ); - }, [token]); + void loadStats(); + }, [loadStats, productId]); const kpis = stats ? overviewKpis(stats) : null; @@ -36,7 +52,17 @@ export default function DashboardOverview() {

Overview of all tracked items

- {stats && kpis ? ( + {error ? ( +
+

Could not load dashboard

+

+ {error}. Check platform-service health or retry the request. +

+ +
+ ) : stats && kpis ? (
{/* KPI row */}
@@ -57,11 +83,11 @@ export default function DashboardOverview() {
- ) : ( + ) : loading ? (
- )} + ) : null}
); }