fix(portal): redirect unauthenticated users to /login
CustomerPortal now redirects to /login after session init completes with no valid session, preventing portal chrome from rendering for unauthenticated users. Dashboard !sessionId branch uses Navigate redirect instead of dead-end UI. Staff redirect in App.tsx verified. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -249,6 +249,11 @@ export function App() {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
||||
// Dev mode: staff users should not land on the customer portal — redirect to admin
|
||||
if (authDisabled && getDevUser()?.type === "staff") {
|
||||
return <Navigate to="/admin" replace />;
|
||||
}
|
||||
|
||||
// Show login BEFORE checking needsSetup (needsSetup is never set for unauthenticated users)
|
||||
if (!authDisabled && !session) {
|
||||
return <LoginPage />;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useCallback, useEffect, useRef } from "react";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { Navigate, useSearchParams } from "react-router-dom";
|
||||
import {
|
||||
Home, Calendar, PawPrint, FileText, CreditCard, MessageSquare,
|
||||
Settings, LogOut, Shield,
|
||||
@@ -37,6 +37,8 @@ export function CustomerPortal() {
|
||||
const [rescheduleAppointment, setRescheduleAppointment] = useState<Record<string, unknown> | null>(null);
|
||||
const [session, setSession] = useState<ImpersonationSession | null>(null);
|
||||
const [sessionExtended, setSessionExtended] = useState(false);
|
||||
const [sessionError, setSessionError] = useState<string | null>(null);
|
||||
const [isInitializing, setIsInitializing] = useState(true);
|
||||
const [clientName, setClientName] = useState<string>("");
|
||||
const { branding } = useBranding();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
@@ -68,7 +70,8 @@ export function CustomerPortal() {
|
||||
})
|
||||
.catch(() => {
|
||||
setSearchParams({}, { replace: true });
|
||||
});
|
||||
})
|
||||
.finally(() => setIsInitializing(false));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -81,16 +84,26 @@ export function CustomerPortal() {
|
||||
body: JSON.stringify({ clientId: devUser.id }),
|
||||
})
|
||||
.then((r) => {
|
||||
if (!r.ok) return null;
|
||||
if (!r.ok) {
|
||||
setSessionError("Failed to create portal session. Please try again.");
|
||||
return null;
|
||||
}
|
||||
return r.json() as Promise<ImpersonationSession>;
|
||||
})
|
||||
.then((s) => {
|
||||
if (s && s.id) {
|
||||
setSession(s);
|
||||
setClientName(devUser.name);
|
||||
setSessionError(null);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch(() => {
|
||||
setSessionError("Failed to connect. Please check your connection and try again.");
|
||||
})
|
||||
.finally(() => setIsInitializing(false));
|
||||
} else {
|
||||
// No sessionId param and no dev user — init is complete with no session
|
||||
setIsInitializing(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -168,6 +181,11 @@ export function CustomerPortal() {
|
||||
|
||||
const avatarInitials = (clientName.split(" ")[0] || "G").charAt(0).toUpperCase();
|
||||
|
||||
// Redirect to login if init is complete and no valid session exists
|
||||
if (!isInitializing && !session) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="min-h-screen bg-[#faf8f5] font-sans"
|
||||
@@ -314,6 +332,11 @@ export function CustomerPortal() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 md:p-8 max-w-6xl">
|
||||
{sessionError && (
|
||||
<div className="bg-red-50 border border-red-200 rounded-2xl p-4 mb-6">
|
||||
<p className="text-red-700 text-sm">{sessionError}</p>
|
||||
</div>
|
||||
)}
|
||||
{renderSection()}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { Calendar, Clock, PawPrint, CreditCard, Star, ChevronRight, AlertTriangle } from "lucide-react";
|
||||
|
||||
interface DashboardProps {
|
||||
@@ -183,13 +184,7 @@ export function Dashboard({
|
||||
}
|
||||
|
||||
if (!sessionId) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="bg-stone-100 rounded-2xl p-5 text-center">
|
||||
<p className="text-stone-600">Please sign in to view your dashboard.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
||||
const upcomingAppointments = getUpcomingAppointments();
|
||||
|
||||
Reference in New Issue
Block a user