diff --git a/src/components/lists/DynamicList.tsx b/src/components/lists/DynamicList.tsx index 2efc377..c92b5c7 100644 --- a/src/components/lists/DynamicList.tsx +++ b/src/components/lists/DynamicList.tsx @@ -25,6 +25,7 @@ import { import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@/components/ui/select'; +import { Combobox } from '@/components/ui/combobox'; import { Badge } from '@/components/ui/badge'; import { Checkbox } from '@/components/ui/checkbox'; import { formatSize as fmtSize, formatDuration as fmtDuration } from '@/lib/durationFormat'; @@ -60,7 +61,14 @@ import { useAuthStore } from '@/stores/authStore'; import { useAccountStore } from '@/stores/accountStore'; import { useCacheStore } from '@/stores/cacheStore'; import { resolveObject, resolveSchema, resolveList, getDisplayProperty } from '@/lib/schemaResolver'; -import { jmapGetBatched, jmapQueryAll, jmapQueryAndGet, jmapQueryAllAndGet, jmapSet, getAccountId } from '@/services/jmap/client'; +import { + jmapGetBatched, + jmapQueryAll, + jmapQueryAndGet, + jmapQueryAllAndGet, + jmapSet, + getAccountId, +} from '@/services/jmap/client'; import type { Schema, Field, MassAction, ItemAction, Filter as FilterDef } from '@/types/schema'; import type { JmapSetResponse, JmapSetError } from '@/types/jmap'; @@ -68,6 +76,13 @@ import type { ResolvedSchema } from '@/lib/schemaResolver'; const PAGE_SIZE = 25; const MAX_REPORTED_ERRORS = 3; +// Combobox threshold: plain handleFilterSelectChange(filterDef.field, v)}> diff --git a/src/i18n/en.json b/src/i18n/en.json index cb36315..df3a917 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -289,6 +289,8 @@ "bulkSuccessUpdate_one": "{{count}} item updated successfully.", "bulkSuccessUpdate_other": "{{count}} items updated successfully.", "clearSelection": "Clear selection", + "comboboxEmptyText": "No matches.", + "comboboxSearchPlaceholder": "Search...", "confirmDescription": "Are you sure you want to proceed with: {{action}}?", "confirmTitle": "Confirm Action", "create": "Create {{name}}", diff --git a/src/lib/logFilters.ts b/src/lib/logFilters.ts new file mode 100644 index 0000000..35fe299 --- /dev/null +++ b/src/lib/logFilters.ts @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + */ + +import type { Schema } from '@/types/schema'; + +/** + * The Stalwart JMAP backend rejects `level`/`event` as filter conditions on + * `x:Log/query` (`unsupportedFilter`), even though both properties are + * already returned per row. Until the backend adds real support, these two + * filters are appended client-side and applied entirely in the browser + * (see the `clientOnly` flag consumed by DynamicList) instead of being sent + * to the server. + */ +export function withClientLogFilters(schema: Schema): Schema { + const logList = schema.lists['x:Log']; + if (!logList || !schema.enums['TracingLevel'] || !schema.enums['EventType']) return schema; + + return { + ...schema, + lists: { + ...schema.lists, + 'x:Log': { + ...logList, + filters: [ + ...(logList.filters ?? []), + { type: 'enum', field: 'level', enumName: 'TracingLevel', label: 'Level', clientOnly: true }, + { type: 'enum', field: 'event', enumName: 'EventType', label: 'Event', clientOnly: true }, + ], + }, + }, + }; +} diff --git a/src/pages/AdminPanel.tsx b/src/pages/AdminPanel.tsx index bbc5e66..813ea84 100644 --- a/src/pages/AdminPanel.tsx +++ b/src/pages/AdminPanel.tsx @@ -12,6 +12,7 @@ import { useSchemaStore } from '@/stores/schemaStore'; import { useAccountStore } from '@/stores/accountStore'; import { useUIStore } from '@/stores/uiStore'; import { fetchSession, fetchSchema, fetchAccountInfo } from '@/services/jmap/client'; +import { withClientLogFilters } from '@/lib/logFilters'; import { setLocale } from '@/i18n'; import { TopBar } from '@/components/layout/TopBar'; import { Sidebar } from '@/components/layout/Sidebar'; @@ -147,7 +148,7 @@ export default function AdminPanel() { if (cancelled) return; - setSchema(schemaData); + setSchema(withClientLogFilters(schemaData)); setAccountInfo(accountData.permissions, accountData.edition, accountData.locale); setLocale(accountData.locale); diff --git a/src/types/schema.ts b/src/types/schema.ts index 958b7e3..20dae49 100644 --- a/src/types/schema.ts +++ b/src/types/schema.ts @@ -303,6 +303,10 @@ export interface FilterEnum { field: string; enumName: string; label: string; + /** Applied client-side after fetch instead of sent to the server as a JMAP + * filter condition. Used for properties the backend query engine doesn't + * (yet) support filtering on, even though it returns them per row. */ + clientOnly?: boolean; } export interface FilterInteger {