Promote uat → main (PROD): GRO-2358 logout on no-access screen (#74)
Promote uat → main (PROD): GRO-2358 — restore logout on 'Portal access not configured' screen. Squashed from uat-to-main/GRO-2358 (0d24fe0). Cherry-pick of validated uat squashbfe3ccf. Pre-merge gates green: CI (Lint+Typecheck 30s, Test 23s, Docker Build 11s); CTO Gitea review APPROVED (comment 13465); QA GRO-2362 done; UAT GRO-2363 4/4 PASS on git.farh.net/groombook/web:2026.06.11-bfe3ccf; Security GRO-2364 cleared. Head branch uat-to-main/GRO-2358 retained for Flea's post-deploy verification. Refs GRO-2358, GRO-2362, GRO-2363, GRO-2364, GRO-2367. Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net> Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
This commit was merged in pull request #74.
This commit is contained in:
@@ -15,6 +15,7 @@ import { ImpersonationBanner } from "./ImpersonationBanner.js";
|
||||
import { AuditLogViewer } from "./AuditLogViewer.js";
|
||||
import { useBranding } from "../BrandingContext.js";
|
||||
import { getDevUser } from "../pages/DevLoginSelector.js";
|
||||
import { signOut } from "../lib/auth-client.js";
|
||||
import type { ImpersonationSession } from "@groombook/types";
|
||||
import type { Appointment as PortalAppointment } from "./sections/Appointments.js";
|
||||
|
||||
@@ -193,6 +194,19 @@ export function CustomerPortal() {
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
// Shared sign-out handler — wires the canonical Better Auth `signOut()` so
|
||||
// every authenticated surface (no-access screen, portal chrome, etc.) uses
|
||||
// the same implementation as `AdminLayout`. Failure to reach the server
|
||||
// still leaves the SPA free to navigate to /login.
|
||||
const handleSignOut = useCallback(async () => {
|
||||
try {
|
||||
await signOut();
|
||||
} catch {
|
||||
// Best-effort; navigate to /login regardless so the user is never trapped.
|
||||
}
|
||||
window.location.href = "/login";
|
||||
}, []);
|
||||
|
||||
const logPageView = useCallback((page: string) => {
|
||||
if (!session) return;
|
||||
void fetch(`/api/impersonation/sessions/${session.id}/log`, {
|
||||
@@ -281,14 +295,7 @@ export function CustomerPortal() {
|
||||
<h1 className="text-lg font-semibold text-stone-800 mb-2">Portal access not configured</h1>
|
||||
<p className="text-sm text-stone-600 mb-6">{authError}</p>
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
await fetch("/api/auth/sign-out", { method: "POST", credentials: "include" });
|
||||
} catch {
|
||||
// Best-effort sign-out; redirect to /login regardless.
|
||||
}
|
||||
window.location.href = "/login";
|
||||
}}
|
||||
onClick={() => { void handleSignOut(); }}
|
||||
className="inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg text-sm font-medium text-stone-700 bg-stone-100 hover:bg-stone-200 transition-colors"
|
||||
>
|
||||
<LogOut size={14} />
|
||||
|
||||
Reference in New Issue
Block a user