From 7241ef61d0f2d1e377df78c48571a72d3fbbbbeb Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sat, 25 Oct 2025 19:19:02 +0000 Subject: [PATCH] Cache guest metadata for faster dashboard links --- .../src/components/Dashboard/Dashboard.tsx | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/frontend-modern/src/components/Dashboard/Dashboard.tsx b/frontend-modern/src/components/Dashboard/Dashboard.tsx index f9b923c..a4877c6 100644 --- a/frontend-modern/src/components/Dashboard/Dashboard.tsx +++ b/frontend-modern/src/components/Dashboard/Dashboard.tsx @@ -20,6 +20,32 @@ import { isNodeOnline } from '@/utils/status'; import { getNodeDisplayName } from '@/utils/nodes'; import { usePersistentSignal } from '@/hooks/usePersistentSignal'; +const GUEST_METADATA_STORAGE_KEY = 'pulseGuestMetadata'; + +const readGuestMetadataCache = (): Record => { + if (typeof window === 'undefined') return {}; + try { + const raw = window.localStorage.getItem(GUEST_METADATA_STORAGE_KEY); + if (!raw) return {}; + const parsed = JSON.parse(raw); + if (parsed && typeof parsed === 'object') { + return parsed as Record; + } + } catch (err) { + console.warn('Failed to parse cached guest metadata:', err); + } + return {}; +}; + +const persistGuestMetadataCache = (metadata: Record) => { + if (typeof window === 'undefined') return; + try { + window.localStorage.setItem(GUEST_METADATA_STORAGE_KEY, JSON.stringify(metadata)); + } catch (err) { + console.warn('Failed to persist guest metadata cache:', err); + } +}; + interface DashboardProps { vms: VM[]; containers: Container[]; @@ -39,7 +65,18 @@ export function Dashboard(props: DashboardProps) { const [search, setSearch] = createSignal(''); const [isSearchLocked, setIsSearchLocked] = createSignal(false); const [selectedNode, setSelectedNode] = createSignal(null); - const [guestMetadata, setGuestMetadata] = createSignal>({}); + const [guestMetadata, setGuestMetadata] = createSignal>( + readGuestMetadataCache(), + ); + + const updateGuestMetadataState = ( + updater: (prev: Record) => Record, + ) => + setGuestMetadata((prev) => { + const next = updater(prev); + persistGuestMetadataCache(next); + return next; + }); // Combine VMs and containers into a single list for filtering const allGuests = createMemo<(VM | Container)[]>(() => [...props.vms, ...props.containers]); @@ -81,7 +118,7 @@ export function Dashboard(props: DashboardProps) { onMount(async () => { try { const metadata = await GuestMetadataAPI.getAllMetadata(); - setGuestMetadata(metadata || {}); + updateGuestMetadataState(() => metadata || {}); } catch (err) { // Silently fail - metadata is optional for display console.debug('Failed to load guest metadata:', err); @@ -90,13 +127,16 @@ export function Dashboard(props: DashboardProps) { // Callback to update a guest's custom URL in metadata const handleCustomUrlUpdate = (guestId: string, url: string) => { - setGuestMetadata((prev) => ({ - ...prev, - [guestId]: { - ...(prev[guestId] || { id: guestId }), - customUrl: url || undefined, - }, - })); + updateGuestMetadataState((prev) => { + const next = { + ...prev, + [guestId]: { + ...(prev[guestId] || { id: guestId }), + customUrl: url || undefined, + }, + }; + return next; + }); }; // Create a mapping from node ID to node object