| | |
| |
|
| | import React, { useState } from "react"; |
| | import { Link } from "react-router-dom"; |
| | import { createPageUrl } from "./utils"; |
| | import { |
| | LayoutDashboard, |
| | History as HistoryIcon, |
| | Key, |
| | ChevronLeft, |
| | Sparkles, |
| | LogOut, |
| | User, |
| | } from "lucide-react"; |
| | import { cn } from "@/lib/utils"; |
| | import { useAuth } from "./contexts/AuthContext"; |
| |
|
| | |
| | |
| | |
| | const logoPath = "/logo.png"; |
| |
|
| | export default function Layout({ children, currentPageName }) { |
| | const [collapsed, setCollapsed] = useState(false); |
| | const { user, logout } = useAuth(); |
| |
|
| | const navItems = [ |
| | { name: "Dashboard", icon: LayoutDashboard, page: "Dashboard" }, |
| | { name: "History", icon: HistoryIcon, page: "History" }, |
| | { name: "API Keys", icon: Key, page: "API Keys" }, |
| | ]; |
| |
|
| | return ( |
| | <div className="min-h-screen bg-[#FAFAFA] flex"> |
| | {/* Sidebar */} |
| | <aside |
| | className={cn( |
| | "fixed left-0 top-0 h-screen bg-white border-r border-slate-200/80 z-50 transition-all duration-300 ease-out flex flex-col", |
| | collapsed ? "w-[72px]" : "w-[260px]" |
| | )} |
| | > |
| | {/* Logo */} |
| | <div |
| | className={cn( |
| | "h-16 flex items-center border-b border-slate-100 px-4", |
| | collapsed ? "justify-center" : "justify-between" |
| | )} |
| | > |
| | <Link to={createPageUrl("Dashboard")} className="flex items-center gap-3"> |
| | <div className="h-9 w-9 flex items-center justify-center flex-shrink-0"> |
| | <img |
| | src={logoPath} |
| | alt="EZOFIS AI Logo" |
| | className="h-full w-full object-contain" |
| | onError={(e) => { |
| | // Fallback: hide image and show placeholder if logo not found |
| | e.target.style.display = 'none'; |
| | }} |
| | /> |
| | </div> |
| | {!collapsed && ( |
| | <div className="flex flex-col"> |
| | <span className="font-semibold text-slate-900 tracking-tight">EZOFIS AI</span> |
| | <span className="text-[10px] text-slate-400 font-medium tracking-wide uppercase"> |
| | VRP Intelligence |
| | </span> |
| | </div> |
| | )} |
| | </Link> |
| | {!collapsed && ( |
| | <button |
| | onClick={() => setCollapsed(true)} |
| | className="h-7 w-7 rounded-lg hover:bg-slate-100 flex items-center justify-center text-slate-400 hover:text-slate-600 transition-colors" |
| | > |
| | <ChevronLeft className="h-4 w-4" /> |
| | </button> |
| | )} |
| | </div> |
| | |
| | {/* Navigation */} |
| | <nav className="flex-1 p-3 space-y-1"> |
| | {navItems.map((item) => { |
| | const isActive = currentPageName === item.page; |
| | return ( |
| | <Link |
| | key={item.name} |
| | to={createPageUrl(item.page)} |
| | className={cn( |
| | "flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all duration-200 group", |
| | isActive |
| | ? "bg-gradient-to-r from-indigo-50 to-violet-50 text-indigo-600" |
| | : "text-slate-500 hover:bg-slate-50 hover:text-slate-700" |
| | )} |
| | > |
| | <item.icon |
| | className={cn( |
| | "h-5 w-5 flex-shrink-0", |
| | isActive ? "text-indigo-600" : "text-slate-400 group-hover:text-slate-600" |
| | )} |
| | /> |
| | {!collapsed && ( |
| | <span className="font-medium text-sm">{item.name}</span> |
| | )} |
| | </Link> |
| | ); |
| | })} |
| | </nav> |
| | |
| | {/* Collapse Toggle (when collapsed) */} |
| | {collapsed && ( |
| | <button |
| | onClick={() => setCollapsed(false)} |
| | className="m-3 h-10 rounded-xl bg-slate-50 hover:bg-slate-100 flex items-center justify-center text-slate-400 hover:text-slate-600 transition-colors" |
| | > |
| | <ChevronLeft className="h-4 w-4 rotate-180" /> |
| | </button> |
| | )} |
| | |
| | {/* Pro Badge */} |
| | {!collapsed && ( |
| | <div className="p-3"> |
| | <div className="p-4 rounded-2xl bg-gradient-to-br from-slate-900 to-slate-800 text-white"> |
| | <div className="flex items-center gap-2 mb-2"> |
| | <Sparkles className="h-4 w-4 text-amber-400" /> |
| | <span className="text-xs font-semibold tracking-wide">DEPLOY CUSTOM AGENT</span> |
| | </div> |
| | <p className="text-xs text-slate-400 mb-3"> |
| | Batch extractions, custom model, field mapping, complex lineitems, tables, workflows, & API access |
| | </p> |
| | <button |
| | className="w-full py-2 px-3 rounded-lg bg-white text-slate-900 text-sm font-semibold hover:bg-slate-100 transition-colors" |
| | onClick={() => window.open("https://calendar.app.google/UTx9ZiBXhpMqCVyaA", "_blank", "noopener,noreferrer")} |
| | > |
| | Book a Custom Demo |
| | </button> |
| | </div> |
| | </div> |
| | )} |
| | |
| | {/* User Profile */} |
| | {!collapsed && user && ( |
| | <div className="p-3 border-t border-slate-200"> |
| | <div className="flex items-center gap-3 p-3 rounded-xl bg-slate-50 hover:bg-slate-100 transition-colors"> |
| | {user.picture ? ( |
| | <img |
| | src={user.picture} |
| | alt={user.name || user.email} |
| | className="h-10 w-10 rounded-lg object-cover" |
| | /> |
| | ) : ( |
| | <div className="h-10 w-10 rounded-lg bg-indigo-100 flex items-center justify-center"> |
| | <User className="h-5 w-5 text-indigo-600" /> |
| | </div> |
| | )} |
| | <div className="flex-1 min-w-0"> |
| | <p className="text-sm font-medium text-slate-900 truncate"> |
| | {user.name || "User"} |
| | </p> |
| | <p className="text-xs text-slate-500 truncate">{user.email}</p> |
| | </div> |
| | </div> |
| | <button |
| | onClick={logout} |
| | className="mt-2 w-full flex items-center gap-2 px-3 py-2 rounded-xl text-sm text-slate-600 hover:bg-red-50 hover:text-red-600 transition-colors" |
| | > |
| | <LogOut className="h-4 w-4" /> |
| | <span>Sign Out</span> |
| | </button> |
| | </div> |
| | )} |
| | </aside> |
| | |
| | {/* Main Content */} |
| | <main |
| | className={cn( |
| | "flex-1 transition-all duration-300", |
| | collapsed ? "ml-[72px]" : "ml-[260px]" |
| | )} |
| | > |
| | {children} |
| | </main> |
| | </div> |
| | ); |
| | } |
| |
|