diff --git a/apps/dashboard/components/DashboardLayout.tsx b/apps/dashboard/components/DashboardLayout.tsx index 377db71..233edd0 100644 --- a/apps/dashboard/components/DashboardLayout.tsx +++ b/apps/dashboard/components/DashboardLayout.tsx @@ -35,9 +35,14 @@ export default function DashboardLayout({ return (
{initialGuildId ? : null} - -
-
+ +
+
{children}
diff --git a/apps/dashboard/components/Header.tsx b/apps/dashboard/components/Header.tsx index 5b9077c..4a28039 100644 --- a/apps/dashboard/components/Header.tsx +++ b/apps/dashboard/components/Header.tsx @@ -1,21 +1,55 @@ export default function Header({ title, subtitle, + onMenuClick, + menuOpen, }: { title: string; subtitle?: string; + /** Opens the mobile sidebar drawer. Omit to hide the toggle button. */ + onMenuClick?: () => void; + /** Whether the mobile sidebar drawer is currently open. */ + menuOpen?: boolean; }) { return ( -
-

- {title} -

+
+ {onMenuClick && ( + + )} - {subtitle ? ( -

- {subtitle} -

- ) : null} +
+

+ {title} +

+ + {subtitle ? ( +

+ {subtitle} +

+ ) : null} +
); } \ No newline at end of file diff --git a/apps/dashboard/components/Sidebar.tsx b/apps/dashboard/components/Sidebar.tsx index 7250a4c..a481b0b 100644 --- a/apps/dashboard/components/Sidebar.tsx +++ b/apps/dashboard/components/Sidebar.tsx @@ -1,19 +1,11 @@ "use client"; +import { useEffect, useRef } from "react"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; import type { Session } from "@/lib/auth/session"; import { useOptionalGuild } from "@/lib/guild/GuildProvider"; - -const navItems = [ - { name: "Dashboard", href: "/dashboard", icon: "📊" }, - { name: "Passes", href: "/passes", icon: "🎫" }, - { name: "Guilds", href: "/guilds", icon: "🏰" }, - { name: "Members", href: "/members", icon: "👥" }, - { name: "Activity", href: "/activity", icon: "📋" }, - { name: "Integrations", href: "/integrations", icon: "🔌" }, - { name: "Settings", href: "/settings", icon: "⚙️" }, -]; +import { navItems, isNavItemActive } from "@/lib/nav-items"; /** * Human-readable label + colour for each role, @@ -61,11 +53,29 @@ export default function Sidebar({ const pathname = usePathname(); const router = useRouter(); const guildCtx = useOptionalGuild(); + const closeButtonRef = useRef(null); const badge = session?.role ? ROLE_BADGE[session.role] : null; + // Mobile drawer: close on Escape and move focus to the close + // button so keyboard users land somewhere sensible. + useEffect(() => { + if (!isOpen || !onClose) return; + + closeButtonRef.current?.focus(); + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + onClose(); + } + }; + + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, [isOpen, onClose]); + const handleGuildChange = (nextId: string) => { if (!guildCtx || nextId === guildCtx.guildId) { return; @@ -90,8 +100,19 @@ export default function Sidebar({ }; return ( -
+ {/* Mobile backdrop — click or Escape to close */} + {isOpen && onClose && ( +