92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
import React from 'react';
|
|
import {
|
|
LayoutDashboard,
|
|
HardDrive,
|
|
FolderSync,
|
|
Layers,
|
|
LineChart,
|
|
GitBranch,
|
|
FileTerminal,
|
|
Server,
|
|
Settings,
|
|
} from 'lucide-react';
|
|
|
|
interface SidebarProps {
|
|
currentTab: string;
|
|
onSelectTab: (tab: string) => void;
|
|
}
|
|
|
|
export const Sidebar: React.FC<SidebarProps> = ({ currentTab, onSelectTab }) => {
|
|
const navItems = [
|
|
{ id: 'overview', label: 'Dashboard', icon: LayoutDashboard },
|
|
{ id: 'storage', label: 'Storage & ZFS', icon: HardDrive, badge: 'ZFS' },
|
|
{ id: 'shares', label: 'File Shares', icon: FolderSync, badge: 'SMB/NFS' },
|
|
{ id: 'apps', label: 'App Store & Runtime', icon: Layers },
|
|
{ id: 'analytics', label: 'Perses Analytics', icon: LineChart, badge: 'Native' },
|
|
{ id: 'gitops', label: 'GitOps & Updates', icon: GitBranch },
|
|
{ id: 'logs', label: 'System Logs', icon: FileTerminal },
|
|
{ id: 'wizard', label: 'Setup Wizard', icon: Settings },
|
|
];
|
|
|
|
return (
|
|
<aside className="w-64 bg-slate-900/90 border-r border-slate-800 flex flex-col backdrop-blur-xl shrink-0">
|
|
{/* Brand Header */}
|
|
<div className="h-16 flex items-center px-6 gap-3 border-b border-slate-800">
|
|
<div className="h-10 w-10 rounded-xl bg-slate-950 border border-slate-800/80 p-1 flex items-center justify-center shadow-lg shadow-emerald-500/10 shrink-0">
|
|
<img src="/logo.svg" alt="NaxOS Logo" className="h-full w-full object-contain" />
|
|
</div>
|
|
<div>
|
|
<div className="font-bold tracking-tight text-white flex items-center gap-1.5">
|
|
NaxOS <span className="text-xs px-1.5 py-0.5 rounded-full bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">v1.0</span>
|
|
</div>
|
|
<p className="text-[11px] text-slate-400">Declarative ZFS Appliance</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Nav List */}
|
|
<nav className="flex-1 px-3 py-4 space-y-1">
|
|
{navItems.map((item) => {
|
|
const Icon = item.icon;
|
|
const isActive = currentTab === item.id;
|
|
return (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => onSelectTab(item.id)}
|
|
className={`w-full flex items-center gap-3 px-3.5 py-2.5 rounded-xl text-sm font-medium transition-all ${
|
|
isActive
|
|
? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 shadow-sm'
|
|
: 'text-slate-400 hover:text-slate-200 hover:bg-slate-800/60'
|
|
}`}
|
|
>
|
|
<Icon className={`h-4 w-4 ${isActive ? 'text-emerald-400' : 'text-slate-400'}`} />
|
|
<span className="flex-1 text-left">{item.label}</span>
|
|
{item.badge && (
|
|
<span
|
|
className={`text-[10px] px-1.5 py-0.5 rounded font-mono font-semibold ${
|
|
isActive
|
|
? 'bg-emerald-500/20 text-emerald-300'
|
|
: 'bg-slate-800 text-slate-400'
|
|
}`}
|
|
>
|
|
{item.badge}
|
|
</span>
|
|
)}
|
|
</button>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* Appliance Status Footer */}
|
|
<div className="p-4 border-t border-slate-800 bg-slate-950/40">
|
|
<div className="flex items-center gap-2.5">
|
|
<div className="h-2.5 w-2.5 rounded-full bg-emerald-500 animate-pulse" />
|
|
<div className="text-xs">
|
|
<p className="text-slate-200 font-medium">System Protected</p>
|
|
<p className="text-slate-500 text-[11px]">GitOps Synchronized</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
);
|
|
};
|