From f800d7d59df73057fd12447ddf92ef2d1f4e7edf Mon Sep 17 00:00:00 2001 From: rcourtman Date: Thu, 23 Oct 2025 18:09:09 +0000 Subject: [PATCH] refactor: unify agent hub layout and copy --- .../src/components/Dashboard/Dashboard.tsx | 15 +- .../components/Settings/AgentStepSection.tsx | 34 ++++ .../src/components/Settings/DockerAgents.tsx | 4 +- .../src/components/Settings/Settings.tsx | 168 +++++++++++------- 4 files changed, 144 insertions(+), 77 deletions(-) create mode 100644 frontend-modern/src/components/Settings/AgentStepSection.tsx diff --git a/frontend-modern/src/components/Dashboard/Dashboard.tsx b/frontend-modern/src/components/Dashboard/Dashboard.tsx index 0e256ee..f9b923c 100644 --- a/frontend-modern/src/components/Dashboard/Dashboard.tsx +++ b/frontend-modern/src/components/Dashboard/Dashboard.tsx @@ -1,5 +1,4 @@ import { createSignal, createMemo, createEffect, For, Show, onMount } from 'solid-js'; -import { createStore, reconcile } from 'solid-js/store'; import { useNavigate } from '@solidjs/router'; import type { VM, Container, Node } from '@/types/api'; import { GuestRow } from './GuestRow'; @@ -42,15 +41,8 @@ export function Dashboard(props: DashboardProps) { const [selectedNode, setSelectedNode] = createSignal(null); const [guestMetadata, setGuestMetadata] = createSignal>({}); - // Stable guest store using reconcile to prevent row remounting during websocket updates - const guestKey = (g: VM | Container) => - g.id ?? (g.instance === g.node ? `${g.node}-${g.vmid}` : `${g.instance}-${g.node}-${g.vmid}`); - const [guestStore, setGuestStore] = createStore<(VM | Container)[]>([]); - - // Reconcile guests whenever props change - createEffect(() => { - setGuestStore(reconcile([...props.vms, ...props.containers], { key: guestKey, merge: true })); - }); + // Combine VMs and containers into a single list for filtering + const allGuests = createMemo<(VM | Container)[]>(() => [...props.vms, ...props.containers]); // Initialize from localStorage with proper type checking const [viewMode, setViewMode] = usePersistentSignal('dashboardViewMode', 'all', { @@ -248,9 +240,6 @@ export function Dashboard(props: DashboardProps) { return () => document.removeEventListener('keydown', handleKeyDown); }); - // Use the stable guest store - const allGuests = createMemo(() => guestStore); - // Filter guests based on current settings const filteredGuests = createMemo(() => { let guests = allGuests(); diff --git a/frontend-modern/src/components/Settings/AgentStepSection.tsx b/frontend-modern/src/components/Settings/AgentStepSection.tsx new file mode 100644 index 0000000..5c1a13c --- /dev/null +++ b/frontend-modern/src/components/Settings/AgentStepSection.tsx @@ -0,0 +1,34 @@ +import type { Component, JSX } from 'solid-js'; +import { Show } from 'solid-js'; + +interface AgentStepSectionProps { + step: string; + title: string; + description?: string; + children: JSX.Element; +} + +export const AgentStepSection: Component = (props) => { + return ( +
+
+
+

+ {props.step} +

+

+ {props.title} +

+
+ +

+ {props.description} +

+
+
+
{props.children}
+
+ ); +}; + +export default AgentStepSection; diff --git a/frontend-modern/src/components/Settings/DockerAgents.tsx b/frontend-modern/src/components/Settings/DockerAgents.tsx index 96b81be..6980c5d 100644 --- a/frontend-modern/src/components/Settings/DockerAgents.tsx +++ b/frontend-modern/src/components/Settings/DockerAgents.tsx @@ -46,8 +46,8 @@ export const DockerAgents: Component = () => { const [generateError, setGenerateError] = createSignal(null); const [latestRecord, setLatestRecord] = createSignal(null); - const tokenStepLabel = 'Step 1 · Generate API token'; - const commandStepLabel = 'Step 2 · Install command'; + const tokenStepLabel = 'Step 2 · Generate API token'; + const commandStepLabel = 'Step 3 · Install command'; const { token: apiToken, diff --git a/frontend-modern/src/components/Settings/Settings.tsx b/frontend-modern/src/components/Settings/Settings.tsx index 8ee6554..bc8ed17 100644 --- a/frontend-modern/src/components/Settings/Settings.tsx +++ b/frontend-modern/src/components/Settings/Settings.tsx @@ -20,6 +20,7 @@ import { SectionHeader } from '@/components/shared/SectionHeader'; import { Toggle } from '@/components/shared/Toggle'; import type { ToggleChangeEvent } from '@/components/shared/Toggle'; import { formField, labelClass, controlClass, formHelpText } from '@/components/shared/Form'; +import { AgentStepSection } from './AgentStepSection'; import Server from 'lucide-solid/icons/server'; import HardDrive from 'lucide-solid/icons/hard-drive'; import Mail from 'lucide-solid/icons/mail'; @@ -448,7 +449,6 @@ const Settings: Component = (props) => { ], }, ]; - const agentCards = agentGroups.flatMap((group) => group.agents); const agentPaths: Record = { pve: '/settings/pve', @@ -2106,66 +2106,85 @@ const Settings: Component = (props) => {
- -
-
-

- Step 1 · Choose a platform -

-

- Select the integration you want to deploy or manage. -

-
-
-
- - {(card) => { - const isActive = () => selectedAgent() === card.id; - return ( - - ); - }} + +
+ + {(card) => { + const isActive = () => selectedAgent() === card.id; + return ( + + ); + }} + +
+ + )}
-
-
+ +
{/* PVE Nodes Tab */} -
+ +
Loading configuration... @@ -2682,11 +2701,17 @@ const Settings: Component = (props) => {
- +
+ {/* PBS Nodes Tab */} -
+ +
Loading configuration... @@ -3092,11 +3117,17 @@ const Settings: Component = (props) => {
- +
+ {/* PMG Nodes Tab */} -
+ +
Loading configuration... @@ -3492,11 +3523,18 @@ const Settings: Component = (props) => {
- +
+ {/* Docker Tab */} - + + + {/* Podman Tab */} @@ -3517,7 +3555,13 @@ const Settings: Component = (props) => { {/* Host Agents */} - + + + {/* System General Tab */}