143 lines
7.8 KiB
TypeScript
143 lines
7.8 KiB
TypeScript
import { useState } from 'react';
|
|
import { Outlet, useLocation } from 'react-router-dom';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { useAuth } from '../context/AuthContext';
|
|
import { Sidebar } from './layout/Sidebar';
|
|
import { MobileNav } from './layout/MobileNav';
|
|
import { MobileNavSheet } from './layout/MobileNavSheet';
|
|
import { CommandPalette } from './ui/CommandPalette';
|
|
import { SessionTimeoutWarning } from './ui/SessionTimeoutWarning';
|
|
import { PageTitleUpdater } from '../hooks/usePageTitle';
|
|
import AnnouncementBanner from './AnnouncementBanner';
|
|
import { DevTools } from './dev/DevTools';
|
|
import { Breadcrumbs } from './ui/Breadcrumbs';
|
|
import { pageVariants } from '../lib/animations';
|
|
import { Search, Bell, Settings, Filter, ChevronDown } from 'lucide-react';
|
|
import ThemeToggle from './ThemeToggle';
|
|
import { UserMenu } from './layout/UserMenu';
|
|
import { NotificationBell } from './notifications/NotificationBell';
|
|
|
|
export default function Layout() {
|
|
const location = useLocation();
|
|
const [mobileSheetOpen, setMobileSheetOpen] = useState(false);
|
|
|
|
return (
|
|
<div className="flex h-screen bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] overflow-hidden font-sans">
|
|
{/* Accessibility: Skip to main content */}
|
|
<a
|
|
href="#main-content"
|
|
className="absolute left-0 top-0 -translate-y-full bg-[var(--color-primary)] text-white px-4 py-2 rounded-br-lg font-medium focus:translate-y-0 z-50 transition-transform duration-fast"
|
|
>
|
|
Skip to main content
|
|
</a>
|
|
|
|
{/* Desktop Sidebar - Persistent on left */}
|
|
<aside className="hidden lg:flex flex-col w-[260px] border-r border-[var(--color-border-subtle)] bg-[var(--color-bg-secondary)] z-30">
|
|
<div className="h-16 flex items-center px-6 border-b border-[var(--color-border-subtle)]">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-9 h-9 rounded-xl bg-[var(--color-primary)] flex items-center justify-center text-[var(--color-text-inverse)] font-bold text-lg shadow-lg shadow-emerald-500/20">
|
|
V
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<span className="text-sm font-bold tracking-tight leading-none text-[var(--color-text-primary)]">Veridian</span>
|
|
<span className="text-[10px] text-[var(--color-text-tertiary)] font-medium uppercase tracking-wider leading-none mt-0.5">Cultivation Platform</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex-1 overflow-y-auto pt-4">
|
|
<Sidebar />
|
|
</div>
|
|
<div className="p-4 border-t border-[var(--color-border-subtle)]">
|
|
<UserMenu />
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Main Content Area */}
|
|
<div className="flex-1 flex flex-col min-w-0 overflow-hidden relative">
|
|
{/* Topbar - Search, Global Filters, Vitals */}
|
|
<header className="h-16 flex items-center justify-between px-4 sm:px-6 lg:px-8 border-b border-[var(--color-border-subtle)] bg-[var(--color-bg-secondary)]/80 backdrop-blur-xl z-20">
|
|
<div className="flex items-center gap-4 flex-1">
|
|
{/* Facility Switcher / Filter */}
|
|
<div className="hidden md:flex items-center gap-2 px-3 py-1.5 rounded-full bg-[var(--color-bg-tertiary)] border border-[var(--color-border-subtle)] cursor-pointer hover:border-[var(--color-border-default)] transition-all">
|
|
<span className="text-xs font-semibold uppercase tracking-wider text-[var(--color-text-tertiary)]">Facility</span>
|
|
<span className="text-xs font-bold text-[var(--color-text-primary)]">NORCAL-01</span>
|
|
<ChevronDown size={14} className="text-[var(--color-text-tertiary)]" />
|
|
</div>
|
|
|
|
{/* Global Search */}
|
|
<div className="relative group max-w-sm w-full hidden sm:block">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-[var(--color-text-tertiary)] group-focus-within:text-[var(--color-primary)] transition-colors" size={16} />
|
|
<input
|
|
type="text"
|
|
placeholder="Search batches, rooms, tasks..."
|
|
className="w-full bg-[var(--color-bg-tertiary)] border border-[var(--color-border-subtle)] rounded-full pl-10 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)]/20 focus:border-[var(--color-primary)] transition-all"
|
|
/>
|
|
<div className="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1 opacity-50">
|
|
<kbd className="text-[10px] font-mono text-[var(--color-text-tertiary)]">⌘</kbd>
|
|
<kbd className="text-[10px] font-mono text-[var(--color-text-tertiary)]">K</kbd>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<div className="hidden md:flex items-center gap-4 mr-4 text-[11px] font-semibold uppercase tracking-wider text-[var(--color-text-tertiary)]">
|
|
<div className="flex items-center gap-1.5">
|
|
<div className="w-2 h-2 rounded-full bg-[var(--color-success)] animate-pulse" />
|
|
<span>Terminal Live</span>
|
|
</div>
|
|
<div className="flex items-center gap-1.5">
|
|
<div className="w-2 h-2 rounded-full bg-[var(--color-accent)]" />
|
|
<span>32 Sensors Active</span>
|
|
</div>
|
|
</div>
|
|
|
|
<ThemeToggle />
|
|
<NotificationBell />
|
|
<button className="lg:hidden p-2 text-[var(--color-text-tertiary)]" onClick={() => setMobileSheetOpen(true)}>
|
|
<Filter size={20} />
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Content Scroller */}
|
|
<main
|
|
id="main-content"
|
|
className="flex-1 overflow-y-auto custom-scrollbar relative"
|
|
role="main"
|
|
>
|
|
<PageTitleUpdater />
|
|
<AnnouncementBanner />
|
|
|
|
<div className="max-w-[1920px] mx-auto p-4 sm:p-6 lg:p-8">
|
|
<Breadcrumbs />
|
|
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={location.pathname}
|
|
variants={pageVariants}
|
|
initial="initial"
|
|
animate="animate"
|
|
exit="exit"
|
|
className="w-full"
|
|
>
|
|
<Outlet />
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
{/* Mobile Interface Components */}
|
|
<MobileNav onMoreClick={() => setMobileSheetOpen(true)} />
|
|
<MobileNavSheet
|
|
isOpen={mobileSheetOpen}
|
|
onClose={() => setMobileSheetOpen(false)}
|
|
/>
|
|
|
|
{/* System Utilities */}
|
|
<CommandPalette />
|
|
<SessionTimeoutWarning />
|
|
<DevTools />
|
|
</div>
|
|
);
|
|
}
|