fix: add auth guard redirect and wire logout into NavAccountCard

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-16 16:11:59 +05:30
parent 3b68605561
commit 8b796bf916
4 changed files with 46 additions and 21 deletions

View File

@@ -44,8 +44,9 @@ const placeholderAccounts: NavAccountType[] = [
export const NavAccountMenu = ({ export const NavAccountMenu = ({
className, className,
selectedAccountId = "olivia", selectedAccountId = "olivia",
onSignOut,
...dialogProps ...dialogProps
}: AriaDialogProps & { className?: string; accounts?: NavAccountType[]; selectedAccountId?: string }) => { }: AriaDialogProps & { className?: string; accounts?: NavAccountType[]; selectedAccountId?: string; onSignOut?: () => void }) => {
const focusManager = useFocusManager(); const focusManager = useFocusManager();
const dialogRef = useRef<HTMLDivElement>(null); const dialogRef = useRef<HTMLDivElement>(null);
@@ -115,7 +116,7 @@ export const NavAccountMenu = ({
</div> </div>
<div className="pt-1 pb-1.5"> <div className="pt-1 pb-1.5">
<NavAccountCardMenuItem label="Sign out" icon={LogOut01} shortcut="⌥⇧Q" /> <NavAccountCardMenuItem label="Sign out" icon={LogOut01} shortcut="⌥⇧Q" onClick={onSignOut} />
</div> </div>
</AriaDialog> </AriaDialog>
); );
@@ -156,10 +157,12 @@ export const NavAccountCard = ({
popoverPlacement, popoverPlacement,
selectedAccountId = "olivia", selectedAccountId = "olivia",
items = placeholderAccounts, items = placeholderAccounts,
onSignOut,
}: { }: {
popoverPlacement?: Placement; popoverPlacement?: Placement;
selectedAccountId?: string; selectedAccountId?: string;
items?: NavAccountType[]; items?: NavAccountType[];
onSignOut?: () => void;
}) => { }) => {
const triggerRef = useRef<HTMLDivElement>(null); const triggerRef = useRef<HTMLDivElement>(null);
const isDesktop = useBreakpoint("lg"); const isDesktop = useBreakpoint("lg");
@@ -200,7 +203,7 @@ export const NavAccountCard = ({
) )
} }
> >
<NavAccountMenu selectedAccountId={selectedAccountId} accounts={items} /> <NavAccountMenu selectedAccountId={selectedAccountId} accounts={items} onSignOut={onSignOut} />
</AriaPopover> </AriaPopover>
</AriaDialogTrigger> </AriaDialogTrigger>
</div> </div>

View File

@@ -0,0 +1,12 @@
import { Navigate, Outlet } from 'react-router';
import { useAuth } from '@/providers/auth-provider';
export const AuthGuard = () => {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return <Outlet />;
};

View File

@@ -8,13 +8,12 @@ import {
faGrid2, faGrid2,
faPlug, faPlug,
} from "@fortawesome/pro-regular-svg-icons"; } from "@fortawesome/pro-regular-svg-icons";
import { useNavigate } from "react-router";
import { MobileNavigationHeader } from "@/components/application/app-navigation/base-components/mobile-header"; import { MobileNavigationHeader } from "@/components/application/app-navigation/base-components/mobile-header";
import { NavAccountCard } from "@/components/application/app-navigation/base-components/nav-account-card"; import { NavAccountCard } from "@/components/application/app-navigation/base-components/nav-account-card";
import { NavItemBase } from "@/components/application/app-navigation/base-components/nav-item"; import { NavItemBase } from "@/components/application/app-navigation/base-components/nav-item";
import type { NavItemType } from "@/components/application/app-navigation/config"; import type { NavItemType } from "@/components/application/app-navigation/config";
import { useAuth } from "@/providers/auth-provider";
// TODO: Wire to useAuth() once auth-provider.tsx is implemented
const isAdmin = false;
const MAIN_SIDEBAR_WIDTH = 292; const MAIN_SIDEBAR_WIDTH = 292;
@@ -58,10 +57,18 @@ interface SidebarProps {
} }
export const Sidebar = ({ activeUrl = "/" }: SidebarProps) => { export const Sidebar = ({ activeUrl = "/" }: SidebarProps) => {
const { logout, isAdmin: authIsAdmin, user } = useAuth();
const navigate = useNavigate();
const handleSignOut = () => {
logout();
navigate('/login');
};
const navSections = [ const navSections = [
{ label: "Main", items: mainItems }, { label: "Main", items: mainItems },
{ label: "Insights", items: insightsItems }, { label: "Insights", items: insightsItems },
...(isAdmin ? [{ label: "Admin", items: adminItems }] : []), ...(authIsAdmin ? [{ label: "Admin", items: adminItems }] : []),
]; ];
const content = ( const content = (
@@ -103,7 +110,7 @@ export const Sidebar = ({ activeUrl = "/" }: SidebarProps) => {
{/* Account card */} {/* Account card */}
<div className="mt-auto flex flex-col gap-5 px-2 py-4 lg:gap-6 lg:px-4 lg:py-4"> <div className="mt-auto flex flex-col gap-5 px-2 py-4 lg:gap-6 lg:px-4 lg:py-4">
<NavAccountCard /> <NavAccountCard onSignOut={handleSignOut} />
</div> </div>
</aside> </aside>
); );

View File

@@ -2,6 +2,7 @@ import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { BrowserRouter, Outlet, Route, Routes } from "react-router"; import { BrowserRouter, Outlet, Route, Routes } from "react-router";
import { AppShell } from "@/components/layout/app-shell"; import { AppShell } from "@/components/layout/app-shell";
import { AuthGuard } from "@/components/layout/auth-guard";
import { NotFound } from "@/pages/not-found"; import { NotFound } from "@/pages/not-found";
import { AllLeadsPage } from "@/pages/all-leads"; import { AllLeadsPage } from "@/pages/all-leads";
import { CampaignDetailPage } from "@/pages/campaign-detail"; import { CampaignDetailPage } from "@/pages/campaign-detail";
@@ -24,6 +25,7 @@ createRoot(document.getElementById("root")!).render(
<RouteProvider> <RouteProvider>
<Routes> <Routes>
<Route path="/login" element={<LoginPage />} /> <Route path="/login" element={<LoginPage />} />
<Route element={<AuthGuard />}>
<Route <Route
element={ element={
<AppShell> <AppShell>
@@ -38,6 +40,7 @@ createRoot(document.getElementById("root")!).render(
<Route path="/outreach" element={<OutreachPage />} /> <Route path="/outreach" element={<OutreachPage />} />
<Route path="*" element={<NotFound />} /> <Route path="*" element={<NotFound />} />
</Route> </Route>
</Route>
</Routes> </Routes>
</RouteProvider> </RouteProvider>
</BrowserRouter> </BrowserRouter>