diff --git a/frontend-modern/src/components/Backups/UnifiedBackups.tsx b/frontend-modern/src/components/Backups/UnifiedBackups.tsx index b3edebfe3..829d2d912 100644 --- a/frontend-modern/src/components/Backups/UnifiedBackups.tsx +++ b/frontend-modern/src/components/Backups/UnifiedBackups.tsx @@ -44,6 +44,7 @@ const UnifiedBackups: Component = () => { const [selectedDateRange, setSelectedDateRange] = createSignal<{ start: number; end: number } | null>(null); const [chartTimeRange, setChartTimeRange] = createSignal(30); const [tooltip, setTooltip] = createSignal<{ text: string; x: number; y: number } | null>(null); + const [selectedPBSInstance, setSelectedPBSInstance] = createSignal(null); const [showFilters, setShowFilters] = createLocalStorageBooleanSignal( STORAGE_KEYS.BACKUPS_SHOW_FILTERS, false // Default to collapsed @@ -53,6 +54,18 @@ const UnifiedBackups: Component = () => { false // Default to absolute time ); + // Auto-set backup type filter to 'remote' when a PBS instance is selected + createEffect(() => { + const pbsInstance = selectedPBSInstance(); + if (pbsInstance) { + // When a PBS instance is selected, only show remote backups + setBackupTypeFilter('remote'); + } else { + // When deselected, reset to show all + setBackupTypeFilter('all'); + } + }); + // Helper functions const getDaySuffix = (day: number) => { if (day >= 11 && day <= 13) return 'th'; @@ -114,7 +127,12 @@ const UnifiedBackups: Component = () => { // Process PBS backups FIRST from the new Go backend (state.pbsBackups) // This ensures we have the complete PBS data with namespaces - state.pbsBackups?.forEach((backup) => { + // Filter by selected PBS instance if one is selected + const pbsBackupsToProcess = selectedPBSInstance() + ? state.pbsBackups?.filter(b => b.instance === selectedPBSInstance()) + : state.pbsBackups; + + pbsBackupsToProcess?.forEach((backup) => { const backupDate = new Date(backup.backupTime); const dateStr = backupDate.toISOString().split('T')[0]; const timeStr = backupDate.toISOString().split('T')[1].split('.')[0].replace(/:/g, ''); @@ -316,6 +334,12 @@ const UnifiedBackups: Component = () => { data = data.filter(item => item.backupType === backupType); } + // PBS instance filter - when a specific PBS is selected + const pbsInstance = selectedPBSInstance(); + if (pbsInstance) { + data = data.filter(item => item.node === pbsInstance); + } + // Sort const key = sortKey(); const dir = sortDirection(); @@ -655,14 +679,38 @@ const UnifiedBackups: Component = () => {
{/* PBS Status Summary */} 0}> -
+
+ +
+ Showing only backups from PBS server: {selectedPBSInstance()} + +
+
+
{(instance) => ( -
- +
{ + // Toggle selection - click again to deselect + setSelectedPBSInstance( + selectedPBSInstance() === instance.name ? null : instance.name + ); + }} + > +
)} +
@@ -1154,44 +1202,52 @@ const UnifiedBackups: Component = () => { {/* Backup Type Filter */} -
+
diff --git a/frontend-modern/src/components/Dashboard/Dashboard.tsx b/frontend-modern/src/components/Dashboard/Dashboard.tsx index 66125fa7a..e06cd0626 100644 --- a/frontend-modern/src/components/Dashboard/Dashboard.tsx +++ b/frontend-modern/src/components/Dashboard/Dashboard.tsx @@ -24,6 +24,7 @@ type StatusMode = 'all' | 'running' | 'stopped'; export function Dashboard(props: DashboardProps) { const { connected, activeAlerts, initialDataReceived } = useWebSocket(); const [search, setSearch] = createSignal(''); + const [selectedNode, setSelectedNode] = createSignal(null); // Initialize from localStorage with proper type checking const storedViewMode = localStorage.getItem('dashboardViewMode'); @@ -174,6 +175,11 @@ export function Dashboard(props: DashboardProps) { const filteredGuests = createMemo(() => { let guests = allGuests(); + // Filter by selected node + if (selectedNode()) { + guests = guests.filter(g => g.node === selectedNode()); + } + // Filter by type if (viewMode() === 'vm') { guests = guests.filter(g => g.type === 'qemu'); @@ -318,16 +324,39 @@ export function Dashboard(props: DashboardProps) { return (
{/* Node Summary Cards - Adaptive Layout */} -
+
+ +
+ Showing guests for node: {selectedNode()} + +
+
+
0}> {/* Regular cards for 1-4 nodes */}
{(node) => ( -
+
{ + // Toggle selection - click again to deselect + setSelectedNode( + selectedNode() === node.name ? null : node.name + ); + }} + > - +
)} @@ -361,6 +390,7 @@ export function Dashboard(props: DashboardProps) {
+
{/* Dashboard Filter */} diff --git a/frontend-modern/src/components/Dashboard/NodeCard.tsx b/frontend-modern/src/components/Dashboard/NodeCard.tsx index ea07562d0..d5cd7da98 100644 --- a/frontend-modern/src/components/Dashboard/NodeCard.tsx +++ b/frontend-modern/src/components/Dashboard/NodeCard.tsx @@ -7,6 +7,7 @@ import { useWebSocket } from '@/App'; interface NodeCardProps { node: Node; + isSelected?: boolean; } const NodeCard: Component = (props) => { @@ -120,6 +121,10 @@ const NodeCard: Component = (props) => { // Determine border/ring style based on status and alerts const getBorderClass = () => { + // Selected nodes get blue ring + if (props.isSelected) { + return 'ring-2 ring-blue-500 border border-gray-200 dark:border-gray-700'; + } // Offline nodes get red ring if (!isOnline()) { return 'ring-2 ring-red-500 border border-gray-200 dark:border-gray-700'; diff --git a/frontend-modern/src/components/Dashboard/PBSCard.tsx b/frontend-modern/src/components/Dashboard/PBSCard.tsx index 06a4c0683..d87282beb 100644 --- a/frontend-modern/src/components/Dashboard/PBSCard.tsx +++ b/frontend-modern/src/components/Dashboard/PBSCard.tsx @@ -4,6 +4,7 @@ import { formatUptime, formatBytes } from '@/utils/format'; interface PBSCardProps { instance: PBSInstance; + isSelected?: boolean; } const PBSCard: Component = (props) => { @@ -111,6 +112,10 @@ const PBSCard: Component = (props) => { }; const getBorderClass = () => { + // Selected PBS gets blue ring + if (props.isSelected) { + return 'ring-2 ring-blue-500 border border-gray-200 dark:border-gray-700'; + } // Offline PBS gets red ring if (!isOnline()) { return 'ring-2 ring-red-500 border border-gray-200 dark:border-gray-700';