102 lines
4.3 KiB
TypeScript
102 lines
4.3 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Box, LayoutTemplate, Users, BookOpen, LogOut, Sparkles } from 'lucide-react';
|
|
|
|
interface LayoutProps {
|
|
children: React.ReactNode;
|
|
activeView: string;
|
|
onNavigate: (view: string) => void;
|
|
onLogout?: () => void;
|
|
}
|
|
|
|
export const Layout: React.FC<LayoutProps> = ({ children, activeView, onNavigate, onLogout }) => {
|
|
const [showDropdown, setShowDropdown] = useState(false);
|
|
const navItems = [
|
|
{ id: 'dashboard', label: 'Dashboard', icon: LayoutTemplate },
|
|
{ id: 'demo', label: 'Demo', icon: Sparkles },
|
|
{ id: 'training', label: 'Training', icon: Box }, // Mapped to Compliants visually in prompt, using logical name
|
|
{ id: 'documents', label: 'Documents', icon: BookOpen },
|
|
{ id: 'models', label: 'Models', icon: Users }, // Contacts in prompt, mapped to models for this use case
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen bg-warm-bg font-sans text-warm-text-primary flex flex-col">
|
|
{/* Top Navigation */}
|
|
<nav className="h-14 bg-warm-bg border-b border-warm-border px-6 flex items-center justify-between shrink-0 sticky top-0 z-40">
|
|
<div className="flex items-center gap-8">
|
|
{/* Logo */}
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-8 h-8 bg-warm-text-primary rounded-full flex items-center justify-center text-white">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Nav Links */}
|
|
<div className="flex h-14">
|
|
{navItems.map(item => {
|
|
const isActive = activeView === item.id || (activeView === 'detail' && item.id === 'documents');
|
|
return (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => onNavigate(item.id)}
|
|
className={`
|
|
relative px-4 h-full flex items-center text-sm font-medium transition-colors
|
|
${isActive ? 'text-warm-text-primary' : 'text-warm-text-muted hover:text-warm-text-secondary'}
|
|
`}
|
|
>
|
|
{item.label}
|
|
{isActive && (
|
|
<div className="absolute bottom-0 left-0 right-0 h-0.5 bg-warm-text-secondary rounded-t-full mx-2" />
|
|
)}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* User Profile */}
|
|
<div className="flex items-center gap-3 pl-6 border-l border-warm-border h-6 relative">
|
|
<button
|
|
onClick={() => setShowDropdown(!showDropdown)}
|
|
className="w-8 h-8 rounded-full bg-warm-selected flex items-center justify-center text-xs font-semibold text-warm-text-secondary border border-warm-divider hover:bg-warm-hover transition-colors"
|
|
>
|
|
AD
|
|
</button>
|
|
|
|
{showDropdown && (
|
|
<>
|
|
<div
|
|
className="fixed inset-0 z-10"
|
|
onClick={() => setShowDropdown(false)}
|
|
/>
|
|
<div className="absolute right-0 top-10 w-48 bg-warm-card border border-warm-border rounded-lg shadow-modal z-20">
|
|
<div className="p-3 border-b border-warm-border">
|
|
<p className="text-sm font-medium text-warm-text-primary">Admin User</p>
|
|
<p className="text-xs text-warm-text-muted mt-0.5">Authenticated</p>
|
|
</div>
|
|
{onLogout && (
|
|
<button
|
|
onClick={() => {
|
|
setShowDropdown(false)
|
|
onLogout()
|
|
}}
|
|
className="w-full px-3 py-2 text-left text-sm text-warm-text-secondary hover:bg-warm-hover transition-colors flex items-center gap-2"
|
|
>
|
|
<LogOut size={14} />
|
|
Sign Out
|
|
</button>
|
|
)}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Main Content */}
|
|
<main className="flex-1 overflow-auto">
|
|
{children}
|
|
</main>
|
|
</div>
|
|
);
|
|
}; |