fix: Allow clearing AI findings when AI is disabled

Users who accumulated AI findings before the patrol-without-AI bug was
fixed (24c4bb0b) could not dismiss them because the AI Insights tab
and Clear All button were only visible when patrol was enabled.

Now the AI Insights tab and Clear All button are visible whenever there
are findings to clear, even if AI/patrol is not enabled.

Related to #885
This commit is contained in:
rcourtman
2025-12-26 11:43:34 +00:00
parent c89ec7bc50
commit e13605d2f3
4 changed files with 114 additions and 51 deletions
@@ -18,6 +18,8 @@ import { getNodeDisplayName } from '@/utils/nodes';
import { usePersistentSignal } from '@/hooks/usePersistentSignal';
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
import { useAlertsActivation } from '@/stores/alertsActivation';
import { useColumnVisibility, type ColumnDef } from '@/hooks/useColumnVisibility';
import { STORAGE_KEYS } from '@/utils/localStorage';
type StorageSortKey = 'name' | 'node' | 'type' | 'status' | 'usage' | 'free' | 'total';
@@ -61,6 +63,24 @@ const Storage: Component = () => {
},
);
// Column definitions for storage table
const STORAGE_COLUMNS: ColumnDef[] = [
{ id: 'type', label: 'Type', priority: 3 },
{ id: 'content', label: 'Content', priority: 4 },
{ id: 'status', label: 'Status', priority: 2 },
{ id: 'shared', label: 'Shared', priority: 5, hiddenByDefault: true },
{ id: 'free', label: 'Free', priority: 4 },
{ id: 'total', label: 'Total', priority: 3 },
];
// Column visibility management
const columnVisibility = useColumnVisibility(
STORAGE_COLUMNS,
STORAGE_KEYS.STORAGE_HIDDEN_COLUMNS
);
const isColumnVisible = (id: string) => !columnVisibility.isHiddenByUser(id);
// PERFORMANCE: Debounce search term to prevent jank during rapid typing
const debouncedSearchTerm = useDebouncedValue(() => searchTerm(), 200);
@@ -601,6 +621,7 @@ const Storage: Component = () => {
statusFilter={statusFilter}
setStatusFilter={setStatusFilter}
searchInputRef={(el) => (searchInputRef = el)}
columnVisibility={columnVisibility}
/>
</Show>
@@ -794,16 +815,22 @@ const Storage: Component = () => {
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-auto">
Storage
</th>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Type
</th>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[15%]">
Content
</th>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Status
</th>
<Show when={viewMode() === 'node'}>
<Show when={isColumnVisible('type')}>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Type
</th>
</Show>
<Show when={isColumnVisible('content')}>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[15%]">
Content
</th>
</Show>
<Show when={isColumnVisible('status')}>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Status
</th>
</Show>
<Show when={viewMode() === 'node' && isColumnVisible('shared')}>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[6%]">
Shared
</th>
@@ -811,12 +838,16 @@ const Storage: Component = () => {
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[25%] min-w-[120px]">
Usage
</th>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Free
</th>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Total
</th>
<Show when={isColumnVisible('free')}>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Free
</th>
</Show>
<Show when={isColumnVisible('total')}>
<th class="px-1.5 py-1.5 text-left text-[11px] sm:text-xs font-medium uppercase tracking-wider w-[10%]">
Total
</th>
</Show>
<th class="px-1.5 py-1.5 w-8"></th>
</tr>
</thead>
@@ -1154,30 +1185,36 @@ const Storage: Component = () => {
</Show>
</div>
</td>
<td class="p-0.5 px-1.5">
<span class="inline-block px-1.5 py-0.5 text-[10px] font-medium rounded bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300">
{storage.type}
</span>
</td>
<td class="p-0.5 px-1.5">
<span
class="text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap truncate max-w-[220px]"
title={storage.content || '-'}
>
{storage.content || '-'}
</span>
</td>
<td class="p-0.5 px-1.5 text-xs whitespace-nowrap">
<span
class={`${storage.status === 'available'
? 'text-green-600 dark:text-green-400'
: 'text-red-600 dark:text-red-400'
}`}
>
{storage.status || 'unknown'}
</span>
</td>
<Show when={viewMode() === 'node'}>
<Show when={isColumnVisible('type')}>
<td class="p-0.5 px-1.5">
<span class="inline-block px-1.5 py-0.5 text-[10px] font-medium rounded bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300">
{storage.type}
</span>
</td>
</Show>
<Show when={isColumnVisible('content')}>
<td class="p-0.5 px-1.5">
<span
class="text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap truncate max-w-[220px]"
title={storage.content || '-'}
>
{storage.content || '-'}
</span>
</td>
</Show>
<Show when={isColumnVisible('status')}>
<td class="p-0.5 px-1.5 text-xs whitespace-nowrap">
<span
class={`${storage.status === 'available'
? 'text-green-600 dark:text-green-400'
: 'text-red-600 dark:text-red-400'
}`}
>
{storage.status || 'unknown'}
</span>
</td>
</Show>
<Show when={viewMode() === 'node' && isColumnVisible('shared')}>
<td class="p-0.5 px-1.5">
<span class="text-xs text-gray-600 dark:text-gray-400">
{storage.shared ? '✓' : '-'}
@@ -1193,12 +1230,16 @@ const Storage: Component = () => {
zfsPool={storage.zfsPool}
/>
</td>
<td class="p-0.5 px-1.5 text-xs whitespace-nowrap">
{formatBytes(storage.free || 0, 0)}
</td>
<td class="p-0.5 px-1.5 text-xs whitespace-nowrap">
{formatBytes(storage.total || 0, 0)}
</td>
<Show when={isColumnVisible('free')}>
<td class="p-0.5 px-1.5 text-xs whitespace-nowrap">
{formatBytes(storage.free || 0, 0)}
</td>
</Show>
<Show when={isColumnVisible('total')}>
<td class="p-0.5 px-1.5 text-xs whitespace-nowrap">
{formatBytes(storage.total || 0, 0)}
</td>
</Show>
<td class="p-0.5 px-1.5"></td>
</tr>
<Show when={isCephStorage() && isExpanded()}>
@@ -1,6 +1,8 @@
import { Component, Show, For, createSignal, onMount, createEffect, onCleanup } from 'solid-js';
import { Card } from '@/components/shared/Card';
import { SearchTipsPopover } from '@/components/shared/SearchTipsPopover';
import { ColumnPicker } from '@/components/shared/ColumnPicker';
import type { ColumnDef } from '@/hooks/useColumnVisibility';
import { STORAGE_KEYS } from '@/utils/localStorage';
import { createSearchHistoryManager } from '@/utils/searchHistory';
@@ -17,6 +19,13 @@ interface StorageFilterProps {
searchInputRef?: (el: HTMLInputElement) => void;
statusFilter?: () => 'all' | 'available' | 'offline';
setStatusFilter?: (value: 'all' | 'available' | 'offline') => void;
// Column visibility (optional)
columnVisibility?: {
availableToggles: () => ColumnDef[];
isHiddenByUser: (id: string) => boolean;
toggle: (id: string) => void;
resetToDefaults: () => void;
};
}
export const StorageFilter: Component<StorageFilterProps> = (props) => {
@@ -387,6 +396,17 @@ export const StorageFilter: Component<StorageFilterProps> = (props) => {
</button>
</div>
{/* Column Picker */}
<Show when={props.columnVisibility}>
<div class="h-5 w-px bg-gray-200 dark:bg-gray-600 hidden sm:block"></div>
<ColumnPicker
columns={props.columnVisibility!.availableToggles()}
isHidden={props.columnVisibility!.isHiddenByUser}
onToggle={props.columnVisibility!.toggle}
onReset={props.columnVisibility!.resetToDefaults}
/>
</Show>
{/* Reset Button - Only show when filters are active */}
<Show when={hasActiveFilters()}>
<div class="h-5 w-px bg-gray-200 dark:bg-gray-600 hidden sm:block"></div>
+6 -5
View File
@@ -2661,7 +2661,8 @@ function OverviewTab(props: {
</div>
{/* Sub-tabs for AI Insights vs Active Alerts */}
<Show when={patrolStatus()?.enabled}>
{/* Show tabs when patrol is enabled OR when there are legacy findings to clear */}
<Show when={patrolStatus()?.enabled || aiFindings().length > 0}>
<div class="flex items-center gap-1 border-b border-gray-200 dark:border-gray-700/50 pb-1">
<button
class={`px-4 py-2 text-sm font-medium rounded-t-lg transition-colors ${overviewSubTab() === 'active-alerts'
@@ -2694,8 +2695,8 @@ function OverviewTab(props: {
</div>
</Show>
{/* AI Insights Section - show when AI tab selected and there are findings */}
<Show when={overviewSubTab() === 'ai-insights' && patrolStatus()?.enabled}>
{/* AI Insights Section - show when AI tab selected and patrol enabled OR has findings to clear */}
<Show when={overviewSubTab() === 'ai-insights' && (patrolStatus()?.enabled || aiFindings().length > 0)}>
<div>
<div class="flex items-center justify-between mb-3">
<SectionHeader
@@ -3778,8 +3779,8 @@ function OverviewTab(props: {
</div>
</Show >
{/* Active Alerts - show when alerts tab selected OR when patrol is disabled (no sub-tabs) */}
< Show when={overviewSubTab() === 'active-alerts' || !patrolStatus()?.enabled
{/* Active Alerts - show when alerts tab selected OR when patrol is disabled AND no legacy findings (no sub-tabs) */}
< Show when={overviewSubTab() === 'active-alerts' || (!patrolStatus()?.enabled && aiFindings().length === 0)
}>
<div>
<SectionHeader title="Active Alerts" size="md" class="mb-3" />
@@ -115,6 +115,7 @@ export const STORAGE_KEYS = {
HOSTS_HIDDEN_COLUMNS: 'hostsHiddenColumns',
DOCKER_HIDDEN_COLUMNS: 'dockerHiddenColumns',
BACKUPS_HIDDEN_COLUMNS: 'backupsHiddenColumns',
STORAGE_HIDDEN_COLUMNS: 'storageHiddenColumns',
// Resources search
RESOURCES_SEARCH_HISTORY: 'resourcesSearchHistory',