78 lines
2.7 KiB
TypeScript
78 lines
2.7 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { NavLink, Navigate, Outlet, useNavigate } from 'react-router-dom';
|
|
import { LogOut } from 'lucide-react';
|
|
import { cn } from '../lib/cn';
|
|
import { useAuth } from '../auth/context';
|
|
import { onAuthErrorAll, orderBookingClient } from '../api/clients';
|
|
import { APP_ID } from '../api/config';
|
|
import { Button } from '../components/buttons';
|
|
import { SCREENS } from './tabs';
|
|
|
|
/** Auth-guarded shell: navy top bar + tab nav + routed <Outlet>. */
|
|
export function ConsoleLayout() {
|
|
const { authed, logout } = useAuth();
|
|
const navigate = useNavigate();
|
|
const user = orderBookingClient.currentUser();
|
|
|
|
// A 401 from any workflow client bounces back to login.
|
|
useEffect(() => {
|
|
onAuthErrorAll(() => {
|
|
logout();
|
|
navigate('/login', { replace: true });
|
|
});
|
|
}, [logout, navigate]);
|
|
|
|
if (!authed) return <Navigate to="/login" replace />;
|
|
|
|
return (
|
|
<div className="min-h-screen bg-app flex flex-col">
|
|
<header className="sticky top-0 z-10 shrink-0 flex items-center justify-between gap-3 px-6 h-[60px] bg-navy-grad border-b border-border-navy">
|
|
<div className="flex flex-col">
|
|
<span className="text-md font-extrabold text-on-navy tracking-[-0.01em] leading-none">Krishna Sales</span>
|
|
<span className="text-2xs text-on-navy-muted">Field Sales · Sandbox {APP_ID}</span>
|
|
</div>
|
|
<nav className="flex items-center gap-1 h-full">
|
|
{SCREENS.map((t) => {
|
|
const Icon = t.icon;
|
|
return (
|
|
<NavLink
|
|
key={t.key}
|
|
to={`/${t.key}`}
|
|
className={({ isActive }) =>
|
|
cn(
|
|
'flex items-center gap-1.5 no-underline font-sans text-sm font-medium px-3 py-1.5 rounded-md transition-all duration-150',
|
|
isActive ? 'bg-white/10 text-white' : 'text-on-navy-muted hover:text-white hover:bg-white/5',
|
|
)
|
|
}
|
|
>
|
|
<Icon size={16} />
|
|
{t.label}
|
|
</NavLink>
|
|
);
|
|
})}
|
|
</nav>
|
|
<div className="flex items-center gap-3">
|
|
{user?.name && <span className="text-sm text-on-navy hidden sm:inline">{user.name}</span>}
|
|
<Button
|
|
variant="secondary"
|
|
size="sm"
|
|
iconLeft={<LogOut size={14} />}
|
|
onClick={() => {
|
|
logout();
|
|
navigate('/login', { replace: true });
|
|
}}
|
|
>
|
|
Sign out
|
|
</Button>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="flex-1 overflow-y-auto p-6">
|
|
<div className="max-w-[1240px] w-full mx-auto flex flex-col gap-5">
|
|
<Outlet />
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|