Skip to content

Feat/a11y discover subs fixes - #1542

Merged
aji70 merged 3 commits into
MyFanss:mainfrom
OxDev-max:feat/a11y-discover-subs-fixes
Jul 27, 2026
Merged

Feat/a11y discover subs fixes#1542
aji70 merged 3 commits into
MyFanss:mainfrom
OxDev-max:feat/a11y-discover-subs-fixes

Conversation

@OxDev-max

Copy link
Copy Markdown
Contributor

Summary

Three independent frontend fixes bundled together:

1. Dashboard shell accessibility (skip-link + focus management)

The creator dashboard shell had no skip navigation and never moved focus on
route change, forcing keyboard and screen-reader users to tab through the
entire sidebar on every single navigation.

  • Added a "Skip to main content" link, visually hidden until focused, at the
    top of the dashboard shell.
  • Gave the <main> landmark a focusable #dashboard-main id and move focus
    to it whenever the route changes (skipped on initial mount so it doesn't
    steal focus on first load).
  • Verified existing icon-only controls (mobile menu toggle, sidebar
    collapse toggle) already carry aria-labels.

2. Discover: map creator fields completely

publicCreatorToProfile hardcoded subscriptionPrice to 0 and
categories to [] even when the /creators API returned
subscription_price, currency, and categories, making every card in
the discovery grid look free and uncategorized regardless of the actual
data.

  • Now maps subscription_price (parsed safely from the numeric string the
    API returns), currency, and categories when present.
  • Falls back to 0 / [] only when the fields are genuinely absent or
    fail to parse — no fabricated data.

3. Subscriptions: authenticated client for the active list

The active-subscriptions fetch called a bare relative path
(/api/v1/subscriptions/me/list) with no Authorization header. Since
this app authenticates with a stored JWT (not cookies), the request would
fail auth against the real backend and any failure — network error or
401 — silently rendered the same "No subscriptions found" empty state,
indistinguishable from a user who genuinely has none.

  • Added src/lib/api/subscriptions.ts: a client mirroring the existing
    profile.ts pattern — absolute, config-driven API base URL, Bearer
    token attached from auth-storage, response normalization with
    fallback field names, and a SubscriptionsUnauthorizedError thrown (and
    stale token cleared) on 401.
  • Wired the subscriptions page to this client for both the initial load
    and the post-renewal refetch.
  • The page now distinguishes three states: loading, "sign in required"
    (401), "couldn't load subscriptions" (other errors), and the genuine
    empty state — so failures are never mistaken for an empty list.

Changes

  • frontend/src/app/dashboard/layout.tsx — skip link, focusable main
    landmark, focus-on-navigate
  • frontend/src/app/dashboard/layout.test.tsx — new: skip link, focus
    handoff, labeled controls
  • frontend/src/lib/api/creators.ts — map subscription_price,
    currency, categories with safe defaults
  • frontend/src/lib/api/creators.test.ts — coverage for present/absent/
    malformed field cases
  • frontend/src/lib/api/subscriptions.ts — new: authenticated
    fetchActiveSubscriptions client
  • frontend/src/lib/api/subscriptions.test.ts — new: URL, auth header,
    normalization, 401 handling
  • frontend/src/app/subscriptions/page.tsx — use the new client, add
    distinct error/unauthorized UI states

Test plan

  • pnpm test in frontend/ (new/updated: layout.test.tsx,
    creators.test.ts, subscriptions.test.ts)
  • pnpm build to confirm no type errors
  • Manually tab through /dashboard — confirm the skip link appears on
    first Tab press and jumps focus into main content
  • Navigate between dashboard sub-routes — confirm focus lands on the
    main content region each time
  • Visit /discover with a creator that has subscription_price /
    categories set — confirm the card shows the real price and tags
    instead of "Free" / no categories
  • Visit /subscriptions while signed in — confirm the active list
    loads (previously would fail auth silently)
  • Visit /subscriptions with an expired/missing token — confirm the
    "Sign in required" state appears instead of a blank "No
    subscriptions found"

Changes

Test Plan

Automated tests added or updated

  • Unit tests (backend/src/**/*.spec.ts) — service/guard/decorator logic in isolation
  • Integration / e2e tests (backend/test/**/*.e2e-spec.ts) — HTTP round-trips with mocked infrastructure
  • Frontend component tests (frontend/src/**/*.test.{ts,tsx}) — React component behaviour
  • Frontend e2e tests (frontend/e2e/**/*.spec.ts) — Playwright browser flows
  • Contract tests (contract/) — Soroban/Rust unit tests via cargo test
  • No new tests required — explain why: ___

How to run the tests locally

# Backend unit tests
cd backend && npm test

# Backend e2e tests (requires no live DB — uses in-memory mocks)
cd backend && npm run test:e2e

# Frontend component tests
cd frontend && npx vitest run

# Frontend e2e tests (requires dev server on :3000 and API on :3001)
cd frontend && npx playwright test

# Contract tests
cd contract && cargo test

Manual verification checklist

  • Happy path works end-to-end in a local environment
  • Error / edge cases handled gracefully (stale state, invalid input, disconnected wallet)
  • No regressions in closely related API or UI flows
  • Rate-limiting, auth guards, and feature flags behave as expected where touched
  • Linting passes: cd backend && npm run lint / cd frontend && npm run lint

Related issues

Notes for reviewers

Closes #1489
Closes #1490
Closes #1491

The creator dashboard shell had no skip navigation and never moved focus
on route change, forcing keyboard/screen-reader users to tab through the
full sidebar on every navigation. Add a "Skip to main content" link,
give the main landmark a focusable id, and focus it whenever the route
changes. Icon-only controls already had aria-labels; add a test covering
the skip link, focus handoff, and labeled controls.
publicCreatorToProfile hardcoded subscriptionPrice to 0 and categories
to [] even when the /creators API returned subscription_price, currency,
and categories, making every discovery card look free and uncategorized.
Map these fields when present (parsing the numeric-string price safely)
and keep defaulting to 0/[] only when they're actually absent or
unparsable. Add tests for the present, absent, and malformed cases.
… list

The active-subscriptions fetch called a bare relative path with no
Authorization header, so it silently depended on the dev-only Next
rewrite and failed auth against the JWT-bearer backend, always falling
back to a plain "no subscriptions" empty state on error or 401. Add a
shared fetchActiveSubscriptions client (absolute API base, Bearer token,
field-normalizing) mirroring the existing profile.ts client, wire the
page to it, and surface a distinct sign-in-required / load-failed state
so failures are never mistaken for a genuinely empty list.
@drips-wave

drips-wave Bot commented Jul 27, 2026

Copy link
Copy Markdown

@OxDev-max Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@aji70
aji70 merged commit b985892 into MyFanss:main Jul 27, 2026
2 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants