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) {
NexaVPN
- +
+ {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;