371 lines
12 KiB
TypeScript
371 lines
12 KiB
TypeScript
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,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
} from 'lucide-react';
|
|
import { NavItem } from '../../types';
|
|
import { useAuthStore } from '@/features/auth/store/authStore';
|
|
import { useUIStore } from '@/stores/ui';
|
|
import { cn } from '@/lib/utils';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
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',
|
|
tracks: '/library',
|
|
gear: '/gear',
|
|
analytics: '/analytics',
|
|
social: '/social',
|
|
marketplace: '/marketplace',
|
|
live: '/live',
|
|
chat: '/chat',
|
|
education: '/education',
|
|
sell: '/marketplace',
|
|
wishlist: '/marketplace',
|
|
purchases: '/marketplace',
|
|
playlists: '/library',
|
|
queue: '/queue',
|
|
developer: '/developer',
|
|
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) {
|
|
setSidebarOpen(false);
|
|
}
|
|
};
|
|
|
|
const activeView =
|
|
currentView ||
|
|
Object.keys(routeMap).find((key) => routeMap[key] === location.pathname) ||
|
|
'dashboard';
|
|
|
|
const handleLogout = () => {
|
|
logout();
|
|
navigate('/login');
|
|
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={cn(
|
|
'fixed left-6 bottom-6 glass-hud rounded-2xl border-white/10 flex flex-col transition-all duration-500 ease-in-out hud-corner',
|
|
'top-24',
|
|
sidebarOpen ? 'w-64 translate-x-0 opacity-100' : '-translate-x-full lg:translate-x-0 lg:opacity-100 lg:w-20',
|
|
)}
|
|
style={{ zIndex: 90 }}
|
|
>
|
|
{/* Hub Header */}
|
|
<div className="p-6 border-b border-white/5 flex items-center gap-4">
|
|
<div className="w-8 h-8 rounded bg-kodo-steel/20 flex items-center justify-center border border-kodo-steel/30 animate-pulse-glow flex-shrink-0">
|
|
<Cpu className="w-5 h-5 text-kodo-steel" />
|
|
</div>
|
|
<div className={cn("transition-all duration-300 overflow-hidden", sidebarOpen ? "w-auto opacity-100" : "w-0 opacity-0")}>
|
|
<h2 className="text-xs font-mono font-bold text-white tracking-widest uppercase whitespace-nowrap">
|
|
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 whitespace-nowrap">
|
|
Active
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Toggle Button */}
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => setSidebarOpen(!sidebarOpen)}
|
|
className={cn(
|
|
"ml-auto text-kodo-secondary hover:text-white hidden lg:flex",
|
|
!sidebarOpen && "absolute left-1/2 -translate-x-1/2 top-20"
|
|
)}
|
|
aria-label={sidebarOpen ? 'Collapse sidebar' : 'Expand sidebar'}
|
|
>
|
|
{sidebarOpen ? (
|
|
<ChevronLeft className="w-4 h-4" />
|
|
) : (
|
|
<ChevronRight className="w-4 h-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Scrollable Nav Content */}
|
|
<div className="flex-1 overflow-y-auto custom-scrollbar p-4 space-y-6">
|
|
{navItems.map((group, idx) => (
|
|
<div key={idx} className="mb-4">
|
|
<h3 className={cn(
|
|
"text-[9px] font-mono font-bold text-kodo-steel/60 uppercase tracking-[0.2em] mb-3 px-4 flex items-center gap-2 transition-all duration-300",
|
|
!sidebarOpen && "justify-center opacity-0 h-0 overflow-hidden mb-0"
|
|
)}>
|
|
{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',
|
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kodo-cyan focus-visible:ring-offset-2 focus-visible:ring-offset-kodo-void',
|
|
// Styles Actifs "Vibrant"
|
|
isActive
|
|
? 'bg-gradient-to-r from-kodo-cyan/20 to-transparent text-white shadow-[0_0_20px_rgba(var(--kodo-cyan),0.15)] border-l-2 border-kodo-cyan'
|
|
: 'text-kodo-secondary hover:text-white hover:bg-white/5 border-l-2 border-transparent',
|
|
!sidebarOpen && "justify-center px-2"
|
|
)}
|
|
>
|
|
{/* Active Glow Overlay */}
|
|
{isActive && (
|
|
<div className="absolute inset-0 bg-kodo-cyan/5 animate-pulse-slow pointer-events-none" />
|
|
)}
|
|
|
|
<div className={cn("flex items-center gap-4 relative z-10", !sidebarOpen && "justify-center")}>
|
|
<span
|
|
className={cn(
|
|
'transition-colors duration-200 flex-shrink-0',
|
|
isActive
|
|
? 'text-kodo-cyan drop-shadow-[0_0_8px_rgba(var(--kodo-cyan),0.6)]'
|
|
: 'text-kodo-secondary group-hover:text-white',
|
|
)}
|
|
>
|
|
{item.icon}
|
|
</span>
|
|
|
|
<span className={cn(
|
|
"transition-all duration-300 whitespace-nowrap origin-left",
|
|
sidebarOpen ? "w-auto opacity-100 scale-100" : "w-0 opacity-0 scale-95 hidden"
|
|
)}>
|
|
{item.label}
|
|
</span>
|
|
</div>
|
|
|
|
{item.badge && sidebarOpen && (
|
|
<span className="ml-auto 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 shadow-[0_0_10px_rgba(var(--kodo-magenta),0.2)]">
|
|
{item.badge}
|
|
</span>
|
|
)}
|
|
|
|
{/* Dot for collapsed state */}
|
|
{item.badge && !sidebarOpen && (
|
|
<span className="absolute top-2 right-2 w-2 h-2 rounded-full bg-kodo-magenta border border-kodo-void shadow-[0_0_8px_rgba(var(--kodo-magenta),0.5)]" />
|
|
)}
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Footer Actions */}
|
|
<div className="p-4 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-4 px-4 py-2.5 text-sm rounded-xl transition-all',
|
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kodo-cyan focus-visible:ring-offset-2 focus-visible:ring-offset-kodo-void',
|
|
activeView === 'settings'
|
|
? 'bg-kodo-cyan/10 text-kodo-cyan border-l-2 border-kodo-cyan'
|
|
: 'text-kodo-secondary hover:text-white hover:bg-white/5 border-l-2 border-transparent',
|
|
!sidebarOpen && "justify-center"
|
|
)}
|
|
title={!sidebarOpen ? "Settings" : undefined}
|
|
>
|
|
<Settings className="w-4 h-4 flex-shrink-0" />
|
|
<span className={cn("transition-all duration-300 whitespace-nowrap", sidebarOpen ? "opacity-100" : "w-0 opacity-0 hidden")}>
|
|
Settings
|
|
</span>
|
|
</Link>
|
|
|
|
<Button
|
|
variant="ghost"
|
|
onClick={handleLogout}
|
|
className={cn(
|
|
"w-full text-kodo-red/80 hover:text-kodo-red hover:bg-kodo-red/10 mt-1 gap-4",
|
|
sidebarOpen ? "justify-start" : "justify-center px-0"
|
|
)}
|
|
title={!sidebarOpen ? "Sign Out" : undefined}
|
|
>
|
|
<LogOut className="w-4 h-4 flex-shrink-0" />
|
|
<span className={cn("transition-all duration-300 whitespace-nowrap", sidebarOpen ? "opacity-100" : "w-0 opacity-0 hidden")}>
|
|
Initialize Signout
|
|
</span>
|
|
</Button>
|
|
</div>
|
|
</aside>
|
|
</>
|
|
);
|
|
};
|