diff --git a/admin-web/src/components/Layout.tsx b/admin-web/src/components/Layout.tsx
index 139d52f..2bfcaf2 100644
--- a/admin-web/src/components/Layout.tsx
+++ b/admin-web/src/components/Layout.tsx
@@ -1,26 +1,40 @@
import { ChevronRight } from "lucide-react";
import { NavLink, Outlet } from "react-router-dom";
-import { appIconMap, topbarIcon, type AppIconKey } from "./app-icons";
+import { appIconMap, type AppIconKey } from "./app-icons";
-const items = [
- ["Dashboard", "/", "dashboard"],
- ["Users", "/users", "users"],
- ["Groups", "/groups", "groups"],
- ["Devices", "/devices", "devices"],
- ["Services", "/services", "services"],
- ["Policies", "/policies", "policies"],
- ["Gateways", "/gateways", "gateways"],
- ["Audit", "/audit", "audit"],
- ["Settings", "/settings", "settings"]
-] as const satisfies ReadonlyArray<[string, string, AppIconKey]>;
+const sections = [
+ {
+ title: "Overview",
+ items: [["Dashboard", "/", "dashboard"]]
+ },
+ {
+ title: "Access",
+ items: [
+ ["Users", "/users", "users"],
+ ["Groups", "/groups", "groups"],
+ ["Devices", "/devices", "devices"],
+ ["Services", "/services", "services"],
+ ["Policies", "/policies", "policies"]
+ ]
+ },
+ {
+ title: "Infrastructure",
+ items: [
+ ["Gateways", "/gateways", "gateways"],
+ ["Audit", "/audit", "audit"],
+ ["Settings", "/settings", "settings"]
+ ]
+ }
+] as const satisfies ReadonlyArray<{
+ title: string;
+ items: ReadonlyArray<[string, string, AppIconKey]>;
+}>;
type LayoutProps = {
onLogout: () => void;
};
-const TopbarIcon = topbarIcon;
-
export function Layout({ onLogout }: LayoutProps) {
return (
@@ -28,31 +42,35 @@ export function Layout({ onLogout }: LayoutProps) {
-
+
+ {sections.map((section) => (
+
+
{section.title}
+
+
+ ))}
+
-
-
-
Enterprise WireGuard
Self-hosted VPN management
diff --git a/admin-web/src/styles/global.css b/admin-web/src/styles/global.css
index a512253..6e7e2d2 100644
--- a/admin-web/src/styles/global.css
+++ b/admin-web/src/styles/global.css
@@ -92,7 +92,6 @@ button {
align-items: flex-start;
}
-.topbar-icon-wrap,
.page-title-icon {
display: grid;
place-items: center;
@@ -174,6 +173,8 @@ button {
.sidebar-brand {
display: flex;
align-items: center;
+ justify-content: center;
+ padding-bottom: 10px;
}
.sidebar-brand-logo {
@@ -182,10 +183,30 @@ button {
display: block;
}
+.nav-sections {
+ display: grid;
+ gap: 26px;
+ margin-top: 24px;
+}
+
+.nav-section {
+ display: grid;
+ gap: 12px;
+}
+
+.nav-section-title {
+ margin: 0;
+ padding: 0 12px;
+ color: var(--muted);
+ font-size: 0.76rem;
+ text-transform: uppercase;
+ letter-spacing: 0.16em;
+}
+
.nav {
display: grid;
gap: 10px;
- margin-top: 28px;
+ margin-top: 0;
}
.nav-link {
@@ -243,13 +264,6 @@ button {
gap: 6px;
}
-.topbar-icon-wrap,
-.page-title-icon {
- width: 52px;
- height: 52px;
- border-radius: 18px;
-}
-
.page {
display: grid;
gap: 22px;
@@ -283,6 +297,12 @@ button {
max-width: 760px;
}
+.page-title-icon {
+ width: 52px;
+ height: 52px;
+ border-radius: 18px;
+}
+
.grid {
display: grid;
gap: 18px;