diff --git a/frontend-modern/src/components/Storage/Storage.tsx b/frontend-modern/src/components/Storage/Storage.tsx
index d04257be8..6afaab51a 100644
--- a/frontend-modern/src/components/Storage/Storage.tsx
+++ b/frontend-modern/src/components/Storage/Storage.tsx
@@ -328,7 +328,7 @@ const Storage: Component = () => {
: 'p-0.5 px-1.5';
return (
-
diff --git a/frontend-modern/src/components/shared/NodeSummaryTable.tsx b/frontend-modern/src/components/shared/NodeSummaryTable.tsx
index ccf7697b6..a7f07c48b 100644
--- a/frontend-modern/src/components/shared/NodeSummaryTable.tsx
+++ b/frontend-modern/src/components/shared/NodeSummaryTable.tsx
@@ -161,7 +161,7 @@ export const NodeSummaryTable: Component = (props) => {
return (
props.onNodeClick(nodeId, item.type)}
diff --git a/frontend-modern/src/components/shared/PBSNodeTable.tsx b/frontend-modern/src/components/shared/PBSNodeTable.tsx
index edead5531..7eb9b3bfa 100644
--- a/frontend-modern/src/components/shared/PBSNodeTable.tsx
+++ b/frontend-modern/src/components/shared/PBSNodeTable.tsx
@@ -37,38 +37,15 @@ export const PBSNodeTable: Component = (props) => {
return props.searchTerm === expectedFilter;
};
- // Filter and sort PBS instances
+ // Filter and sort PBS instances - but don't hide them when filtering, similar to PVE nodes
const sortedInstances = createMemo(() => {
if (!props.pbsInstances) return [];
let instances = [...props.pbsInstances];
- // If we have filtered backups in backups tab, only show PBS instances with matching backups
- if (props.currentTab === 'backups' && props.filteredBackups !== undefined) {
- const pbsWithBackups = new Set();
-
- props.filteredBackups.forEach(b => {
- // PBS backups can have node as the PBS instance name or 'PBS' generic
- // Check if it's a PBS backup (has datastore or node is PBS instance)
- if (b.datastore || b.node === 'PBS' || b.backupType === 'remote') {
- if (b.node === 'PBS' || !b.node) {
- // Generic PBS backup, show all PBS instances
- props.pbsInstances?.forEach(pbs => pbsWithBackups.add(pbs.name));
- } else if (props.pbsInstances?.some(pbs => pbs.name === b.node)) {
- // Specific PBS instance
- pbsWithBackups.add(b.node);
- }
- }
- });
-
- // If we have any PBS backups, filter to matching instances
- if (pbsWithBackups.size > 0) {
- instances = instances.filter(pbs => pbsWithBackups.has(pbs.name));
- } else {
- // No PBS backups found in the filtered results, hide PBS table
- instances = [];
- }
- }
+ // For PBS instances, always show all of them
+ // The selection/highlighting will indicate which one is filtered
+ // This keeps the PBS cards as a stable navigation element
return instances.sort((a, b) => {
// Healthy/online instances first
diff --git a/frontend-modern/src/components/shared/PVENodeTable.tsx b/frontend-modern/src/components/shared/PVENodeTable.tsx
index 8e679a90c..d1c700dda 100644
--- a/frontend-modern/src/components/shared/PVENodeTable.tsx
+++ b/frontend-modern/src/components/shared/PVENodeTable.tsx
@@ -41,7 +41,7 @@ export const PVENodeTable: Component = (props) => {
let nodes = [...props.nodes];
- // Special handling for backups tab since it uses different filtering logic
+ // Special handling for backups tab - only hide nodes that have no backups at all
if (props.currentTab === 'backups') {
// Filter nodes to only show those with backups
// First, check if nodes have any backups at all (from backupCounts)
@@ -49,57 +49,11 @@ export const PVENodeTable: Component = (props) => {
const backupCount = props.backupCounts?.[node.name] || 0;
return backupCount > 0;
});
-
- // Then apply additional filtering if filteredBackups is provided
- if (props.filteredBackups !== undefined) {
- const nodesWithItems = new Set();
-
- // Filtering is active, only show nodes with matching backups
- props.filteredBackups.forEach(b => {
- // Only count snapshots and local backups that actually belong to PVE nodes
- if (b.backupType === 'snapshot' || b.backupType === 'local') {
- // Make sure it's actually a PVE node (not a PBS instance name)
- if (props.nodes.some(n => n.name === b.node)) {
- nodesWithItems.add(b.node);
- }
- }
- // PBS/remote backups have node set to PBS instance name, not PVE node
- // so we don't add them to PVE node filter
- });
-
- // Only show nodes that have matching items
- if (nodesWithItems.size > 0) {
- nodes = nodes.filter(node => nodesWithItems.has(node.name));
- } else {
- // If we have active filtering but no nodes have matching items, hide all nodes
- nodes = [];
- }
- }
- // If filteredBackups is undefined, still filter by backupCounts
- } else if (hasActiveFilter()) {
- // Handle other tabs with normal filtering logic
- const nodesWithItems = new Set();
-
- switch (props.currentTab) {
- case 'dashboard':
- // Filter based on VMs and containers
- props.vms?.forEach(vm => nodesWithItems.add(vm.node));
- props.containers?.forEach(ct => nodesWithItems.add(ct.node));
- break;
- case 'storage':
- // Filter based on storage
- props.storage?.forEach(s => nodesWithItems.add(s.node));
- break;
- }
-
- // Only show nodes that have filtered items
- if (nodesWithItems.size > 0) {
- nodes = nodes.filter(node => nodesWithItems.has(node.name));
- } else if (hasActiveFilter()) {
- // If we have active filtering but no nodes have matching items, hide all nodes
- nodes = [];
- }
+ // Don't further filter based on search - let highlighting handle that
}
+ // For other tabs, ALWAYS show all nodes - don't filter them out
+ // The node cards should remain as a stable navigation element
+ // Only the data below gets filtered
return nodes.sort((a, b) => {
// Online nodes first
@@ -167,9 +121,6 @@ export const PVENodeTable: Component = (props) => {
|
PVE Nodes
|
-
- Status
- |
Cluster
|
@@ -216,36 +167,29 @@ export const PVENodeTable: Component = (props) => {
const filteredGuestCount = createMemo(() => getFilteredGuestCount());
const counts = getNodeCounts(node);
+ // Check if this node is selected
const isSelected = () => props.selectedNode === node.name;
return (
props.onNodeClick(node.name)}
>
|
- {node.name}
-
-
- ({filteredGuestCount()} matched)
-
-
+ {node.name}
|
-
-
- {node.status}
-
- |
{node.clusterName || '-'}
diff --git a/frontend-modern/src/components/shared/Tooltip.tsx b/frontend-modern/src/components/shared/Tooltip.tsx
index d5db1a7bb..8d718ccff 100644
--- a/frontend-modern/src/components/shared/Tooltip.tsx
+++ b/frontend-modern/src/components/shared/Tooltip.tsx
@@ -36,10 +36,10 @@ const Tooltip: Component = (props) => {
// Calculate position to keep tooltip on screen
const rect = tooltipRef.getBoundingClientRect();
- const padding = 10;
+ const padding = 20; // Increased padding for better separation
let x = props.x + padding;
- let y = props.y - rect.height - padding;
+ let y = props.y - rect.height - padding - 10; // Extra 10px vertical separation
// Keep within viewport
if (x + rect.width > window.innerWidth) {
diff --git a/frontend-modern/src/components/shared/UnifiedNodeSelector.tsx b/frontend-modern/src/components/shared/UnifiedNodeSelector.tsx
index d0c0c078b..0920233d0 100644
--- a/frontend-modern/src/components/shared/UnifiedNodeSelector.tsx
+++ b/frontend-modern/src/components/shared/UnifiedNodeSelector.tsx
@@ -26,6 +26,9 @@ export const UnifiedNodeSelector: Component = (props)
setSelectedNode(null);
});
+ // No longer syncing with search term - selection is independent
+ // This allows users to select a node AND search within it
+
// Calculate backup counts for nodes and PBS instances
const backupCounts = createMemo(() => {
const counts: Record = {};
diff --git a/frontend-modern/src/styles/animations.css b/frontend-modern/src/styles/animations.css
index 4db936f06..f96140f66 100644
--- a/frontend-modern/src/styles/animations.css
+++ b/frontend-modern/src/styles/animations.css
@@ -190,4 +190,21 @@
opacity: 0;
transform: translate(-50%, -50%) scale(1.5);
}
-}
\ No newline at end of file
+}
+/* Node row click animation */
+@keyframes nodeClick {
+ 0% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(0.98);
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
+ }
+ 100% {
+ transform: scale(1);
+ }
+}
+
+.node-click {
+ animation: nodeClick 0.15s ease-out;
+}
diff --git a/frontend-modern/vite.config.ts b/frontend-modern/vite.config.ts
index daf3bab70..20d300881 100644
--- a/frontend-modern/vite.config.ts
+++ b/frontend-modern/vite.config.ts
@@ -14,12 +14,12 @@ export default defineConfig({
host: '0.0.0.0', // Listen on all interfaces for remote access
proxy: {
'/ws': {
- target: 'ws://127.0.0.1:3000',
+ target: 'ws://127.0.0.1:7656',
ws: true,
changeOrigin: true,
},
'/api': {
- target: 'http://127.0.0.1:3000',
+ target: 'http://127.0.0.1:7656',
changeOrigin: true,
},
},
diff --git a/rebuild.sh b/rebuild.sh
deleted file mode 100755
index e2e44afb4..000000000
--- a/rebuild.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-# Rebuild script for Pulse development
-
-echo "Building frontend..."
-cd /opt/pulse/frontend-modern && npm run build
-
-echo "Copying frontend to embed location..."
-cp -r /opt/pulse/frontend-modern/dist /opt/pulse/internal/api/frontend-modern/
-
-echo "Building Go binary..."
-cd /opt/pulse && go build -o pulse ./cmd/pulse
-
-echo "Restarting service..."
-sudo systemctl restart pulse-backend
-
-echo "Done!"
-echo "Access Pulse at: http://localhost:7655"
\ No newline at end of file
| |