Promote dev → uat: GRO-2373 in-portal chrome sign-out button #78

Merged
Flea Flicker merged 14 commits from promote/GRO-2373-dev-to-uat into uat 2026-06-11 22:03:10 +00:00
Member

Summary

Promotes the GRO-2373 fix from dev to uat.

GRO-2373 is a pre-existing UAT defect discovered during the GRO-2370 regression run: the customer portal chrome (Home, Appointments, My Pets, Report Cards, Billing, Messages, Settings) had no visible sign-out control. The CMPO ruling for GRO-2355 required the logout control to be reachable from the OOBE screen, the in-portal screen, and the deep-link deleted-portal screen — GRO-2358 (P1) covered no-access + OOBE but missed the in-portal chrome.

What changes

  • src/portal/CustomerPortal.tsx: new Sign out button in the sidebar footer (above the "Customer Portal v1.0" version label, alongside End Impersonation). Always visible to authenticated customers. Wires to the existing handleSignOut() → canonical signOut() from lib/auth-client (same handler as OOBE, no-access card, AdminLayout).
  • src/__tests__/portal.test.tsx: new test renders CustomerPortal with a successful SSO bridge, lands on the chrome, clicks portal-chrome-signout, and asserts the shared signOutSpy fires + window.location.href/login.
  • UAT_PLAYBOOK.md: §5.25.6f added — "In-portal chrome sidebar exposes a Sign out button (GRO-2373)" with the full reachability assertion (visible on every portal sub-route, same shared signOut() as OOBE / no-access / AdminLayout, 200 from /api/auth/sign-out, navigates to /login, clears Authentik cookie).

Test verification

  • 30/30 portal tests pass (29 prior + 1 new GRO-2373 test).
  • Lint: clean.
  • Typecheck: only pre-existing admin-side errors (RouteMap / Routes missing devDeps — unrelated to this PR).

Conflict resolution

