diff --git a/frontend-modern/src/components/Alerts/ResourceTable.tsx b/frontend-modern/src/components/Alerts/ResourceTable.tsx index 8c691c0..f937862 100644 --- a/frontend-modern/src/components/Alerts/ResourceTable.tsx +++ b/frontend-modern/src/components/Alerts/ResourceTable.tsx @@ -869,7 +869,7 @@ export function ResourceTable(props: ResourceTableProps) { return ( {/* Alert toggle column */} @@ -1380,7 +1380,7 @@ export function ResourceTable(props: ResourceTableProps) { return ( {/* Alert toggle column */} diff --git a/frontend-modern/src/components/Backups/UnifiedBackups.tsx b/frontend-modern/src/components/Backups/UnifiedBackups.tsx index 65a5e32..d6848ef 100644 --- a/frontend-modern/src/components/Backups/UnifiedBackups.tsx +++ b/frontend-modern/src/components/Backups/UnifiedBackups.tsx @@ -2137,7 +2137,7 @@ const UnifiedBackups: Component = () => { {(item) => ( - + {item.vmid} { - const base = 'transition-all duration-200 relative animate-enter'; + const base = 'transition-all duration-200 relative'; const hover = 'hover:shadow-sm'; const alertBg = hasUnacknowledgedAlert() ? props.alertStyles?.severity === 'critical' diff --git a/frontend-modern/src/components/Docker/DockerHostSummaryTable.tsx b/frontend-modern/src/components/Docker/DockerHostSummaryTable.tsx index 0b43e9f..0d77dfc 100644 --- a/frontend-modern/src/components/Docker/DockerHostSummaryTable.tsx +++ b/frontend-modern/src/components/Docker/DockerHostSummaryTable.tsx @@ -227,7 +227,7 @@ export const DockerHostSummaryTable: Component = (p }; const rowClass = () => { - const baseHover = 'cursor-pointer transition-all duration-200 relative hover:bg-gray-50 dark:hover:bg-gray-700/50 hover:shadow-sm animate-enter'; + const baseHover = 'cursor-pointer transition-all duration-200 relative hover:bg-gray-50 dark:hover:bg-gray-700/50 hover:shadow-sm'; if (selected) { return 'cursor-pointer transition-all duration-200 relative bg-blue-50 dark:bg-blue-900/20 hover:bg-blue-100 dark:hover:bg-blue-900/30 hover:shadow-sm z-10'; diff --git a/frontend-modern/src/components/Docker/DockerHosts.tsx b/frontend-modern/src/components/Docker/DockerHosts.tsx index fcda3f5..ee0bf62 100644 --- a/frontend-modern/src/components/Docker/DockerHosts.tsx +++ b/frontend-modern/src/components/Docker/DockerHosts.tsx @@ -1,5 +1,6 @@ import type { Component } from 'solid-js'; import { Show, createMemo, createSignal, createEffect, onMount, onCleanup } from 'solid-js'; +import { createStore } from 'solid-js/store'; import { useNavigate } from '@solidjs/router'; import type { DockerHost } from '@/types/api'; import { Card } from '@/components/shared/Card'; @@ -73,8 +74,13 @@ export const DockerHosts: Component = (props) => { return value; }; + // Cache for stable summary objects to prevent re-animations + const summaryCache = new Map(); + const hostSummaries = createMemo(() => { - return sortedHosts().map((host) => { + const usedKeys = new Set(); + + const result = sortedHosts().map((host) => { const totalContainers = host.containers?.length ?? 0; const runningContainers = host.containers?.filter((container) => container.state?.toLowerCase() === 'running').length ?? 0; @@ -113,7 +119,7 @@ export const DockerHosts: Component = (props) => { const lastSeenRelative = host.lastSeen ? formatRelativeTime(host.lastSeen) : '—'; const lastSeenAbsolute = host.lastSeen ? new Date(host.lastSeen).toLocaleString() : ''; - return { + const newSummary: DockerHostSummary = { host, cpuPercent, memoryPercent, @@ -127,7 +133,29 @@ export const DockerHosts: Component = (props) => { lastSeenRelative, lastSeenAbsolute, }; + + const key = host.id; + usedKeys.add(key); + + let entry = summaryCache.get(key); + if (!entry) { + entry = createStore(newSummary); + summaryCache.set(key, entry); + } else { + const [_, setState] = entry; + setState(newSummary); + } + return entry[0]; }); + + // Prune cache + for (const key of summaryCache.keys()) { + if (!usedKeys.has(key)) { + summaryCache.delete(key); + } + } + + return result; }); let searchInputRef: HTMLInputElement | undefined; @@ -168,7 +196,7 @@ export const DockerHosts: Component = (props) => { } }) .catch((err) => { - logger.debug('Failed to load docker metadata', err); + logger.debug('Failed to load docker metadata', err); }); }); onCleanup(() => document.removeEventListener('keydown', handleKeyDown)); diff --git a/frontend-modern/src/components/Docker/DockerUnifiedTable.tsx b/frontend-modern/src/components/Docker/DockerUnifiedTable.tsx index d9b2f1e..e22d9c2 100644 --- a/frontend-modern/src/components/Docker/DockerUnifiedTable.tsx +++ b/frontend-modern/src/components/Docker/DockerUnifiedTable.tsx @@ -1253,7 +1253,7 @@ const DockerContainerRow: Component<{ return ( <>
{ + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; +}; + const DockerUnifiedTable: Component = (props) => { // Use the responsive grid template hook for dynamic column visibility const { gridTemplate, visibleColumns, isMobile } = useGridTemplate({ columns: DOCKER_COLUMNS }); + // Caches for stable object references to prevent re-animations + const rowCache = new Map(); + const tasksCache = new Map(); + const tokens = createMemo(() => parseSearchTerm(props.searchTerm)); const [sortKey, setSortKey] = usePersistentSignal('dockerUnifiedSortKey', 'host', { deserialize: (value) => (SORT_KEYS.includes(value as SortKey) ? (value as SortKey) : 'host'), @@ -2146,6 +2158,8 @@ const DockerUnifiedTable: Component = (props) => { const filter = props.statsFilter ?? null; const searchTokens = tokens(); const selectedHostId = props.selectedHostId ? props.selectedHostId() : null; + const usedCacheKeys = new Set(); + const usedTaskCacheKeys = new Set(); sortedHosts().forEach((host) => { if (!hostMatchesFilter(filter, host)) { @@ -2177,12 +2191,22 @@ const DockerUnifiedTable: Component = (props) => { const matchesSearch = searchTokens.every((token) => containerMatchesToken(token, host, container)); if (!matchesSearch) return; - containerRows.push({ - kind: 'container', - id: container.id || `${host.id}-container-${container.name}`, - host, - container, - }); + const rowId = container.id || `${host.id}-container-${container.name}`; + const cacheKey = `c:${host.id}:${rowId}`; + usedCacheKeys.add(cacheKey); + + let row = rowCache.get(cacheKey); + if (!row || row.kind !== 'container' || row.host !== host || row.container !== container) { + row = { + kind: 'container', + id: rowId, + host, + container, + }; + rowCache.set(cacheKey, row); + } + + containerRows.push(row as Extract); }); services.forEach((service) => { @@ -2190,7 +2214,7 @@ const DockerUnifiedTable: Component = (props) => { const matchesSearch = searchTokens.every((token) => serviceMatchesToken(token, host, service)); if (!matchesSearch) return; - const associatedTasks = tasks.filter((task) => { + let associatedTasks = tasks.filter((task) => { if (service.id && task.serviceId) { return task.serviceId === service.id; } @@ -2200,18 +2224,39 @@ const DockerUnifiedTable: Component = (props) => { return false; }); + // Use stable array reference for tasks if content matches + const taskCacheKey = `s:${host.id}:${service.id || service.name}`; + usedTaskCacheKeys.add(taskCacheKey); + const cachedTasks = tasksCache.get(taskCacheKey); + if (cachedTasks && areTasksEqual(cachedTasks, associatedTasks)) { + associatedTasks = cachedTasks; + } else { + tasksCache.set(taskCacheKey, associatedTasks); + } + associatedTasks.forEach((task) => { if (task.containerId) serviceOwnedContainers.add(task.containerId.toLowerCase()); if (task.containerName) serviceOwnedContainers.add(task.containerName.toLowerCase()); }); - serviceRows.push({ - kind: 'service', - id: service.id || `${host.id}-service-${service.name}`, - host, - service, - tasks: associatedTasks, - }); + const rowId = service.id || `${host.id}-service-${service.name}`; + const cacheKey = `s:${host.id}:${rowId}`; + usedCacheKeys.add(cacheKey); + + let row = rowCache.get(cacheKey); + // Check if row needs update (host/service changed, or tasks array changed) + if (!row || row.kind !== 'service' || row.host !== host || row.service !== service || row.tasks !== associatedTasks) { + row = { + kind: 'service', + id: rowId, + host, + service, + tasks: associatedTasks, + }; + rowCache.set(cacheKey, row); + } + + serviceRows.push(row as Extract); }); if (serviceRows.length > 0) { @@ -2260,6 +2305,18 @@ const DockerUnifiedTable: Component = (props) => { } }); + // Prune caches + for (const key of rowCache.keys()) { + if (!usedCacheKeys.has(key)) { + rowCache.delete(key); + } + } + for (const key of tasksCache.keys()) { + if (!usedTaskCacheKeys.has(key)) { + tasksCache.delete(key); + } + } + return groups; }); diff --git a/frontend-modern/src/components/Hosts/HostsOverview.tsx b/frontend-modern/src/components/Hosts/HostsOverview.tsx index 8886b9d..a1ed51a 100644 --- a/frontend-modern/src/components/Hosts/HostsOverview.tsx +++ b/frontend-modern/src/components/Hosts/HostsOverview.tsx @@ -243,7 +243,7 @@ export const HostsOverview: Component = (props) => { }); const rowClass = () => { - const base = 'border-b border-gray-200 dark:border-gray-700 transition-all duration-200 animate-enter'; + const base = 'border-b border-gray-200 dark:border-gray-700 transition-all duration-200'; const hover = 'hover:bg-gray-50 dark:hover:bg-gray-800/50'; const clickable = hasDrawerContent() ? 'cursor-pointer' : ''; const expanded = drawerOpen() ? 'bg-gray-50 dark:bg-gray-800/40' : ''; diff --git a/frontend-modern/src/components/PMG/MailGateway.tsx b/frontend-modern/src/components/PMG/MailGateway.tsx index f497343..63dc038 100644 --- a/frontend-modern/src/components/PMG/MailGateway.tsx +++ b/frontend-modern/src/components/PMG/MailGateway.tsx @@ -133,14 +133,14 @@ const MailGateway: Component = () => {
- + - + - + - + - +
{formatNum(total)}
{formatDec(total / 24)}/hr
Total processed
{formatNum(inbound)}
@@ -149,7 +149,7 @@ const MailGateway: Component = () => {
Inbound
{formatNum(outbound)}
@@ -158,7 +158,7 @@ const MailGateway: Component = () => {
Outbound
{formatNum(spam)}
@@ -167,7 +167,7 @@ const MailGateway: Component = () => {
Spam caught
{formatNum(virus)}
@@ -189,7 +189,7 @@ const MailGateway: Component = () => {
- + - +
{formatDec(bytesIn / 1024 / 1024)} MB
@@ -198,7 +198,7 @@ const MailGateway: Component = () => {
Inbound bytes
{formatDec(bytesOut / 1024 / 1024)} MB
@@ -223,7 +223,7 @@ const MailGateway: Component = () => {
- + - + - + - + - +
{formatNum(qSpam)}
@@ -232,7 +232,7 @@ const MailGateway: Component = () => {
Spam quarantined
{formatNum(qVirus)}
@@ -241,7 +241,7 @@ const MailGateway: Component = () => {
Virus quarantined
{formatNum(qAttachment)}
@@ -250,7 +250,7 @@ const MailGateway: Component = () => {
Attachments blocked
{formatNum(qBlacklist)}
@@ -259,7 +259,7 @@ const MailGateway: Component = () => {
Blacklisted
{formatNum(qTotal)}
@@ -315,7 +315,7 @@ const MailGateway: Component = () => {
- + - + - + - + - + - + - + + + + + + }> {(agent) => ( - + + = (props) => { return globalEnabled; }; + type TableItem = Node | PBSInstance; + + const isPVE = (item: TableItem): item is Node => { + return (item as Node).pveVersion !== undefined; + }; + type CountSortKey = 'vmCount' | 'containerCount' | 'storageCount' | 'diskCount' | 'backupCount'; type SortKey = | 'default' @@ -131,11 +137,6 @@ export const NodeSummaryTable: Component = (props) => { | 'temperature' | CountSortKey; - interface SortableItem { - type: 'pve' | 'pbs'; - data: Node | PBSInstance; - } - interface CountColumn { header: string; key: CountSortKey; @@ -240,9 +241,9 @@ export const NodeSummaryTable: Component = (props) => { return counts; }); - const getCpuTemperatureValue = (item: SortableItem) => { - if (item.type !== 'pve') return null; - const node = item.data as Node; + const getCpuTemperatureValue = (item: TableItem) => { + if (!isPVE(item)) return null; + const node = item; const value = getCpuTemperature(node.temperature); return value !== null ? Math.round(value) : null; }; @@ -270,12 +271,12 @@ export const NodeSummaryTable: Component = (props) => { } }; - const isItemOnline = (item: SortableItem) => { - if (item.type === 'pve') { - const node = item.data as Node; + const isItemOnline = (item: TableItem) => { + if (isPVE(item)) { + const node = item; return node.status === 'online' && (node.uptime || 0) > 0; } - const pbs = item.data as PBSInstance; + const pbs = item; return pbs.status === 'healthy' || pbs.status === 'online'; }; @@ -290,63 +291,63 @@ export const NodeSummaryTable: Component = (props) => { ); }; - const getCpuPercent = (item: SortableItem) => { - if (item.type === 'pve') { - const node = item.data as Node; + const getCpuPercent = (item: TableItem) => { + if (isPVE(item)) { + const node = item; return Math.round((node.cpu || 0) * 100); } - const pbs = item.data as PBSInstance; + const pbs = item; return Math.round(pbs.cpu || 0); }; - const getMemoryPercent = (item: SortableItem) => { - if (item.type === 'pve') { - const node = item.data as Node; + const getMemoryPercent = (item: TableItem) => { + if (isPVE(item)) { + const node = item; return Math.round(node.memory?.usage || 0); } - const pbs = item.data as PBSInstance; + const pbs = item; if (!pbs.memoryTotal) return 0; return Math.round((pbs.memoryUsed / pbs.memoryTotal) * 100); }; - const getDiskPercent = (item: SortableItem) => { - if (item.type === 'pve') { - const node = item.data as Node; + const getDiskPercent = (item: TableItem) => { + if (isPVE(item)) { + const node = item; if (!node.disk || node.disk.total === 0) return 0; return Math.round((node.disk.used / node.disk.total) * 100); } - const pbs = item.data as PBSInstance; + const pbs = item; const totals = getPbsTotals(pbs); if (totals.total === 0) return 0; return Math.round((totals.used / totals.total) * 100); }; - const getDiskSublabel = (item: SortableItem) => { - if (item.type === 'pve') { - const node = item.data as Node; + const getDiskSublabel = (item: TableItem) => { + if (isPVE(item)) { + const node = item; if (!node.disk) return undefined; return `${formatBytes(node.disk.used, 0)}/${formatBytes(node.disk.total, 0)}`; } - const pbs = item.data as PBSInstance; + const pbs = item; if (!pbs.datastores || pbs.datastores.length === 0) return undefined; const totals = getPbsTotals(pbs); return `${formatBytes(totals.used, 0)}/${formatBytes(totals.total, 0)}`; }; - const getTemperatureValue = (item: SortableItem) => { + const getTemperatureValue = (item: TableItem) => { return getCpuTemperatureValue(item); }; - const getCountValue = (item: SortableItem, key: CountSortKey): number | null => { - if (item.type === 'pbs') { - const pbs = item.data as PBSInstance; + const getCountValue = (item: TableItem, key: CountSortKey): number | null => { + if (!isPVE(item)) { + const pbs = item; if (key === 'backupCount') { return props.backupCounts?.[pbs.name] ?? 0; } return null; } - const node = item.data as Node; + const node = item; const keyId = nodeKey(node.instance, node.name); switch (key) { @@ -365,16 +366,16 @@ export const NodeSummaryTable: Component = (props) => { } }; - const getSortValue = (item: SortableItem, key: SortKey): number | string | null => { + const getSortValue = (item: TableItem, key: SortKey): number | string | null => { switch (key) { case 'name': - return item.type === 'pve' - ? getNodeDisplayName(item.data as Node) - : (item.data as PBSInstance).name; + return isPVE(item) + ? getNodeDisplayName(item) + : item.name; case 'uptime': - return item.type === 'pve' - ? (item.data as Node).uptime ?? 0 - : (item.data as PBSInstance).uptime ?? 0; + return isPVE(item) + ? item.uptime ?? 0 + : item.uptime ?? 0; case 'cpu': return getCpuPercent(item); case 'memory': @@ -394,19 +395,21 @@ export const NodeSummaryTable: Component = (props) => { } }; - const defaultComparison = (a: SortableItem, b: SortableItem) => { - if (a.type !== b.type) return a.type === 'pve' ? -1 : 1; + const defaultComparison = (a: TableItem, b: TableItem) => { + const aIsPVE = isPVE(a); + const bIsPVE = isPVE(b); + if (aIsPVE !== bIsPVE) return aIsPVE ? -1 : 1; const aOnline = isItemOnline(a); const bOnline = isItemOnline(b); if (aOnline !== bOnline) return aOnline ? -1 : 1; - const aName = a.type === 'pve' - ? getNodeDisplayName(a.data as Node) - : (a.data as PBSInstance).name; - const bName = b.type === 'pve' - ? getNodeDisplayName(b.data as Node) - : (b.data as PBSInstance).name; + const aName = aIsPVE + ? getNodeDisplayName(a) + : a.name; + const bName = bIsPVE + ? getNodeDisplayName(b) + : b.name; return aName.localeCompare(bName); }; @@ -434,10 +437,10 @@ export const NodeSummaryTable: Component = (props) => { }; const sortedItems = createMemo(() => { - const items: SortableItem[] = []; + const items: TableItem[] = []; - props.nodes?.forEach((node) => items.push({ type: 'pve', data: node })); - props.pbsInstances?.forEach((pbs) => items.push({ type: 'pbs', data: pbs })); + if (props.nodes) items.push(...props.nodes); + if (props.pbsInstances) items.push(...props.pbsInstances); const key = sortKey(); const direction = sortDirection(); @@ -516,30 +519,30 @@ export const NodeSummaryTable: Component = (props) => {
{(item) => { - const isPVE = item.type === 'pve'; - const isPBS = item.type === 'pbs'; - const node = isPVE ? (item.data as Node) : null; - const pbs = isPBS ? (item.data as PBSInstance) : null; + const isPVEItem = isPVE(item); + const isPBSItem = !isPVEItem; + const node = isPVEItem ? (item as Node) : null; + const pbs = isPBSItem ? (item as PBSInstance) : null; const online = isItemOnline(item); const statusIndicator = createMemo(() => - isPVE ? getNodeStatusIndicator(node as Node) : getPBSStatusIndicator(pbs as PBSInstance), + isPVEItem ? getNodeStatusIndicator(node as Node) : getPBSStatusIndicator(pbs as PBSInstance), ); const cpuPercentValue = getCpuPercent(item); const memoryPercentValue = getMemoryPercent(item); const diskPercentValue = getDiskPercent(item); const diskSublabel = getDiskSublabel(item); const cpuTemperatureValue = getCpuTemperatureValue(item); - const uptimeValue = isPVE ? node?.uptime ?? 0 : isPBS ? pbs?.uptime ?? 0 : 0; + const uptimeValue = isPVEItem ? node?.uptime ?? 0 : isPBSItem ? pbs?.uptime ?? 0 : 0; const displayName = () => { - if (isPVE) return getNodeDisplayName(node as Node); + if (isPVEItem) return getNodeDisplayName(node as Node); return (pbs as PBSInstance).name; }; - const showActualName = () => isPVE && hasAlternateDisplayName(node as Node); + const showActualName = () => isPVEItem && hasAlternateDisplayName(node as Node); - const nodeId = isPVE ? node!.id : pbs!.name; + const nodeId = isPVEItem ? node!.id : pbs!.name; const isSelected = () => props.selectedNode === nodeId; - const resourceId = isPVE ? node!.id || node!.name : pbs!.id || pbs!.name; + const resourceId = isPVEItem ? node!.id || node!.name : pbs!.id || pbs!.name; const metricsKey = buildMetricKey('node', resourceId); const alertStyles = createMemo(() => getAlertStyles(resourceId, activeAlerts, alertsEnabled()), @@ -570,7 +573,7 @@ export const NodeSummaryTable: Component = (props) => { }); const rowClass = createMemo(() => { - const baseHover = 'cursor-pointer transition-all duration-200 relative hover:shadow-sm group animate-enter'; + const baseHover = 'cursor-pointer transition-all duration-200 relative hover:shadow-sm group'; if (isSelected()) { return `cursor-pointer transition-all duration-200 relative hover:shadow-sm z-10 group`; @@ -621,7 +624,7 @@ export const NodeSummaryTable: Component = (props) => { /> = (props) => {
{formatNum(bouncesIn)}
@@ -324,7 +324,7 @@ const MailGateway: Component = () => {
Bounces inbound
{formatNum(bouncesOut)}
@@ -333,7 +333,7 @@ const MailGateway: Component = () => {
Bounces outbound
{formatNum(rbl)}
@@ -342,7 +342,7 @@ const MailGateway: Component = () => {
RBL rejects
{formatNum(pregreet)}
@@ -351,7 +351,7 @@ const MailGateway: Component = () => {
Pregreet rejects
{formatNum(greylist)}
@@ -360,7 +360,7 @@ const MailGateway: Component = () => {
Greylisted
{formatNum(junk)}
@@ -369,7 +369,7 @@ const MailGateway: Component = () => {
Junk mail
{pmg.mailStats?.averageProcessTimeMs ? formatDec(pmg.mailStats.averageProcessTimeMs / 1000, 2) : '—'} s @@ -421,7 +421,7 @@ const MailGateway: Component = () => { const oldestClass = oldestAge > 1800 ? 'text-amber-600 dark:text-amber-400' : 'text-gray-700 dark:text-gray-300'; return ( -
{node.name} {node.role || '—'} diff --git a/frontend-modern/src/components/Replication/Replication.tsx b/frontend-modern/src/components/Replication/Replication.tsx index 1ef362e..b3acbec 100644 --- a/frontend-modern/src/components/Replication/Replication.tsx +++ b/frontend-modern/src/components/Replication/Replication.tsx @@ -121,7 +121,7 @@ const Replication: Component = () => { {(job) => { const badge = getStatusBadge(job); return ( -
{job.guestName || `VM ${job.guestId ?? ''}`} diff --git a/frontend-modern/src/components/Settings/APITokenManager.tsx b/frontend-modern/src/components/Settings/APITokenManager.tsx index da835b9..c0b81c4 100644 --- a/frontend-modern/src/components/Settings/APITokenManager.tsx +++ b/frontend-modern/src/components/Settings/APITokenManager.tsx @@ -422,30 +422,30 @@ export const APITokenManager: Component = (props) => {
Full access tokens
{wildcardCount()}

{hasWildcardTokens() @@ -582,9 +582,9 @@ export const APITokenManager: Component = (props) => { return (

@@ -601,8 +601,8 @@ export const APITokenManager: Component = (props) => { return ( @@ -717,8 +717,8 @@ export const APITokenManager: Component = (props) => {
@@ -568,7 +568,7 @@ export const PbsNodesTable: Component = (props) => { {(node) => { const statusMeta = createMemo(() => resolvePbsStatusMeta(node, props.statePbs)); return ( -
@@ -751,7 +751,7 @@ export const PmgNodesTable: Component = (props) => { {(node) => { const statusMeta = createMemo(() => resolvePmgStatusMeta(node, props.statePmg)); return ( -
diff --git a/frontend-modern/src/components/Settings/UnifiedAgents.tsx b/frontend-modern/src/components/Settings/UnifiedAgents.tsx index ed95053..7c71468 100644 --- a/frontend-modern/src/components/Settings/UnifiedAgents.tsx +++ b/frontend-modern/src/components/Settings/UnifiedAgents.tsx @@ -656,7 +656,7 @@ export const UnifiedAgents: Component = () => {
{agent.displayName || agent.hostname} diff --git a/frontend-modern/src/components/Settings/__tests__/UnifiedAgents.test.tsx b/frontend-modern/src/components/Settings/__tests__/UnifiedAgents.test.tsx index 539f331..02e51c2 100644 --- a/frontend-modern/src/components/Settings/__tests__/UnifiedAgents.test.tsx +++ b/frontend-modern/src/components/Settings/__tests__/UnifiedAgents.test.tsx @@ -385,8 +385,12 @@ describe('UnifiedAgents platform commands', () => { expect(screen.getByText('Enable Docker monitoring')).toBeInTheDocument(); }); - // Docker monitoring is enabled by default + // Docker monitoring is disabled by default const checkbox = screen.getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + // Enable it + fireEvent.click(checkbox); expect(checkbox).toBeChecked(); // Find a copy button and check that the command includes the docker flag diff --git a/frontend-modern/src/components/Storage/DiskList.tsx b/frontend-modern/src/components/Storage/DiskList.tsx index ee6b1fa..7c07531 100644 --- a/frontend-modern/src/components/Storage/DiskList.tsx +++ b/frontend-modern/src/components/Storage/DiskList.tsx @@ -191,7 +191,7 @@ export const DiskList: Component = (props) => { return ( <> -
{disk.node} diff --git a/frontend-modern/src/components/Storage/Storage.tsx b/frontend-modern/src/components/Storage/Storage.tsx index d886ac9..35a3b15 100644 --- a/frontend-modern/src/components/Storage/Storage.tsx +++ b/frontend-modern/src/components/Storage/Storage.tsx @@ -1043,7 +1043,7 @@ const Storage: Component = () => { return ( <>