feat(portal): replace mock data with real session-driven API calls #152

Merged
groombook-engineer[bot] merged 25 commits from feat/gro-203-rbac-super-user into main 2026-03-29 07:08:35 +00:00
2 changed files with 17 additions and 13 deletions
Showing only changes of commit c91f29d542 - Show all commits
+8 -5
View File
@@ -236,20 +236,23 @@ export function App() {
); );
} }
// Still loading auth state or setup check // Still loading auth state or setup check (skip setup check in dev mode)
if (authDisabled === null || sessionLoading || needsSetup === null) return null; if (authDisabled === null || sessionLoading) return null;
// Dev mode: show login selector // Dev mode: show login selector (no setup check needed in dev mode)
if (authDisabled && location.pathname === "/login") { if (authDisabled && location.pathname === "/login") {
return <DevLoginSelector />; return <DevLoginSelector />;
} }
// Dev mode: use dev login selector // Dev mode: use dev login selector (no setup check needed in dev mode)
if (authDisabled && !getDevUser()) { if (authDisabled && !getDevUser()) {
return <Navigate to="/login" replace />; return <Navigate to="/login" replace />;
} }
// Production mode: if no session, show login page (avoids redirect loops) // Production: need setup check
if (needsSetup === null) return null;
// Production mode: if no session, redirect to Authentik sign-in
if (!authDisabled && !session) { if (!authDisabled && !session) {
return <LoginPage />; return <LoginPage />;
} }
+9 -8
View File
@@ -122,21 +122,22 @@ export function CustomerPortal() {
const isReadOnly = session?.status === "active"; const isReadOnly = session?.status === "active";
const renderSection = () => { const renderSection = () => {
const sessionId = session?.id ?? null;
switch (activeSection) { switch (activeSection) {
case "dashboard": case "dashboard":
return <Dashboard onNavigate={handleNavClick} readOnly={!!isReadOnly} onReschedule={handleReschedule} />; return <Dashboard onNavigate={handleNavClick} readOnly={!!isReadOnly} sessionId={sessionId} clientName={clientName} onReschedule={handleReschedule} />;
case "appointments": case "appointments":
return <AppointmentsSection readOnly={!!isReadOnly} sessionId={session?.id ?? null} />; return <AppointmentsSection readOnly={!!isReadOnly} sessionId={sessionId} />;
case "pets": case "pets":
return <PetProfiles readOnly={!!isReadOnly} />; return <PetProfiles readOnly={!!isReadOnly} sessionId={sessionId} />;
case "reports": case "reports":
return <ReportCards />; return <ReportCards sessionId={sessionId} />;
case "billing": case "billing":
return <BillingPayments readOnly={!!isReadOnly} />; return <BillingPayments readOnly={!!isReadOnly} sessionId={sessionId} />;
case "messages": case "messages":
return <Communication readOnly={!!isReadOnly} />; return <Communication readOnly={!!isReadOnly} sessionId={sessionId} />;
case "settings": case "settings":
return <AccountSettings readOnly={!!isReadOnly} />; return <AccountSettings readOnly={!!isReadOnly} sessionId={sessionId} />;
} }
}; };
@@ -279,7 +280,7 @@ export function CustomerPortal() {
</h1> </h1>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="text-sm text-stone-600">Hi, {CUSTOMER.name.split(" ")[0]}</span> <span className="text-sm text-stone-600">Hi, {clientName.split(" ")[0] || "Guest"}</span>
<div className="w-8 h-8 rounded-full flex items-center justify-center text-white text-sm font-medium" style={{ background: branding.accentColor }}> <div className="w-8 h-8 rounded-full flex items-center justify-center text-white text-sm font-medium" style={{ background: branding.accentColor }}>
SM SM
</div> </div>