diff --git a/apps/web/src/portal/CustomerPortal.tsx b/apps/web/src/portal/CustomerPortal.tsx
index 2a5d424..2547664 100644
--- a/apps/web/src/portal/CustomerPortal.tsx
+++ b/apps/web/src/portal/CustomerPortal.tsx
@@ -42,6 +42,9 @@ export function CustomerPortal() {
// Track whether we've attempted to fetch a session — used to prevent premature redirect
// when a session fetch is in-flight (E2E mocks resolve synchronously, batched with setInitComplete)
const [sessionAttempted, setSessionAttempted] = useState(false);
+ // Track whether an impersonation session fetch from URL param is in-flight
+ // Dashboard will not redirect while this is true, allowing the session to load
+ const [isImpersonating, setIsImpersonating] = useState(false);
const { branding } = useBranding();
const [searchParams, setSearchParams] = useSearchParams();
@@ -54,6 +57,7 @@ export function CustomerPortal() {
const sessionId = searchParams.get("sessionId");
if (sessionId) {
+ setIsImpersonating(true);
// Real impersonation session from URL param
fetch(`/api/impersonation/sessions/${sessionId}`)
.then((r) => {
@@ -75,7 +79,7 @@ export function CustomerPortal() {
setSessionAttempted(true);
setSearchParams({}, { replace: true });
})
- .finally(() => setInitComplete(true));
+ .finally(() => { setInitComplete(true); setIsImpersonating(false); });
return;
}
@@ -162,7 +166,7 @@ export function CustomerPortal() {
const sessionId = session?.id ?? null;
switch (activeSection) {
case "dashboard":
- return ;
+ return ;
case "appointments":
return ;
case "pets":
diff --git a/apps/web/src/portal/sections/Dashboard.tsx b/apps/web/src/portal/sections/Dashboard.tsx
index f252e8a..bf6f0e4 100644
--- a/apps/web/src/portal/sections/Dashboard.tsx
+++ b/apps/web/src/portal/sections/Dashboard.tsx
@@ -8,6 +8,8 @@ interface DashboardProps {
onNavigate: (section: "appointments" | "pets" | "billing" | "reports") => void;
readOnly: boolean;
onReschedule: (appointmentId: string) => void;
+ /** True when a sessionId param was in the URL and the session is still loading */
+ isImpersonating?: boolean;
}
interface Appointment {
@@ -73,6 +75,7 @@ export function Dashboard({
onNavigate,
readOnly,
onReschedule,
+ isImpersonating,
}: DashboardProps) {
const [appointments, setAppointments] = useState([]);
const [pets, setPets] = useState([]);
@@ -183,7 +186,7 @@ export function Dashboard({
);
}
- if (!sessionId) {
+ if (!sessionId && !isImpersonating) {
return ;
}