Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5040ac2f16 | ||
|
|
926a1b8165 |
@@ -730,7 +730,8 @@ EOF
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now "$SERVICE_NAME.service"
|
||||
systemctl enable "$SERVICE_NAME.service"
|
||||
systemctl restart "$SERVICE_NAME.service"
|
||||
systemctl status "$SERVICE_NAME.service" --no-pager || true
|
||||
else
|
||||
echo "systemctl not found. Run manually: python3 $INSTALL_DIR/nexafabric-agent.py --config $CONFIG_DIR/config.json"
|
||||
|
||||
@@ -18,31 +18,76 @@ import {
|
||||
SquareStack,
|
||||
Sun,
|
||||
Users,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { token } from "../api/client";
|
||||
import { useTheme } from "../stores/theme";
|
||||
|
||||
const nav = [
|
||||
const navGroups = [
|
||||
{
|
||||
label: "Operate",
|
||||
items: [
|
||||
{ to: "/", label: "Dashboard", icon: LayoutDashboard },
|
||||
{ to: "/clusters", label: "Clusters", icon: Server },
|
||||
{ to: "/nodes", label: "Nodes", icon: Activity },
|
||||
{ to: "/workloads", label: "VMs/LXCs", icon: Blocks },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Network",
|
||||
items: [
|
||||
{ to: "/networks", label: "Networks", icon: Network },
|
||||
{ to: "/ipam", label: "IPAM", icon: Database },
|
||||
{ to: "/services", label: "Service Catalog", icon: SquareStack },
|
||||
{ to: "/tenants", label: "Tenants", icon: BriefcaseBusiness },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Security",
|
||||
items: [
|
||||
{ to: "/security-groups", label: "Security Groups", icon: Shield },
|
||||
{ to: "/policies", label: "Policies", icon: GitBranch },
|
||||
{ to: "/services", label: "Service Catalog", icon: SquareStack },
|
||||
{ to: "/designer", label: "Policy Designer", icon: LockKeyhole },
|
||||
{ to: "/firewall", label: "Firewall Preview", icon: Flame },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const systemNav = [
|
||||
{ to: "/jobs", label: "Jobs", icon: ClipboardList },
|
||||
{ to: "/audit", label: "Audit Logs", icon: BookOpen },
|
||||
{ to: "/users", label: "Users", icon: Users },
|
||||
{ to: "/settings", label: "Settings", icon: Settings },
|
||||
];
|
||||
|
||||
function SidebarLink({ item }: { item: { to: string; label: string; icon: LucideIcon } }) {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<NavLink
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
end={item.to === "/"}
|
||||
className={({ isActive }) =>
|
||||
`group relative flex h-9 items-center gap-3 rounded-md px-3 text-sm transition-colors ${
|
||||
isActive
|
||||
? "bg-accent/10 text-accent dark:bg-accent/15"
|
||||
: "text-slate-600 hover:bg-slate-100 hover:text-slate-950 dark:text-slate-300 dark:hover:bg-slate-800/80 dark:hover:text-white"
|
||||
}`
|
||||
}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<>
|
||||
<span className={`absolute left-0 h-5 w-0.5 rounded-r-full ${isActive ? "bg-accent" : "bg-transparent"}`} />
|
||||
<Icon size={17} className={isActive ? "text-accent" : "text-slate-400 group-hover:text-slate-700 dark:group-hover:text-slate-200"} />
|
||||
<span className="truncate">{item.label}</span>
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
|
||||
export function Layout() {
|
||||
const navigate = useNavigate();
|
||||
const { dark, toggle } = useTheme();
|
||||
@@ -60,29 +105,31 @@ export function Layout() {
|
||||
<div className="min-h-screen bg-canvas text-slate-900 dark:text-slate-100">
|
||||
<aside className="fixed inset-y-0 left-0 hidden w-64 border-r border-border bg-panel md:block">
|
||||
<div className="flex h-16 items-center border-b border-border px-5">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="grid h-9 w-9 place-items-center rounded-md bg-accent text-sm font-semibold text-white">NF</div>
|
||||
<div>
|
||||
<div className="text-lg font-semibold">NexaFabric</div>
|
||||
<div className="text-base font-semibold leading-5">NexaFabric</div>
|
||||
<div className="text-xs text-slate-500 dark:text-slate-400">Control Plane</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="h-[calc(100vh-4rem)] overflow-y-auto p-3">
|
||||
{nav.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<NavLink
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
className={({ isActive }) =>
|
||||
`mb-1 flex h-10 items-center gap-3 rounded-md px-3 text-sm ${
|
||||
isActive ? "bg-accent text-white" : "text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800"
|
||||
}`
|
||||
}
|
||||
>
|
||||
<Icon size={18} />
|
||||
<span>{item.label}</span>
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<nav className="flex h-[calc(100vh-4rem)] flex-col overflow-y-auto p-3">
|
||||
<div className="space-y-5">
|
||||
{navGroups.map((group) => (
|
||||
<section key={group.label}>
|
||||
<div className="mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-slate-400 dark:text-slate-500">{group.label}</div>
|
||||
<div className="space-y-1">
|
||||
{group.items.map((item) => <SidebarLink key={item.to} item={item} />)}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-auto border-t border-border pt-3">
|
||||
<div className="mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-slate-400 dark:text-slate-500">System</div>
|
||||
<div className="space-y-1">
|
||||
{systemNav.map((item) => <SidebarLink key={item.to} item={item} />)}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</aside>
|
||||
<main className="md:pl-64">
|
||||
|
||||
Reference in New Issue
Block a user