From d50ee666040a5f9f55e25c2af7a639d237cd4dcf Mon Sep 17 00:00:00 2001 From: rcourtman Date: Thu, 22 Jan 2026 00:30:23 +0000 Subject: [PATCH] Fix TypeScript type error in GuestDrawer metricsResource Add explicit ResourceType typing to metricsResource function to fix type mismatch with HistoryChart/UnifiedHistoryChart props. --- frontend-modern/src/api/charts.ts | 2 +- .../src/components/Dashboard/GuestDrawer.tsx | 506 ++++++++++++------ 2 files changed, 329 insertions(+), 179 deletions(-) diff --git a/frontend-modern/src/api/charts.ts b/frontend-modern/src/api/charts.ts index 0c0dd9e48..ac2b3366a 100644 --- a/frontend-modern/src/api/charts.ts +++ b/frontend-modern/src/api/charts.ts @@ -48,7 +48,7 @@ export interface ChartsResponse { // Persistent metrics history types (SQLite-backed, longer retention) export type HistoryTimeRange = '1h' | '6h' | '12h' | '24h' | '7d' | '30d' | '90d'; -export type ResourceType = 'node' | 'guest' | 'storage' | 'docker' | 'dockerHost'; +export type ResourceType = 'node' | 'guest' | 'vm' | 'container' | 'storage' | 'docker' | 'dockerHost'; export interface MetricsHistoryParams { resourceType: ResourceType; diff --git a/frontend-modern/src/components/Dashboard/GuestDrawer.tsx b/frontend-modern/src/components/Dashboard/GuestDrawer.tsx index 8aca01702..977fa0acf 100644 --- a/frontend-modern/src/components/Dashboard/GuestDrawer.tsx +++ b/frontend-modern/src/components/Dashboard/GuestDrawer.tsx @@ -1,7 +1,10 @@ -import { Component, Show, For } from 'solid-js'; +import { Component, Show, For, createSignal } from 'solid-js'; import { VM, Container } from '@/types/api'; import { formatBytes, formatUptime } from '@/utils/format'; import { DiskList } from './DiskList'; +import { HistoryChart } from '../shared/HistoryChart'; +import { UnifiedHistoryChart } from '../shared/UnifiedHistoryChart'; +import { HistoryTimeRange, ResourceType } from '@/api/charts'; type Guest = VM | Container; @@ -73,187 +76,334 @@ export const GuestDrawer: Component = (props) => { return networkInterfaces().length > 0; }; + const fallbackGuestId = () => { + return props.guest.id || `${props.guest.instance}:${props.guest.node}:${props.guest.vmid}`; + }; + + const metricsResource = (): { type: ResourceType; id: string } => { + const key = props.metricsKey || ''; + const separatorIndex = key.indexOf(':'); + const fallbackType: ResourceType = isVM(props.guest) ? 'vm' : 'container'; + + if (separatorIndex === -1) { + return { type: fallbackType, id: fallbackGuestId() }; + } + + const kind = key.slice(0, separatorIndex); + const id = key.slice(separatorIndex + 1) || fallbackGuestId(); + const type: ResourceType = kind === 'vm' || kind === 'container' ? kind : fallbackType; + + return { type, id }; + }; + + const [activeTab, setActiveTab] = createSignal<'overview' | 'history'>('overview'); + const [historyRange, setHistoryRange] = createSignal('24h'); + const [viewMode, setViewMode] = createSignal<'unified' | 'split'>('unified'); + return (
- {/* Flex layout - items grow to fill space, max ~4 per row */} -
- {/* System Info - always show */} -
-
System
-
- -
- CPUs - {props.guest.cpus} -
-
- 0}> -
- Uptime - {formatUptime(props.guest.uptime)} -
-
- -
- Node - {props.guest.node} -
-
- -
- Agent - - {isVM(props.guest) ? `QEMU ${agentVersion()}` : agentVersion()} - -
-
-
-
- - {/* Guest Info - OS and IPs */} - 0}> -
-
Guest Info
-
- -
- 0}> - {osName()} - - 0 && osVersion().length > 0}> - - - 0}> - {osVersion()} - -
-
- 0}> -
- - {(ip) => ( - - {ip} - - )} - -
-
-
-
-
- - {/* Memory Details */} - 0}> -
-
Memory
-
- {(line) =>
{line}
}
-
-
-
- - {/* Backup Info */} - -
-
Backup
-
- {(() => { - const backupDate = new Date(props.guest.lastBackup); - const now = new Date(); - const daysSince = Math.floor((now.getTime() - backupDate.getTime()) / (1000 * 60 * 60 * 24)); - const isOld = daysSince > 7; - const isCritical = daysSince > 30; - return ( - <> -
- Last Backup - - {daysSince === 0 ? 'Today' : daysSince === 1 ? 'Yesterday' : `${daysSince}d ago`} - -
-
- {backupDate.toLocaleDateString()} -
- - ); - })()} -
-
-
- - {/* Tags */} - 0 : props.guest.tags.length > 0)}> -
-
Tags
-
- - {(tag) => ( - - {tag.trim()} - - )} - -
-
-
- - {/* Filesystems */} - 0}> -
-
Filesystems
-
- -
-
-
- - {/* Network Interfaces */} - -
-
Network
-
- - {(iface) => { - const addresses = iface.addresses ?? []; - const hasTraffic = (iface.rxBytes ?? 0) > 0 || (iface.txBytes ?? 0) > 0; - return ( -
-
- {iface.name || 'interface'} - - {iface.mac} - -
- 0}> -
- - {(ip) => ( - - {ip} - - )} - -
-
- -
- RX {formatBytes(iface.rxBytes ?? 0)} - TX {formatBytes(iface.txBytes ?? 0)} -
-
-
- ); - }} -
-
-
-
+ {/* Tabs */} +
+ +
+ + {/* Flex layout - items grow to fill space, max ~4 per row */} +
+ {/* System Info - always show */} +
+
System
+
+ +
+ CPUs + {props.guest.cpus} +
+
+ 0}> +
+ Uptime + {formatUptime(props.guest.uptime)} +
+
+ +
+ Node + {props.guest.node} +
+
+ +
+ Agent + + {isVM(props.guest) ? `QEMU ${agentVersion()}` : agentVersion()} + +
+
+
+
+ + {/* Guest Info - OS and IPs */} + 0}> +
+
Guest Info
+
+ +
+ 0}> + {osName()} + + 0 && osVersion().length > 0}> + + + 0}> + {osVersion()} + +
+
+ 0}> +
+ + {(ip) => ( + + {ip} + + )} + +
+
+
+
+
+ + {/* Memory Details */} + 0}> +
+
Memory
+
+ {(line) =>
{line}
}
+
+
+
+ + {/* Backup Info */} + +
+
Backup
+
+ {(() => { + const backupDate = new Date(props.guest.lastBackup); + const now = new Date(); + const daysSince = Math.floor((now.getTime() - backupDate.getTime()) / (1000 * 60 * 60 * 24)); + const isOld = daysSince > 7; + const isCritical = daysSince > 30; + return ( + <> +
+ Last Backup + + {daysSince === 0 ? 'Today' : daysSince === 1 ? 'Yesterday' : `${daysSince}d ago`} + +
+
+ {backupDate.toLocaleDateString()} +
+ + ); + })()} +
+
+
+ + {/* Tags */} + 0 : props.guest.tags.length > 0)}> +
+
Tags
+
+ + {(tag) => ( + + {tag.trim()} + + )} + +
+
+
+ + {/* Filesystems */} + 0}> +
+
Filesystems
+
+ +
+
+
+ + {/* Network Interfaces */} + +
+
Network
+
+ + {(iface) => { + const addresses = iface.addresses ?? []; + const hasTraffic = (iface.rxBytes ?? 0) > 0 || (iface.txBytes ?? 0) > 0; + return ( +
+
+ {iface.name || 'interface'} + + {iface.mac} + +
+ 0}> +
+ + {(ip) => ( + + {ip} + + )} + +
+
+ +
+ RX {formatBytes(iface.rxBytes ?? 0)} + TX {formatBytes(iface.txBytes ?? 0)} +
+
+
+ ); + }} +
+
+
+
+
+
+ + +
+ {/* Toolbar: Range and View Toggle */} +
+
+ Controls +
+ + +
+
+ +
+ Range +
+ {(['24h', '7d', '30d', '90d'] as HistoryTimeRange[]).map(r => ( + + ))} +
+
+
+ + + + + + +
+ + + +
+
+
+
); };