veza/apps/web/src/components/layout/Sidebar.tsx

237 lines
9.4 KiB
TypeScript
Raw Normal View History

import React from 'react';
import { useNavigate, useLocation, Link } from 'react-router-dom';
import { Home, Users, Disc, Radio, Settings, LogOut, ShoppingBag, GraduationCap, BarChart2, Shield, Box, MessageSquare, Cloud, Layers, Cpu, Heart, ListMusic, CreditCard, DollarSign, Terminal } from 'lucide-react';
import { NavItem } from '../../types';
import { useAuthStore } from '@/features/auth/store/authStore';
import { useUIStore } from '@/stores/ui';
import { cn } from '@/lib/utils';
interface SidebarProps {
currentView?: string;
onNavigate?: (viewId: string) => void;
onLogout?: () => void;
}
const navItems: { section: string; items: NavItem[] }[] = [
{
section: 'My Studio',
items: [
{ id: 'dashboard', label: 'Command Center', icon: <Home className="w-4 h-4" /> },
{ id: 'studio', label: 'Cloud Files', icon: <Cloud className="w-4 h-4" /> },
{ id: 'tracks', label: 'Projects', icon: <Layers className="w-4 h-4" /> },
{ id: 'gear', label: 'Gear Locker', icon: <Box className="w-4 h-4" /> },
{ id: 'analytics', label: 'Performance', icon: <BarChart2 className="w-4 h-4" /> },
]
},
{
section: 'Veza Network',
items: [
{ id: 'social', label: 'Community Feed', icon: <Users className="w-4 h-4" /> },
{ id: 'marketplace', label: 'Marketplace', icon: <ShoppingBag className="w-4 h-4" /> },
{ id: 'live', label: 'Live Sessions', icon: <Radio className="w-4 h-4 text-kodo-red" />, badge: 3 },
{ id: 'chat', label: 'Channels', icon: <MessageSquare className="w-4 h-4" />, badge: 12 },
{ id: 'education', label: 'Academy', icon: <GraduationCap className="w-4 h-4" /> },
]
},
{
section: 'Commerce',
items: [
{ id: 'sell', label: 'Seller Dashboard', icon: <DollarSign className="w-4 h-4" /> },
{ id: 'wishlist', label: 'Wishlist', icon: <Heart className="w-4 h-4" /> },
{ id: 'purchases', label: 'Purchases', icon: <CreditCard className="w-4 h-4" /> },
]
},
{
section: 'Library',
items: [
{ id: 'playlists', label: 'Playlists', icon: <ListMusic className="w-4 h-4" /> },
{ id: 'queue', label: 'Play Queue', icon: <Disc className="w-4 h-4" /> },
]
},
{
section: 'System',
items: [
{ id: 'developer', label: 'Developer API', icon: <Terminal className="w-4 h-4" /> },
{ id: 'admin', label: 'Admin Panel', icon: <Shield className="w-4 h-4" /> },
]
}
];
// Mapping des IDs de navigation vers les routes React Router
const routeMap: Record<string, string> = {
dashboard: '/dashboard',
studio: '/library', // Fixed: was /dashboard
tracks: '/library',
gear: '/gear', // Fixed: was /dashboard (will create page)
analytics: '/analytics',
social: '/social', // Fixed: was /dashboard (will create page)
marketplace: '/marketplace',
live: '/live', // Fixed: was /dashboard (will create page)
chat: '/chat',
education: '/education', // Fixed: was /dashboard (will create page)
sell: '/marketplace',
wishlist: '/marketplace',
purchases: '/marketplace',
playlists: '/playlists',
queue: '/queue', // Fixed: was /dashboard (will create page)
developer: '/developer', // Fixed: was /dashboard (will create page)
admin: '/admin',
settings: '/settings',
};
export const Sidebar: React.FC<SidebarProps> = ({ currentView, onNavigate, onLogout }) => {
const navigate = useNavigate();
const location = useLocation();
const { logout } = useAuthStore();
const { sidebarOpen, setSidebarOpen } = useUIStore();
const handleMobileNav = () => {
if (window.innerWidth < 1024) { // lg breakpoint
setSidebarOpen(false);
}
};
// Déterminer la vue actuelle depuis l'URL
const activeView = currentView || Object.keys(routeMap).find(
key => routeMap[key] === location.pathname
) || 'dashboard';
2026-01-07 18:39:21 +00:00
const handleLogout = () => {
logout();
navigate('/login');
// Appeler onLogout si fourni (pour compatibilité)
if (onLogout) {
onLogout();
}
};
return (
<>
{/* Mobile Backdrop */}
{sidebarOpen && (
<div
className="fixed inset-0 bg-black/60 backdrop-blur-sm lg:hidden"
style={{ zIndex: 'var(--z-modal-backdrop)' }}
onClick={() => setSidebarOpen(false)}
aria-hidden="true"
/>
)}
<aside role="navigation" aria-label="Sidebar" className={`
fixed left-6 top-24 bottom-6 w-64 glass-hud rounded-2xl border-white/10 flex flex-col transition-all duration-500 ease-in-out hud-corner
${sidebarOpen ? 'translate-x-0 opacity-100' : '-translate-x-full lg:translate-x-0 lg:opacity-100 lg:w-20'}
`} style={{ zIndex: 'var(--z-fixed)' }}>
{/* Hub Header Integration */}
<div className="p-6 border-b border-white/5 flex items-center gap-3">
<div className="w-8 h-8 rounded bg-kodo-cyan/20 flex items-center justify-center border border-kodo-cyan/30 animate-pulse-glow">
<Cpu className="w-5 h-5 text-kodo-cyan" />
</div>
{sidebarOpen && (
<div className="animate-fadeIn">
<h2 className="text-xs font-mono font-bold text-white tracking-widest uppercase">System Hub</h2>
<div className="flex items-center gap-1.5 mt-0.5">
<span className="w-1 h-1 rounded-full bg-kodo-lime animate-pulse" />
<span className="text-[10px] font-mono text-kodo-lime opacity-80 uppercase">Active</span>
</div>
</div>
)}
</div>
<div className="flex-1 overflow-y-auto custom-scrollbar p-3 space-y-6 mt-4">
{navItems.map((group, idx) => (
<div key={idx} className="mb-4">
{sidebarOpen && (
<h3 className="text-[9px] font-mono font-bold text-kodo-cyan/60 uppercase tracking-[0.2em] mb-3 px-3 flex items-center gap-2 animate-fadeIn">
{group.section}
</h3>
)}
<div className="space-y-1">
{group.items.map((item) => {
const route = routeMap[item.id] || '/dashboard';
const isActive = activeView === item.id;
return (
<Link
key={item.id}
to={route}
title={!sidebarOpen ? item.label : undefined}
onClick={() => {
handleMobileNav();
if (onNavigate) {
onNavigate(item.id);
}
}}
className={cn(
"w-full flex items-center px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 group relative overflow-hidden",
isActive
? "glass-hud-active text-kodo-cyan"
: "text-kodo-secondary hover:text-white hover:bg-white/5"
)}
>
{/* Active indicator bar */}
{isActive && (
<div className="absolute left-0 top-1 bottom-1 w-1 bg-kodo-cyan rounded-full shadow-[0_0_10px_rgba(102,252,241,0.5)]" />
)}
<div className="flex items-center gap-3 relative z-10 w-full">
<span className={cn(
"transition-all duration-300 transform group-hover:scale-110",
isActive ? "text-kodo-cyan drop-shadow-[0_0_8px_rgba(102,252,241,0.4)]" : "text-kodo-secondary"
)}>
{item.icon}
</span>
{sidebarOpen && (
<span className="animate-fadeIn flex-1 truncate">
{item.label}
</span>
)}
</div>
{item.badge && sidebarOpen && (
<span className="ml-2 bg-kodo-magenta/20 text-kodo-magenta text-[9px] px-1.5 py-0.5 rounded-full font-mono font-bold border border-kodo-magenta/30">
{item.badge}
</span>
)}
</Link>
);
})}
</div>
</div>
))}
</div>
<div className="p-3 border-t border-white/5 bg-white/2">
<Link
to="/settings"
onClick={() => {
handleMobileNav();
if (onNavigate) {
onNavigate('settings');
}
}}
className={cn(
"w-full flex items-center gap-3 px-4 py-2.5 text-sm rounded-xl transition-all",
activeView === 'settings'
? "glass-hud-active text-kodo-cyan"
: "text-kodo-secondary hover:text-white hover:bg-white/5"
)}
>
<Settings className="w-4 h-4" />
{sidebarOpen && <span className="animate-fadeIn">Settings</span>}
</Link>
<button
onClick={handleLogout}
className="w-full flex items-center gap-3 px-4 py-2.5 text-red-400/80 hover:text-red-400 hover:bg-red-500/10 rounded-xl transition-all text-sm mt-1"
>
<LogOut className="w-4 h-4" />
{sidebarOpen && <span className="animate-fadeIn">Initialize Signout</span>}
</button>
</div>
</aside>
</>
);
};