refactor: organize sidebar navigation into categorized sections with Overview, Access, and Infrastructure groups

Group navigation items into three sections with section titles. Add nav-sections and nav-section containers with gap spacing and uppercase section title styling.

Remove topbar icon wrapper and associated styling. Center sidebar brand logo with justify-content and add bottom padding. Adjust nav margin-top to 0 and move gap spacing to nav-sections level.

Move page-title-icon dimensions
This commit is contained in:
2026-03-24 17:58:22 +01:00
parent 8282c1fbf4
commit c1f1b6c41f
2 changed files with 82 additions and 44 deletions

View File

@@ -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 (
<div className="shell">
@@ -28,31 +42,35 @@ export function Layout({ onLogout }: LayoutProps) {
<div className="sidebar-brand">
<img className="sidebar-brand-logo" src="/NexaVPN_Logo.png" alt="NexaVPN" />
</div>
<nav className="nav">
{items.map(([label, path, iconKey]) => {
const Icon = appIconMap[iconKey];
return (
<NavLink
key={path}
to={path}
className={({ isActive }) => (isActive ? "nav-link active" : "nav-link")}
>
<span className="nav-link-copy">
<Icon className="nav-link-icon" size={18} strokeWidth={2} />
<span>{label}</span>
</span>
<ChevronRight className="nav-link-chevron" size={16} strokeWidth={2} />
</NavLink>
);
})}
</nav>
<div className="nav-sections">
{sections.map((section) => (
<div className="nav-section" key={section.title}>
<p className="nav-section-title">{section.title}</p>
<nav className="nav">
{section.items.map(([label, path, iconKey]) => {
const Icon = appIconMap[iconKey];
return (
<NavLink
key={path}
to={path}
className={({ isActive }) => (isActive ? "nav-link active" : "nav-link")}
>
<span className="nav-link-copy">
<Icon className="nav-link-icon" size={18} strokeWidth={2} />
<span>{label}</span>
</span>
<ChevronRight className="nav-link-chevron" size={16} strokeWidth={2} />
</NavLink>
);
})}
</nav>
</div>
))}
</div>
</aside>
<main className="content">
<header className="topbar">
<div className="topbar-brand">
<div className="topbar-icon-wrap" aria-hidden="true">
<TopbarIcon size={22} strokeWidth={2} />
</div>
<div className="topbar-copy">
<p className="eyebrow">Enterprise WireGuard</p>
<h2>Self-hosted VPN management</h2>

View File

@@ -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;