fix(GRO-2011): /login renders blank — always fetch /api/setup/status #35

Closed
Flea Flicker wants to merge 2 commits from gro-1867-portal-better-auth into dev

2 Commits

Author SHA1 Message Date
Flea Flicker 3297903d5c fix(GRO-2011): always fetch /api/setup/status, even for unauth users
CI / Test (pull_request) Successful in 19s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Image (pull_request) Successful in 39s
The second useEffect in App skipped the setup/status fetch when
`!authDisabled && !session` was true. In the deployed bundle the
`needsSetup` state therefore stayed `null` for unauth users, and a
later render short-circuit rendered nothing — producing the blank
white viewport at https://uat.groombook.dev/login.

Drop the unauth skip clause so `/api/setup/status` is always fetched
as soon as the auth state is known. The unauth branch in the render
is handled before `needsSetup` is consulted, so this is safe and
removes the stuck-`null` state.

Adds:
- New unit test in src/__tests__/App.test.tsx asserting the
  unauthenticated path calls /api/setup/status.
- UAT playbook entry TC-WEB-5.1.5 covering the blank-viewport
  regression scenario.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-01 16:02:46 +00:00
gb_flea 775fb1594c GRO-1867: bridge Better Auth session to CustomerPortal
CI / Test (pull_request) Successful in 19s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Image (pull_request) Successful in 48s
Adds a third initialisation path to src/portal/CustomerPortal.tsx so real
customers authenticated via Authentik SSO can reach /portal without being
bounced back to /login.

After the existing impersonation (?sessionId=) and dev-mode (localStorage
dev-user) paths, the portal now:

  1. Calls GET /api/auth/get-session (credentials: include) to detect an
     active Better Auth session.
  2. If the user is a non-staff customer, POSTs /api/portal/session-from-auth
     (the endpoint shipped by GRO-1866) to mint a portal session.
  3. Stores the returned sessionId in portalSessionId state and threads it
     through renderSection -> sections so all /api/portal/* calls include
     the X-Impersonation-Session-Id header.
  4. On 404 (no client row), renders a friendly "Portal access not
     configured" card with a Sign out button instead of looping back to
     /login. On 401/network error, falls through to the existing /login
     redirect guard.

The bridge skips when impersonation or dev-user is active and when the
Better Auth user is staff (App.tsx already routes staff to /admin). The
impersonation banner remains gated on session?.status === "active", so the
SSO-bridged session does not show staff chrome.

Tests:
  - 4 new vitest cases in src/__tests__/portal.test.tsx cover the success,
    404 fallback, missing-Better-Auth-session, and staff-role paths.
  - pnpm vitest run src/__tests__/portal.test.tsx -> 18 passed
  - pnpm typecheck -> clean

UAT_PLAYBOOK.md: adds §5.25 (TC-WEB-5.25.1 - TC-WEB-5.25.11) covering the
new flow end-to-end on UAT.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-01 15:44:11 +00:00