From 48e06d8cda5b0a76be094bd8f52fb94186a24b63 Mon Sep 17 00:00:00 2001 From: sveng93 Date: Sun, 17 May 2026 09:23:24 +0200 Subject: [PATCH 1/2] feat: add setting for single system routes --- internal/site/src/components/routes/home.tsx | 15 ++++++++++ .../components/routes/settings/general.tsx | 29 ++++++++++++++++++- .../src/components/routes/system/info-bar.tsx | 2 ++ internal/site/src/types.d.ts | 1 + 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/internal/site/src/components/routes/home.tsx b/internal/site/src/components/routes/home.tsx index a635c556f..a56760fe2 100644 --- a/internal/site/src/components/routes/home.tsx +++ b/internal/site/src/components/routes/home.tsx @@ -1,16 +1,31 @@ import { useLingui } from "@lingui/react/macro" +import { useStore } from "@nanostores/react" +import { getPagePath } from "@nanostores/router" import { memo, Suspense, useEffect, useMemo } from "react" +import { $router, navigate } from "@/components/router" import SystemsTable from "@/components/systems-table/systems-table" import { ActiveAlerts } from "@/components/active-alerts" import { FooterRepoLink } from "@/components/footer-repo-link" +import { $systems, $userSettings } from "@/lib/stores" +import { saveSettings } from "@/components/routes/settings/layout" export default memo(() => { const { t } = useLingui() + const systems = useStore($systems) + const { singleNodeMode } = useStore($userSettings, { keys: ["singleNodeMode"] }) useEffect(() => { document.title = `${t`All Systems`} / Beszel` }, [t]) + useEffect(() => { + if (singleNodeMode && systems.length === 1) { + navigate(getPagePath($router, "system", { id: systems[0].id })) + } else if (singleNodeMode && systems.length > 1) { + saveSettings({ singleNodeMode: false }) + } + }, [systems, singleNodeMode]) + return useMemo( () => ( <> diff --git a/internal/site/src/components/routes/settings/general.tsx b/internal/site/src/components/routes/settings/general.tsx index dfd4bc547..c5866bcf4 100644 --- a/internal/site/src/components/routes/settings/general.tsx +++ b/internal/site/src/components/routes/settings/general.tsx @@ -4,6 +4,7 @@ import { LanguagesIcon, LoaderCircleIcon, SaveIcon } from "lucide-react" import { useState } from "react" import { useStore } from "@nanostores/react" import { Button } from "@/components/ui/button" +import { Switch } from "@/components/ui/switch" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" @@ -12,22 +13,26 @@ import Slider from "@/components/ui/slider" import { HourFormat, Unit } from "@/lib/enums" import { dynamicActivate } from "@/lib/i18n" import languages from "@/lib/languages" -import { $userSettings, defaultLayoutWidth } from "@/lib/stores" +import { $systems, $userSettings, defaultLayoutWidth } from "@/lib/stores" import { chartTimeData, currentHour12 } from "@/lib/utils" import type { UserSettings } from "@/types" import { saveSettings } from "./layout" export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) { const [isLoading, setIsLoading] = useState(false) + const [singleNodeMode, setSingleNodeMode] = useState(!!userSettings.singleNodeMode) const { i18n } = useLingui() const currentUserSettings = useStore($userSettings) const layoutWidth = currentUserSettings.layoutWidth ?? defaultLayoutWidth + const systems = useStore($systems) + const multiSystem = systems.length > 1 async function handleSubmit(e: React.FormEvent) { e.preventDefault() setIsLoading(true) const formData = new FormData(e.target as HTMLFormElement) const data = Object.fromEntries(formData) as Partial + data.singleNodeMode = singleNodeMode await saveSettings(data) setIsLoading(false) } @@ -108,6 +113,28 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us /> +
+
+

+ Single node mode +

+

+ Redirect to the system dashboard automatically when only one system is configured. +

+
+
+ + +
+
+

diff --git a/internal/site/src/components/routes/system/info-bar.tsx b/internal/site/src/components/routes/system/info-bar.tsx index 2ef1f0d8e..ba2c9494b 100644 --- a/internal/site/src/components/routes/system/info-bar.tsx +++ b/internal/site/src/components/routes/system/info-bar.tsx @@ -23,6 +23,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" +import AlertButton from "@/components/alerts/alert-button" import { FreeBsdIcon, TuxIcon, WebSocketIcon, WindowsIcon } from "@/components/ui/icons" import { Separator } from "@/components/ui/separator" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" @@ -201,6 +202,7 @@ export default function InfoBar({

+ diff --git a/internal/site/src/types.d.ts b/internal/site/src/types.d.ts index 74c01a2ad..5bd7acccb 100644 --- a/internal/site/src/types.d.ts +++ b/internal/site/src/types.d.ts @@ -297,6 +297,7 @@ export interface UserSettings { colorCrit?: number hourFormat?: HourFormat layoutWidth?: number + singleNodeMode?: boolean } type ChartDataContainer = { From 1bca03826195baaae195a799d67b90e93cbb4c9d Mon Sep 17 00:00:00 2001 From: sveng93 Date: Sun, 17 May 2026 14:40:59 +0200 Subject: [PATCH 2/2] feat: add action menu to the info bar --- internal/site/src/components/alerts/alert-button.tsx | 12 +++++++++--- .../site/src/components/routes/system/info-bar.tsx | 4 +++- .../systems-table/systems-table-columns.tsx | 6 +++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/internal/site/src/components/alerts/alert-button.tsx b/internal/site/src/components/alerts/alert-button.tsx index 05d8a1bcb..bbdfd9a91 100644 --- a/internal/site/src/components/alerts/alert-button.tsx +++ b/internal/site/src/components/alerts/alert-button.tsx @@ -9,7 +9,13 @@ import { cn } from "@/lib/utils" import type { SystemRecord } from "@/types" import { AlertDialogContent } from "./alerts-sheet" -export default memo(function AlertsButton({ system }: { system: SystemRecord }) { +export default memo(function AlertsButton({ + system, + variant = "ghost", +}: { + system: SystemRecord + variant?: "ghost" | "outline" +}) { const [opened, setOpened] = useState(false) const alerts = useStore($alerts) @@ -18,7 +24,7 @@ export default memo(function AlertsButton({ system }: { system: SystemRecord }) () => ( -
- + + diff --git a/internal/site/src/components/systems-table/systems-table-columns.tsx b/internal/site/src/components/systems-table/systems-table-columns.tsx index 432191fae..0f2ab0b14 100644 --- a/internal/site/src/components/systems-table/systems-table-columns.tsx +++ b/internal/site/src/components/systems-table/systems-table-columns.tsx @@ -582,7 +582,7 @@ export function IndicatorDot({ system, className }: { system: SystemRecord; clas ) } -export const ActionsButton = memo(({ system }: { system: SystemRecord }) => { +export const ActionsButton = memo(({ system, variant = "ghost" }: { system: SystemRecord; variant?: "ghost" | "outline" }) => { const [deleteOpen, setDeleteOpen] = useState(false) const [editOpen, setEditOpen] = useState(false) const editOpened = useRef(false) @@ -594,7 +594,7 @@ export const ActionsButton = memo(({ system }: { system: SystemRecord }) => { <> -