diff --git a/apps/web/src/pages/Clients.tsx b/apps/web/src/pages/Clients.tsx
index 50df051..242d33a 100644
--- a/apps/web/src/pages/Clients.tsx
+++ b/apps/web/src/pages/Clients.tsx
@@ -360,6 +360,12 @@ export function ClientsPage() {
)}
+
+ 👁 View as Customer
+
diff --git a/apps/web/src/portal/CustomerPortal.tsx b/apps/web/src/portal/CustomerPortal.tsx
index e70543a..bb77072 100644
--- a/apps/web/src/portal/CustomerPortal.tsx
+++ b/apps/web/src/portal/CustomerPortal.tsx
@@ -1,4 +1,4 @@
-import { useState, useReducer, useCallback } from "react";
+import { useState, useReducer, useCallback, useEffect } from "react";
import {
Home, Calendar, PawPrint, FileText, CreditCard, MessageSquare,
Settings, Eye, LogOut, Clock, Shield,
@@ -101,6 +101,27 @@ export function CustomerPortal() {
const [impersonation, dispatchImpersonation] = useReducer(impersonationReducer, null);
const { branding } = useBranding();
+ // Auto-start impersonation from URL params (staff flow from admin panel).
+ // Runs once on mount only — impersonation state is managed by the reducer after init.
+ const [impersonationInitDone, setImpersonationInitDone] = useState(false);
+ useEffect(() => {
+ if (impersonationInitDone) return;
+ const params = new URLSearchParams(window.location.search);
+ if (params.get("impersonate") === "true") {
+ const clientName = params.get("clientName") || "Unknown Customer";
+ const reason = params.get("reason") || `Viewing portal as ${clientName}`;
+ const staffName = params.get("staffName") || "Staff";
+ dispatchImpersonation({
+ type: "START",
+ staffName,
+ staffRole: "Admin",
+ reason,
+ });
+ window.history.replaceState({}, "", window.location.pathname);
+ }
+ setImpersonationInitDone(true);
+ }, [impersonationInitDone]);
+
const logPageView = useCallback((page: string) => {
if (impersonation?.active) {
dispatchImpersonation({