From a0112ecf20ea312b483493b7d626aeee166f2043 Mon Sep 17 00:00:00 2001 From: Abdullah Azmi Date: Mon, 19 Oct 2026 03:52:28 +0000 Subject: [PATCH] feat(frontend): display count badge next to Paused tab in dashboard navigation Fixes #1638 --- .../dashboard/dashboard-view.test.tsx | 74 +++++++++++++++++++ .../components/dashboard/dashboard-view.tsx | 22 +++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/dashboard/dashboard-view.test.tsx b/frontend/src/components/dashboard/dashboard-view.test.tsx index 6f3e2e11..5bdfe6c2 100644 --- a/frontend/src/components/dashboard/dashboard-view.test.tsx +++ b/frontend/src/components/dashboard/dashboard-view.test.tsx @@ -43,6 +43,7 @@ vi.mock("@/lib/soroban", () => ({ getTokenAddress: vi.fn((symbol: string) => symbol), toSorobanErrorMessage: vi.fn((e) => String(e)), fetchTokenBalanceDisplay: vi.fn().mockResolvedValue("1000"), + TOKEN_ADDRESSES: { XLM: "CXLM" }, })); vi.mock("@/lib/stellar", () => ({ @@ -73,6 +74,10 @@ vi.mock("./StreamDetailsModal", () => ({ StreamDetailsModal: () =>
, })); +vi.mock("./CashflowProjectionChart", () => ({ + CashflowProjectionChart: () =>
, +})); + vi.mock("../ui/Button", () => ({ Button: ({ children, @@ -216,4 +221,73 @@ describe("DashboardView + useDashboard cache integration", () => { callsBeforeRetry, ); }, 20000); + + it("hides the Paused tab count badge when there are zero paused streams", async () => { + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + + render( + + + , + ); + + await waitFor(() => + expect(screen.getByText(/start your first stream/i)).toBeInTheDocument(), + ); + + expect(screen.queryByTestId("paused-tab-badge")).toBeNull(); + }); + + it("displays the exact count badge next to the Paused tab when paused streams exist", async () => { + const makeBackendStream = (id: string, streamId: number, isPaused: boolean) => ({ + id, + streamId, + sender: PUBLIC_KEY, + recipient: "GRECIPIENT0000000000000000000000000000000000000000000000", + tokenAddress: "CXLM", + ratePerSecond: "100", + depositedAmount: "100000000", + withdrawnAmount: "0", + startTime: 1_700_000_000, + lastUpdateTime: 1_700_000_000, + createdAt: "2024-01-01T00:00:00.000Z", + updatedAt: "2024-01-01T00:00:00.000Z", + isActive: true, + isPaused, + events: [], + }); + + const outgoing = [ + makeBackendStream("out-1", 101, true), + makeBackendStream("out-2", 102, false), + ]; + const incoming = [ + makeBackendStream("in-1", 201, true), + makeBackendStream("in-2", 202, true), + ]; + + vi.stubGlobal( + "fetch", + vi.fn(async (input: RequestInfo | URL) => { + const url = String(input); + return jsonResponse(url.includes("sender=") ? outgoing : incoming); + }), + ); + + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + + render( + + + , + ); + + const badge = await screen.findByTestId("paused-tab-badge"); + expect(badge).toHaveTextContent("3"); + expect(badge).toHaveAttribute("aria-label", "3 paused streams"); + }); }); diff --git a/frontend/src/components/dashboard/dashboard-view.tsx b/frontend/src/components/dashboard/dashboard-view.tsx index 511c6586..7c5956f9 100644 --- a/frontend/src/components/dashboard/dashboard-view.tsx +++ b/frontend/src/components/dashboard/dashboard-view.tsx @@ -616,6 +616,15 @@ export function DashboardView({ session, onDisconnect }: DashboardViewProps) { [snapshot], ); + const pausedCount = React.useMemo( + () => + [ + ...(snapshot?.outgoingStreams ?? []), + ...(snapshot?.incomingStreams ?? []), + ].filter((stream) => stream.status === "Paused").length, + [snapshot], + ); + // Keep the keyboard-selected row visible as j/k move the highlight. React.useEffect(() => { if (!selectedStreamId) return; @@ -981,7 +990,7 @@ export function DashboardView({ session, onDisconnect }: DashboardViewProps) { ))}