@open-mercato/search 0.6.7 → 0.6.8-develop.6874.1.982d6097d8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +7 -1
- package/dist/lib/presenter-enricher.js +12 -12
- package/dist/lib/presenter-enricher.js.map +2 -2
- package/dist/modules/search/ai-tools.js +5 -2
- package/dist/modules/search/ai-tools.js.map +2 -2
- package/dist/modules/search/api/openapi.js +2 -0
- package/dist/modules/search/api/openapi.js.map +2 -2
- package/dist/modules/search/api/settings/vector-store/route.js +34 -0
- package/dist/modules/search/api/settings/vector-store/route.js.map +2 -2
- package/dist/modules/search/events.js +13 -0
- package/dist/modules/search/events.js.map +7 -0
- package/dist/modules/search/frontend/components/GlobalSearchDialog.js +2 -9
- package/dist/modules/search/frontend/components/GlobalSearchDialog.js.map +2 -2
- package/dist/modules/search/frontend/components/HybridSearchTable.js +2 -11
- package/dist/modules/search/frontend/components/HybridSearchTable.js.map +2 -2
- package/dist/modules/search/frontend/components/SearchSettingsPageClient.js.map +2 -2
- package/dist/modules/search/frontend/components/TopbarSearchInline.js +2 -9
- package/dist/modules/search/frontend/components/TopbarSearchInline.js.map +2 -2
- package/dist/modules/search/frontend/components/sections/VectorSearchSection.js +15 -3
- package/dist/modules/search/frontend/components/sections/VectorSearchSection.js.map +2 -2
- package/dist/modules/search/frontend/lib/entityTypeLabel.js +19 -0
- package/dist/modules/search/frontend/lib/entityTypeLabel.js.map +7 -0
- package/dist/modules/search/i18n/de.json +49 -0
- package/dist/modules/search/i18n/en.json +49 -0
- package/dist/modules/search/i18n/es.json +49 -0
- package/dist/modules/search/i18n/ko.json +239 -0
- package/dist/modules/search/i18n/pl.json +49 -0
- package/dist/modules/search/lib/auto-indexing.js +7 -8
- package/dist/modules/search/lib/auto-indexing.js.map +2 -2
- package/dist/service.js +2 -5
- package/dist/service.js.map +2 -2
- package/dist/strategies/vector.strategy.js +3 -1
- package/dist/strategies/vector.strategy.js.map +2 -2
- package/dist/vector/drivers/pgvector/index.js +55 -1
- package/dist/vector/drivers/pgvector/index.js.map +2 -2
- package/dist/vector/types.js.map +2 -2
- package/package.json +11 -10
- package/src/__tests__/pgvector-extension-availability.test.ts +158 -0
- package/src/__tests__/presenter-enricher.test.ts +48 -0
- package/src/__tests__/presenter-locale.test.ts +76 -0
- package/src/lib/presenter-enricher.ts +14 -14
- package/src/modules/search/README.md +4 -1
- package/src/modules/search/ai-tools.ts +5 -2
- package/src/modules/search/api/__tests__/global-search.routes.test.ts +167 -0
- package/src/modules/search/api/openapi.ts +2 -0
- package/src/modules/search/api/settings/vector-store/route.ts +43 -0
- package/src/modules/search/events.ts +10 -0
- package/src/modules/search/frontend/components/GlobalSearchDialog.tsx +2 -15
- package/src/modules/search/frontend/components/HybridSearchTable.tsx +2 -17
- package/src/modules/search/frontend/components/SearchSettingsPageClient.tsx +2 -0
- package/src/modules/search/frontend/components/TopbarSearchInline.tsx +2 -15
- package/src/modules/search/frontend/components/sections/VectorSearchSection.tsx +32 -2
- package/src/modules/search/frontend/components/sections/__tests__/VectorSearchSection.test.tsx +36 -1
- package/src/modules/search/frontend/lib/__tests__/entityTypeLabel.test.ts +25 -0
- package/src/modules/search/frontend/lib/entityTypeLabel.ts +22 -0
- package/src/modules/search/i18n/de.json +49 -0
- package/src/modules/search/i18n/en.json +49 -0
- package/src/modules/search/i18n/es.json +49 -0
- package/src/modules/search/i18n/ko.json +239 -0
- package/src/modules/search/i18n/pl.json +49 -0
- package/src/modules/search/lib/auto-indexing.ts +7 -12
- package/src/service.ts +2 -7
- package/src/strategies/vector.strategy.ts +7 -1
- package/src/vector/drivers/pgvector/index.ts +77 -1
- package/src/vector/types.ts +14 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/search/frontend/components/TopbarSearchInline.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport NextLink from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport {\n Search,\n Loader2,\n Zap,\n User,\n Users,\n Building,\n StickyNote,\n Briefcase,\n CheckSquare,\n FileText,\n Mail,\n Phone,\n Calendar,\n Clock,\n Star,\n Tag,\n Flag,\n Heart,\n Bookmark,\n Package,\n Truck,\n ShoppingCart,\n CreditCard,\n DollarSign,\n Target,\n Award,\n Trophy,\n Rocket,\n Lightbulb,\n MessageSquare,\n Bell,\n Settings,\n Globe,\n MapPin,\n Link as LinkIcon,\n Folder,\n Database,\n Activity,\n X,\n} from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport type { SearchResult, SearchResultLink } from '@open-mercato/shared/modules/search'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n getCurrentOrganizationScope,\n subscribeOrganizationScopeChanged,\n} from '@open-mercato/shared/lib/frontend/organizationEvents'\nimport { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'\nimport { parseSelectedOrganizationCookie } from '@open-mercato/core/modules/directory/utils/scopeCookies'\nimport { ForbiddenError } from '@open-mercato/ui/backend/utils/api'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { resolveSearchMinTokenLength } from '@open-mercato/shared/lib/search/config'\nimport { fetchGlobalSearchResults } from '../utils'\n\nconst MIN_QUERY_LENGTH = resolveSearchMinTokenLength()\n\nfunction normalizeLinks(links?: SearchResultLink[] | null): SearchResultLink[] {\n if (!Array.isArray(links)) return []\n return links.filter((link) => typeof link?.href === 'string')\n}\n\nfunction pickPrimaryLink(result: SearchResult): string | null {\n if (result.url) return result.url\n const links = normalizeLinks(result.links)\n if (!links.length) return null\n const primary = links.find((link) => link.kind === 'primary')\n return (primary ?? links[0]).href\n}\n\nfunction hasActiveOrganizationSelection(): boolean {\n const fromEvent = getCurrentOrganizationScope().organizationId\n if (typeof fromEvent === 'string' && fromEvent.trim().length > 0) return true\n const cookieHeader = typeof document === 'undefined' ? null : document.cookie\n const cookieValue = parseSelectedOrganizationCookie(cookieHeader)\n if (!cookieValue) return false\n return !isAllOrganizationsSelection(cookieValue)\n}\n\nfunction humanizeSegment(segment: string): string {\n return segment\n .split(/[_-]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(' ')\n}\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n bolt: Zap,\n zap: Zap,\n user: User,\n users: Users,\n building: Building,\n 'sticky-note': StickyNote,\n briefcase: Briefcase,\n 'check-square': CheckSquare,\n 'file-text': FileText,\n mail: Mail,\n phone: Phone,\n calendar: Calendar,\n clock: Clock,\n star: Star,\n tag: Tag,\n flag: Flag,\n heart: Heart,\n bookmark: Bookmark,\n package: Package,\n truck: Truck,\n 'shopping-cart': ShoppingCart,\n 'credit-card': CreditCard,\n 'dollar-sign': DollarSign,\n target: Target,\n award: Award,\n trophy: Trophy,\n rocket: Rocket,\n lightbulb: Lightbulb,\n 'message-square': MessageSquare,\n bell: Bell,\n settings: Settings,\n globe: Globe,\n 'map-pin': MapPin,\n link: LinkIcon,\n folder: Folder,\n database: Database,\n activity: Activity,\n}\n\nfunction resolveIcon(name?: string): LucideIcon | null {\n if (!name) return null\n return ICON_MAP[name.toLowerCase()] ?? null\n}\n\nfunction formatEntityId(entityId: string): string {\n if (!entityId.includes(':')) return humanizeSegment(entityId)\n const [module, entity] = entityId.split(':')\n return `${humanizeSegment(module)} \u00B7 ${humanizeSegment(entity)}`\n}\n\nexport type TopbarSearchInlineProps = {\n /** Whether embedding provider is configured for vector search */\n embeddingConfigured: boolean\n /** Warning text to show when vector search is enabled but not configured */\n missingConfigMessage: string\n}\n\nexport function TopbarSearchInline({\n embeddingConfigured,\n missingConfigMessage,\n}: TopbarSearchInlineProps) {\n const router = useRouter()\n const t = useT()\n const [query, setQuery] = React.useState('')\n const [results, setResults] = React.useState<SearchResult[]>([])\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [open, setOpen] = React.useState(false)\n const [expanded, setExpanded] = React.useState(false)\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n const [showScopeHint, setShowScopeHint] = React.useState<boolean>(() => hasActiveOrganizationSelection())\n const [anchor, setAnchor] = React.useState<{ top: number; left: number; width: number } | null>(null)\n const inputRef = React.useRef<HTMLInputElement | null>(null)\n const popoverRef = React.useRef<HTMLDivElement | null>(null)\n const containerRef = React.useRef<HTMLElement | null>(null)\n const listRef = React.useRef<HTMLDivElement | null>(null)\n const abortRef = React.useRef<AbortController | null>(null)\n\n const expandAndFocus = React.useCallback(() => {\n setExpanded(true)\n // Wait one tick so the input is mounted/transitioned before focusing\n requestAnimationFrame(() => {\n inputRef.current?.focus()\n inputRef.current?.select()\n })\n }, [])\n\n const collapseAndReset = React.useCallback(() => {\n setOpen(false)\n setExpanded(false)\n setQuery('')\n inputRef.current?.blur()\n }, [])\n\n React.useEffect(() => {\n setShowScopeHint(hasActiveOrganizationSelection())\n return subscribeOrganizationScopeChanged((detail) => {\n setShowScopeHint(Boolean(detail.organizationId && detail.organizationId.trim().length > 0))\n })\n }, [])\n\n // Cmd/Ctrl+K expands + focuses the input\n React.useEffect(() => {\n const handler = (event: KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {\n event.preventDefault()\n expandAndFocus()\n }\n }\n window.addEventListener('keydown', handler)\n return () => window.removeEventListener('keydown', handler)\n }, [expandAndFocus])\n\n // Click outside closes the popover AND collapses input (unless user has typed something)\n React.useEffect(() => {\n if (!expanded) return\n const handler = (event: MouseEvent) => {\n const target = event.target as Node | null\n if (!target) return\n if (containerRef.current?.contains(target)) return\n if (popoverRef.current?.contains(target)) return\n collapseAndReset()\n }\n document.addEventListener('mousedown', handler)\n return () => document.removeEventListener('mousedown', handler)\n }, [expanded, collapseAndReset])\n\n // Fetch results on query change (debounced)\n React.useEffect(() => {\n abortRef.current?.abort()\n const trimmed = query.trim()\n if (trimmed.length < MIN_QUERY_LENGTH) {\n setResults([])\n setError(null)\n setLoading(false)\n return\n }\n\n const controller = new AbortController()\n abortRef.current = controller\n setLoading(true)\n\n const handle = setTimeout(async () => {\n try {\n const data = await fetchGlobalSearchResults(query, {\n limit: 10,\n signal: controller.signal,\n })\n setResults(data.results)\n setError(data.error ?? null)\n setSelectedIndex(0)\n } catch (err: unknown) {\n if (controller.signal.aborted) return\n const abortError = err as { name?: string }\n if (abortError?.name === 'AbortError') return\n if (err instanceof ForbiddenError) {\n setError(t('search.dialog.errors.noPermission'))\n } else {\n setError(err instanceof Error ? err.message : t('search.dialog.errors.searchFailed'))\n }\n setResults([])\n } finally {\n if (!controller.signal.aborted) setLoading(false)\n }\n }, 220)\n\n return () => {\n clearTimeout(handle)\n controller.abort()\n }\n }, [query, t])\n\n // Auto-scroll active item into view\n React.useEffect(() => {\n const container = listRef.current\n const active = container?.querySelector<HTMLElement>('[data-active=\"true\"]')\n if (!container || !active) return\n const { top: containerTop, bottom: containerBottom } = container.getBoundingClientRect()\n const { top: activeTop, bottom: activeBottom } = active.getBoundingClientRect()\n if (activeTop < containerTop) {\n container.scrollTop -= containerTop - activeTop\n } else if (activeBottom > containerBottom) {\n container.scrollTop += activeBottom - containerBottom\n }\n }, [selectedIndex])\n\n const openResult = React.useCallback(\n (result: SearchResult | undefined) => {\n if (!result) return\n const href = pickPrimaryLink(result)\n if (!href) return\n router.push(href)\n collapseAndReset()\n },\n [router, collapseAndReset],\n )\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n if (!open) setOpen(true)\n setSelectedIndex((prev) => (results.length ? (prev + 1) % results.length : 0))\n return\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n setSelectedIndex((prev) => {\n if (!results.length) return 0\n return prev <= 0 ? results.length - 1 : prev - 1\n })\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n if (open) {\n setOpen(false)\n } else if (query) {\n setQuery('')\n } else {\n collapseAndReset()\n }\n return\n }\n if (event.key === 'Enter') {\n event.preventDefault()\n const target = results[selectedIndex]\n openResult(target)\n return\n }\n },\n [open, query, results, selectedIndex, openResult, collapseAndReset],\n )\n\n const showVectorWarning = !embeddingConfigured && !error\n const trimmed = query.trim()\n const tooShort = trimmed.length > 0 && trimmed.length < MIN_QUERY_LENGTH\n const showPopover =\n open && (loading || results.length > 0 || error !== null || tooShort || showVectorWarning)\n const showClear = query.length > 0\n\n // The results panel is portaled to <body> so it escapes the sticky header's\n // stacking context (z-sticky) \u2014 rendered inline it stays trapped at the\n // header's z-index and page content with its own z-index paints over it\n // (#3097). Anchor it under the input and keep it aligned on scroll/resize.\n React.useEffect(() => {\n if (!showPopover) return\n const update = () => {\n const el = containerRef.current\n if (!el) return\n const rect = el.getBoundingClientRect()\n const width = Math.max(rect.width, 320)\n const left = Math.min(Math.max(rect.left, 8), Math.max(window.innerWidth - width - 8, 8))\n setAnchor({ top: rect.bottom + 4, left, width })\n }\n update()\n window.addEventListener('resize', update)\n window.addEventListener('scroll', update, true)\n return () => {\n window.removeEventListener('resize', update)\n window.removeEventListener('scroll', update, true)\n }\n }, [showPopover])\n\n if (!expanded) {\n return (\n <span\n ref={(el) => {\n containerRef.current = el\n }}\n className=\"inline-flex\"\n >\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"lg\"\n onClick={expandAndFocus}\n aria-label={t('search.dialog.actions.openGlobalSearch', 'Open global search')}\n title={t('search.dialog.actions.openGlobalSearch', 'Open global search')}\n >\n <Search className=\"size-4\" aria-hidden=\"true\" />\n </IconButton>\n </span>\n )\n }\n\n return (\n <div\n ref={(el) => {\n containerRef.current = el\n }}\n data-search-expanded=\"true\"\n className=\"relative min-w-0 sm:w-[260px] md:w-[320px] max-sm:absolute max-sm:inset-x-3 max-sm:top-1/2 max-sm:-translate-y-1/2 max-sm:z-popover\"\n >\n <div\n className={cn(\n 'flex h-9 items-center gap-2 rounded-md border border-input bg-background px-3 text-sm shadow-xs transition-colors hover:bg-muted/40',\n open ? 'border-foreground bg-background shadow-focus' : '',\n )}\n onClick={() => inputRef.current?.focus()}\n >\n <Search className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <input\n ref={inputRef}\n type=\"text\"\n value={query}\n onChange={(event) => {\n setQuery(event.target.value)\n if (!open) setOpen(true)\n }}\n onFocus={() => setOpen(true)}\n onKeyDown={handleKeyDown}\n placeholder={t('search.dialog.actions.search', 'Search')}\n aria-label={t('search.dialog.actions.openGlobalSearch', 'Search')}\n aria-autocomplete=\"list\"\n aria-expanded={open}\n aria-controls=\"topbar-search-results\"\n className=\"flex-1 min-w-0 bg-transparent placeholder:text-muted-foreground/70 focus:outline-none\"\n // Inline search; no need for native browser autocomplete or autocapitalize\n autoComplete=\"off\"\n spellCheck={false}\n />\n {loading ? (\n <Loader2 className=\"size-4 shrink-0 animate-spin text-muted-foreground\" aria-hidden=\"true\" />\n ) : showClear ? (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation()\n setQuery('')\n inputRef.current?.focus()\n }}\n className=\"rounded-sm p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n aria-label={t('search.dialog.actions.clear', 'Clear search')}\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n ) : (\n <kbd className=\"hidden md:inline-flex shrink-0 items-center rounded border bg-muted/50 px-1.5 text-overline font-medium uppercase tracking-wider text-muted-foreground\">\n \u2318K\n </kbd>\n )}\n </div>\n\n {showPopover && anchor ? createPortal(\n <div\n ref={popoverRef}\n id=\"topbar-search-results\"\n role=\"listbox\"\n style={{ position: 'fixed', top: anchor.top, left: anchor.left, width: anchor.width }}\n className=\"z-popover rounded-md border bg-popover text-popover-foreground shadow-md\"\n >\n {error ? (\n <p className=\"border-b px-3 py-2 text-sm text-destructive\">{error}</p>\n ) : null}\n {showVectorWarning && !error ? (\n <div className=\"border-b bg-status-warning-bg px-3 py-2 text-xs text-status-warning-text\">\n <p>{missingConfigMessage}</p>\n <NextLink\n href=\"/backend/config/search\"\n onClick={() => collapseAndReset()}\n className=\"mt-1 inline-flex items-center font-medium underline underline-offset-2 hover:no-underline\"\n >\n {t('search.dialog.warnings.configureLink', 'Configure search settings')}\n </NextLink>\n </div>\n ) : null}\n {showScopeHint ? (\n <p className=\"border-b px-3 py-1.5 text-overline font-medium uppercase tracking-wider text-muted-foreground\">\n {t('search.scopeHint.currentOrg', 'Scoped to current organization')}\n </p>\n ) : null}\n\n {tooShort && !error ? (\n <div className=\"px-3 py-3 text-sm text-muted-foreground\">\n {t('search.dialog.empty.hint', { count: MIN_QUERY_LENGTH })}\n </div>\n ) : null}\n\n {!tooShort && results.length === 0 && !loading && !error ? (\n <div className=\"px-3 py-3 text-sm text-muted-foreground\">\n {t('search.dialog.empty.none', 'No results')}\n </div>\n ) : null}\n\n {results.length > 0 ? (\n <div ref={listRef} className=\"max-h-[380px] overflow-y-auto p-1\">\n {results.map((result, index) => {\n const presenter = result.presenter\n const isActive = index === selectedIndex\n const hasLink = pickPrimaryLink(result) !== null\n const Icon = presenter?.icon ? resolveIcon(presenter.icon) : null\n return (\n <button\n key={`${result.entityId}:${result.recordId}`}\n type=\"button\"\n data-active={isActive}\n onClick={() => openResult(result)}\n onMouseEnter={() => setSelectedIndex(index)}\n role=\"option\"\n aria-selected={isActive}\n className={cn(\n 'flex w-full items-start gap-3 rounded-sm px-2 py-2 text-left transition-colors',\n isActive ? 'bg-muted' : 'hover:bg-muted/60',\n !hasLink && 'opacity-60',\n )}\n >\n {Icon ? (\n <span className=\"mt-0.5 inline-flex size-7 shrink-0 items-center justify-center rounded-md border bg-background\">\n <Icon className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </span>\n ) : (\n <span className=\"mt-0.5 inline-flex size-7 shrink-0 items-center justify-center rounded-md border bg-background\">\n <FileText className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </span>\n )}\n <span className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'truncate text-sm font-medium',\n !hasLink ? 'text-muted-foreground' : 'text-foreground',\n )}\n >\n {presenter?.title ?? result.recordId}\n </span>\n </span>\n <span className=\"flex items-center gap-2 text-overline text-muted-foreground\">\n <span className=\"truncate\">{formatEntityId(result.entityId)}</span>\n {presenter?.subtitle ? (\n <>\n <span aria-hidden=\"true\">\u00B7</span>\n <span className=\"truncate\">{presenter.subtitle}</span>\n </>\n ) : null}\n </span>\n </span>\n </button>\n )\n })}\n </div>\n ) : null}\n </div>,\n document.body,\n ) : null}\n </div>\n )\n}\n\nexport default TopbarSearchInline\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { createPortal } from 'react-dom'\nimport NextLink from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport {\n Search,\n Loader2,\n Zap,\n User,\n Users,\n Building,\n StickyNote,\n Briefcase,\n CheckSquare,\n FileText,\n Mail,\n Phone,\n Calendar,\n Clock,\n Star,\n Tag,\n Flag,\n Heart,\n Bookmark,\n Package,\n Truck,\n ShoppingCart,\n CreditCard,\n DollarSign,\n Target,\n Award,\n Trophy,\n Rocket,\n Lightbulb,\n MessageSquare,\n Bell,\n Settings,\n Globe,\n MapPin,\n Link as LinkIcon,\n Folder,\n Database,\n Activity,\n X,\n} from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport type { SearchResult, SearchResultLink } from '@open-mercato/shared/modules/search'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { resolveEntityTypeLabel } from '../lib/entityTypeLabel'\nimport {\n getCurrentOrganizationScope,\n subscribeOrganizationScopeChanged,\n} from '@open-mercato/shared/lib/frontend/organizationEvents'\nimport { isAllOrganizationsSelection } from '@open-mercato/core/modules/directory/constants'\nimport { parseSelectedOrganizationCookie } from '@open-mercato/core/modules/directory/utils/scopeCookies'\nimport { ForbiddenError } from '@open-mercato/ui/backend/utils/api'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { resolveSearchMinTokenLength } from '@open-mercato/shared/lib/search/config'\nimport { fetchGlobalSearchResults } from '../utils'\n\nconst MIN_QUERY_LENGTH = resolveSearchMinTokenLength()\n\nfunction normalizeLinks(links?: SearchResultLink[] | null): SearchResultLink[] {\n if (!Array.isArray(links)) return []\n return links.filter((link) => typeof link?.href === 'string')\n}\n\nfunction pickPrimaryLink(result: SearchResult): string | null {\n if (result.url) return result.url\n const links = normalizeLinks(result.links)\n if (!links.length) return null\n const primary = links.find((link) => link.kind === 'primary')\n return (primary ?? links[0]).href\n}\n\nfunction hasActiveOrganizationSelection(): boolean {\n const fromEvent = getCurrentOrganizationScope().organizationId\n if (typeof fromEvent === 'string' && fromEvent.trim().length > 0) return true\n const cookieHeader = typeof document === 'undefined' ? null : document.cookie\n const cookieValue = parseSelectedOrganizationCookie(cookieHeader)\n if (!cookieValue) return false\n return !isAllOrganizationsSelection(cookieValue)\n}\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n bolt: Zap,\n zap: Zap,\n user: User,\n users: Users,\n building: Building,\n 'sticky-note': StickyNote,\n briefcase: Briefcase,\n 'check-square': CheckSquare,\n 'file-text': FileText,\n mail: Mail,\n phone: Phone,\n calendar: Calendar,\n clock: Clock,\n star: Star,\n tag: Tag,\n flag: Flag,\n heart: Heart,\n bookmark: Bookmark,\n package: Package,\n truck: Truck,\n 'shopping-cart': ShoppingCart,\n 'credit-card': CreditCard,\n 'dollar-sign': DollarSign,\n target: Target,\n award: Award,\n trophy: Trophy,\n rocket: Rocket,\n lightbulb: Lightbulb,\n 'message-square': MessageSquare,\n bell: Bell,\n settings: Settings,\n globe: Globe,\n 'map-pin': MapPin,\n link: LinkIcon,\n folder: Folder,\n database: Database,\n activity: Activity,\n}\n\nfunction resolveIcon(name?: string): LucideIcon | null {\n if (!name) return null\n return ICON_MAP[name.toLowerCase()] ?? null\n}\n\nexport type TopbarSearchInlineProps = {\n /** Whether embedding provider is configured for vector search */\n embeddingConfigured: boolean\n /** Warning text to show when vector search is enabled but not configured */\n missingConfigMessage: string\n}\n\nexport function TopbarSearchInline({\n embeddingConfigured,\n missingConfigMessage,\n}: TopbarSearchInlineProps) {\n const router = useRouter()\n const t = useT()\n const [query, setQuery] = React.useState('')\n const [results, setResults] = React.useState<SearchResult[]>([])\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [open, setOpen] = React.useState(false)\n const [expanded, setExpanded] = React.useState(false)\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n const [showScopeHint, setShowScopeHint] = React.useState<boolean>(() => hasActiveOrganizationSelection())\n const [anchor, setAnchor] = React.useState<{ top: number; left: number; width: number } | null>(null)\n const inputRef = React.useRef<HTMLInputElement | null>(null)\n const popoverRef = React.useRef<HTMLDivElement | null>(null)\n const containerRef = React.useRef<HTMLElement | null>(null)\n const listRef = React.useRef<HTMLDivElement | null>(null)\n const abortRef = React.useRef<AbortController | null>(null)\n\n const expandAndFocus = React.useCallback(() => {\n setExpanded(true)\n // Wait one tick so the input is mounted/transitioned before focusing\n requestAnimationFrame(() => {\n inputRef.current?.focus()\n inputRef.current?.select()\n })\n }, [])\n\n const collapseAndReset = React.useCallback(() => {\n setOpen(false)\n setExpanded(false)\n setQuery('')\n inputRef.current?.blur()\n }, [])\n\n React.useEffect(() => {\n setShowScopeHint(hasActiveOrganizationSelection())\n return subscribeOrganizationScopeChanged((detail) => {\n setShowScopeHint(Boolean(detail.organizationId && detail.organizationId.trim().length > 0))\n })\n }, [])\n\n // Cmd/Ctrl+K expands + focuses the input\n React.useEffect(() => {\n const handler = (event: KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {\n event.preventDefault()\n expandAndFocus()\n }\n }\n window.addEventListener('keydown', handler)\n return () => window.removeEventListener('keydown', handler)\n }, [expandAndFocus])\n\n // Click outside closes the popover AND collapses input (unless user has typed something)\n React.useEffect(() => {\n if (!expanded) return\n const handler = (event: MouseEvent) => {\n const target = event.target as Node | null\n if (!target) return\n if (containerRef.current?.contains(target)) return\n if (popoverRef.current?.contains(target)) return\n collapseAndReset()\n }\n document.addEventListener('mousedown', handler)\n return () => document.removeEventListener('mousedown', handler)\n }, [expanded, collapseAndReset])\n\n // Fetch results on query change (debounced)\n React.useEffect(() => {\n abortRef.current?.abort()\n const trimmed = query.trim()\n if (trimmed.length < MIN_QUERY_LENGTH) {\n setResults([])\n setError(null)\n setLoading(false)\n return\n }\n\n const controller = new AbortController()\n abortRef.current = controller\n setLoading(true)\n\n const handle = setTimeout(async () => {\n try {\n const data = await fetchGlobalSearchResults(query, {\n limit: 10,\n signal: controller.signal,\n })\n setResults(data.results)\n setError(data.error ?? null)\n setSelectedIndex(0)\n } catch (err: unknown) {\n if (controller.signal.aborted) return\n const abortError = err as { name?: string }\n if (abortError?.name === 'AbortError') return\n if (err instanceof ForbiddenError) {\n setError(t('search.dialog.errors.noPermission'))\n } else {\n setError(err instanceof Error ? err.message : t('search.dialog.errors.searchFailed'))\n }\n setResults([])\n } finally {\n if (!controller.signal.aborted) setLoading(false)\n }\n }, 220)\n\n return () => {\n clearTimeout(handle)\n controller.abort()\n }\n }, [query, t])\n\n // Auto-scroll active item into view\n React.useEffect(() => {\n const container = listRef.current\n const active = container?.querySelector<HTMLElement>('[data-active=\"true\"]')\n if (!container || !active) return\n const { top: containerTop, bottom: containerBottom } = container.getBoundingClientRect()\n const { top: activeTop, bottom: activeBottom } = active.getBoundingClientRect()\n if (activeTop < containerTop) {\n container.scrollTop -= containerTop - activeTop\n } else if (activeBottom > containerBottom) {\n container.scrollTop += activeBottom - containerBottom\n }\n }, [selectedIndex])\n\n const openResult = React.useCallback(\n (result: SearchResult | undefined) => {\n if (!result) return\n const href = pickPrimaryLink(result)\n if (!href) return\n router.push(href)\n collapseAndReset()\n },\n [router, collapseAndReset],\n )\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n if (!open) setOpen(true)\n setSelectedIndex((prev) => (results.length ? (prev + 1) % results.length : 0))\n return\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n setSelectedIndex((prev) => {\n if (!results.length) return 0\n return prev <= 0 ? results.length - 1 : prev - 1\n })\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n if (open) {\n setOpen(false)\n } else if (query) {\n setQuery('')\n } else {\n collapseAndReset()\n }\n return\n }\n if (event.key === 'Enter') {\n event.preventDefault()\n const target = results[selectedIndex]\n openResult(target)\n return\n }\n },\n [open, query, results, selectedIndex, openResult, collapseAndReset],\n )\n\n const showVectorWarning = !embeddingConfigured && !error\n const trimmed = query.trim()\n const tooShort = trimmed.length > 0 && trimmed.length < MIN_QUERY_LENGTH\n const showPopover =\n open && (loading || results.length > 0 || error !== null || tooShort || showVectorWarning)\n const showClear = query.length > 0\n\n // The results panel is portaled to <body> so it escapes the sticky header's\n // stacking context (z-sticky) \u2014 rendered inline it stays trapped at the\n // header's z-index and page content with its own z-index paints over it\n // (#3097). Anchor it under the input and keep it aligned on scroll/resize.\n React.useEffect(() => {\n if (!showPopover) return\n const update = () => {\n const el = containerRef.current\n if (!el) return\n const rect = el.getBoundingClientRect()\n const width = Math.max(rect.width, 320)\n const left = Math.min(Math.max(rect.left, 8), Math.max(window.innerWidth - width - 8, 8))\n setAnchor({ top: rect.bottom + 4, left, width })\n }\n update()\n window.addEventListener('resize', update)\n window.addEventListener('scroll', update, true)\n return () => {\n window.removeEventListener('resize', update)\n window.removeEventListener('scroll', update, true)\n }\n }, [showPopover])\n\n if (!expanded) {\n return (\n <span\n ref={(el) => {\n containerRef.current = el\n }}\n className=\"inline-flex\"\n >\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"lg\"\n onClick={expandAndFocus}\n aria-label={t('search.dialog.actions.openGlobalSearch', 'Open global search')}\n title={t('search.dialog.actions.openGlobalSearch', 'Open global search')}\n >\n <Search className=\"size-4\" aria-hidden=\"true\" />\n </IconButton>\n </span>\n )\n }\n\n return (\n <div\n ref={(el) => {\n containerRef.current = el\n }}\n data-search-expanded=\"true\"\n className=\"relative min-w-0 sm:w-[260px] md:w-[320px] max-sm:absolute max-sm:inset-x-3 max-sm:top-1/2 max-sm:-translate-y-1/2 max-sm:z-popover\"\n >\n <div\n className={cn(\n 'flex h-9 items-center gap-2 rounded-md border border-input bg-background px-3 text-sm shadow-xs transition-colors hover:bg-muted/40',\n open ? 'border-foreground bg-background shadow-focus' : '',\n )}\n onClick={() => inputRef.current?.focus()}\n >\n <Search className=\"size-4 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <input\n ref={inputRef}\n type=\"text\"\n value={query}\n onChange={(event) => {\n setQuery(event.target.value)\n if (!open) setOpen(true)\n }}\n onFocus={() => setOpen(true)}\n onKeyDown={handleKeyDown}\n placeholder={t('search.dialog.actions.search', 'Search')}\n aria-label={t('search.dialog.actions.openGlobalSearch', 'Search')}\n aria-autocomplete=\"list\"\n aria-expanded={open}\n aria-controls=\"topbar-search-results\"\n className=\"flex-1 min-w-0 bg-transparent placeholder:text-muted-foreground/70 focus:outline-none\"\n // Inline search; no need for native browser autocomplete or autocapitalize\n autoComplete=\"off\"\n spellCheck={false}\n />\n {loading ? (\n <Loader2 className=\"size-4 shrink-0 animate-spin text-muted-foreground\" aria-hidden=\"true\" />\n ) : showClear ? (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation()\n setQuery('')\n inputRef.current?.focus()\n }}\n className=\"rounded-sm p-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n aria-label={t('search.dialog.actions.clear', 'Clear search')}\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n ) : (\n <kbd className=\"hidden md:inline-flex shrink-0 items-center rounded border bg-muted/50 px-1.5 text-overline font-medium uppercase tracking-wider text-muted-foreground\">\n \u2318K\n </kbd>\n )}\n </div>\n\n {showPopover && anchor ? createPortal(\n <div\n ref={popoverRef}\n id=\"topbar-search-results\"\n role=\"listbox\"\n style={{ position: 'fixed', top: anchor.top, left: anchor.left, width: anchor.width }}\n className=\"z-popover rounded-md border bg-popover text-popover-foreground shadow-md\"\n >\n {error ? (\n <p className=\"border-b px-3 py-2 text-sm text-destructive\">{error}</p>\n ) : null}\n {showVectorWarning && !error ? (\n <div className=\"border-b bg-status-warning-bg px-3 py-2 text-xs text-status-warning-text\">\n <p>{missingConfigMessage}</p>\n <NextLink\n href=\"/backend/config/search\"\n onClick={() => collapseAndReset()}\n className=\"mt-1 inline-flex items-center font-medium underline underline-offset-2 hover:no-underline\"\n >\n {t('search.dialog.warnings.configureLink', 'Configure search settings')}\n </NextLink>\n </div>\n ) : null}\n {showScopeHint ? (\n <p className=\"border-b px-3 py-1.5 text-overline font-medium uppercase tracking-wider text-muted-foreground\">\n {t('search.scopeHint.currentOrg', 'Scoped to current organization')}\n </p>\n ) : null}\n\n {tooShort && !error ? (\n <div className=\"px-3 py-3 text-sm text-muted-foreground\">\n {t('search.dialog.empty.hint', { count: MIN_QUERY_LENGTH })}\n </div>\n ) : null}\n\n {!tooShort && results.length === 0 && !loading && !error ? (\n <div className=\"px-3 py-3 text-sm text-muted-foreground\">\n {t('search.dialog.empty.none', 'No results')}\n </div>\n ) : null}\n\n {results.length > 0 ? (\n <div ref={listRef} className=\"max-h-[380px] overflow-y-auto p-1\">\n {results.map((result, index) => {\n const presenter = result.presenter\n const isActive = index === selectedIndex\n const hasLink = pickPrimaryLink(result) !== null\n const Icon = presenter?.icon ? resolveIcon(presenter.icon) : null\n return (\n <button\n key={`${result.entityId}:${result.recordId}`}\n type=\"button\"\n data-active={isActive}\n onClick={() => openResult(result)}\n onMouseEnter={() => setSelectedIndex(index)}\n role=\"option\"\n aria-selected={isActive}\n className={cn(\n 'flex w-full items-start gap-3 rounded-sm px-2 py-2 text-left transition-colors',\n isActive ? 'bg-muted' : 'hover:bg-muted/60',\n !hasLink && 'opacity-60',\n )}\n >\n {Icon ? (\n <span className=\"mt-0.5 inline-flex size-7 shrink-0 items-center justify-center rounded-md border bg-background\">\n <Icon className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </span>\n ) : (\n <span className=\"mt-0.5 inline-flex size-7 shrink-0 items-center justify-center rounded-md border bg-background\">\n <FileText className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </span>\n )}\n <span className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'truncate text-sm font-medium',\n !hasLink ? 'text-muted-foreground' : 'text-foreground',\n )}\n >\n {presenter?.title ?? result.recordId}\n </span>\n </span>\n <span className=\"flex items-center gap-2 text-overline text-muted-foreground\">\n <span className=\"truncate\">{resolveEntityTypeLabel(t, result.entityId)}</span>\n {presenter?.subtitle ? (\n <>\n <span aria-hidden=\"true\">\u00B7</span>\n <span className=\"truncate\">{presenter.subtitle}</span>\n </>\n ) : null}\n </span>\n </span>\n </button>\n )\n })}\n </div>\n ) : null}\n </div>,\n document.body,\n ) : null}\n </div>\n )\n}\n\nexport default TopbarSearchInline\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["trimmed"]
|
|
7
7
|
}
|
|
@@ -294,6 +294,17 @@ function VectorSearchSection({
|
|
|
294
294
|
const hasUnsavedEmbeddingChanges = selectedProvider !== null && selectedProvider !== savedProvider || selectedModel !== null && selectedModel !== savedModel || selectedProvider !== null && selectedModel === null && displayProviderInfo.defaultModel !== savedModel || isCustomModel && (customModelName.trim() !== "" || customDimension !== 768);
|
|
295
295
|
const isEmbeddingConfigured = embeddingSettings?.configuredProviders?.includes(savedProvider);
|
|
296
296
|
const providerOptions = ["openai", "google", "mistral", "cohere", "bedrock", "ollama"];
|
|
297
|
+
const activeDriver = vectorStoreConfig?.drivers.find((driver) => driver.id === vectorStoreConfig.currentDriver);
|
|
298
|
+
const isStoreUnavailable = activeDriver?.available === false;
|
|
299
|
+
const storeUnavailableReason = activeDriver?.unavailableReason ?? null;
|
|
300
|
+
const storeUnavailableBanner = isStoreUnavailable ? /* @__PURE__ */ jsx("div", { className: "p-4 rounded-md bg-status-warning-bg border border-status-warning-border", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
301
|
+
/* @__PURE__ */ jsx("svg", { className: "h-5 w-5 text-status-warning-icon flex-shrink-0 mt-0.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }),
|
|
302
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
303
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-warning-text", children: t("search.settings.vector.storeUnavailable", "Vector store is not available") }),
|
|
304
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-status-warning-text mt-1", children: t("search.settings.vector.storeUnavailableHint", "Vector indexing and semantic search are skipped until the store is reachable. Install the pgvector extension on your PostgreSQL server; the status is rechecked about once a minute.") }),
|
|
305
|
+
storeUnavailableReason ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-warning-text mt-1 font-mono", children: storeUnavailableReason }) : null
|
|
306
|
+
] })
|
|
307
|
+
] }) }) : null;
|
|
297
308
|
const autoIndexingChecked = embeddingSettings ? embeddingSettings.autoIndexingEnabled : true;
|
|
298
309
|
const autoIndexingDisabled = embeddingLoading || embeddingSaving || Boolean(embeddingSettings?.autoIndexingLocked);
|
|
299
310
|
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-card p-5 shadow-sm", children: [
|
|
@@ -309,11 +320,12 @@ function VectorSearchSection({
|
|
|
309
320
|
/* @__PURE__ */ jsx(Spinner, { size: "sm" }),
|
|
310
321
|
/* @__PURE__ */ jsx("span", { children: t("search.settings.loadingLabel", "Loading settings...") })
|
|
311
322
|
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
323
|
+
storeUnavailableBanner,
|
|
312
324
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
313
325
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mb-2", children: t("search.settings.vector.store", "Vector Store") }),
|
|
314
326
|
/* @__PURE__ */ jsx("div", { className: "grid gap-2 sm:grid-cols-3", children: vectorStoreConfig?.drivers.map((driver) => {
|
|
315
327
|
const isCurrent = driver.id === vectorStoreConfig.currentDriver;
|
|
316
|
-
const isReady = driver.configured && driver.implemented;
|
|
328
|
+
const isReady = driver.configured && driver.implemented && driver.available !== false;
|
|
317
329
|
return /* @__PURE__ */ jsxs(
|
|
318
330
|
"div",
|
|
319
331
|
{
|
|
@@ -491,7 +503,7 @@ function VectorSearchSection({
|
|
|
491
503
|
/* @__PURE__ */ jsx(TabsContent, { value: "index", children: embeddingLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
|
|
492
504
|
/* @__PURE__ */ jsx(Spinner, { size: "sm" }),
|
|
493
505
|
/* @__PURE__ */ jsx("span", { children: t("search.settings.loadingLabel", "Loading settings...") })
|
|
494
|
-
] }) : !isEmbeddingConfigured ? /* @__PURE__ */ jsx("div", { className: "p-4 rounded-md bg-status-warning-bg border border-status-warning-border", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
506
|
+
] }) : isStoreUnavailable ? storeUnavailableBanner : !isEmbeddingConfigured ? /* @__PURE__ */ jsx("div", { className: "p-4 rounded-md bg-status-warning-bg border border-status-warning-border", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
495
507
|
/* @__PURE__ */ jsx("svg", { className: "h-5 w-5 text-status-warning-icon flex-shrink-0 mt-0.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }),
|
|
496
508
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
497
509
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-warning-text", children: t("search.settings.vectorNotConfigured", "No embedding provider configured") }),
|
|
@@ -634,7 +646,7 @@ function VectorSearchSection({
|
|
|
634
646
|
] }),
|
|
635
647
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-3", children: [
|
|
636
648
|
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: handleEmbeddingCancelChange, disabled: embeddingSaving, children: t("search.settings.actions.cancel", "Cancel") }),
|
|
637
|
-
/* @__PURE__ */ jsxs(Button, { type: "button", variant: "destructive", onClick: handleEmbeddingConfirmChange, disabled: embeddingSaving, children: [
|
|
649
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", variant: "destructive-solid", onClick: handleEmbeddingConfirmChange, disabled: embeddingSaving, children: [
|
|
638
650
|
embeddingSaving ? /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "mr-2" }) : null,
|
|
639
651
|
t("search.settings.actions.confirm", "Confirm")
|
|
640
652
|
] })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/search/frontend/components/sections/VectorSearchSection.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@open-mercato/ui/primitives/tabs'\nimport { Check, Plus } from 'lucide-react'\n\n// Types\ntype EmbeddingProviderId = 'openai' | 'google' | 'mistral' | 'cohere' | 'bedrock' | 'ollama'\n\ntype EmbeddingProviderConfig = {\n providerId: EmbeddingProviderId\n model: string\n dimension: number\n outputDimensionality?: number\n baseUrl?: string\n updatedAt: string\n}\n\ntype EmbeddingModelInfo = {\n id: string\n name: string\n dimension: number\n configurableDimension?: boolean\n minDimension?: number\n maxDimension?: number\n}\n\ntype EmbeddingProviderInfo = {\n name: string\n envKeyRequired: string\n defaultModel: string\n models: EmbeddingModelInfo[]\n}\n\ntype EmbeddingSettings = {\n openaiConfigured: boolean\n autoIndexingEnabled: boolean\n autoIndexingLocked: boolean\n lockReason: string | null\n embeddingConfig: EmbeddingProviderConfig | null\n embeddingConfigSource?: 'tenant' | 'instance' | 'env'\n configuredProviders: EmbeddingProviderId[]\n providerAvailability?: { providerId: EmbeddingProviderId; available: boolean; reason?: string; models?: number }[]\n indexedDimension: number | null\n reindexRequired: boolean\n documentCount: number | null\n}\n\ntype EmbeddingSettingsResponse = {\n settings?: EmbeddingSettings\n error?: string\n}\n\ntype VectorDriverId = 'pgvector' | 'qdrant' | 'chromadb'\n\ntype VectorDriverEnvVar = {\n name: string\n set: boolean\n hint: string\n}\n\ntype VectorDriverStatus = {\n id: VectorDriverId\n name: string\n configured: boolean\n implemented: boolean\n envVars: VectorDriverEnvVar[]\n}\n\ntype VectorStoreConfigResponse = {\n currentDriver: VectorDriverId\n configured: boolean\n drivers: VectorDriverStatus[]\n}\n\ntype ReindexLock = {\n type: 'fulltext' | 'vector'\n action: string\n startedAt: string\n elapsedMinutes: number\n}\n\ntype ActivityLog = {\n id: string\n source: string\n handler: string\n level: 'info' | 'error' | 'warn'\n entityType: string | null\n recordId: string | null\n message: string\n details: unknown\n occurredAt: string\n}\n\nconst EMBEDDING_PROVIDERS: Record<EmbeddingProviderId, EmbeddingProviderInfo> = {\n openai: {\n name: 'OpenAI',\n envKeyRequired: 'OPENAI_API_KEY',\n defaultModel: 'text-embedding-3-small',\n models: [\n { id: 'text-embedding-3-small', name: 'text-embedding-3-small', dimension: 1536 },\n { id: 'text-embedding-3-large', name: 'text-embedding-3-large', dimension: 3072, configurableDimension: true, minDimension: 256, maxDimension: 3072 },\n { id: 'text-embedding-ada-002', name: 'text-embedding-ada-002', dimension: 1536 },\n ],\n },\n google: {\n name: 'Google Generative AI',\n envKeyRequired: 'GOOGLE_GENERATIVE_AI_API_KEY',\n defaultModel: 'text-embedding-004',\n models: [\n { id: 'text-embedding-004', name: 'text-embedding-004', dimension: 768, configurableDimension: true, minDimension: 1, maxDimension: 768 },\n { id: 'embedding-001', name: 'embedding-001', dimension: 768 },\n ],\n },\n mistral: {\n name: 'Mistral',\n envKeyRequired: 'MISTRAL_API_KEY',\n defaultModel: 'mistral-embed',\n models: [\n { id: 'mistral-embed', name: 'mistral-embed', dimension: 1024 },\n ],\n },\n cohere: {\n name: 'Cohere',\n envKeyRequired: 'COHERE_API_KEY',\n defaultModel: 'embed-english-v3.0',\n models: [\n { id: 'embed-english-v3.0', name: 'embed-english-v3.0', dimension: 1024 },\n { id: 'embed-multilingual-v3.0', name: 'embed-multilingual-v3.0', dimension: 1024 },\n { id: 'embed-english-light-v3.0', name: 'embed-english-light-v3.0', dimension: 384 },\n { id: 'embed-multilingual-light-v3.0', name: 'embed-multilingual-light-v3.0', dimension: 384 },\n ],\n },\n bedrock: {\n name: 'Amazon Bedrock',\n envKeyRequired: 'AWS_ACCESS_KEY_ID',\n defaultModel: 'amazon.titan-embed-text-v2:0',\n models: [\n { id: 'amazon.titan-embed-text-v2:0', name: 'Titan Embed Text v2', dimension: 1024, configurableDimension: true, minDimension: 256, maxDimension: 1024 },\n { id: 'amazon.titan-embed-text-v1', name: 'Titan Embed Text v1', dimension: 1536 },\n { id: 'cohere.embed-english-v3', name: 'Cohere Embed English v3', dimension: 1024 },\n { id: 'cohere.embed-multilingual-v3', name: 'Cohere Embed Multilingual v3', dimension: 1024 },\n ],\n },\n ollama: {\n name: 'Ollama (Local)',\n envKeyRequired: 'OLLAMA_BASE_URL',\n defaultModel: 'nomic-embed-text',\n models: [\n { id: 'nomic-embed-text', name: 'nomic-embed-text', dimension: 768 },\n { id: 'mxbai-embed-large', name: 'mxbai-embed-large', dimension: 1024 },\n { id: 'all-minilm', name: 'all-minilm', dimension: 384 },\n { id: 'snowflake-arctic-embed', name: 'snowflake-arctic-embed', dimension: 1024 },\n ],\n },\n}\n\nexport type VectorSearchSectionProps = {\n embeddingSettings: EmbeddingSettings | null\n embeddingLoading: boolean\n vectorStoreConfig: VectorStoreConfigResponse | null\n vectorStoreConfigLoading: boolean\n vectorReindexLock: ReindexLock | null\n onEmbeddingSettingsUpdate: (settings: EmbeddingSettings) => void\n onRefreshEmbeddings: () => Promise<void>\n}\n\nexport function VectorSearchSection({\n embeddingSettings,\n embeddingLoading,\n vectorStoreConfig,\n vectorStoreConfigLoading,\n vectorReindexLock,\n onEmbeddingSettingsUpdate,\n onRefreshEmbeddings,\n}: VectorSearchSectionProps) {\n const t = useT()\n const [embeddingSaving, setEmbeddingSaving] = React.useState(false)\n const autoIndexingPreviousRef = React.useRef<boolean>(true)\n\n // Staged embedding selection\n const [selectedProvider, setSelectedProvider] = React.useState<EmbeddingProviderId | null>(null)\n const [selectedModel, setSelectedModel] = React.useState<string | null>(null)\n const [customModelName, setCustomModelName] = React.useState<string>('')\n const [customDimension, setCustomDimension] = React.useState<number>(768)\n\n const [pendingEmbeddingConfig, setPendingEmbeddingConfig] = React.useState<EmbeddingProviderConfig | null>(null)\n const [showEmbeddingConfirmDialog, setShowEmbeddingConfirmDialog] = React.useState(false)\n\n // Vector reindex state\n const [vectorReindexing, setVectorReindexing] = React.useState(false)\n const [showVectorReindexDialog, setShowVectorReindexDialog] = React.useState(false)\n\n // Activity logs state\n const [activityLogs, setActivityLogs] = React.useState<ActivityLog[]>([])\n const [activityLoading, setActivityLoading] = React.useState(true)\n\n // Fetch activity logs\n const fetchActivityLogs = React.useCallback(async () => {\n setActivityLoading(true)\n try {\n const response = await fetch('/api/query_index/status')\n if (response.ok) {\n const body = await response.json() as { logs?: ActivityLog[]; errors?: ActivityLog[] }\n const allLogs: ActivityLog[] = []\n if (body.logs) {\n allLogs.push(...body.logs)\n }\n if (body.errors) {\n allLogs.push(...body.errors.map(err => ({ ...err, level: 'error' as const })))\n }\n // Filter for vector-related logs\n const vectorLogs = allLogs.filter(log => {\n const lowerSource = log.source?.toLowerCase() ?? ''\n const lowerMessage = log.message?.toLowerCase() ?? ''\n const lowerHandler = log.handler?.toLowerCase() ?? ''\n return lowerSource.includes('vector') || lowerMessage.includes('vector') ||\n lowerMessage.includes('embedding') || lowerHandler.includes('vector')\n })\n vectorLogs.sort((a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime())\n setActivityLogs(vectorLogs.slice(0, 50))\n }\n } catch {\n // Silently fail\n } finally {\n setActivityLoading(false)\n }\n }, [])\n\n React.useEffect(() => {\n fetchActivityLogs()\n }, [fetchActivityLogs])\n\n useAppEvent('progress.job.updated', () => {\n void fetchActivityLogs()\n }, [fetchActivityLogs])\n\n useAppEvent('progress.job.completed', () => {\n void fetchActivityLogs()\n }, [fetchActivityLogs])\n\n useAppEvent('om:bridge:reconnected', () => {\n void fetchActivityLogs()\n }, [fetchActivityLogs])\n\n // Update auto-indexing\n const updateAutoIndexing = React.useCallback(async (nextValue: boolean) => {\n autoIndexingPreviousRef.current = embeddingSettings?.autoIndexingEnabled ?? true\n if (embeddingSettings) {\n onEmbeddingSettingsUpdate({ ...embeddingSettings, autoIndexingEnabled: nextValue })\n }\n setEmbeddingSaving(true)\n try {\n const body = await readApiResultOrThrow<EmbeddingSettingsResponse>(\n '/api/search/embeddings',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ autoIndexingEnabled: nextValue }),\n },\n { errorMessage: t('search.settings.errors.saveFailed', 'Failed to save settings'), allowNullResult: true },\n )\n if (body?.settings) {\n onEmbeddingSettingsUpdate(body.settings)\n autoIndexingPreviousRef.current = body.settings.autoIndexingEnabled\n }\n flash(t('search.settings.messages.saved', 'Settings saved'), 'success')\n } catch {\n if (embeddingSettings) {\n onEmbeddingSettingsUpdate({ ...embeddingSettings, autoIndexingEnabled: autoIndexingPreviousRef.current })\n }\n } finally {\n setEmbeddingSaving(false)\n }\n }, [embeddingSettings, onEmbeddingSettingsUpdate, t])\n\n // Provider handlers\n const handleProviderChange = (providerId: EmbeddingProviderId) => {\n setSelectedProvider(providerId)\n setSelectedModel(null)\n setCustomModelName('')\n setCustomDimension(768)\n }\n\n const handleModelChange = (modelId: string) => {\n setSelectedModel(modelId)\n }\n\n const handleApplyEmbeddingChanges = () => {\n const newProviderId = selectedProvider ?? embeddingSettings?.embeddingConfig?.providerId ?? 'openai'\n const newProviderInfo = EMBEDDING_PROVIDERS[newProviderId]\n const newModelId = selectedModel ?? (selectedProvider ? newProviderInfo.defaultModel : embeddingSettings?.embeddingConfig?.model ?? newProviderInfo.defaultModel)\n\n let modelName: string\n let dimension: number\n\n if (newModelId === 'custom') {\n modelName = customModelName.trim()\n dimension = customDimension\n if (!modelName) {\n flash(t('search.settings.errors.modelRequired', 'Please enter a model name'), 'error')\n return\n }\n if (dimension <= 0) {\n flash(t('search.settings.errors.dimensionRequired', 'Please enter a valid dimension'), 'error')\n return\n }\n } else {\n const newModel = newProviderInfo.models.find((m) => m.id === newModelId) ?? newProviderInfo.models[0]\n modelName = newModel.id\n dimension = newModel.dimension\n }\n\n const newConfig: EmbeddingProviderConfig = {\n providerId: newProviderId,\n model: modelName,\n dimension,\n updatedAt: new Date().toISOString(),\n }\n\n if (embeddingSettings?.indexedDimension || embeddingSettings?.embeddingConfig) {\n setPendingEmbeddingConfig(newConfig)\n setShowEmbeddingConfirmDialog(true)\n } else {\n applyEmbeddingConfig(newConfig)\n }\n }\n\n const handleCancelEmbeddingSelection = () => {\n setSelectedProvider(null)\n setSelectedModel(null)\n setCustomModelName('')\n setCustomDimension(768)\n }\n\n const applyEmbeddingConfig = async (config: EmbeddingProviderConfig) => {\n setEmbeddingSaving(true)\n setShowEmbeddingConfirmDialog(false)\n setPendingEmbeddingConfig(null)\n\n try {\n await readApiResultOrThrow<EmbeddingSettingsResponse>(\n '/api/search/embeddings',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ embeddingConfig: config }),\n },\n { errorMessage: t('search.settings.errors.saveFailed', 'Failed to save settings'), allowNullResult: true },\n )\n setSelectedProvider(null)\n setSelectedModel(null)\n flash(t('search.settings.messages.providerSaved', 'Embedding provider saved'), 'success')\n await onRefreshEmbeddings()\n } catch {\n // Error handled by readApiResultOrThrow\n } finally {\n setEmbeddingSaving(false)\n }\n }\n\n const handleEmbeddingConfirmChange = () => {\n if (pendingEmbeddingConfig) {\n applyEmbeddingConfig(pendingEmbeddingConfig)\n }\n }\n\n const handleEmbeddingCancelChange = () => {\n setShowEmbeddingConfirmDialog(false)\n setPendingEmbeddingConfig(null)\n }\n\n // Vector reindex handlers\n const handleVectorReindexClick = () => {\n setShowVectorReindexDialog(true)\n }\n\n const handleVectorReindexConfirm = async () => {\n setShowVectorReindexDialog(false)\n setVectorReindexing(true)\n try {\n await readApiResultOrThrow<{ ok: boolean }>(\n '/api/search/embeddings/reindex',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ purgeFirst: true }),\n },\n { errorMessage: t('search.settings.errors.reindexFailed', 'Reindex failed'), allowNullResult: true },\n )\n flash(t('search.settings.messages.reindexStarted', 'Reindex started'), 'success')\n await fetchActivityLogs()\n } catch {\n // Error handled by readApiResultOrThrow\n } finally {\n setVectorReindexing(false)\n }\n }\n\n const handleVectorReindexCancel = () => {\n setShowVectorReindexDialog(false)\n }\n\n // Computed values\n const savedProvider = embeddingSettings?.embeddingConfig?.providerId ?? 'openai'\n const savedProviderInfo = EMBEDDING_PROVIDERS[savedProvider]\n const savedModel = embeddingSettings?.embeddingConfig?.model ?? savedProviderInfo.defaultModel\n const savedDimension = embeddingSettings?.embeddingConfig?.dimension ?? savedProviderInfo.models[0]?.dimension ?? 768\n\n const savedModelIsPredefined = savedProviderInfo.models.some((m) => m.id === savedModel)\n const savedCustomModel = !savedModelIsPredefined && savedModel ? { id: savedModel, name: savedModel, dimension: savedDimension } : null\n\n const displayProvider = selectedProvider ?? savedProvider\n const displayProviderInfo = EMBEDDING_PROVIDERS[displayProvider]\n const displayModel = selectedModel ?? (selectedProvider ? displayProviderInfo.defaultModel : savedModel)\n const isCustomModel = displayModel === 'custom'\n\n const displayModelIsSavedCustom = !isCustomModel && displayProvider === savedProvider && savedCustomModel && displayModel === savedCustomModel.id\n\n const displayModelInfo = isCustomModel\n ? null\n : displayModelIsSavedCustom\n ? savedCustomModel\n : displayProviderInfo.models.find((m) => m.id === displayModel) ?? displayProviderInfo.models[0]\n const displayDimension = isCustomModel ? customDimension : (displayModelInfo?.dimension ?? 768)\n\n const hasUnsavedEmbeddingChanges = (selectedProvider !== null && selectedProvider !== savedProvider) ||\n (selectedModel !== null && selectedModel !== savedModel) ||\n (selectedProvider !== null && selectedModel === null && displayProviderInfo.defaultModel !== savedModel) ||\n (isCustomModel && (customModelName.trim() !== '' || customDimension !== 768))\n\n const isEmbeddingConfigured = embeddingSettings?.configuredProviders?.includes(savedProvider)\n const providerOptions: EmbeddingProviderId[] = ['openai', 'google', 'mistral', 'cohere', 'bedrock', 'ollama']\n\n const autoIndexingChecked = embeddingSettings ? embeddingSettings.autoIndexingEnabled : true\n const autoIndexingDisabled = embeddingLoading || embeddingSaving || Boolean(embeddingSettings?.autoIndexingLocked)\n\n return (\n <div className=\"rounded-lg border border-border bg-card p-5 shadow-sm\">\n <h2 className=\"text-lg font-semibold mb-2\">\n {t('search.settings.vector.sectionTitle', 'Vector Search')}\n </h2>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('search.settings.vector.sectionDescription', 'AI-powered semantic search using embeddings.')}\n </p>\n\n <Tabs defaultValue=\"configuration\">\n <TabsList className=\"mb-4\">\n <TabsTrigger value=\"configuration\">\n {t('search.settings.tabs.configuration', 'Configuration')}\n </TabsTrigger>\n <TabsTrigger value=\"index\">\n {t('search.settings.tabs.indexManagement', 'Index Management')}\n </TabsTrigger>\n <TabsTrigger value=\"activity\">\n {t('search.settings.tabs.activity', 'Activity')}\n </TabsTrigger>\n </TabsList>\n\n {/* Configuration Tab */}\n <TabsContent value=\"configuration\">\n {(embeddingLoading || vectorStoreConfigLoading) ? (\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Spinner size=\"sm\" />\n <span>{t('search.settings.loadingLabel', 'Loading settings...')}</span>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Vector Store Driver Status */}\n <div>\n <h3 className=\"text-sm font-semibold mb-2\">{t('search.settings.vector.store', 'Vector Store')}</h3>\n <div className=\"grid gap-2 sm:grid-cols-3\">\n {vectorStoreConfig?.drivers.map((driver) => {\n const isCurrent = driver.id === vectorStoreConfig.currentDriver\n const isReady = driver.configured && driver.implemented\n return (\n <div\n key={driver.id}\n className={`flex items-start gap-3 p-3 rounded-md border ${\n isCurrent && isReady\n ? 'border-status-success-border bg-status-success-bg'\n : !driver.implemented\n ? 'border-border bg-muted/30 opacity-60'\n : 'border-border bg-muted/30'\n }`}\n >\n <div className={`flex h-8 w-8 items-center justify-center rounded-full flex-shrink-0 ${\n isCurrent && isReady\n ? 'bg-status-success-bg text-status-success-icon'\n : 'bg-muted text-muted-foreground'\n }`}>\n <svg className=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4\" />\n </svg>\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className={`text-sm font-medium ${isCurrent && isReady ? 'text-status-success-text' : ''}`}>\n {driver.name}\n </p>\n {isCurrent && (\n <span className=\"text-overline px-1.5 py-0.5 rounded bg-status-success-bg text-status-success-text\">\n {t('search.settings.vector.active', 'Active')}\n </span>\n )}\n {!driver.implemented && (\n <span className=\"text-overline px-1.5 py-0.5 rounded bg-muted text-muted-foreground\">\n {t('search.settings.vector.comingSoon', 'Coming soon')}\n </span>\n )}\n </div>\n <div className=\"mt-1 space-y-0.5\">\n {driver.envVars.map((envVar) => (\n <div key={envVar.name} className=\"flex items-center gap-1.5\">\n <div className={`h-1.5 w-1.5 rounded-full ${envVar.set ? 'bg-status-success-icon' : 'bg-muted-foreground/40'}`} />\n <code className=\"text-overline text-muted-foreground font-mono\">{envVar.name}</code>\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n\n {/* Embedding Provider Selection */}\n <div>\n <h3 className=\"text-sm font-semibold mb-2\">{t('search.settings.vector.providers', 'Embedding Provider')}</h3>\n <p className=\"text-xs text-muted-foreground mb-1\">{t('search.settings.vector.providersHint', 'Select a provider to generate embeddings. Only reachable providers can be selected.')}</p>\n {embeddingSettings?.embeddingConfigSource && embeddingSettings.embeddingConfigSource !== 'tenant' && (\n <p className=\"text-xs text-muted-foreground mb-3\">\n {t('search.settings.vector.inheritingDefault', 'This tenant is inheriting the instance/environment default. Saving a provider creates an override for this tenant.')}\n </p>\n )}\n {embeddingSettings?.embeddingConfigSource === 'tenant' && (\n <p className=\"text-xs text-muted-foreground mb-3\">\n {t('search.settings.vector.usingTenantConfig', 'This tenant is using its own embedding settings.')}\n </p>\n )}\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3 items-start\">\n {providerOptions.map((providerId) => {\n const info = EMBEDDING_PROVIDERS[providerId]\n const availability = embeddingSettings?.providerAvailability?.find((entry) => entry.providerId === providerId)\n const isConfigured = availability\n ? availability.available\n : embeddingSettings?.configuredProviders?.includes(providerId)\n const isSelected = displayProvider === providerId\n const isCurrentlySaved = savedProvider === providerId\n return (\n <div\n key={providerId}\n className={`rounded-lg border-2 transition-all ${\n isSelected\n ? 'border-primary bg-primary/5 ring-1 ring-primary/20'\n : isConfigured\n ? 'border-border hover:border-primary/50 hover:bg-muted/50'\n : 'cursor-not-allowed border-border bg-muted/30 opacity-50'\n }`}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => handleProviderChange(providerId)}\n disabled={!isConfigured || embeddingLoading || embeddingSaving}\n aria-pressed={isSelected}\n aria-expanded={isConfigured ? isSelected : undefined}\n aria-controls={isSelected && isConfigured ? `provider-${providerId}-configuration` : undefined}\n className=\"h-auto w-full justify-start whitespace-normal rounded-lg p-3 text-left\"\n >\n <div className=\"flex w-full items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <p className={`text-sm font-medium ${isSelected ? 'text-primary' : isConfigured ? '' : 'text-muted-foreground'}`}>\n {info.name}\n </p>\n {isCurrentlySaved && isConfigured && (\n <span className=\"text-overline rounded bg-status-success-bg px-1.5 py-0.5 text-status-success-text\">\n {t('search.settings.vector.active', 'Active')}\n </span>\n )}\n </div>\n {isConfigured ? (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {info.models.length} {t('search.settings.vector.modelsAvailable', 'models available')}\n </p>\n ) : availability?.reason ? (\n <p className=\"mt-1 text-xs text-status-warning-text\">\n {availability.reason}\n </p>\n ) : (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {t('search.settings.vector.setEnvVar', 'Set')} <code className=\"rounded bg-muted px-1 font-mono text-overline\">{info.envKeyRequired}</code>\n </p>\n )}\n </div>\n <div className={`flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full ${\n isSelected\n ? 'bg-primary text-primary-foreground'\n : isConfigured\n ? 'bg-status-success-bg text-status-success-icon'\n : 'bg-muted text-muted-foreground'\n }`}>\n {isConfigured ? (\n <Check className=\"size-3\" aria-hidden=\"true\" />\n ) : (\n <Plus className=\"size-3\" aria-hidden=\"true\" />\n )}\n </div>\n </div>\n </Button>\n\n {/* Model Selection */}\n {isSelected && isConfigured && (\n <div\n id={`provider-${providerId}-configuration`}\n className=\"mx-3 mb-3 space-y-2 border-t border-border pt-3\"\n >\n <div className=\"space-y-1\">\n <Label htmlFor={`model-${providerId}`} className=\"text-xs font-medium\">\n {t('search.settings.model.label', 'Model')}\n </Label>\n <Select\n value={displayModel}\n onValueChange={handleModelChange}\n disabled={embeddingLoading || embeddingSaving}\n >\n <SelectTrigger id={`model-${providerId}`} size=\"sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {savedCustomModel && displayProvider === savedProvider && (\n <SelectItem key={savedCustomModel.id} value={savedCustomModel.id}>\n {savedCustomModel.name} ({savedCustomModel.dimension}d)\n </SelectItem>\n )}\n {displayProviderInfo.models.map((model) => (\n <SelectItem key={model.id} value={model.id}>\n {model.name} ({model.dimension}d)\n </SelectItem>\n ))}\n <SelectItem value=\"custom\">{t('search.settings.model.custom', 'Custom...')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {isCustomModel && (\n <div className=\"space-y-2 rounded border border-input bg-muted/30 p-2\">\n <div className=\"space-y-1\">\n <Label htmlFor={`custom-model-name-${providerId}`} className=\"text-xs font-medium\">\n {t('search.settings.model.namePlaceholder', 'Model name')}\n </Label>\n <Input\n id={`custom-model-name-${providerId}`}\n type=\"text\"\n size=\"sm\"\n value={customModelName}\n onChange={(e) => setCustomModelName(e.target.value)}\n placeholder={t('search.settings.model.namePlaceholder', 'Model name')}\n disabled={embeddingLoading || embeddingSaving}\n />\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor={`custom-model-dimension-${providerId}`} className=\"text-xs font-medium\">\n {t('search.settings.dimension.label', 'Dimensions')}\n </Label>\n <Input\n id={`custom-model-dimension-${providerId}`}\n type=\"number\"\n size=\"sm\"\n value={customDimension}\n onChange={(e) => setCustomDimension(Number(e.target.value) || 768)}\n placeholder=\"768\"\n min={1}\n disabled={embeddingLoading || embeddingSaving}\n />\n </div>\n </div>\n )}\n\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"text-muted-foreground\">\n {t('search.settings.dimension.label', 'Dimensions')}: {displayDimension}\n </span>\n {embeddingSettings?.indexedDimension && embeddingSettings.indexedDimension !== displayDimension && (\n <span className=\"text-status-warning-text\">\n {t('search.settings.dimension.mismatch', 'mismatch')}: {embeddingSettings.indexedDimension}\n </span>\n )}\n </div>\n\n {hasUnsavedEmbeddingChanges && (\n <div className=\"flex gap-2 pt-1\">\n <Button type=\"button\" variant=\"default\" size=\"sm\" className=\"flex-1\" onClick={handleApplyEmbeddingChanges} disabled={embeddingLoading || embeddingSaving}>\n {embeddingSaving ? <Spinner size=\"sm\" className=\"mr-1\" /> : null}\n {t('search.settings.actions.apply', 'Apply')}\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCancelEmbeddingSelection} disabled={embeddingLoading || embeddingSaving}>\n {t('search.settings.actions.cancel', 'Cancel')}\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n )\n })}\n </div>\n </div>\n\n {/* Setup Instructions */}\n <div className=\"p-3 rounded-md bg-status-info-bg border border-status-info-border\">\n <div className=\"flex items-start gap-2\">\n <svg className=\"h-5 w-5 text-status-info-icon flex-shrink-0 mt-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n <div className=\"text-sm text-status-info-text\">\n <p className=\"font-medium mb-1\">{t('search.settings.vector.howTo', 'How to set up')}</p>\n <p className=\"text-xs\">{t('search.settings.vector.howToDescription', 'Add the API key for your preferred provider to your .env file. Only providers with configured API keys can be selected.')}</p>\n </div>\n </div>\n </div>\n </div>\n )}\n </TabsContent>\n\n {/* Index Management Tab */}\n <TabsContent value=\"index\">\n {embeddingLoading ? (\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Spinner size=\"sm\" />\n <span>{t('search.settings.loadingLabel', 'Loading settings...')}</span>\n </div>\n ) : !isEmbeddingConfigured ? (\n <div className=\"p-4 rounded-md bg-status-warning-bg border border-status-warning-border\">\n <div className=\"flex items-start gap-3\">\n <svg className=\"h-5 w-5 text-status-warning-icon flex-shrink-0 mt-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-warning-text\">\n {t('search.settings.vectorNotConfigured', 'No embedding provider configured')}\n </p>\n <p className=\"text-xs text-status-warning-text mt-1\">\n {t('search.settings.vectorNotConfiguredHint', 'Configure an embedding provider in the Configuration tab to enable indexing.')}\n </p>\n </div>\n </div>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Document Count */}\n {embeddingSettings?.documentCount !== null && embeddingSettings?.documentCount !== undefined && (\n <div className=\"rounded-md border border-border p-4 max-w-xs\">\n <p className=\"text-sm text-muted-foreground\">{t('search.settings.vectorDocumentsLabel', 'Embeddings')}</p>\n <p className=\"text-2xl font-bold\">{embeddingSettings.documentCount.toLocaleString()}</p>\n </div>\n )}\n\n {/* Auto-Indexing Toggle */}\n <div className=\"flex items-start gap-4 p-4 rounded-md border border-border\">\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <input\n id=\"search-auto-indexing\"\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border-muted-foreground/40\"\n checked={autoIndexingChecked}\n onChange={(event) => updateAutoIndexing(event.target.checked)}\n disabled={autoIndexingDisabled}\n />\n <Label htmlFor=\"search-auto-indexing\" className=\"text-sm font-medium\">\n {t('search.settings.autoIndexing.label', 'Enable auto-indexing')}\n </Label>\n {embeddingSaving ? <Spinner size=\"sm\" className=\"text-muted-foreground\" /> : null}\n </div>\n <p className=\"text-xs text-muted-foreground mt-1 ml-6\">\n {t('search.settings.autoIndexing.description', 'Automatically index new and updated records for vector search.')}\n </p>\n {embeddingSettings?.autoIndexingLocked && (\n <p className=\"text-xs text-destructive mt-1 ml-6\">\n {t('search.settings.autoIndexing.locked', 'Disabled via environment variable.')}\n </p>\n )}\n </div>\n </div>\n\n {/* Reindex Actions */}\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold\">{t('search.settings.vectorReindex.title', 'Reindex Data')}</h3>\n <p className=\"text-xs text-muted-foreground\">\n {t('search.settings.vectorReindex.description', 'Rebuild vector embeddings for all indexed entities. This will purge existing data and regenerate all embeddings.')}\n </p>\n\n {/* Active reindex lock banner */}\n {vectorReindexLock && (\n <div className=\"p-3 rounded-md bg-status-info-bg border border-status-info-border\">\n <div className=\"flex items-start gap-3\">\n <Spinner size=\"sm\" className=\"flex-shrink-0 mt-0.5 text-status-info-icon\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-info-text\">\n {t('search.settings.reindexInProgress', 'Reindex operation in progress')}\n </p>\n <p className=\"text-xs text-status-info-text mt-1\">\n {t('search.settings.reindexInProgressDetails', 'Action: {{action}} | Started {{minutes}} minutes ago', {\n action: vectorReindexLock.action,\n minutes: vectorReindexLock.elapsedMinutes,\n })}\n </p>\n </div>\n </div>\n </div>\n )}\n\n <div className=\"flex items-center gap-2 p-2 rounded bg-status-warning-bg\">\n <svg className=\"h-4 w-4 text-status-warning-icon flex-shrink-0\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n <p className=\"text-xs text-status-warning-text\">\n {t('search.settings.vectorReindex.warning', 'This may take a while for large datasets and will consume API credits.')}\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"default\"\n size=\"sm\"\n onClick={handleVectorReindexClick}\n disabled={embeddingLoading || embeddingSaving || vectorReindexing || vectorReindexLock !== null}\n >\n {vectorReindexing || vectorReindexLock !== null ? (\n <>\n <Spinner size=\"sm\" className=\"mr-2\" />\n {t('search.settings.vectorReindex.running', 'Reindexing...')}\n </>\n ) : (\n t('search.settings.vectorReindex.button', 'Full Reindex')\n )}\n </Button>\n </div>\n </div>\n )}\n </TabsContent>\n\n {/* Activity Tab */}\n <TabsContent value=\"activity\">\n {activityLoading ? (\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Spinner size=\"sm\" />\n <span>{t('search.settings.loadingLabel', 'Loading...')}</span>\n </div>\n ) : activityLogs.length === 0 ? (\n <div className=\"p-4 rounded-md bg-muted/50 text-center\">\n <p className=\"text-sm text-muted-foreground\">\n {t('search.settings.activity.noLogs', 'No recent indexing activity')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-2 max-h-80 overflow-y-auto\">\n {activityLogs.map((log) => (\n <div\n key={log.id}\n className={`p-2 rounded-md text-sm ${\n log.level === 'error'\n ? 'bg-status-error-bg border border-status-error-border'\n : 'bg-muted/50'\n }`}\n >\n <div className=\"flex items-start gap-2\">\n {log.level === 'error' && (\n <svg className=\"h-4 w-4 text-status-error-icon flex-shrink-0 mt-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n )}\n <div className=\"flex-1 min-w-0\">\n <p className={`text-xs ${log.level === 'error' ? 'text-status-error-text' : 'text-foreground'}`}>\n {log.message}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {(() => {\n const d = new Date(log.occurredAt)\n const pad = (n: number) => n.toString().padStart(2, '0')\n return `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`\n })()}\n {log.entityType && ` \u00B7 ${log.entityType}`}\n </p>\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n <div className=\"mt-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={fetchActivityLogs}\n disabled={activityLoading}\n >\n {activityLoading ? (\n <>\n <Spinner size=\"sm\" className=\"mr-2\" />\n {t('search.settings.loadingLabel', 'Loading...')}\n </>\n ) : (\n t('search.settings.refreshLabel', 'Refresh')\n )}\n </Button>\n </div>\n </TabsContent>\n </Tabs>\n\n {/* Vector Reindex Confirmation Dialog */}\n {showVectorReindexDialog && (\n <div className=\"fixed inset-0 z-modal flex items-center justify-center bg-black/50\">\n <div className=\"mx-4 max-w-md rounded-lg border border-border bg-card p-6 shadow-lg\">\n <h3 className=\"text-lg font-semibold mb-2\">{t('search.settings.reindex.confirmTitle', 'Confirm Reindex')}</h3>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('search.settings.reindex.confirmDescription', 'This will rebuild all vector embeddings. Existing data will be purged first.')}\n </p>\n <div className=\"flex justify-end gap-3\">\n <Button type=\"button\" variant=\"outline\" onClick={handleVectorReindexCancel}>\n {t('search.settings.actions.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" variant=\"default\" onClick={handleVectorReindexConfirm}>\n {t('search.settings.reindex.confirmButton', 'Start Reindex')}\n </Button>\n </div>\n </div>\n </div>\n )}\n\n {/* Embedding Provider Change Confirmation Dialog */}\n {showEmbeddingConfirmDialog && pendingEmbeddingConfig && (\n <div className=\"fixed inset-0 z-modal flex items-center justify-center bg-black/50\">\n <div className=\"mx-4 max-w-lg rounded-lg border border-border bg-card p-6 shadow-lg\">\n <h3 className=\"text-lg font-semibold mb-2\">{t('search.settings.change.title', 'Confirm Provider Change')}</h3>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('search.settings.change.description', 'Changing the embedding provider will require reindexing all data.')}\n </p>\n <div className=\"mb-4 p-3 rounded-md bg-muted/50 text-sm\">\n <p className=\"font-medium\">\n {embeddingSettings?.embeddingConfig\n ? `${EMBEDDING_PROVIDERS[embeddingSettings.embeddingConfig.providerId].name} (${embeddingSettings.embeddingConfig.model})`\n : 'Default'}\n {' \u2192 '}\n {EMBEDDING_PROVIDERS[pendingEmbeddingConfig.providerId].name} ({pendingEmbeddingConfig.model})\n </p>\n <p className=\"text-muted-foreground\">\n {embeddingSettings?.indexedDimension ?? 'N/A'} \u2192 {pendingEmbeddingConfig.dimension} dimensions\n </p>\n </div>\n <ul className=\"mb-4 space-y-1 text-sm\">\n <li className=\"flex items-start gap-2\">\n <span className=\"text-destructive\">\u2022</span>\n <span>{t('search.settings.change.bullet1', 'Existing vector data will be cleared')}</span>\n </li>\n <li className=\"flex items-start gap-2\">\n <span className=\"text-destructive\">\u2022</span>\n <span>{t('search.settings.change.bullet2', 'Vector search will be unavailable during reindex')}</span>\n </li>\n </ul>\n <div className=\"flex justify-end gap-3\">\n <Button type=\"button\" variant=\"outline\" onClick={handleEmbeddingCancelChange} disabled={embeddingSaving}>\n {t('search.settings.actions.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" variant=\"destructive\" onClick={handleEmbeddingConfirmChange} disabled={embeddingSaving}>\n {embeddingSaving ? <Spinner size=\"sm\" className=\"mr-2\" /> : null}\n {t('search.settings.actions.confirm', 'Confirm')}\n </Button>\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport default VectorSearchSection\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@open-mercato/ui/primitives/tabs'\nimport { Check, Plus } from 'lucide-react'\n\n// Types\ntype EmbeddingProviderId = 'openai' | 'google' | 'mistral' | 'cohere' | 'bedrock' | 'ollama'\n\ntype EmbeddingProviderConfig = {\n providerId: EmbeddingProviderId\n model: string\n dimension: number\n outputDimensionality?: number\n baseUrl?: string\n updatedAt: string\n}\n\ntype EmbeddingModelInfo = {\n id: string\n name: string\n dimension: number\n configurableDimension?: boolean\n minDimension?: number\n maxDimension?: number\n}\n\ntype EmbeddingProviderInfo = {\n name: string\n envKeyRequired: string\n defaultModel: string\n models: EmbeddingModelInfo[]\n}\n\ntype EmbeddingSettings = {\n openaiConfigured: boolean\n autoIndexingEnabled: boolean\n autoIndexingLocked: boolean\n lockReason: string | null\n embeddingConfig: EmbeddingProviderConfig | null\n embeddingConfigSource?: 'tenant' | 'instance' | 'env'\n configuredProviders: EmbeddingProviderId[]\n providerAvailability?: { providerId: EmbeddingProviderId; available: boolean; reason?: string; models?: number }[]\n indexedDimension: number | null\n reindexRequired: boolean\n documentCount: number | null\n}\n\ntype EmbeddingSettingsResponse = {\n settings?: EmbeddingSettings\n error?: string\n}\n\ntype VectorDriverId = 'pgvector' | 'qdrant' | 'chromadb'\n\ntype VectorDriverEnvVar = {\n name: string\n set: boolean\n hint: string\n}\n\ntype VectorDriverStatus = {\n id: VectorDriverId\n name: string\n configured: boolean\n implemented: boolean\n available?: boolean | null\n unavailableReason?: string | null\n envVars: VectorDriverEnvVar[]\n}\n\ntype VectorStoreConfigResponse = {\n currentDriver: VectorDriverId\n configured: boolean\n drivers: VectorDriverStatus[]\n}\n\ntype ReindexLock = {\n type: 'fulltext' | 'vector'\n action: string\n startedAt: string\n elapsedMinutes: number\n}\n\ntype ActivityLog = {\n id: string\n source: string\n handler: string\n level: 'info' | 'error' | 'warn'\n entityType: string | null\n recordId: string | null\n message: string\n details: unknown\n occurredAt: string\n}\n\nconst EMBEDDING_PROVIDERS: Record<EmbeddingProviderId, EmbeddingProviderInfo> = {\n openai: {\n name: 'OpenAI',\n envKeyRequired: 'OPENAI_API_KEY',\n defaultModel: 'text-embedding-3-small',\n models: [\n { id: 'text-embedding-3-small', name: 'text-embedding-3-small', dimension: 1536 },\n { id: 'text-embedding-3-large', name: 'text-embedding-3-large', dimension: 3072, configurableDimension: true, minDimension: 256, maxDimension: 3072 },\n { id: 'text-embedding-ada-002', name: 'text-embedding-ada-002', dimension: 1536 },\n ],\n },\n google: {\n name: 'Google Generative AI',\n envKeyRequired: 'GOOGLE_GENERATIVE_AI_API_KEY',\n defaultModel: 'text-embedding-004',\n models: [\n { id: 'text-embedding-004', name: 'text-embedding-004', dimension: 768, configurableDimension: true, minDimension: 1, maxDimension: 768 },\n { id: 'embedding-001', name: 'embedding-001', dimension: 768 },\n ],\n },\n mistral: {\n name: 'Mistral',\n envKeyRequired: 'MISTRAL_API_KEY',\n defaultModel: 'mistral-embed',\n models: [\n { id: 'mistral-embed', name: 'mistral-embed', dimension: 1024 },\n ],\n },\n cohere: {\n name: 'Cohere',\n envKeyRequired: 'COHERE_API_KEY',\n defaultModel: 'embed-english-v3.0',\n models: [\n { id: 'embed-english-v3.0', name: 'embed-english-v3.0', dimension: 1024 },\n { id: 'embed-multilingual-v3.0', name: 'embed-multilingual-v3.0', dimension: 1024 },\n { id: 'embed-english-light-v3.0', name: 'embed-english-light-v3.0', dimension: 384 },\n { id: 'embed-multilingual-light-v3.0', name: 'embed-multilingual-light-v3.0', dimension: 384 },\n ],\n },\n bedrock: {\n name: 'Amazon Bedrock',\n envKeyRequired: 'AWS_ACCESS_KEY_ID',\n defaultModel: 'amazon.titan-embed-text-v2:0',\n models: [\n { id: 'amazon.titan-embed-text-v2:0', name: 'Titan Embed Text v2', dimension: 1024, configurableDimension: true, minDimension: 256, maxDimension: 1024 },\n { id: 'amazon.titan-embed-text-v1', name: 'Titan Embed Text v1', dimension: 1536 },\n { id: 'cohere.embed-english-v3', name: 'Cohere Embed English v3', dimension: 1024 },\n { id: 'cohere.embed-multilingual-v3', name: 'Cohere Embed Multilingual v3', dimension: 1024 },\n ],\n },\n ollama: {\n name: 'Ollama (Local)',\n envKeyRequired: 'OLLAMA_BASE_URL',\n defaultModel: 'nomic-embed-text',\n models: [\n { id: 'nomic-embed-text', name: 'nomic-embed-text', dimension: 768 },\n { id: 'mxbai-embed-large', name: 'mxbai-embed-large', dimension: 1024 },\n { id: 'all-minilm', name: 'all-minilm', dimension: 384 },\n { id: 'snowflake-arctic-embed', name: 'snowflake-arctic-embed', dimension: 1024 },\n ],\n },\n}\n\nexport type VectorSearchSectionProps = {\n embeddingSettings: EmbeddingSettings | null\n embeddingLoading: boolean\n vectorStoreConfig: VectorStoreConfigResponse | null\n vectorStoreConfigLoading: boolean\n vectorReindexLock: ReindexLock | null\n onEmbeddingSettingsUpdate: (settings: EmbeddingSettings) => void\n onRefreshEmbeddings: () => Promise<void>\n}\n\nexport function VectorSearchSection({\n embeddingSettings,\n embeddingLoading,\n vectorStoreConfig,\n vectorStoreConfigLoading,\n vectorReindexLock,\n onEmbeddingSettingsUpdate,\n onRefreshEmbeddings,\n}: VectorSearchSectionProps) {\n const t = useT()\n const [embeddingSaving, setEmbeddingSaving] = React.useState(false)\n const autoIndexingPreviousRef = React.useRef<boolean>(true)\n\n // Staged embedding selection\n const [selectedProvider, setSelectedProvider] = React.useState<EmbeddingProviderId | null>(null)\n const [selectedModel, setSelectedModel] = React.useState<string | null>(null)\n const [customModelName, setCustomModelName] = React.useState<string>('')\n const [customDimension, setCustomDimension] = React.useState<number>(768)\n\n const [pendingEmbeddingConfig, setPendingEmbeddingConfig] = React.useState<EmbeddingProviderConfig | null>(null)\n const [showEmbeddingConfirmDialog, setShowEmbeddingConfirmDialog] = React.useState(false)\n\n // Vector reindex state\n const [vectorReindexing, setVectorReindexing] = React.useState(false)\n const [showVectorReindexDialog, setShowVectorReindexDialog] = React.useState(false)\n\n // Activity logs state\n const [activityLogs, setActivityLogs] = React.useState<ActivityLog[]>([])\n const [activityLoading, setActivityLoading] = React.useState(true)\n\n // Fetch activity logs\n const fetchActivityLogs = React.useCallback(async () => {\n setActivityLoading(true)\n try {\n const response = await fetch('/api/query_index/status')\n if (response.ok) {\n const body = await response.json() as { logs?: ActivityLog[]; errors?: ActivityLog[] }\n const allLogs: ActivityLog[] = []\n if (body.logs) {\n allLogs.push(...body.logs)\n }\n if (body.errors) {\n allLogs.push(...body.errors.map(err => ({ ...err, level: 'error' as const })))\n }\n // Filter for vector-related logs\n const vectorLogs = allLogs.filter(log => {\n const lowerSource = log.source?.toLowerCase() ?? ''\n const lowerMessage = log.message?.toLowerCase() ?? ''\n const lowerHandler = log.handler?.toLowerCase() ?? ''\n return lowerSource.includes('vector') || lowerMessage.includes('vector') ||\n lowerMessage.includes('embedding') || lowerHandler.includes('vector')\n })\n vectorLogs.sort((a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime())\n setActivityLogs(vectorLogs.slice(0, 50))\n }\n } catch {\n // Silently fail\n } finally {\n setActivityLoading(false)\n }\n }, [])\n\n React.useEffect(() => {\n fetchActivityLogs()\n }, [fetchActivityLogs])\n\n useAppEvent('progress.job.updated', () => {\n void fetchActivityLogs()\n }, [fetchActivityLogs])\n\n useAppEvent('progress.job.completed', () => {\n void fetchActivityLogs()\n }, [fetchActivityLogs])\n\n useAppEvent('om:bridge:reconnected', () => {\n void fetchActivityLogs()\n }, [fetchActivityLogs])\n\n // Update auto-indexing\n const updateAutoIndexing = React.useCallback(async (nextValue: boolean) => {\n autoIndexingPreviousRef.current = embeddingSettings?.autoIndexingEnabled ?? true\n if (embeddingSettings) {\n onEmbeddingSettingsUpdate({ ...embeddingSettings, autoIndexingEnabled: nextValue })\n }\n setEmbeddingSaving(true)\n try {\n const body = await readApiResultOrThrow<EmbeddingSettingsResponse>(\n '/api/search/embeddings',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ autoIndexingEnabled: nextValue }),\n },\n { errorMessage: t('search.settings.errors.saveFailed', 'Failed to save settings'), allowNullResult: true },\n )\n if (body?.settings) {\n onEmbeddingSettingsUpdate(body.settings)\n autoIndexingPreviousRef.current = body.settings.autoIndexingEnabled\n }\n flash(t('search.settings.messages.saved', 'Settings saved'), 'success')\n } catch {\n if (embeddingSettings) {\n onEmbeddingSettingsUpdate({ ...embeddingSettings, autoIndexingEnabled: autoIndexingPreviousRef.current })\n }\n } finally {\n setEmbeddingSaving(false)\n }\n }, [embeddingSettings, onEmbeddingSettingsUpdate, t])\n\n // Provider handlers\n const handleProviderChange = (providerId: EmbeddingProviderId) => {\n setSelectedProvider(providerId)\n setSelectedModel(null)\n setCustomModelName('')\n setCustomDimension(768)\n }\n\n const handleModelChange = (modelId: string) => {\n setSelectedModel(modelId)\n }\n\n const handleApplyEmbeddingChanges = () => {\n const newProviderId = selectedProvider ?? embeddingSettings?.embeddingConfig?.providerId ?? 'openai'\n const newProviderInfo = EMBEDDING_PROVIDERS[newProviderId]\n const newModelId = selectedModel ?? (selectedProvider ? newProviderInfo.defaultModel : embeddingSettings?.embeddingConfig?.model ?? newProviderInfo.defaultModel)\n\n let modelName: string\n let dimension: number\n\n if (newModelId === 'custom') {\n modelName = customModelName.trim()\n dimension = customDimension\n if (!modelName) {\n flash(t('search.settings.errors.modelRequired', 'Please enter a model name'), 'error')\n return\n }\n if (dimension <= 0) {\n flash(t('search.settings.errors.dimensionRequired', 'Please enter a valid dimension'), 'error')\n return\n }\n } else {\n const newModel = newProviderInfo.models.find((m) => m.id === newModelId) ?? newProviderInfo.models[0]\n modelName = newModel.id\n dimension = newModel.dimension\n }\n\n const newConfig: EmbeddingProviderConfig = {\n providerId: newProviderId,\n model: modelName,\n dimension,\n updatedAt: new Date().toISOString(),\n }\n\n if (embeddingSettings?.indexedDimension || embeddingSettings?.embeddingConfig) {\n setPendingEmbeddingConfig(newConfig)\n setShowEmbeddingConfirmDialog(true)\n } else {\n applyEmbeddingConfig(newConfig)\n }\n }\n\n const handleCancelEmbeddingSelection = () => {\n setSelectedProvider(null)\n setSelectedModel(null)\n setCustomModelName('')\n setCustomDimension(768)\n }\n\n const applyEmbeddingConfig = async (config: EmbeddingProviderConfig) => {\n setEmbeddingSaving(true)\n setShowEmbeddingConfirmDialog(false)\n setPendingEmbeddingConfig(null)\n\n try {\n await readApiResultOrThrow<EmbeddingSettingsResponse>(\n '/api/search/embeddings',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ embeddingConfig: config }),\n },\n { errorMessage: t('search.settings.errors.saveFailed', 'Failed to save settings'), allowNullResult: true },\n )\n setSelectedProvider(null)\n setSelectedModel(null)\n flash(t('search.settings.messages.providerSaved', 'Embedding provider saved'), 'success')\n await onRefreshEmbeddings()\n } catch {\n // Error handled by readApiResultOrThrow\n } finally {\n setEmbeddingSaving(false)\n }\n }\n\n const handleEmbeddingConfirmChange = () => {\n if (pendingEmbeddingConfig) {\n applyEmbeddingConfig(pendingEmbeddingConfig)\n }\n }\n\n const handleEmbeddingCancelChange = () => {\n setShowEmbeddingConfirmDialog(false)\n setPendingEmbeddingConfig(null)\n }\n\n // Vector reindex handlers\n const handleVectorReindexClick = () => {\n setShowVectorReindexDialog(true)\n }\n\n const handleVectorReindexConfirm = async () => {\n setShowVectorReindexDialog(false)\n setVectorReindexing(true)\n try {\n await readApiResultOrThrow<{ ok: boolean }>(\n '/api/search/embeddings/reindex',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ purgeFirst: true }),\n },\n { errorMessage: t('search.settings.errors.reindexFailed', 'Reindex failed'), allowNullResult: true },\n )\n flash(t('search.settings.messages.reindexStarted', 'Reindex started'), 'success')\n await fetchActivityLogs()\n } catch {\n // Error handled by readApiResultOrThrow\n } finally {\n setVectorReindexing(false)\n }\n }\n\n const handleVectorReindexCancel = () => {\n setShowVectorReindexDialog(false)\n }\n\n // Computed values\n const savedProvider = embeddingSettings?.embeddingConfig?.providerId ?? 'openai'\n const savedProviderInfo = EMBEDDING_PROVIDERS[savedProvider]\n const savedModel = embeddingSettings?.embeddingConfig?.model ?? savedProviderInfo.defaultModel\n const savedDimension = embeddingSettings?.embeddingConfig?.dimension ?? savedProviderInfo.models[0]?.dimension ?? 768\n\n const savedModelIsPredefined = savedProviderInfo.models.some((m) => m.id === savedModel)\n const savedCustomModel = !savedModelIsPredefined && savedModel ? { id: savedModel, name: savedModel, dimension: savedDimension } : null\n\n const displayProvider = selectedProvider ?? savedProvider\n const displayProviderInfo = EMBEDDING_PROVIDERS[displayProvider]\n const displayModel = selectedModel ?? (selectedProvider ? displayProviderInfo.defaultModel : savedModel)\n const isCustomModel = displayModel === 'custom'\n\n const displayModelIsSavedCustom = !isCustomModel && displayProvider === savedProvider && savedCustomModel && displayModel === savedCustomModel.id\n\n const displayModelInfo = isCustomModel\n ? null\n : displayModelIsSavedCustom\n ? savedCustomModel\n : displayProviderInfo.models.find((m) => m.id === displayModel) ?? displayProviderInfo.models[0]\n const displayDimension = isCustomModel ? customDimension : (displayModelInfo?.dimension ?? 768)\n\n const hasUnsavedEmbeddingChanges = (selectedProvider !== null && selectedProvider !== savedProvider) ||\n (selectedModel !== null && selectedModel !== savedModel) ||\n (selectedProvider !== null && selectedModel === null && displayProviderInfo.defaultModel !== savedModel) ||\n (isCustomModel && (customModelName.trim() !== '' || customDimension !== 768))\n\n const isEmbeddingConfigured = embeddingSettings?.configuredProviders?.includes(savedProvider)\n const providerOptions: EmbeddingProviderId[] = ['openai', 'google', 'mistral', 'cohere', 'bedrock', 'ollama']\n\n const activeDriver = vectorStoreConfig?.drivers.find((driver) => driver.id === vectorStoreConfig.currentDriver)\n const isStoreUnavailable = activeDriver?.available === false\n const storeUnavailableReason = activeDriver?.unavailableReason ?? null\n\n const storeUnavailableBanner = isStoreUnavailable ? (\n <div className=\"p-4 rounded-md bg-status-warning-bg border border-status-warning-border\">\n <div className=\"flex items-start gap-3\">\n <svg className=\"h-5 w-5 text-status-warning-icon flex-shrink-0 mt-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-warning-text\">\n {t('search.settings.vector.storeUnavailable', 'Vector store is not available')}\n </p>\n <p className=\"text-xs text-status-warning-text mt-1\">\n {t('search.settings.vector.storeUnavailableHint', 'Vector indexing and semantic search are skipped until the store is reachable. Install the pgvector extension on your PostgreSQL server; the status is rechecked about once a minute.')}\n </p>\n {storeUnavailableReason ? (\n <p className=\"text-xs text-status-warning-text mt-1 font-mono\">{storeUnavailableReason}</p>\n ) : null}\n </div>\n </div>\n </div>\n ) : null\n\n const autoIndexingChecked = embeddingSettings ? embeddingSettings.autoIndexingEnabled : true\n const autoIndexingDisabled = embeddingLoading || embeddingSaving || Boolean(embeddingSettings?.autoIndexingLocked)\n\n return (\n <div className=\"rounded-lg border border-border bg-card p-5 shadow-sm\">\n <h2 className=\"text-lg font-semibold mb-2\">\n {t('search.settings.vector.sectionTitle', 'Vector Search')}\n </h2>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('search.settings.vector.sectionDescription', 'AI-powered semantic search using embeddings.')}\n </p>\n\n <Tabs defaultValue=\"configuration\">\n <TabsList className=\"mb-4\">\n <TabsTrigger value=\"configuration\">\n {t('search.settings.tabs.configuration', 'Configuration')}\n </TabsTrigger>\n <TabsTrigger value=\"index\">\n {t('search.settings.tabs.indexManagement', 'Index Management')}\n </TabsTrigger>\n <TabsTrigger value=\"activity\">\n {t('search.settings.tabs.activity', 'Activity')}\n </TabsTrigger>\n </TabsList>\n\n {/* Configuration Tab */}\n <TabsContent value=\"configuration\">\n {(embeddingLoading || vectorStoreConfigLoading) ? (\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Spinner size=\"sm\" />\n <span>{t('search.settings.loadingLabel', 'Loading settings...')}</span>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {storeUnavailableBanner}\n {/* Vector Store Driver Status */}\n <div>\n <h3 className=\"text-sm font-semibold mb-2\">{t('search.settings.vector.store', 'Vector Store')}</h3>\n <div className=\"grid gap-2 sm:grid-cols-3\">\n {vectorStoreConfig?.drivers.map((driver) => {\n const isCurrent = driver.id === vectorStoreConfig.currentDriver\n const isReady = driver.configured && driver.implemented && driver.available !== false\n return (\n <div\n key={driver.id}\n className={`flex items-start gap-3 p-3 rounded-md border ${\n isCurrent && isReady\n ? 'border-status-success-border bg-status-success-bg'\n : !driver.implemented\n ? 'border-border bg-muted/30 opacity-60'\n : 'border-border bg-muted/30'\n }`}\n >\n <div className={`flex h-8 w-8 items-center justify-center rounded-full flex-shrink-0 ${\n isCurrent && isReady\n ? 'bg-status-success-bg text-status-success-icon'\n : 'bg-muted text-muted-foreground'\n }`}>\n <svg className=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4\" />\n </svg>\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className={`text-sm font-medium ${isCurrent && isReady ? 'text-status-success-text' : ''}`}>\n {driver.name}\n </p>\n {isCurrent && (\n <span className=\"text-overline px-1.5 py-0.5 rounded bg-status-success-bg text-status-success-text\">\n {t('search.settings.vector.active', 'Active')}\n </span>\n )}\n {!driver.implemented && (\n <span className=\"text-overline px-1.5 py-0.5 rounded bg-muted text-muted-foreground\">\n {t('search.settings.vector.comingSoon', 'Coming soon')}\n </span>\n )}\n </div>\n <div className=\"mt-1 space-y-0.5\">\n {driver.envVars.map((envVar) => (\n <div key={envVar.name} className=\"flex items-center gap-1.5\">\n <div className={`h-1.5 w-1.5 rounded-full ${envVar.set ? 'bg-status-success-icon' : 'bg-muted-foreground/40'}`} />\n <code className=\"text-overline text-muted-foreground font-mono\">{envVar.name}</code>\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n\n {/* Embedding Provider Selection */}\n <div>\n <h3 className=\"text-sm font-semibold mb-2\">{t('search.settings.vector.providers', 'Embedding Provider')}</h3>\n <p className=\"text-xs text-muted-foreground mb-1\">{t('search.settings.vector.providersHint', 'Select a provider to generate embeddings. Only reachable providers can be selected.')}</p>\n {embeddingSettings?.embeddingConfigSource && embeddingSettings.embeddingConfigSource !== 'tenant' && (\n <p className=\"text-xs text-muted-foreground mb-3\">\n {t('search.settings.vector.inheritingDefault', 'This tenant is inheriting the instance/environment default. Saving a provider creates an override for this tenant.')}\n </p>\n )}\n {embeddingSettings?.embeddingConfigSource === 'tenant' && (\n <p className=\"text-xs text-muted-foreground mb-3\">\n {t('search.settings.vector.usingTenantConfig', 'This tenant is using its own embedding settings.')}\n </p>\n )}\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3 items-start\">\n {providerOptions.map((providerId) => {\n const info = EMBEDDING_PROVIDERS[providerId]\n const availability = embeddingSettings?.providerAvailability?.find((entry) => entry.providerId === providerId)\n const isConfigured = availability\n ? availability.available\n : embeddingSettings?.configuredProviders?.includes(providerId)\n const isSelected = displayProvider === providerId\n const isCurrentlySaved = savedProvider === providerId\n return (\n <div\n key={providerId}\n className={`rounded-lg border-2 transition-all ${\n isSelected\n ? 'border-primary bg-primary/5 ring-1 ring-primary/20'\n : isConfigured\n ? 'border-border hover:border-primary/50 hover:bg-muted/50'\n : 'cursor-not-allowed border-border bg-muted/30 opacity-50'\n }`}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => handleProviderChange(providerId)}\n disabled={!isConfigured || embeddingLoading || embeddingSaving}\n aria-pressed={isSelected}\n aria-expanded={isConfigured ? isSelected : undefined}\n aria-controls={isSelected && isConfigured ? `provider-${providerId}-configuration` : undefined}\n className=\"h-auto w-full justify-start whitespace-normal rounded-lg p-3 text-left\"\n >\n <div className=\"flex w-full items-start justify-between gap-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <p className={`text-sm font-medium ${isSelected ? 'text-primary' : isConfigured ? '' : 'text-muted-foreground'}`}>\n {info.name}\n </p>\n {isCurrentlySaved && isConfigured && (\n <span className=\"text-overline rounded bg-status-success-bg px-1.5 py-0.5 text-status-success-text\">\n {t('search.settings.vector.active', 'Active')}\n </span>\n )}\n </div>\n {isConfigured ? (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {info.models.length} {t('search.settings.vector.modelsAvailable', 'models available')}\n </p>\n ) : availability?.reason ? (\n <p className=\"mt-1 text-xs text-status-warning-text\">\n {availability.reason}\n </p>\n ) : (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {t('search.settings.vector.setEnvVar', 'Set')} <code className=\"rounded bg-muted px-1 font-mono text-overline\">{info.envKeyRequired}</code>\n </p>\n )}\n </div>\n <div className={`flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full ${\n isSelected\n ? 'bg-primary text-primary-foreground'\n : isConfigured\n ? 'bg-status-success-bg text-status-success-icon'\n : 'bg-muted text-muted-foreground'\n }`}>\n {isConfigured ? (\n <Check className=\"size-3\" aria-hidden=\"true\" />\n ) : (\n <Plus className=\"size-3\" aria-hidden=\"true\" />\n )}\n </div>\n </div>\n </Button>\n\n {/* Model Selection */}\n {isSelected && isConfigured && (\n <div\n id={`provider-${providerId}-configuration`}\n className=\"mx-3 mb-3 space-y-2 border-t border-border pt-3\"\n >\n <div className=\"space-y-1\">\n <Label htmlFor={`model-${providerId}`} className=\"text-xs font-medium\">\n {t('search.settings.model.label', 'Model')}\n </Label>\n <Select\n value={displayModel}\n onValueChange={handleModelChange}\n disabled={embeddingLoading || embeddingSaving}\n >\n <SelectTrigger id={`model-${providerId}`} size=\"sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {savedCustomModel && displayProvider === savedProvider && (\n <SelectItem key={savedCustomModel.id} value={savedCustomModel.id}>\n {savedCustomModel.name} ({savedCustomModel.dimension}d)\n </SelectItem>\n )}\n {displayProviderInfo.models.map((model) => (\n <SelectItem key={model.id} value={model.id}>\n {model.name} ({model.dimension}d)\n </SelectItem>\n ))}\n <SelectItem value=\"custom\">{t('search.settings.model.custom', 'Custom...')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n {isCustomModel && (\n <div className=\"space-y-2 rounded border border-input bg-muted/30 p-2\">\n <div className=\"space-y-1\">\n <Label htmlFor={`custom-model-name-${providerId}`} className=\"text-xs font-medium\">\n {t('search.settings.model.namePlaceholder', 'Model name')}\n </Label>\n <Input\n id={`custom-model-name-${providerId}`}\n type=\"text\"\n size=\"sm\"\n value={customModelName}\n onChange={(e) => setCustomModelName(e.target.value)}\n placeholder={t('search.settings.model.namePlaceholder', 'Model name')}\n disabled={embeddingLoading || embeddingSaving}\n />\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor={`custom-model-dimension-${providerId}`} className=\"text-xs font-medium\">\n {t('search.settings.dimension.label', 'Dimensions')}\n </Label>\n <Input\n id={`custom-model-dimension-${providerId}`}\n type=\"number\"\n size=\"sm\"\n value={customDimension}\n onChange={(e) => setCustomDimension(Number(e.target.value) || 768)}\n placeholder=\"768\"\n min={1}\n disabled={embeddingLoading || embeddingSaving}\n />\n </div>\n </div>\n )}\n\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"text-muted-foreground\">\n {t('search.settings.dimension.label', 'Dimensions')}: {displayDimension}\n </span>\n {embeddingSettings?.indexedDimension && embeddingSettings.indexedDimension !== displayDimension && (\n <span className=\"text-status-warning-text\">\n {t('search.settings.dimension.mismatch', 'mismatch')}: {embeddingSettings.indexedDimension}\n </span>\n )}\n </div>\n\n {hasUnsavedEmbeddingChanges && (\n <div className=\"flex gap-2 pt-1\">\n <Button type=\"button\" variant=\"default\" size=\"sm\" className=\"flex-1\" onClick={handleApplyEmbeddingChanges} disabled={embeddingLoading || embeddingSaving}>\n {embeddingSaving ? <Spinner size=\"sm\" className=\"mr-1\" /> : null}\n {t('search.settings.actions.apply', 'Apply')}\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleCancelEmbeddingSelection} disabled={embeddingLoading || embeddingSaving}>\n {t('search.settings.actions.cancel', 'Cancel')}\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n )\n })}\n </div>\n </div>\n\n {/* Setup Instructions */}\n <div className=\"p-3 rounded-md bg-status-info-bg border border-status-info-border\">\n <div className=\"flex items-start gap-2\">\n <svg className=\"h-5 w-5 text-status-info-icon flex-shrink-0 mt-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n <div className=\"text-sm text-status-info-text\">\n <p className=\"font-medium mb-1\">{t('search.settings.vector.howTo', 'How to set up')}</p>\n <p className=\"text-xs\">{t('search.settings.vector.howToDescription', 'Add the API key for your preferred provider to your .env file. Only providers with configured API keys can be selected.')}</p>\n </div>\n </div>\n </div>\n </div>\n )}\n </TabsContent>\n\n {/* Index Management Tab */}\n <TabsContent value=\"index\">\n {embeddingLoading ? (\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Spinner size=\"sm\" />\n <span>{t('search.settings.loadingLabel', 'Loading settings...')}</span>\n </div>\n ) : isStoreUnavailable ? (\n storeUnavailableBanner\n ) : !isEmbeddingConfigured ? (\n <div className=\"p-4 rounded-md bg-status-warning-bg border border-status-warning-border\">\n <div className=\"flex items-start gap-3\">\n <svg className=\"h-5 w-5 text-status-warning-icon flex-shrink-0 mt-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-warning-text\">\n {t('search.settings.vectorNotConfigured', 'No embedding provider configured')}\n </p>\n <p className=\"text-xs text-status-warning-text mt-1\">\n {t('search.settings.vectorNotConfiguredHint', 'Configure an embedding provider in the Configuration tab to enable indexing.')}\n </p>\n </div>\n </div>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Document Count */}\n {embeddingSettings?.documentCount !== null && embeddingSettings?.documentCount !== undefined && (\n <div className=\"rounded-md border border-border p-4 max-w-xs\">\n <p className=\"text-sm text-muted-foreground\">{t('search.settings.vectorDocumentsLabel', 'Embeddings')}</p>\n <p className=\"text-2xl font-bold\">{embeddingSettings.documentCount.toLocaleString()}</p>\n </div>\n )}\n\n {/* Auto-Indexing Toggle */}\n <div className=\"flex items-start gap-4 p-4 rounded-md border border-border\">\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <input\n id=\"search-auto-indexing\"\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border-muted-foreground/40\"\n checked={autoIndexingChecked}\n onChange={(event) => updateAutoIndexing(event.target.checked)}\n disabled={autoIndexingDisabled}\n />\n <Label htmlFor=\"search-auto-indexing\" className=\"text-sm font-medium\">\n {t('search.settings.autoIndexing.label', 'Enable auto-indexing')}\n </Label>\n {embeddingSaving ? <Spinner size=\"sm\" className=\"text-muted-foreground\" /> : null}\n </div>\n <p className=\"text-xs text-muted-foreground mt-1 ml-6\">\n {t('search.settings.autoIndexing.description', 'Automatically index new and updated records for vector search.')}\n </p>\n {embeddingSettings?.autoIndexingLocked && (\n <p className=\"text-xs text-destructive mt-1 ml-6\">\n {t('search.settings.autoIndexing.locked', 'Disabled via environment variable.')}\n </p>\n )}\n </div>\n </div>\n\n {/* Reindex Actions */}\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold\">{t('search.settings.vectorReindex.title', 'Reindex Data')}</h3>\n <p className=\"text-xs text-muted-foreground\">\n {t('search.settings.vectorReindex.description', 'Rebuild vector embeddings for all indexed entities. This will purge existing data and regenerate all embeddings.')}\n </p>\n\n {/* Active reindex lock banner */}\n {vectorReindexLock && (\n <div className=\"p-3 rounded-md bg-status-info-bg border border-status-info-border\">\n <div className=\"flex items-start gap-3\">\n <Spinner size=\"sm\" className=\"flex-shrink-0 mt-0.5 text-status-info-icon\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-info-text\">\n {t('search.settings.reindexInProgress', 'Reindex operation in progress')}\n </p>\n <p className=\"text-xs text-status-info-text mt-1\">\n {t('search.settings.reindexInProgressDetails', 'Action: {{action}} | Started {{minutes}} minutes ago', {\n action: vectorReindexLock.action,\n minutes: vectorReindexLock.elapsedMinutes,\n })}\n </p>\n </div>\n </div>\n </div>\n )}\n\n <div className=\"flex items-center gap-2 p-2 rounded bg-status-warning-bg\">\n <svg className=\"h-4 w-4 text-status-warning-icon flex-shrink-0\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n <p className=\"text-xs text-status-warning-text\">\n {t('search.settings.vectorReindex.warning', 'This may take a while for large datasets and will consume API credits.')}\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"default\"\n size=\"sm\"\n onClick={handleVectorReindexClick}\n disabled={embeddingLoading || embeddingSaving || vectorReindexing || vectorReindexLock !== null}\n >\n {vectorReindexing || vectorReindexLock !== null ? (\n <>\n <Spinner size=\"sm\" className=\"mr-2\" />\n {t('search.settings.vectorReindex.running', 'Reindexing...')}\n </>\n ) : (\n t('search.settings.vectorReindex.button', 'Full Reindex')\n )}\n </Button>\n </div>\n </div>\n )}\n </TabsContent>\n\n {/* Activity Tab */}\n <TabsContent value=\"activity\">\n {activityLoading ? (\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Spinner size=\"sm\" />\n <span>{t('search.settings.loadingLabel', 'Loading...')}</span>\n </div>\n ) : activityLogs.length === 0 ? (\n <div className=\"p-4 rounded-md bg-muted/50 text-center\">\n <p className=\"text-sm text-muted-foreground\">\n {t('search.settings.activity.noLogs', 'No recent indexing activity')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-2 max-h-80 overflow-y-auto\">\n {activityLogs.map((log) => (\n <div\n key={log.id}\n className={`p-2 rounded-md text-sm ${\n log.level === 'error'\n ? 'bg-status-error-bg border border-status-error-border'\n : 'bg-muted/50'\n }`}\n >\n <div className=\"flex items-start gap-2\">\n {log.level === 'error' && (\n <svg className=\"h-4 w-4 text-status-error-icon flex-shrink-0 mt-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n )}\n <div className=\"flex-1 min-w-0\">\n <p className={`text-xs ${log.level === 'error' ? 'text-status-error-text' : 'text-foreground'}`}>\n {log.message}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {(() => {\n const d = new Date(log.occurredAt)\n const pad = (n: number) => n.toString().padStart(2, '0')\n return `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`\n })()}\n {log.entityType && ` \u00B7 ${log.entityType}`}\n </p>\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n <div className=\"mt-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={fetchActivityLogs}\n disabled={activityLoading}\n >\n {activityLoading ? (\n <>\n <Spinner size=\"sm\" className=\"mr-2\" />\n {t('search.settings.loadingLabel', 'Loading...')}\n </>\n ) : (\n t('search.settings.refreshLabel', 'Refresh')\n )}\n </Button>\n </div>\n </TabsContent>\n </Tabs>\n\n {/* Vector Reindex Confirmation Dialog */}\n {showVectorReindexDialog && (\n <div className=\"fixed inset-0 z-modal flex items-center justify-center bg-black/50\">\n <div className=\"mx-4 max-w-md rounded-lg border border-border bg-card p-6 shadow-lg\">\n <h3 className=\"text-lg font-semibold mb-2\">{t('search.settings.reindex.confirmTitle', 'Confirm Reindex')}</h3>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('search.settings.reindex.confirmDescription', 'This will rebuild all vector embeddings. Existing data will be purged first.')}\n </p>\n <div className=\"flex justify-end gap-3\">\n <Button type=\"button\" variant=\"outline\" onClick={handleVectorReindexCancel}>\n {t('search.settings.actions.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" variant=\"default\" onClick={handleVectorReindexConfirm}>\n {t('search.settings.reindex.confirmButton', 'Start Reindex')}\n </Button>\n </div>\n </div>\n </div>\n )}\n\n {/* Embedding Provider Change Confirmation Dialog */}\n {showEmbeddingConfirmDialog && pendingEmbeddingConfig && (\n <div className=\"fixed inset-0 z-modal flex items-center justify-center bg-black/50\">\n <div className=\"mx-4 max-w-lg rounded-lg border border-border bg-card p-6 shadow-lg\">\n <h3 className=\"text-lg font-semibold mb-2\">{t('search.settings.change.title', 'Confirm Provider Change')}</h3>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {t('search.settings.change.description', 'Changing the embedding provider will require reindexing all data.')}\n </p>\n <div className=\"mb-4 p-3 rounded-md bg-muted/50 text-sm\">\n <p className=\"font-medium\">\n {embeddingSettings?.embeddingConfig\n ? `${EMBEDDING_PROVIDERS[embeddingSettings.embeddingConfig.providerId].name} (${embeddingSettings.embeddingConfig.model})`\n : 'Default'}\n {' \u2192 '}\n {EMBEDDING_PROVIDERS[pendingEmbeddingConfig.providerId].name} ({pendingEmbeddingConfig.model})\n </p>\n <p className=\"text-muted-foreground\">\n {embeddingSettings?.indexedDimension ?? 'N/A'} \u2192 {pendingEmbeddingConfig.dimension} dimensions\n </p>\n </div>\n <ul className=\"mb-4 space-y-1 text-sm\">\n <li className=\"flex items-start gap-2\">\n <span className=\"text-destructive\">\u2022</span>\n <span>{t('search.settings.change.bullet1', 'Existing vector data will be cleared')}</span>\n </li>\n <li className=\"flex items-start gap-2\">\n <span className=\"text-destructive\">\u2022</span>\n <span>{t('search.settings.change.bullet2', 'Vector search will be unavailable during reindex')}</span>\n </li>\n </ul>\n <div className=\"flex justify-end gap-3\">\n <Button type=\"button\" variant=\"outline\" onClick={handleEmbeddingCancelChange} disabled={embeddingSaving}>\n {t('search.settings.actions.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" variant=\"destructive-solid\" onClick={handleEmbeddingConfirmChange} disabled={embeddingSaving}>\n {embeddingSaving ? <Spinner size=\"sm\" className=\"mr-2\" /> : null}\n {t('search.settings.actions.confirm', 'Confirm')}\n </Button>\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport default VectorSearchSection\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function humanizeSegment(segment) {
|
|
2
|
+
return segment.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
3
|
+
}
|
|
4
|
+
function formatEntityId(entityId) {
|
|
5
|
+
if (!entityId.includes(":")) return humanizeSegment(entityId);
|
|
6
|
+
const [module, entity] = entityId.split(":");
|
|
7
|
+
return `${humanizeSegment(module)} \xB7 ${humanizeSegment(entity)}`;
|
|
8
|
+
}
|
|
9
|
+
function resolveEntityTypeLabel(t, entityId) {
|
|
10
|
+
const fallback = formatEntityId(entityId);
|
|
11
|
+
if (!entityId.includes(":")) return fallback;
|
|
12
|
+
const [module, entity] = entityId.split(":");
|
|
13
|
+
return t(`search.entityType.${module}.${entity}`, fallback);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
formatEntityId,
|
|
17
|
+
resolveEntityTypeLabel
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=entityTypeLabel.js.map
|