fix: node selection in storage and backups tabs now filters without showing in search

- Storage tab: clicking nodes filters data without adding 'node:xxx' to search
- Backups tab: clicking nodes filters data without adding 'node:xxx' to search
- Both tabs now use separate selectedNode state for filtering
- Search field stays clean for actual text searches
- Visual improvements: added hover lift effect to NodeSummaryTable rows
This commit is contained in:
Pulse Monitor
2025-08-29 14:53:03 +00:00
parent 59e325517a
commit 8ae1a86e69
3 changed files with 30 additions and 33 deletions
@@ -43,6 +43,7 @@ interface DateGroup {
const UnifiedBackups: Component = () => {
const { state } = useWebSocket();
const [searchTerm, setSearchTerm] = createSignal('');
const [selectedNode, setSelectedNode] = createSignal<string | null>(null);
const [typeFilter, setTypeFilter] = createSignal<'all' | FilterableGuestType>('all');
const [backupTypeFilter, setBackupTypeFilter] = createSignal<'all' | BackupType>('all');
const [groupByMode, setGroupByMode] = createSignal<'date' | 'guest'>('date');
@@ -354,6 +355,7 @@ const UnifiedBackups: Component = () => {
const type = typeFilter();
const backupType = backupTypeFilter();
const dateRange = selectedDateRange();
const nodeFilter = selectedNode();
// Date range filter
if (dateRange) {
@@ -362,6 +364,11 @@ const UnifiedBackups: Component = () => {
);
}
// Node selection filter
if (nodeFilter) {
data = data.filter(item => item.node.toLowerCase() === nodeFilter.toLowerCase());
}
// Search filter - with advanced filtering support like Dashboard
if (search) {
// Check for special PBS namespace filter format first
@@ -592,6 +599,7 @@ const UnifiedBackups: Component = () => {
// Reset filters
const resetFilters = () => {
setSearchTerm('');
setSelectedNode(null);
setIsSearchLocked(false);
setTypeFilter('all');
setBackupTypeFilter('all');
@@ -619,7 +627,7 @@ const UnifiedBackups: Component = () => {
// Escape key behavior
if (e.key === 'Escape') {
// Clear search and reset filters
if (searchTerm().trim() || typeFilter() !== 'all' || backupTypeFilter() !== 'all' ||
if (searchTerm().trim() || selectedNode() || typeFilter() !== 'all' || backupTypeFilter() !== 'all' ||
selectedDateRange() !== null || sortKey() !== 'backupTime' || sortDirection() !== 'desc') {
resetFilters();
@@ -888,14 +896,8 @@ const UnifiedBackups: Component = () => {
<UnifiedNodeSelector
currentTab="backups"
onNodeSelect={(nodeId) => {
if (nodeId) {
const nodeFilter = `node:${nodeId}`;
setSearchTerm(nodeFilter);
setIsSearchLocked(true);
} else {
setSearchTerm('');
setIsSearchLocked(false);
}
setSelectedNode(nodeId);
setIsSearchLocked(!!nodeId);
}}
onNamespaceSelect={(namespaceFilter) => {
setSearchTerm(namespaceFilter);
@@ -13,6 +13,7 @@ const Storage: Component = () => {
const { state, connected, activeAlerts, initialDataReceived } = useWebSocket();
const [viewMode, setViewMode] = createSignal<'node' | 'storage'>('node');
const [searchTerm, setSearchTerm] = createSignal('');
const [selectedNode, setSelectedNode] = createSignal<string | null>(null);
// TODO: Implement sorting in sortedStorage function
// const [sortKey, setSortKey] = createSignal('name');
// const [sortDirection, setSortDirection] = createSignal<'asc' | 'desc'>('asc');
@@ -59,23 +60,23 @@ const Storage: Component = () => {
const sortedStorage = createMemo(() => {
let storage = [...filteredStorage()];
// Apply node selection filter
const nodeFilter = selectedNode();
if (nodeFilter) {
storage = storage.filter(s => s.node.toLowerCase() === nodeFilter.toLowerCase());
}
// Apply search filter
const search = searchTerm().toLowerCase();
if (search) {
// Check if it's a node filter
if (search.startsWith('node:')) {
const nodeName = search.substring(5).trim();
storage = storage.filter(s => s.node.toLowerCase() === nodeName.toLowerCase());
} else {
// Regular search
storage = storage.filter(s =>
s.name.toLowerCase().includes(search) ||
s.node.toLowerCase().includes(search) ||
s.type.toLowerCase().includes(search) ||
s.content?.toLowerCase().includes(search) ||
(s.status && s.status.toLowerCase().includes(search))
);
}
// Regular search
storage = storage.filter(s =>
s.name.toLowerCase().includes(search) ||
s.node.toLowerCase().includes(search) ||
s.type.toLowerCase().includes(search) ||
s.content?.toLowerCase().includes(search) ||
(s.status && s.status.toLowerCase().includes(search))
);
}
// Always sort by name alphabetically for consistent order
@@ -116,6 +117,7 @@ const Storage: Component = () => {
const resetFilters = () => {
setSearchTerm('');
setSelectedNode(null);
setViewMode('node');
// setSortKey('name');
// setSortDirection('asc');
@@ -151,7 +153,7 @@ const Storage: Component = () => {
// Escape key behavior
if (e.key === 'Escape') {
// Clear search and reset filters
if (searchTerm().trim() || viewMode() !== 'node') {
if (searchTerm().trim() || selectedNode() || viewMode() !== 'node') {
resetFilters();
// Blur the search input if it's focused
@@ -173,14 +175,7 @@ const Storage: Component = () => {
});
const handleNodeSelect = (nodeId: string | null) => {
if (nodeId) {
// Set search to filter by node
const nodeFilter = `node:${nodeId}`;
setSearchTerm(nodeFilter);
} else {
// Clear node filter from search
setSearchTerm('');
}
setSelectedNode(nodeId);
};
return (
@@ -173,7 +173,7 @@ export const NodeSummaryTable: Component<NodeSummaryTableProps> = (props) => {
return (
<tr
class={`cursor-pointer transition-colors ${
class={`cursor-pointer transition-all duration-150 hover:-translate-y-px hover:shadow-md ${
isSelected()
? 'bg-blue-100 dark:bg-blue-900/30 hover:bg-blue-100 dark:hover:bg-blue-900/30'
: 'hover:bg-gray-50 dark:hover:bg-gray-700/50'