krishnasales_mobile/src/screens/ConsoleLayout.tsx
Bhanu Prakash Sai Potteri b85775a413 Modernize mobile UI
- StatsTiles: on-brand compact tiles (token accents) vs generic gradients
- RecordView: pill search, filter chips, elevated list cards w/ chevron
- Bottom nav: active sunrise pill + elevation
2026-07-08 11:34:43 +05:30

91 lines
3.4 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 { SCREENS } from './tabs';
/** Auth-guarded shell: slim navy top bar + fixed bottom 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-20 shrink-0 flex items-center justify-between gap-3 px-4 h-14 pt-[env(safe-area-inset-top)] bg-navy-grad shadow-md">
<div className="flex flex-col justify-center leading-none">
<span className="text-base font-extrabold text-on-navy tracking-[-0.01em] leading-none">Krishna Sales</span>
<span className="text-2xs text-on-navy-muted">Sandbox {APP_ID}{user?.name ? ` · ${user.name}` : ''}</span>
</div>
<button
type="button"
aria-label="Sign out"
onClick={() => {
logout();
navigate('/login', { replace: true });
}}
className="inline-flex items-center justify-center size-9 rounded-md text-on-navy-muted hover:text-white hover:bg-white/10 transition-colors duration-150 cursor-pointer"
>
<LogOut size={18} />
</button>
</header>
<main className="flex-1 overflow-y-auto px-4 pt-4 pb-24">
<div className="max-w-[720px] w-full mx-auto flex flex-col gap-5">
<Outlet />
</div>
</main>
<nav
className="fixed bottom-0 inset-x-0 z-20 h-16 pb-[env(safe-area-inset-bottom)] bg-card border-t border-border-subtle grid shadow-[0_-2px_16px_rgba(11,27,59,0.06)]"
style={{ gridTemplateColumns: `repeat(${SCREENS.length}, minmax(0, 1fr))` }}
>
{SCREENS.map((t) => {
const Icon = t.icon;
return (
<NavLink
key={t.key}
to={`/${t.key}`}
className="flex flex-col items-center justify-center gap-1 no-underline min-h-[48px]"
>
{({ isActive }) => (
<>
<span
className={cn(
'flex items-center justify-center h-7 w-14 rounded-pill transition-colors duration-150',
isActive ? 'bg-sunrise-100 text-sunrise-600' : 'text-faint',
)}
>
<Icon size={20} />
</span>
<span
className={cn(
'text-2xs leading-none transition-colors duration-150',
isActive ? 'font-semibold text-sunrise-600' : 'font-medium text-faint',
)}
>
{t.label}
</span>
</>
)}
</NavLink>
);
})}
</nav>
</div>
);
}