diff --git a/dashboard/app/(private)/layout.tsx b/dashboard/app/(private)/layout.tsx index 12a66f7..b47b8f6 100644 --- a/dashboard/app/(private)/layout.tsx +++ b/dashboard/app/(private)/layout.tsx @@ -6,14 +6,67 @@ import { Globe, Home, Layers, + Menu, Smartphone, Terminal, + X, } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; import { usePathname } from "next/navigation"; import type React from "react"; +import { useEffect, useState } from "react"; import Profile from "@/app/components/profile"; +import { useConfig } from "@/app/hooks/config"; + +const navigationItems = [ + { basePath: "/dashboard", label: "Dashboard", icon: Home }, + { basePath: "/devices", label: "Devices", icon: Cpu }, + { basePath: "/distributions", label: "Distributions", icon: Layers }, + { basePath: "/commands", label: "Commands", icon: Terminal }, + { basePath: "/ip-addresses", label: "IP Addresses", icon: Globe }, + { basePath: "/modems", label: "Modems", icon: Smartphone }, +]; + +function SidebarLabel({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +function MobileSidebarLabel({ children }: { children: React.ReactNode }) { + return ( + {children} + ); +} + +function useApiVersion() { + const { config } = useConfig(); + const [version, setVersion] = useState(null); + + useEffect(() => { + if (!config?.API_BASE_URL) return; + const controller = new AbortController(); + fetch(`${config.API_BASE_URL}/health`, { signal: controller.signal }) + .then((res) => { + if (!res.ok) return; + return res.text().then((text) => { + // Response format: "I'm good: 0.2.116" + const match = text.match(/:\s*(.+)/); + if (match) setVersion(match[1].trim()); + }); + }) + .catch((err) => { + if (err?.name === "AbortError") return; + console.error("Failed to fetch API version:", err); + }); + return () => controller.abort(); + }, [config?.API_BASE_URL]); + + return version; +} export default function PrivateLayout({ children, @@ -21,38 +74,130 @@ export default function PrivateLayout({ children: React.ReactNode; }>) { const pathname = usePathname(); - const navigationItems = [ - { basePath: "/dashboard", label: "Dashboard", icon: Home }, - { basePath: "/devices", label: "Devices", icon: Cpu }, - { - basePath: "/distributions", - label: "Distributions", - icon: Layers, - }, - { basePath: "/commands", label: "Commands", icon: Terminal }, - { - basePath: "/ip-addresses", - label: "IP Addresses", - icon: Globe, - }, - { basePath: "/modems", label: "Modems", icon: Smartphone }, - ]; - const isActive = (path: string) => { - return pathname.startsWith(path); - }; + const [mobileOpen, setMobileOpen] = useState(false); + const apiVersion = useApiVersion(); + + const isActive = (path: string) => pathname.startsWith(path); return ( -
- {/* Top Navigation Bar */} -
-
-
- {/* Left side - Logo and Navigation */} -
- {/* Logo */} +
+ {/* Desktop sidebar */} + + + {/* Mobile top bar */} +
+ + + Smith Logo + +
+ + {/* Mobile sidebar overlay */} + {mobileOpen && ( +
+ {/* Backdrop */} +
setMobileOpen(false)} + onKeyDown={() => {}} + role="presentation" + /> + {/* Sidebar panel */} +
- - {/* Mobile Navigation */} -
- +
-
+ )} - {/* Main Content */} -
{children}
+ {/* Main content */} +
+
{children}
+
); } diff --git a/dashboard/app/components/profile.tsx b/dashboard/app/components/profile.tsx index 1443ad3..f3feb03 100644 --- a/dashboard/app/components/profile.tsx +++ b/dashboard/app/components/profile.tsx @@ -1,11 +1,16 @@ "use client"; import { useAuth0 } from "@auth0/auth0-react"; -import { ChevronDown, LogOut, User } from "lucide-react"; +import { ChevronDown, ChevronRight, LogOut, User } from "lucide-react"; import Image from "next/image"; import { useEffect, useRef, useState } from "react"; -export default function Profile() { +interface ProfileProps { + sidebar?: boolean; + expanded?: boolean; +} + +export default function Profile({ sidebar, expanded }: ProfileProps) { const { user, isAuthenticated, isLoading, logout } = useAuth0(); const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); @@ -33,9 +38,14 @@ export default function Profile() { if (isLoading) { return ( -
-
-
+
+
); } @@ -48,6 +58,86 @@ export default function Profile() { logout({ logoutParams: { returnTo: window.location.origin } }); }; + if (sidebar) { + return ( +
+ + + {isOpen && ( +
+
+
+ {user.picture ? ( + {user.name + ) : ( +
+ +
+ )} +
+

+ {user.name} +

+

{user.email}

+
+
+
+ +
+ +
+
+ )} +
+ ); + } + return (