Skip to content

Commit 2782f19

Browse files
dashboard: §10.21 — strip fixtures, fix cosmetic + behavioural gaps (#32)
* dashboard: strip dev API banners + retire /stacks dupe + hide /team nav User-facing dashboard had ContractBanner blocks ("GET /api/v1/resources · returns {ok: true, items: Resource[]} · Backed by resourcesH.List() ...") on every page — that's internal dev docs that shouldn't ship to users. Stripped from ResourcesPage, BillingPage, DeploymentsPage, DeployDetailPage, ResourceDetailPage, TeamPage. ContractsPage keeps them — that page IS the design-ref artifact and is meant for internal use. Other cleanup landing in this same PR: • Delete /app/stacks duplicate route + StacksPage.tsx — same data as /app/deployments. UI calls them "Deployments" (user language); API stays /api/v1/stacks (existing). One page, one route. • Hide /app/team NavRow from sidebar — team-management features (invitations, role changes) haven't fully shipped yet. The route still resolves for direct-URL access. • Replace PAGE_META /resources/:id hardcoded "flashcards-db" title with the generic "Resource" so the H1 isn't a lie. • Replace PAGE_META /deployments/:id hardcoded "flashcards" title with the generic "Deployment". Tests: 162/165 pass (3 pre-existing skips), tsc clean. The three FIXTURE_BILLING / FIXTURE_INVOICES fallback tests in api/index.test.ts were updated to assert error propagation instead of fallback behavior — concurrent §10.21.1 work removed those fallbacks from src/api/index.ts and the tests now describe reality. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * dashboard: §10.21 finish — delete fixtures.ts + close behavioural gaps Removes the remaining FIXTURE_* fallback layer, deletes src/api/fixtures.ts, and closes the six dashboard fidelity gaps from RETRO-2026-05-12 §10.21: 1. fixtures.ts deleted. test-only data is inlined into BillingPage.test.tsx — no runtime module ships mock identities. 2. avatar tooltip now reads ctx.me?.user?.email (previously hardcoded "aanya@acme.dev"). landed in cb485cb. 3. PAGE_META /resources/:id no longer hardcodes "flashcards-db" — header is the generic "Resource" until a per-resource dynamic title lands. landed in cb485cb. 4. "11 gaps" badge on /contracts removed. landed in cb485cb. 5. /claim?t=<bad-jwt> renders an explicit invalid/expired banner + pricing CTA instead of a blank email form. landed in cb485cb. 6. OverviewPage "Recently active" rows render reliably for any resources.length >= 1; Link now targets /app/resources/:id; added data-testids + minWidth:0 to fix grid overflow. NEW here. BillingPage now handles the post-§10.21.1 reality where fetchBilling can reject — surfaces a real billing-unavailable banner with the API error message + a support mailto, instead of holding the skeleton forever or rendering with fixture data. Tests: - src/pages/BillingPage.test.tsx: +2 (billing-error banner state + error-message surfacing). - src/pages/ClaimPage.test.tsx: +1 (claim-invalid state when ?t is malformed). - src/pages/OverviewPage.test.tsx: +3 (recently-active rows render, name + type, link target is /app/resources/:id). 168 passed, 3 skipped, tsc clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b13b8ee commit 2782f19

7 files changed

Lines changed: 237 additions & 296 deletions

File tree

src/api/fixtures.ts

Lines changed: 0 additions & 276 deletions
This file was deleted.

src/api/index.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ import {
2727
deleteResource,
2828
listAPIKeys,
2929
} from './index'
30-
import { FIXTURE_BILLING, FIXTURE_INVOICES } from './fixtures'
30+
// §10.21: FIXTURE_BILLING / FIXTURE_INVOICES imports retired. The 503
31+
// fallback paths in fetchBilling() and listInvoices() were removed —
32+
// errors now propagate so consumers can render real error banners.
3133

3234
// ─── Test helpers ────────────────────────────────────────────────────────
3335

src/pages/BillingPage.test.tsx

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,50 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
1313
import { render, screen, waitFor, fireEvent, cleanup } from '@testing-library/react'
1414
import userEvent from '@testing-library/user-event'
1515
import { BillingPage } from './BillingPage'
16-
import { FIXTURE_BILLING, FIXTURE_INVOICES, FIXTURE_USER, FIXTURE_TEAM } from '../api/fixtures'
16+
import type { BillingDetails, DashboardTeam, Invoice, User } from '../api'
17+
18+
// §10.21: the runtime `api/fixtures.ts` module is gone — test-only data
19+
// lives here, inlined and minimal. These shapes match the api types and
20+
// exist solely so the BillingPage tests can pin expected rendering.
21+
const FIXTURE_USER: User = {
22+
id: 'u_test',
23+
email: 'aanya@acme.dev',
24+
tier: 'pro',
25+
team_id: 't_test',
26+
created_at: new Date(Date.now() - 42 * 86_400_000).toISOString(),
27+
display_name: 'Aanya Patel',
28+
role: 'owner',
29+
}
30+
31+
const FIXTURE_TEAM: DashboardTeam = {
32+
id: 't_test',
33+
name: 'acme-corp',
34+
slug: 'acme-corp',
35+
owner_id: 'u_test',
36+
member_count: 1,
37+
tier: 'pro',
38+
created_at: new Date(Date.now() - 42 * 86_400_000).toISOString(),
39+
display_name: 'acme-corp',
40+
default_env: 'production',
41+
}
42+
43+
const FIXTURE_BILLING: BillingDetails = {
44+
status: 'active',
45+
current_period_end: new Date(Date.now() + 9 * 86_400_000).toISOString(),
46+
razorpay_configured: true,
47+
subscription_status: 'active',
48+
payment_last4: '4242',
49+
payment_exp_month: 9,
50+
payment_exp_year: 27,
51+
payment_network: 'visa',
52+
cancel_at_period_end: false,
53+
}
54+
55+
const FIXTURE_INVOICES: Invoice[] = [
56+
{ id: 'inv_QzN8bD', period_start: '2026-04-22', period_end: '2026-05-22', plan: 'pro', amount_cents: 4900, currency: 'USD', status: 'paid' },
57+
{ id: 'inv_Pp7K2c', period_start: '2026-03-22', period_end: '2026-04-22', plan: 'pro', amount_cents: 4900, currency: 'USD', status: 'paid' },
58+
{ id: 'inv_Lm4F9a', period_start: '2026-02-20', period_end: '2026-03-22', plan: 'hobby', amount_cents: 900, currency: 'USD', status: 'paid' },
59+
]
1760

1861
// ─── Module-level mocks ──────────────────────────────────────────────────
1962
// We mock the api module so tests never hit fetch(); each test sets up
@@ -132,6 +175,33 @@ afterEach(() => {
132175
restoreLocation()
133176
})
134177

178+
// ─── §10.21: backend-down error state (no fixture fallback) ──────────────
179+
describe('BillingPage — backend-down error state (§10.21)', () => {
180+
it('renders the billing-error banner when fetchBilling rejects (no fixture fallback)', async () => {
181+
;(api.fetchBilling as any).mockRejectedValue(Object.assign(new Error('Razorpay is not configured'), { status: 503 }))
182+
;(api.listInvoices as any).mockResolvedValue({ ok: true, invoices: [] })
183+
;(api.listResources as any).mockResolvedValue({ ok: true, items: [], total: 0 })
184+
render(<BillingPage />)
185+
await waitFor(() => {
186+
expect(screen.getByTestId('billing-error')).toBeTruthy()
187+
})
188+
// Critical: must NOT render the upgrade CTA (which would imply a working
189+
// billing surface). The error state is exclusive.
190+
expect(screen.queryByRole('button', { name: /upgrade to/i })).toBeNull()
191+
})
192+
193+
it('surfaces the error message on the billing-error banner', async () => {
194+
;(api.fetchBilling as any).mockRejectedValue(new Error('Razorpay is not configured'))
195+
;(api.listInvoices as any).mockResolvedValue({ ok: true, invoices: [] })
196+
;(api.listResources as any).mockResolvedValue({ ok: true, items: [], total: 0 })
197+
const { container } = render(<BillingPage />)
198+
await waitFor(() => {
199+
expect(screen.getByTestId('billing-error')).toBeTruthy()
200+
})
201+
expect(container.textContent).toContain('Razorpay is not configured')
202+
})
203+
})
204+
135205
// ─── Initial render ──────────────────────────────────────────────────────
136206
describe('BillingPage — initial render', () => {
137207
it('shows a skeleton while billing is loading', () => {

0 commit comments

Comments
 (0)