diff --git a/frontend-modern/src/components/Backups/UnifiedBackups.tsx b/frontend-modern/src/components/Backups/UnifiedBackups.tsx
index 7ad0c0f96..0f70d9155 100644
--- a/frontend-modern/src/components/Backups/UnifiedBackups.tsx
+++ b/frontend-modern/src/components/Backups/UnifiedBackups.tsx
@@ -3,6 +3,7 @@ import { useWebSocket } from '@/App';
import { formatBytes, formatAbsoluteTime, formatRelativeTime } from '@/utils/format';
import { createLocalStorageBooleanSignal, STORAGE_KEYS } from '@/utils/localStorage';
import PBSCard from '@/components/Dashboard/PBSCard';
+import { parseFilterStack, evaluateFilterStack } from '@/utils/searchQuery';
type BackupType = 'snapshot' | 'local' | 'remote';
type GuestType = 'VM' | 'LXC' | 'Template' | 'ISO';
@@ -312,25 +313,52 @@ const UnifiedBackups: Component = () => {
);
}
- // Search filter
+ // Search filter - with advanced filtering support like Dashboard
if (search) {
- const searchTerms = search.split(',').map(term => term.trim()).filter(term => term.length > 0);
- data = data.filter(item =>
- searchTerms.some(term => {
- const searchFields = [
- item.vmid?.toString(),
- item.name,
- item.node,
- item.backupName,
- item.description,
- item.storage,
- item.datastore,
- item.namespace
- ].filter(Boolean).map(field => field!.toString().toLowerCase());
-
- return searchFields.some(field => field.includes(term));
- })
- );
+ // Split by commas first
+ const searchParts = search.split(',').map(t => t.trim()).filter(t => t);
+
+ // Separate filters from text searches
+ const filters: string[] = [];
+ const textSearches: string[] = [];
+
+ searchParts.forEach(part => {
+ if (part.includes('>') || part.includes('<') || part.includes(':')) {
+ filters.push(part);
+ } else {
+ textSearches.push(part.toLowerCase());
+ }
+ });
+
+ // Apply filters if any
+ if (filters.length > 0) {
+ // Join filters with AND operator
+ const filterString = filters.join(' AND ');
+ const stack = parseFilterStack(filterString);
+ if (stack.filters.length > 0) {
+ data = data.filter(item => evaluateFilterStack(item, stack));
+ }
+ }
+
+ // Apply text search if any
+ if (textSearches.length > 0) {
+ data = data.filter(item =>
+ textSearches.some(term => {
+ const searchFields = [
+ item.vmid?.toString(),
+ item.name,
+ item.node,
+ item.backupName,
+ item.description,
+ item.storage,
+ item.datastore,
+ item.namespace
+ ].filter(Boolean).map(field => field!.toString().toLowerCase());
+
+ return searchFields.some(field => field.includes(term));
+ })
+ );
+ }
}
// Type filter
@@ -1163,7 +1191,7 @@ const UnifiedBackups: Component = () => {
{
if (!isSearchLocked()) {
@@ -1179,6 +1207,36 @@ const UnifiedBackups: Component = () => {
+
+ {/* Help tooltip button */}
+