The promotion merge from dev to uat produced conflicts in UAT_PLAYBOOK.md and src/__tests__/portal.test.tsx (both had additive-only changes — the GRO-2359 promote landed 5.25.6e on uat; the GRO-2373 work added 5.25.6f and the new test on dev). Both files were resolved by taking the dev side (the union of uat's prior additions plus dev's new additions), per the dev-to-uat playbook union-merge pattern (GRO-2156 #182).

Why this is NOT a GRO-2359 regression

GRO-2359 (OOBE portal-creation routing) does not modify the in-portal chrome. The missing button is a pre-existing gap; the OOBE and no-access logouts are still functional. GRO-2373 is purely a chrome-layout fix.

Hand-off

  • QA (Lint Roller) — please review the diff and the new test in the bundle.
  • UAT (Shedward Scissorhands) — separate regression task will be created after this PR merges, per the pre-merge diff gate vs post-deploy UAT regression distinction (GRO-2214/GRO-2239).

🤖 Generated with Claude Code

Co-Authored-By: Paperclip noreply@paperclip.ing

## Summary Promotes the GRO-2373 fix from `dev` to `uat`. GRO-2373 is a pre-existing UAT defect discovered during the GRO-2370 regression run: the customer portal chrome (Home, Appointments, My Pets, Report Cards, Billing, Messages, Settings) had no visible sign-out control. The CMPO ruling for GRO-2355 required the logout control to be reachable from the OOBE screen, the in-portal screen, and the deep-link deleted-portal screen — GRO-2358 (P1) covered no-access + OOBE but missed the in-portal chrome. ## What changes - `src/portal/CustomerPortal.tsx`: new `Sign out` button in the sidebar footer (above the "Customer Portal v1.0" version label, alongside `End Impersonation`). Always visible to authenticated customers. Wires to the existing `handleSignOut()` → canonical `signOut()` from `lib/auth-client` (same handler as OOBE, no-access card, `AdminLayout`). - `src/__tests__/portal.test.tsx`: new test renders `CustomerPortal` with a successful SSO bridge, lands on the chrome, clicks `portal-chrome-signout`, and asserts the shared `signOutSpy` fires + `window.location.href` → `/login`. - `UAT_PLAYBOOK.md`: §5.25.6f added — "In-portal chrome sidebar exposes a Sign out button (GRO-2373)" with the full reachability assertion (visible on every portal sub-route, same shared `signOut()` as OOBE / no-access / AdminLayout, 200 from `/api/auth/sign-out`, navigates to `/login`, clears Authentik cookie). ## Test verification - 30/30 portal tests pass (29 prior + 1 new GRO-2373 test). - Lint: clean. - Typecheck: only pre-existing admin-side errors (RouteMap / Routes missing devDeps — unrelated to this PR). ## Conflict resolution The promotion merge from `dev` to `uat` produced conflicts in `UAT_PLAYBOOK.md` and `src/__tests__/portal.test.tsx` (both had additive-only changes — the GRO-2359 promote landed 5.25.6e on uat; the GRO-2373 work added 5.25.6f and the new test on dev). Both files were resolved by taking the dev side (the union of uat's prior additions plus dev's new additions), per the dev-to-uat playbook union-merge pattern (GRO-2156 #182). ## Why this is NOT a GRO-2359 regression GRO-2359 (OOBE portal-creation routing) does not modify the in-portal chrome. The missing button is a pre-existing gap; the OOBE and no-access logouts are still functional. GRO-2373 is purely a chrome-layout fix. ## Hand-off - **QA** ([Lint Roller](/agents/525c2c39-1196-4682-9cd1-0bcfcb0d0f31)) — please review the diff and the new test in the bundle. - **UAT** ([Shedward Scissorhands](/agents/c24bab42-4a3c-4a80-b4df-425eeb77088f)) — separate regression task will be created after this PR merges, per the pre-merge diff gate vs post-deploy UAT regression distinction (GRO-2214/GRO-2239). --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Paperclip <noreply@paperclip.ing>
Flea Flicker added 14 commits 2026-06-11 18:27:21 +00:00
fix(portal): send preferredTime as HH:MM:SS and format booking slot labels (GRO-2211) (#51)
CI / Test (push) Successful in 18s
CI / Lint & Typecheck (push) Successful in 25s
CI / Build & Push Docker Image (push) Successful in 12s
CI / Test (pull_request) Successful in 22s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 12s
0d52ddd9f0
docs(uat): add §5.12e Book New preferredTime test cases (GRO-2218) (#53)
CI / Test (push) Failing after 6s
CI / Lint & Typecheck (push) Successful in 21s
CI / Build & Push Docker Image (push) Has been skipped
CI / Test (pull_request) Successful in 18s
CI / Lint & Typecheck (pull_request) Successful in 27s
CI / Build & Push Docker Image (pull_request) Successful in 11s
c7417dc9e3
fix(portal): show Weight/DoB + Size Category in pet read view (GRO-2207) (#54)
CI / Test (push) Successful in 22s
CI / Test (pull_request) Successful in 18s
CI / Lint & Typecheck (pull_request) Successful in 34s
CI / Build & Push Docker Image (pull_request) Successful in 15s
CI / Lint & Typecheck (push) Failing after 12m56s
CI / Build & Push Docker Image (push) Has been skipped
3d0c3c551b
fix(GRO-2234): transparent re-mint on 401 for portal Book New submit (#55)
CI / Test (push) Successful in 23s
CI / Lint & Typecheck (push) Successful in 31s
CI / Build & Push Docker Image (push) Successful in 16s
1ceac35437
fix(GRO-2236): portal Book New service cards show price + duration (#57)
CI / Test (push) Successful in 18s
CI / Lint & Typecheck (push) Successful in 28s
CI / Build & Push Docker Image (push) Successful in 14s
CI / Test (pull_request) Successful in 24s
CI / Lint & Typecheck (pull_request) Successful in 31s
CI / Build & Push Docker Image (pull_request) Successful in 10s
98c8a7bb83
Co-authored-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
Co-committed-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
feat(GRO-2158): route planner page at /admin/routes (#60)
CI / Test (push) Successful in 24s
CI / Lint & Typecheck (push) Successful in 31s
CI / Build & Push Docker Image (push) Successful in 12s
c58e4e4b23
feat(GRO-2159): drag-to-reorder + re-optimize on route planner (#63)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 28s
CI / Build & Push Docker Image (push) Successful in 11s
59a29a2d03
feat(GRO-2160): route nav export buttons + offline map polish (#66)
CI / Test (push) Successful in 20s
CI / Lint & Typecheck (push) Successful in 25s
CI / Build & Push Docker Image (push) Successful in 11s
044eeaae61
feat(GRO-2319): live-render full StatusBadge palette in portal (#69)
CI / Test (push) Successful in 23s
CI / Lint & Typecheck (push) Successful in 26s
CI / Build & Push Docker Image (push) Successful in 13s
66bac2c6f8
fix(GRO-2358): wire signOut() at higher layer for no-access screen (#72)
CI / Test (push) Successful in 17s
CI / Lint & Typecheck (push) Successful in 23s
CI / Build & Push Docker Image (push) Successful in 40s
dee7465190
feat(GRO-2359): route Authentik new-SSO users into OOBE (web)
CI / Test (pull_request) Successful in 25s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 44s
250c7a5ac9
The post-auth handler in CustomerPortal previously rendered the
"Portal access not configured" card when the SSO bridge returned 404
(no client row for the user's email). That trapped first-time SSO
users on a dead-end screen with no path to portal creation.

This change routes the 404 to a new OOBE component (src/portal/OOBE.tsx)
that drives portal creation:
  * Mounts inline inside CustomerPortal so the post-auth flow stays
    inside the portal render tree (no App-level router needed).
  * Also reachable as a direct deep-link via the new /onboarding route
    in App.tsx (for grooming admins or recovery flows).
  * Submits to a new POST /api/portal/clients-from-auth endpoint in
    groombook-api (companion commit) that creates a fresh client row
    bound to the Better Auth email. 409 means the email already has a
    portal record — the OOBE shows a portal-selection message.
  * Uses the canonical shared signOut() from lib/auth-client (GRO-2358
    invariant) for the Sign out button.
  * Full window.location.href reload on submit success to reset the
    bridge's cached state and land the user in their portal.

The no-access card itself is preserved for the deep-link deleted-portal
case (a customer whose portal was disabled/deleted), signalled via
?noAccess=deleted-portal on a portal sub-route. The OOBE handles the
first-time-creation case; the no-access card handles the "had a portal
but lost it" case.

Test coverage:
  * "routes to /onboarding when session-from-auth returns 404 (GRO-2359)"
    — proves the post-auth 404 mounts the OOBE inline, not the legacy
    no-access card.
  * 6 new OOBE tests: render from direct link, name prefill, form
    submission, 409 portal-selection, required-name validation, shared
    signOut(), redirect on no-session.
  * P1 no-access tests reworked to use ?noAccess=deleted-portal so the
    GRO-2358 signOut invariant is still verified on the only surviving
    path to the no-access card.

UAT_PLAYBOOK §5.25.5–6e rewritten to cover the OOBE flow (form submit,
409, deep-link mount, deleted-portal no-access card).

Paired with the api PR on feature/2357-p2-portal-clients-from-auth.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Merge pull request 'feat(GRO-2359): route Authentik new-SSO users into OOBE' (#75) from feature/2357-p2-sso-to-oobe-routing into dev
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Image (push) Successful in 18s
7a8b59ab87
GRO-2359 (web): feat(GRO-2359): route Authentik new-SSO users into OOBE (#75)
fix(GRO-2373): add Sign out button to in-portal chrome sidebar (#77)
CI / Test (push) Successful in 35s
CI / Lint & Typecheck (push) Successful in 48s
CI / Build & Push Docker Image (push) Successful in 13s
ddc4e3e052
Promote dev → uat: GRO-2373 in-portal chrome sign-out button
CI / Test (pull_request) Successful in 22s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 15s
acf69ae830
Lint Roller approved these changes 2026-06-11 18:31:11 +00:00
Lint Roller left a comment
Member

QA approved: all acceptance criteria verified.

CustomerPortal.tsx — Sign out button in sidebar footer (data-testid=portal-chrome-signout), present on every sub-route. Calls the canonical signOut() from lib/auth-client then sets window.location.href to /login — same pattern as OOBE, no-access card, and AdminLayout. No raw fetch("/api/auth/sign-out").

portal.test.tsx — GRO-2373 test renders the authenticated chrome via SSO bridge, locates the button by testid, asserts signOutSpy fires once and window.location.href -> /login. All prior tests preserved.

UAT_PLAYBOOK.md — §5.25.6f added; §5.25.6a–e preserved; conflict-resolution union is correct.

CI — Lint & Typecheck: success, Test: success, Build & Push: success.

QA approved: all acceptance criteria verified. CustomerPortal.tsx — Sign out button in sidebar footer (data-testid=portal-chrome-signout), present on every sub-route. Calls the canonical signOut() from lib/auth-client then sets window.location.href to /login — same pattern as OOBE, no-access card, and AdminLayout. No raw fetch("/api/auth/sign-out"). portal.test.tsx — GRO-2373 test renders the authenticated chrome via SSO bridge, locates the button by testid, asserts signOutSpy fires once and window.location.href -> /login. All prior tests preserved. UAT_PLAYBOOK.md — §5.25.6f added; §5.25.6a–e preserved; conflict-resolution union is correct. CI — Lint & Typecheck: success, Test: success, Build & Push: success.
Flea Flicker merged commit 532869f926 into uat 2026-06-11 22:03:10 +00:00
Sign in to join this conversation.