diff --git a/apps/desktop/src/components/config/DriverStoreDialog.vue b/apps/desktop/src/components/config/DriverStoreDialog.vue index 778763ac68..2fd54308ef 100644 --- a/apps/desktop/src/components/config/DriverStoreDialog.vue +++ b/apps/desktop/src/components/config/DriverStoreDialog.vue @@ -32,6 +32,8 @@ import { import { PRESTOSQL_DRIVER_DB_TYPE, prestoSqlBuiltinDriverRow, prestoSqlMavenBundle } from "@/lib/database/prestoSqlBuiltinDriver"; import type { DriverStoreFocus } from "@/lib/connection/agentDriverInstallHint"; import { isOfflineDriverPackage, webDriverImportAccept } from "@/lib/driverStore/driverImportSelection"; +import { DRIVER_CATEGORIES, getCategoryForAgentDriver, assertAgentDriverCategoriesComplete } from "@/lib/connection/driver-category-definitions"; +import { selectUpdatableDrivers, selectStableDrivers, hasAnyUpdatableDriverMatching } from "@/lib/connection/driverListFilter"; const { t } = useI18n(); const { toast } = useToast(); @@ -174,6 +176,7 @@ async function applyDriverStoreDir(kind: DriverStoreDirKind, newDir: string | nu const drivers = ref([]); const agentDriverSearch = ref(""); +const selectedDriverCategory = ref("all"); const installing = ref(null); const upgradingAll = ref(false); const upgradingCompletedCount = ref(0); @@ -374,6 +377,14 @@ function removeQueuedDriverInstall(dbType: string) { async function refreshAgents() { updateAgentDrivers(await api.listInstalledAgents()); void loadDriverStoreUsage(); + // DEV: validate category coverage + if (import.meta.env.DEV) { + try { + assertAgentDriverCategoriesComplete(drivers.value.map((d) => d.db_type)); + } catch (e) { + console.warn("[DriverStore] Agent driver category mapping incomplete:", e); + } + } } async function forceRefresh() { @@ -756,39 +767,86 @@ type JdbcDriverListItem = bundle: JdbcMavenBundleInfo; }; -const filteredAgentDrivers = computed(() => { - const query = agentDriverSearch.value.trim().toLowerCase(); - if (!query) return builtinDriverRows.value; - return builtinDriverRows.value.filter((driver) => [driver.label, driver.db_type, driver.version, driver.installed_version, driverRequiresJavaRuntime(driver) ? driver.jre : ""].filter(Boolean).join(" ").toLowerCase().includes(query)); +// Resolved category list with i18n +const driverCategories = computed(() => + DRIVER_CATEGORIES.map((cat) => ({ + key: cat.key, + title: t(cat.titleKey), + })), +); + +const driverSearchQuery = computed(() => agentDriverSearch.value.trim().toLowerCase()); +const isDriverSearchActive = computed(() => !!driverSearchQuery.value); + +// Category label helper (for search results badges) — defined before searchedDrivers +function driverCategoryLabel(dbType: string): string { + const catKey = getCategoryForAgentDriver(dbType); + if (catKey === "all") return t("driverStore.driverCategoryAll"); + const catDef = DRIVER_CATEGORIES.find((c) => c.key === catKey); + return catDef ? t(catDef.titleKey) : t("driverStore.driverCategoryAll"); +} + +// Search only stable drivers — updatable drivers already appear in the global update section. +const stableBuiltinDrivers = computed(() => selectStableDrivers(builtinDriverRows.value)); + +// Filter stable drivers through search (or pass all through if no search) +const searchedDrivers = computed(() => { + const query = driverSearchQuery.value; + if (!query) return stableBuiltinDrivers.value; + return stableBuiltinDrivers.value.filter((driver) => [driver.label, driver.db_type, driver.version, driver.installed_version, driverRequiresJavaRuntime(driver) ? driver.jre : "", driverCategoryLabel(driver.db_type)].filter(Boolean).join(" ").toLowerCase().includes(query)); }); -/** filteredAgentDrivers reordered: updatable drivers first, then the rest (installed + not-installed). */ -const orderedFilteredDrivers = computed(() => { - const updatable: AgentDriverInfo[] = []; - const stable: AgentDriverInfo[] = []; - for (const driver of filteredAgentDrivers.value) { - if (driver.update_available) { - updatable.push(driver); - } else { - stable.push(driver); - } +// When search is active: group by category +const searchedDriversByCategory = computed(() => { + if (!isDriverSearchActive.value) return []; + const grouped = new Map(); + for (const driver of searchedDrivers.value) { + const cat = getCategoryForAgentDriver(driver.db_type); + if (!grouped.has(cat)) grouped.set(cat, []); + grouped.get(cat)!.push(driver); } - return [...updatable, ...stable]; + return [...grouped.entries()].map(([catKey, drivers]) => { + const catDef = DRIVER_CATEGORIES.find((c) => c.key === catKey); + return { + key: catKey, + title: catKey === "all" ? t("driverStore.driverCategoryAll") : catDef ? t(catDef.titleKey) : t("driverStore.driverCategoryAll"), + drivers, + }; + }); }); -/** Number of updatable drivers in the currently filtered list. */ -const filteredUpdatableCount = computed(() => { - let count = 0; - for (const driver of orderedFilteredDrivers.value) { - if (driver.update_available) count++; - else break; - } - return count; +// When search is NOT active: filter by selected category +const categoryFilteredDrivers = computed(() => { + if (isDriverSearchActive.value || selectedDriverCategory.value === "all") return searchedDrivers.value; + return searchedDrivers.value.filter((driver) => getCategoryForAgentDriver(driver.db_type) === selectedDriverCategory.value); }); -/** Filtered driver rows split: updatable-only and stable-only (no