Spaces:
Running
Running
File size: 4,290 Bytes
eb22aa0 ce2d6ca 5dca218 ce2d6ca eb22aa0 ce2d6ca eb22aa0 db82489 eb22aa0 ce2d6ca 0505270 ce2d6ca | 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 | import { ReactNode, useState, useRef, useEffect } from 'react';
import { useAuthStore } from '../../store/authStore';
import { useNavigate, useLocation } from 'react-router-dom';
import {
LayoutDashboard, FolderOpen, LogOut, User, Settings,
Menu, X, ChevronRight, Save, Cloud, Code2, Blocks, FileType
} from 'lucide-react';
interface AppLayoutProps {
children: ReactNode;
sidebarContent?: ReactNode;
showSidebar?: boolean;
}
export default function AppLayout({ children, sidebarContent, showSidebar }: AppLayoutProps) {
const { user, logout } = useAuthStore();
const navigate = useNavigate();
const location = useLocation();
const isEditor = location.pathname.startsWith('/project/');
const [showProfileMenu, setShowProfileMenu] = useState(false);
const profileRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (profileRef.current && !profileRef.current.contains(e.target as Node)) {
setShowProfileMenu(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
return (
<div className="min-h-screen bg-surface-950 flex flex-col">
{/* Top Navigation */}
<header className="h-14 glass border-b border-surface-700 flex items-center justify-between px-4 z-50">
<div className="flex items-center gap-3">
<button
onClick={() => navigate('/dashboard')}
className="flex items-center gap-2 text-white font-bold text-lg hover:text-primary-400 transition-colors"
>
<Blocks className="w-6 h-6 text-primary-500" />
RealBlocks
</button>
{isEditor && (
<>
<ChevronRight className="w-4 h-4 text-surface-400" />
<span className="text-surface-200 text-sm font-medium">
{useAuthStore.getState().token ? 'Project Editor' : ''}
</span>
</>
)}
</div>
<div className="flex items-center gap-2">
{user && (
<>
<span className="text-surface-300 text-sm hidden md:block">
{user.username}
</span>
<div className="relative" ref={profileRef}>
<button
onClick={() => setShowProfileMenu(!showProfileMenu)}
className="w-8 h-8 rounded-full bg-primary-600 flex items-center justify-center text-white text-sm font-medium hover:bg-primary-500 transition-colors"
>
{user.username.charAt(0).toUpperCase()}
</button>
{showProfileMenu && (
<div className="absolute right-0 top-full mt-2 w-48 bg-surface-800 border border-surface-700 rounded-lg shadow-xl py-1 z-50">
<button
onClick={() => { setShowProfileMenu(false); navigate('/settings'); }}
className="w-full flex items-center gap-2 px-4 py-2 text-sm text-surface-200 hover:bg-surface-700 transition-colors"
>
<Settings className="w-4 h-4" />
Settings
</button>
<div className="border-t border-surface-700 my-1" />
<button
onClick={() => { logout(); navigate('/login'); }}
className="w-full flex items-center gap-2 px-4 py-2 text-sm text-red-400 hover:bg-surface-700 transition-colors"
>
<LogOut className="w-4 h-4" />
Logout
</button>
</div>
)}
</div>
</>
)}
</div>
</header>
{/* Main Content Area */}
<div className="flex-1 flex overflow-hidden">
{/* Sidebar (for editor) */}
{showSidebar && sidebarContent && (
<aside className="w-64 panel flex-shrink-0 overflow-y-auto">
{sidebarContent}
</aside>
)}
{/* Main Content */}
<main className="flex-1 overflow-auto">
{children}
</main>
</div>
</div>
);
}
|