Spaces:
Running
Running
File size: 4,715 Bytes
89a11b6 a79917c 89a11b6 a79917c 89a11b6 a79917c 89a11b6 a79917c 89a11b6 a79917c 89a11b6 a79917c 89a11b6 a79917c 89a11b6 a79917c 89a11b6 a79917c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
// frontend/src/Layout.jsx
import React, { useState } from "react";
import { Link } from "react-router-dom";
import { createPageUrl } from "./utils";
import {
ScanText,
LayoutDashboard,
History as HistoryIcon,
ChevronLeft,
Sparkles,
} from "lucide-react";
import { cn } from "@/lib/utils";
export default function Layout({ children, currentPageName }) {
const [collapsed, setCollapsed] = useState(false);
const navItems = [
{ name: "Dashboard", icon: LayoutDashboard, page: "Dashboard" },
{ name: "History", icon: HistoryIcon, page: "History" },
];
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 rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 flex items-center justify-center shadow-lg shadow-indigo-500/25">
<ScanText className="h-5 w-5 text-white" />
</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">
Agentic Extract
</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">UPGRADE TO PRO</span>
</div>
<p className="text-xs text-slate-400 mb-3">
Unlock unlimited extractions & 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">
Upgrade Now
</button>
</div>
</div>
)}
</aside>
{/* Main Content */}
<main
className={cn(
"flex-1 transition-all duration-300",
collapsed ? "ml-[72px]" : "ml-[260px]"
)}
>
{children}
</main>
</div>
);
}
|