feat(ui): implement modern NaxOS Web Dashboard with Perses analytics and ZFS migration wizard
CI & Build NaxOS Web Dashboard / build (push) Failing after 1m29s
CI & Build NaxOS Web Dashboard / build (push) Failing after 1m29s
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
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-9 w-9 rounded-xl bg-gradient-to-tr from-emerald-500 to-teal-400 p-0.5 shadow-lg shadow-emerald-500/20">
|
||||
<div className="h-full w-full bg-slate-950 rounded-[10px] flex items-center justify-center">
|
||||
<Server className="h-5 w-5 text-emerald-400" />
|
||||
</div>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user