From b12b4ba4efa61d78ab0aa5ebbebf310d7cd9b9fe Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 29 Sep 2025 12:48:04 +0000 Subject: [PATCH] Fix frontend typings and update notifications layout --- frontend-modern/src/App.tsx | 2 +- frontend-modern/src/Test.tsx | 4 +- .../src/components/Alerts/ResourceTable.tsx | 70 +++- .../src/components/Alerts/ThresholdsTable.tsx | 63 ++-- .../src/components/Backups/UnifiedBackups.tsx | 24 +- .../src/components/Dashboard/Dashboard.tsx | 1 - .../src/components/Dashboard/GuestRow.tsx | 9 +- .../src/components/Dashboard/PBSCard.tsx | 1 - .../src/components/Dashboard/TagBadges.tsx | 4 +- .../src/components/Settings/Settings.tsx | 324 +++++++++++------- .../src/components/Storage/DiskList.tsx | 7 +- .../src/components/Storage/Storage.tsx | 31 +- .../src/components/shared/SectionHeader.tsx | 2 +- .../src/components/shared/Toggle.tsx | 4 +- .../components/shared/UnifiedNodeSelector.tsx | 5 +- frontend-modern/src/pages/Alerts.tsx | 40 +-- frontend-modern/src/stores/websocket.ts | 6 +- frontend-modern/src/types/alerts.ts | 9 +- frontend-modern/src/types/api.ts | 20 +- frontend-modern/src/types/backups.ts | 83 +---- frontend-modern/src/utils/searchQuery.ts | 67 ++-- 21 files changed, 394 insertions(+), 382 deletions(-) diff --git a/frontend-modern/src/App.tsx b/frontend-modern/src/App.tsx index 089cbb568..c7806c348 100644 --- a/frontend-modern/src/App.tsx +++ b/frontend-modern/src/App.tsx @@ -99,7 +99,7 @@ function App() { ? savedDarkMode === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; const [darkMode, setDarkMode] = createSignal(initialDarkMode); - const [hasLoadedServerTheme, setHasLoadedServerTheme] = createSignal(false); + const [, setHasLoadedServerTheme] = createSignal(false); // Apply dark mode immediately on initialization if (initialDarkMode) { diff --git a/frontend-modern/src/Test.tsx b/frontend-modern/src/Test.tsx index b8f206cc7..32e522122 100644 --- a/frontend-modern/src/Test.tsx +++ b/frontend-modern/src/Test.tsx @@ -1,6 +1,6 @@ export default function Test() { - return
+ return

TEST - APP IS WORKING!

If you see this, the basic app infrastructure works.

; -} \ No newline at end of file +} diff --git a/frontend-modern/src/components/Alerts/ResourceTable.tsx b/frontend-modern/src/components/Alerts/ResourceTable.tsx index e2eaeb129..800ff03fa 100644 --- a/frontend-modern/src/components/Alerts/ResourceTable.tsx +++ b/frontend-modern/src/components/Alerts/ResourceTable.tsx @@ -3,15 +3,15 @@ import type { Alert } from '@/types/api'; import { Card } from '@/components/shared/Card'; import { SectionHeader } from '@/components/shared/SectionHeader'; -interface Resource { +export interface Resource { id: string; name: string; node?: string; instance?: string; type?: string; resourceType?: string; - thresholds?: Record; - defaults?: Record; + thresholds?: Record; + defaults?: Record; disabled?: boolean; disableConnectivity?: boolean; hasOverride?: boolean; @@ -28,15 +28,15 @@ interface ResourceTableProps { groupedResources?: Record; columns: string[]; activeAlerts?: Record; - onEdit: (resourceId: string, thresholds: Record, defaults: Record) => void; + onEdit: (resourceId: string, thresholds: Record, defaults: Record) => void; onSaveEdit: (resourceId: string) => void; onCancelEdit: () => void; onRemoveOverride: (resourceId: string) => void; onToggleDisabled?: (resourceId: string) => void; onToggleNodeConnectivity?: (nodeId: string) => void; editingId: () => string | null; - editingThresholds: () => Record; - setEditingThresholds: (value: Record) => void; + editingThresholds: () => Record; + setEditingThresholds: (value: Record) => void; formatMetricValue: (metric: string, value: number | undefined) => string; hasActiveAlert: (resourceId: string, metric: string) => boolean; } @@ -117,15 +117,24 @@ export function ResourceTable(props: ResourceTableProps) { {(resource) => { const isEditing = () => props.editingId() === resource.id; - const thresholds = () => isEditing() ? props.editingThresholds() : resource.thresholds; + const thresholds = (): Record => { + if (isEditing()) { + return props.editingThresholds(); + } + return resource.thresholds ?? {}; + }; const displayValue = (metric: string): number => { const thresh = thresholds(); const defaults = resource.defaults || {}; if (isEditing()) { - const val = thresh?.[metric] || defaults[metric]; - return typeof val === 'string' ? (parseFloat(val) || 0) : (val || 0); + const val = thresh[metric] ?? defaults[metric]; + return typeof val === 'number' ? val : Number(val) || 0; } - return resource.thresholds?.[metric] || defaults[metric] || 0; + const liveValue = resource.thresholds?.[metric]; + if (typeof liveValue === 'number') { + return liveValue; + } + return typeof defaults[metric] === 'number' ? (defaults[metric] as number) : 0; }; const isOverridden = (metric: string) => { return resource.thresholds?.[metric] !== undefined && resource.thresholds?.[metric] !== null; @@ -308,7 +317,11 @@ export function ResourceTable(props: ResourceTableProps) { }>
); -}; \ No newline at end of file +}; diff --git a/frontend-modern/src/components/Storage/Storage.tsx b/frontend-modern/src/components/Storage/Storage.tsx index 6420f510e..8a21fa6a0 100644 --- a/frontend-modern/src/components/Storage/Storage.tsx +++ b/frontend-modern/src/components/Storage/Storage.tsx @@ -2,7 +2,6 @@ import { Component, For, Show, createSignal, createMemo, createEffect } from 'so import { useWebSocket } from '@/App'; import { getAlertStyles } from '@/utils/alerts'; import { formatBytes } from '@/utils/format'; -import { createTooltipSystem } from '@/components/shared/Tooltip'; import type { Storage as StorageType } from '@/types/api'; import { ComponentErrorBoundary } from '@/components/ErrorBoundary'; import { UnifiedNodeSelector } from '@/components/shared/UnifiedNodeSelector'; @@ -10,7 +9,6 @@ import { StorageFilter } from './StorageFilter'; import { DiskList } from './DiskList'; import { Card } from '@/components/shared/Card'; import { EmptyState } from '@/components/shared/EmptyState'; -import { SectionHeader } from '@/components/shared/SectionHeader'; const Storage: Component = () => { @@ -23,9 +21,6 @@ const Storage: Component = () => { // const [sortKey, setSortKey] = createSignal('name'); // const [sortDirection, setSortDirection] = createSignal<'asc' | 'desc'>('asc'); - // Create tooltip system - const TooltipComponent = createTooltipSystem(); - // Create a mapping from node name to host URL const nodeHostMap = createMemo(() => { const map: Record = {}; @@ -407,14 +402,16 @@ const Storage: Component = () => { const rowClass = `${isDisabled ? 'opacity-60' : ''} ${alertBg} hover:shadow-sm transition-all duration-200`; // Create row style with inset box-shadow for alert border - const rowStyle = createMemo(() => { - const styles: Record = {}; - if (alertStyles.hasAlert) { - const color = alertStyles.severity === 'critical' ? '#ef4444' : '#eab308'; - styles['box-shadow'] = `inset 4px 0 0 0 ${color}`; - } - return styles; - }); + const rowStyle = createMemo(() => { + const styles: Record = {}; + if (alertStyles.hasAlert) { + const color = alertStyles.severity === 'critical' ? '#ef4444' : '#eab308'; + styles['box-shadow'] = `inset 4px 0 0 0 ${color}`; + } + return styles; + }); + + const zfsPool = storage.zfsPool; return ( <> @@ -428,17 +425,17 @@ const Storage: Component = () => { {storage.name} {/* ZFS Health Badge */} - + - {storage.zfsPool.state} + {zfsPool?.state} {/* ZFS Error Badge */} - 0 || storage.zfsPool.writeErrors > 0 || storage.zfsPool.checksumErrors > 0)}> + 0 || zfsPool.writeErrors > 0 || zfsPool.checksumErrors > 0)}> ERRORS diff --git a/frontend-modern/src/components/shared/SectionHeader.tsx b/frontend-modern/src/components/shared/SectionHeader.tsx index 5a2ce6655..95b938f37 100644 --- a/frontend-modern/src/components/shared/SectionHeader.tsx +++ b/frontend-modern/src/components/shared/SectionHeader.tsx @@ -8,7 +8,7 @@ type SectionHeaderProps = { size?: 'sm' | 'md' | 'lg'; titleClass?: string; descriptionClass?: string; -} & JSX.HTMLAttributes; +} & Omit, 'title'>; export function SectionHeader(props: SectionHeaderProps) { const merged = mergeProps({ align: 'left' as const, size: 'md' as const, titleClass: '', descriptionClass: '' }, props); diff --git a/frontend-modern/src/components/shared/Toggle.tsx b/frontend-modern/src/components/shared/Toggle.tsx index 8a1759f93..da9518237 100644 --- a/frontend-modern/src/components/shared/Toggle.tsx +++ b/frontend-modern/src/components/shared/Toggle.tsx @@ -10,7 +10,7 @@ export function Toggle(props: ToggleProps) { const merged = mergeProps({ containerClass: '' }, props); const [local, rest] = splitProps(merged, ['label', 'description', 'containerClass', 'class', 'disabled']); - const isDisabled = () => Boolean(local.disabled ?? rest.disabled); + const isDisabled = () => Boolean(local.disabled); const isChecked = () => { const value = rest.checked as unknown; if (typeof value === 'function') { @@ -26,7 +26,7 @@ export function Toggle(props: ToggleProps) { return (