From 4d4e4fba9b9265b72a52f3e67897e9266959de91 Mon Sep 17 00:00:00 2001 From: Stavros Date: Fri, 27 Mar 2026 00:27:42 +0200 Subject: [PATCH] feat: use dropdown menu as navigation on mobile devices (#1840) Co-authored-by: henrygd --- internal/site/src/components/add-system.tsx | 26 +-- internal/site/src/components/navbar.tsx | 208 ++++++++++++++------ 2 files changed, 151 insertions(+), 83 deletions(-) diff --git a/internal/site/src/components/add-system.tsx b/internal/site/src/components/add-system.tsx index 1ee852f5..7d04ddbc 100644 --- a/internal/site/src/components/add-system.tsx +++ b/internal/site/src/components/add-system.tsx @@ -1,8 +1,8 @@ -import { msg, t } from "@lingui/core/macro" +import { t } from "@lingui/core/macro" import { Trans } from "@lingui/react/macro" import { useStore } from "@nanostores/react" import { getPagePath } from "@nanostores/router" -import { ChevronDownIcon, ExternalLinkIcon, PlusIcon } from "lucide-react" +import { ChevronDownIcon, ExternalLinkIcon } from "lucide-react" import { memo, useEffect, useRef, useState } from "react" import { Button } from "@/components/ui/button" import { @@ -12,7 +12,6 @@ import { DialogFooter, DialogHeader, DialogTitle, - DialogTrigger, } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" @@ -35,28 +34,19 @@ import { DropdownMenu, DropdownMenuTrigger } from "./ui/dropdown-menu" import { AppleIcon, DockerIcon, FreeBsdIcon, TuxIcon, WindowsIcon } from "./ui/icons" import { InputCopy } from "./ui/input-copy" -export function AddSystemButton({ className }: { className?: string }) { - if (isReadOnlyUser()) { - return null - } - const [open, setOpen] = useState(false) +// To avoid a refactor of the dialog, we will just keep this function as a "skeleton" for the actual dialog +export function AddSystemDialog({ open, setOpen }: { open: boolean; setOpen: (open: boolean) => void }) { const opened = useRef(false) if (open) { opened.current = true } + if (isReadOnlyUser()) { + return null + } + return ( - - - {opened.current && } ) diff --git a/internal/site/src/components/navbar.tsx b/internal/site/src/components/navbar.tsx index 73db27e3..4c42ecd6 100644 --- a/internal/site/src/components/navbar.tsx +++ b/internal/site/src/components/navbar.tsx @@ -6,6 +6,8 @@ import { HardDriveIcon, LogOutIcon, LogsIcon, + MenuIcon, + PlusIcon, SearchIcon, ServerIcon, SettingsIcon, @@ -21,15 +23,18 @@ import { DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { isAdmin, isReadOnlyUser, logOut, pb } from "@/lib/api" import { cn, runOnce } from "@/lib/utils" -import { AddSystemButton } from "./add-system" +import { AddSystemDialog } from "./add-system" import { LangToggle } from "./lang-toggle" import { Logo } from "./logo" import { ModeToggle } from "./mode-toggle" -import { $router, basePath, Link, prependBasePath } from "./router" +import { $router, basePath, Link, navigate, prependBasePath } from "./router" import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip" const CommandPalette = lazy(() => import("./command-palette")) @@ -37,8 +42,18 @@ const CommandPalette = lazy(() => import("./command-palette")) const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0 export default function Navbar() { + const [addSystemDialogOpen, setAddSystemDialogOpen] = useState(false) + const [commandPaletteOpen, setCommandPaletteOpen] = useState(false) + + const AdminLinks = AdminDropdownGroup() + return (
+ + + + + - + + {/* mobile menu */} +
+ + + + import("@/components/routes/settings/general")} + className="ms-3" + aria-label="Open Menu" + > + + + + {pb.authStore.record?.email} + + + navigate(getPagePath($router, "home"))} className="flex items-center"> + + All Containers + + navigate(getPagePath($router, "smart"))} className="flex items-center"> + + S.M.A.R.T. + + navigate(getPagePath($router, "settings", { name: "general" }))} + className="flex items-center" + > + + Settings + + {isAdmin() && ( + + + + Admin + + {AdminLinks} + + )} + { + setAddSystemDialogOpen(true) + }} + > + + Add System + + + + + + + Log Out + + + + +
+ + {/* desktop nav */} {/** biome-ignore lint/a11y/noStaticElementInteractions: ignore */} -
import("@/components/routes/settings/general")}> +
import("@/components/routes/settings/general")} + > {pb.authStore.record?.email} - - {isAdmin() && ( - <> - - - - - Users - - - - - - - - Systems - - - - - - - - Logs - - - - - - - - Backups - - - - - - )} - + {isAdmin() && ( + <> + {AdminLinks} + + + )} @@ -149,7 +211,10 @@ export default function Navbar() { - +
) @@ -161,28 +226,41 @@ const Kbd = ({ children }: { children: React.ReactNode }) => ( ) -function SearchButton() { - const [open, setOpen] = useState(false) - +function AdminDropdownGroup() { return ( - <> - - - - - + + + + + + + Systems + + + + + + + + Logs + + + + + + + + Backups + + + + ) }