Spaces:
Sleeping
Sleeping
| import React, { useState } from 'react'; | |
| import { Link, useLocation } from 'react-router-dom'; | |
| import { createPageUrl } from '@/utils'; | |
| import { motion, AnimatePresence } from 'framer-motion'; | |
| import { | |
| LayoutDashboard, | |
| FolderOpen, | |
| Calendar, | |
| PenTool, | |
| Link2, | |
| Settings, | |
| Menu, | |
| X, | |
| Bell, | |
| Search, | |
| ChevronDown, | |
| Linkedin, | |
| Sparkles, | |
| LogOut, | |
| User, | |
| HelpCircle | |
| } from 'lucide-react'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Input } from '@/components/ui/input'; | |
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | |
| import { Badge } from '@/components/ui/badge'; | |
| import { | |
| DropdownMenu, | |
| DropdownMenuContent, | |
| DropdownMenuItem, | |
| DropdownMenuLabel, | |
| DropdownMenuSeparator, | |
| DropdownMenuTrigger, | |
| } from '@/components/ui/dropdown-menu'; | |
| const navItems = [ | |
| { name: 'Dashboard', icon: LayoutDashboard, href: 'Dashboard' }, | |
| { name: 'Repository', icon: FolderOpen, href: 'Repository' }, | |
| { name: 'Scheduler', icon: Calendar, href: 'Scheduler' }, | |
| { name: 'Post Editor', icon: PenTool, href: 'PostEditor' }, | |
| { name: 'Integrations', icon: Link2, href: 'Integrations' }, | |
| ]; | |
| export default function Layout({ children, currentPageName }) { | |
| const [sidebarOpen, setSidebarOpen] = useState(false); | |
| const location = useLocation(); | |
| const currentPage = navItems.find(item => createPageUrl(item.href) === location.pathname)?.href || 'Dashboard'; | |
| return ( | |
| <div className="min-h-screen bg-slate-50"> | |
| {/* Mobile Sidebar Overlay */} | |
| <AnimatePresence> | |
| {sidebarOpen && ( | |
| <motion.div | |
| initial={{ opacity: 0 }} | |
| animate={{ opacity: 1 }} | |
| exit={{ opacity: 0 }} | |
| className="fixed inset-0 bg-black/50 z-40 lg:hidden" | |
| onClick={() => setSidebarOpen(false)} | |
| /> | |
| )} | |
| </AnimatePresence> | |
| {/* Sidebar */} | |
| <aside className={`fixed top-0 left-0 z-50 h-full w-64 bg-white border-r border-slate-200 transform transition-transform duration-300 ease-in-out lg:translate-x-0 ${ | |
| sidebarOpen ? 'translate-x-0' : '-translate-x-full' | |
| }`}> | |
| <div className="flex flex-col h-full"> | |
| {/* Logo */} | |
| <div className="h-16 flex items-center justify-between px-4 border-b border-slate-100"> | |
| <Link to={createPageUrl('Dashboard')} className="flex items-center gap-2"> | |
| <div className="w-9 h-9 rounded-xl bg-gradient-to-br from-blue-600 to-indigo-600 flex items-center justify-center shadow-lg shadow-blue-500/30"> | |
| <Linkedin className="w-5 h-5 text-white" /> | |
| </div> | |
| <div> | |
| <span className="font-bold text-slate-900 text-lg">PostGen</span> | |
| <span className="text-xs text-slate-500 block -mt-1">LinkedIn Scheduler</span> | |
| </div> | |
| </Link> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| className="lg:hidden" | |
| onClick={() => setSidebarOpen(false)} | |
| > | |
| <X className="w-5 h-5" /> | |
| </Button> | |
| </div> | |
| {/* Navigation */} | |
| <nav className="flex-1 p-4 space-y-1 overflow-y-auto"> | |
| {navItems.map((item) => { | |
| const isActive = currentPage === item.href; | |
| return ( | |
| <Link | |
| key={item.name} | |
| to={createPageUrl(item.href)} | |
| onClick={() => setSidebarOpen(false)} | |
| className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all ${ | |
| isActive | |
| ? 'bg-blue-50 text-blue-700' | |
| : 'text-slate-600 hover:bg-slate-50 hover:text-slate-900' | |
| }`} | |
| > | |
| <item.icon className={`w-5 h-5 ${isActive ? 'text-blue-600' : 'text-slate-400'}`} /> | |
| {item.name} | |
| {item.name === 'Repository' && ( | |
| <Badge className="ml-auto bg-blue-100 text-blue-700 text-[10px] px-1.5 py-0 border-0"> | |
| 156 | |
| </Badge> | |
| )} | |
| </Link> | |
| ); | |
| })} | |
| </nav> | |
| {/* Quick Create */} | |
| <div className="p-4 border-t border-slate-100"> | |
| <Link to={createPageUrl('PostEditor')}> | |
| <Button className="w-full gap-2 bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 shadow-lg shadow-blue-500/25"> | |
| <Sparkles className="w-4 h-4" /> | |
| Create Post | |
| </Button> | |
| </Link> | |
| </div> | |
| {/* User Profile */} | |
| <div className="p-4 border-t border-slate-100"> | |
| <DropdownMenu> | |
| <DropdownMenuTrigger asChild> | |
| <button className="flex items-center gap-3 w-full p-2 rounded-xl hover:bg-slate-50 transition-colors"> | |
| <Avatar className="h-9 w-9"> | |
| <AvatarImage src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop" /> | |
| <AvatarFallback>AB</AvatarFallback> | |
| </Avatar> | |
| <div className="flex-1 text-left"> | |
| <p className="text-sm font-medium text-slate-900">Alex Business</p> | |
| <p className="text-xs text-slate-500">Pro Plan</p> | |
| </div> | |
| <ChevronDown className="w-4 h-4 text-slate-400" /> | |
| </button> | |
| </DropdownMenuTrigger> | |
| <DropdownMenuContent align="end" className="w-56"> | |
| <DropdownMenuLabel>My Account</DropdownMenuLabel> | |
| <DropdownMenuSeparator /> | |
| <DropdownMenuItem> | |
| <User className="w-4 h-4 mr-2" /> | |
| Profile Settings | |
| </DropdownMenuItem> | |
| <DropdownMenuItem> | |
| <Settings className="w-4 h-4 mr-2" /> | |
| Preferences | |
| </DropdownMenuItem> | |
| <DropdownMenuItem> | |
| <HelpCircle className="w-4 h-4 mr-2" /> | |
| Help & Support | |
| </DropdownMenuItem> | |
| <DropdownMenuSeparator /> | |
| <DropdownMenuItem className="text-red-600"> | |
| <LogOut className="w-4 h-4 mr-2" /> | |
| Sign Out | |
| </DropdownMenuItem> | |
| </DropdownMenuContent> | |
| </DropdownMenu> | |
| </div> | |
| </div> | |
| </aside> | |
| {/* Main Content */} | |
| <div className="lg:pl-64"> | |
| {/* Top Header */} | |
| <header className="h-16 bg-white border-b border-slate-200 sticky top-0 z-30"> | |
| <div className="h-full px-4 flex items-center justify-between gap-4"> | |
| {/* Mobile Menu Toggle */} | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| className="lg:hidden" | |
| onClick={() => setSidebarOpen(true)} | |
| > | |
| <Menu className="w-5 h-5" /> | |
| </Button> | |
| {/* Search */} | |
| <div className="flex-1 max-w-md hidden sm:block"> | |
| <div className="relative"> | |
| <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" /> | |
| <Input | |
| placeholder="Search posts, assets, schedules..." | |
| className="pl-10 bg-slate-50 border-slate-200 focus:bg-white" | |
| /> | |
| </div> | |
| </div> | |
| {/* Right Actions */} | |
| <div className="flex items-center gap-2"> | |
| <Button variant="ghost" size="icon" className="relative"> | |
| <Bell className="w-5 h-5 text-slate-600" /> | |
| <span className="absolute top-1.5 right-1.5 w-2 h-2 bg-red-500 rounded-full" /> | |
| </Button> | |
| {/* Connection Status */} | |
| <div className="hidden md:flex items-center gap-2 px-3 py-1.5 rounded-lg bg-emerald-50 border border-emerald-100"> | |
| <div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" /> | |
| <span className="text-xs font-medium text-emerald-700">LinkedIn Connected</span> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| {/* Page Content */} | |
| <main> | |
| {children} | |
| </main> | |
| </div> | |
| </div> | |
| ); | |
| } | |