From e3e554a4ad206d7bde67b3429b40ffafe91f9d65 Mon Sep 17 00:00:00 2001 From: Pulse Monitor Date: Tue, 2 Sep 2025 22:22:04 +0000 Subject: [PATCH] fix: eliminate gaps in table rows with alert borders - Replace border-left with inset box-shadow for alert indicators - Prevents unprofessional gaps between background and table edges - Add alert support to NodeSummaryTable with same gap-free technique - Generate offline alerts in mock data for testing - Fix alert matching to use full resource ID for nodes --- .../src/components/Dashboard/Dashboard.tsx | 9 ++-- .../src/components/Dashboard/GuestRow.tsx | 24 +++++---- .../components/shared/NodeSummaryTable.tsx | 49 +++++++++++++------ internal/mock/generator.go | 17 +++++++ 4 files changed, 71 insertions(+), 28 deletions(-) diff --git a/frontend-modern/src/components/Dashboard/Dashboard.tsx b/frontend-modern/src/components/Dashboard/Dashboard.tsx index 2583a7d72..2c7dd1cf3 100644 --- a/frontend-modern/src/components/Dashboard/Dashboard.tsx +++ b/frontend-modern/src/components/Dashboard/Dashboard.tsx @@ -676,15 +676,15 @@ export function Dashboard(props: DashboardProps) { {/* Table View */} 0}> +
- +
-
handleSort('name')} onKeyDown={(e) => e.key === 'Enter' && handleSort('name')} tabindex="0" @@ -761,7 +761,7 @@ export function Dashboard(props: DashboardProps) { <>
+
+
diff --git a/frontend-modern/src/components/Dashboard/GuestRow.tsx b/frontend-modern/src/components/Dashboard/GuestRow.tsx index 625ae69df..289f63b8d 100644 --- a/frontend-modern/src/components/Dashboard/GuestRow.tsx +++ b/frontend-modern/src/components/Dashboard/GuestRow.tsx @@ -105,7 +105,7 @@ export function GuestRow(props: GuestRowProps) { // Get row styling - include alert styles if present const rowClass = createMemo(() => { - const base = 'transition-all duration-200'; + const base = 'transition-all duration-200 relative'; const hover = 'hover:shadow-sm'; // Extract only the background color from alert styles, not the border const alertBg = props.alertStyles?.hasAlert @@ -118,19 +118,25 @@ export function GuestRow(props: GuestRowProps) { return `${base} ${hover} ${defaultHover} ${alertBg} ${stoppedDimming}`; }); - // Get first cell styling with left border for alerts + // Get first cell styling const firstCellClass = createMemo(() => { const base = 'p-1 px-2 whitespace-nowrap relative'; - const alertBorder = props.alertStyles?.hasAlert - ? (props.alertStyles.severity === 'critical' - ? 'border-l-4 border-l-red-500 dark:border-l-red-400' - : 'border-l-4 border-l-yellow-500 dark:border-l-yellow-400') - : ''; - return `${base} ${alertBorder}`; + // Add extra padding when alert is present for visual spacing + const padding = props.alertStyles?.hasAlert ? 'pl-4' : ''; + return `${base} ${padding}`; + }); + + // Get row styles including box-shadow for alert border + const rowStyle = createMemo(() => { + if (!props.alertStyles?.hasAlert) return {}; + const color = props.alertStyles.severity === 'critical' ? '#ef4444' : '#eab308'; + return { + 'box-shadow': `inset 4px 0 0 0 ${color}` + }; }); return ( - + {/* Name - Sticky column */}
diff --git a/frontend-modern/src/components/shared/NodeSummaryTable.tsx b/frontend-modern/src/components/shared/NodeSummaryTable.tsx index 22368c3f2..72bf5d184 100644 --- a/frontend-modern/src/components/shared/NodeSummaryTable.tsx +++ b/frontend-modern/src/components/shared/NodeSummaryTable.tsx @@ -2,6 +2,8 @@ import { Component, For, Show, createMemo } from 'solid-js'; import type { Node, VM, Container, Storage, PBSInstance } from '@/types/api'; import { formatBytes, formatUptime } from '@/utils/format'; import { MetricBar } from '@/components/Dashboard/MetricBar'; +import { useWebSocket } from '@/App'; +import { getAlertStyles } from '@/utils/alerts'; interface NodeSummaryTableProps { nodes: Node[]; @@ -16,6 +18,7 @@ interface NodeSummaryTableProps { } export const NodeSummaryTable: Component = (props) => { + const { activeAlerts } = useWebSocket(); // Combine and sort nodes based on tab const sortedItems = createMemo(() => { const items: Array<{ type: 'pve' | 'pbs'; data: Node | PBSInstance }> = []; @@ -98,12 +101,12 @@ export const NodeSummaryTable: Component = (props) => { // This prevents the table from disappearing on refresh while data loads return ( -
-
- +
+
+
- @@ -170,24 +173,40 @@ export const NodeSummaryTable: Component = (props) => { const nodeId = isPVE ? node!.name : pbs!.name; const isSelected = () => props.selectedNode === nodeId; + // Use the full resource ID for alert matching + const resourceId = isPVE ? (node!.id || node!.name) : (pbs!.id || pbs!.name); + const alertStyles = getAlertStyles(resourceId, activeAlerts); + + // Get row styles including box-shadow for alert border + const rowStyle = createMemo(() => { + const styles: any = {}; + if (isSelected()) { + styles['box-shadow'] = '0 0 0 1px rgba(59, 130, 246, 0.5), 0 2px 4px -1px rgba(0, 0, 0, 0.1)'; + } + if (alertStyles.hasAlert) { + const color = alertStyles.severity === 'critical' ? '#ef4444' : '#eab308'; + styles['box-shadow'] = `inset 4px 0 0 0 ${color}${isSelected() ? ', 0 0 0 1px rgba(59, 130, 246, 0.5), 0 2px 4px -1px rgba(0, 0, 0, 0.1)' : ''}`; + } + return styles; + }); return ( props.onNodeClick(nodeId, item.type)} > -
+ {props.currentTab === 'backups' ? 'Node / PBS' : 'Node'} Status
+