forge/frontend/components/Sidebar.tsx

304 lines
10 KiB
TypeScript

'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { clsx } from 'clsx';
import { useStore } from '@/lib/store';
import { isAdmin } from '@/lib/auth';
import {
Home,
Image,
Video,
Mic,
FileText,
History,
Settings,
ChevronLeft,
ChevronRight,
Sparkles,
Maximize,
Eraser,
Captions,
Volume2,
Type,
Wand2,
ImagePlus,
Film,
Shield,
Users,
TrendingUp,
Clock,
FolderOpen,
AudioLines,
Network,
FileCode,
FileType,
FileEdit,
Scissors,
} from 'lucide-react';
const modules = [
{
category: 'Image',
icon: Image,
items: [
{ name: 'Generate', href: '/image/generate', icon: ImagePlus },
{ name: 'Nano Edit', href: '/image/edit-pro', icon: Sparkles },
{ name: 'Upscale', href: '/image/upscale', icon: Maximize },
{ name: 'Remove Background', href: '/image/remove-bg', icon: Eraser },
],
},
{
category: 'Video',
icon: Video,
items: [
{ name: 'Generate', href: '/video/generate', icon: Film },
{ name: 'Upscale', href: '/video/upscale', icon: Maximize },
{ name: 'Frame Extractor', href: '/video/extract', icon: Scissors },
{ name: 'Subtitles', href: '/video/subtitles', icon: Captions },
],
},
{
category: 'Audio',
icon: Mic,
items: [
{ name: 'Text to Speech', href: '/audio/text-to-speech', icon: Volume2 },
{ name: 'Voice to Text', href: '/audio/voice-to-text', icon: Type },
{ name: 'Sound Effects', href: '/audio/sound-effects', icon: AudioLines },
],
},
{
category: 'Text',
icon: FileText,
items: [
{ name: 'Prompt Studio', href: '/text/prompt-studio', icon: Wand2 },
{ name: 'Alt Text Generator', href: '/text/alt-text', icon: FileText },
{ name: 'Mermaid Generator', href: '/text/mermaid-generator', icon: Network },
{ name: 'Mermaid Renderer', href: '/text/mermaid-renderer', icon: FileCode },
{ name: 'Markdown Converter', href: '/text/markdown-converter', icon: FileType },
{ name: 'Markdown Generator', href: '/text/markdown-generator', icon: FileEdit },
],
},
];
export default function Sidebar() {
const pathname = usePathname();
const { user, sidebarCollapsed, toggleSidebar } = useStore();
const userIsAdmin = isAdmin(user as any);
return (
<aside
className={clsx(
'bg-forge-dark border-r border-gray-800 flex flex-col transition-all duration-300',
sidebarCollapsed ? 'w-20' : 'w-64'
)}
>
{/* Logo */}
<div className="p-4 border-b border-gray-800">
<Link href="/" className="flex items-center gap-3">
<div className="w-10 h-10 flex items-center justify-center">
<img src="/THE_FORGE_LOGO.png" alt="Forge AI" className="w-full h-full object-contain" />
</div>
{!sidebarCollapsed && (
<span className="text-xl font-bold text-white">FORGE AI</span>
)}
</Link>
</div>
{/* Navigation */}
<nav className="flex-1 overflow-y-auto py-4">
{/* Dashboard */}
<Link
href="/"
className={clsx(
'flex items-center gap-3 px-4 py-3 mx-2 rounded-lg transition-colors',
pathname === '/'
? 'bg-forge-yellow/10 text-forge-yellow'
: 'text-gray-400 hover:text-white hover:bg-forge-gray'
)}
>
<Home className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>Dashboard</span>}
</Link>
{/* Modules */}
{modules.map((module) => (
<div key={module.category} className="mt-4">
{!sidebarCollapsed && (
<div className="px-4 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wider">
{module.category}
</div>
)}
{sidebarCollapsed && (
<div className="px-4 py-2 flex justify-center">
<module.icon className="w-4 h-4 text-gray-500" />
</div>
)}
{module.items.map((item) => (
<Link
key={item.href}
href={item.href}
className={clsx(
'flex items-center gap-3 px-4 py-2.5 mx-2 rounded-lg transition-colors',
pathname === item.href
? 'bg-forge-yellow/10 text-forge-yellow'
: 'text-gray-400 hover:text-white hover:bg-forge-gray'
)}
>
<item.icon className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>{item.name}</span>}
</Link>
))}
</div>
))}
{/* My Files */}
<div className="mt-6 pt-6 border-t border-gray-800">
<Link
href="/files"
className={clsx(
'flex items-center gap-3 px-4 py-3 mx-2 rounded-lg transition-colors',
pathname === '/files'
? 'bg-forge-yellow/10 text-forge-yellow'
: 'text-gray-400 hover:text-white hover:bg-forge-gray'
)}
>
<FolderOpen className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>My Files</span>}
</Link>
</div>
{/* History & Settings */}
<div className="mt-4">
<Link
href="/history"
className={clsx(
'flex items-center gap-3 px-4 py-3 mx-2 rounded-lg transition-colors',
pathname === '/history'
? 'bg-forge-yellow/10 text-forge-yellow'
: 'text-gray-400 hover:text-white hover:bg-forge-gray'
)}
>
<History className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>History</span>}
</Link>
<Link
href="/settings"
className={clsx(
'flex items-center gap-3 px-4 py-3 mx-2 rounded-lg transition-colors',
pathname === '/settings'
? 'bg-forge-yellow/10 text-forge-yellow'
: 'text-gray-400 hover:text-white hover:bg-forge-gray'
)}
>
<Settings className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>Settings</span>}
</Link>
</div>
{/* Admin Section - Only visible to admins */}
{userIsAdmin && (
<div className="mt-4 pt-4 border-t border-red-900/50">
{!sidebarCollapsed && (
<div className="px-4 py-2 text-xs font-semibold text-red-400 uppercase tracking-wider">
Admin
</div>
)}
{sidebarCollapsed && (
<div className="px-4 py-2 flex justify-center">
<Shield className="w-4 h-4 text-red-400" />
</div>
)}
<Link
href="/admin"
className={clsx(
'flex items-center gap-3 px-4 py-2.5 mx-2 rounded-lg transition-colors',
pathname === '/admin'
? 'bg-red-900/20 text-red-400'
: 'text-gray-400 hover:text-red-400 hover:bg-red-900/10'
)}
>
<Shield className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>Dashboard</span>}
</Link>
<Link
href="/admin/users"
className={clsx(
'flex items-center gap-3 px-4 py-2.5 mx-2 rounded-lg transition-colors',
pathname === '/admin/users'
? 'bg-red-900/20 text-red-400'
: 'text-gray-400 hover:text-red-400 hover:bg-red-900/10'
)}
>
<Users className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>Users</span>}
</Link>
<Link
href="/admin/reports"
className={clsx(
'flex items-center gap-3 px-4 py-2.5 mx-2 rounded-lg transition-colors',
pathname === '/admin/reports'
? 'bg-red-900/20 text-red-400'
: 'text-gray-400 hover:text-red-400 hover:bg-red-900/10'
)}
>
<TrendingUp className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>Reports</span>}
</Link>
<Link
href="/admin/usage"
className={clsx(
'flex items-center gap-3 px-4 py-2.5 mx-2 rounded-lg transition-colors',
pathname === '/admin/usage'
? 'bg-red-900/20 text-red-400'
: 'text-gray-400 hover:text-red-400 hover:bg-red-900/10'
)}
>
<History className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>Usage Search</span>}
</Link>
<Link
href="/admin/logs"
className={clsx(
'flex items-center gap-3 px-4 py-2.5 mx-2 rounded-lg transition-colors',
pathname === '/admin/logs'
? 'bg-red-900/20 text-red-400'
: 'text-gray-400 hover:text-red-400 hover:bg-red-900/10'
)}
>
<Clock className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>Audit Logs</span>}
</Link>
<Link
href="/admin/voices"
className={clsx(
'flex items-center gap-3 px-4 py-2.5 mx-2 rounded-lg transition-colors',
pathname === '/admin/voices'
? 'bg-red-900/20 text-red-400'
: 'text-gray-400 hover:text-red-400 hover:bg-red-900/10'
)}
>
<Mic className="w-5 h-5 flex-shrink-0" />
{!sidebarCollapsed && <span>Voices</span>}
</Link>
</div>
)}
</nav>
{/* Collapse Toggle */}
<button
onClick={toggleSidebar}
className="p-4 border-t border-gray-800 text-gray-400 hover:text-white transition-colors"
>
{sidebarCollapsed ? (
<ChevronRight className="w-5 h-5 mx-auto" />
) : (
<div className="flex items-center gap-2">
<ChevronLeft className="w-5 h-5" />
<span>Collapse</span>
</div>
)}
</button>
</aside>
);
}