@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
|
@@ -49,6 +49,7 @@ import { Button } from '@open-mercato/ui/primitives/button'
|
|
|
49
49
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
50
50
|
import type { SearchResult, SearchResultLink, SearchStrategyId } from '@open-mercato/shared/modules/search'
|
|
51
51
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
52
|
+
import { resolveEntityTypeLabel } from '../lib/entityTypeLabel'
|
|
52
53
|
import {
|
|
53
54
|
getCurrentOrganizationScope,
|
|
54
55
|
subscribeOrganizationScopeChanged,
|
|
@@ -87,14 +88,6 @@ function hasActiveOrganizationSelection(): boolean {
|
|
|
87
88
|
return !isAllOrganizationsSelection(cookieValue);
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
function humanizeSegment(segment: string): string {
|
|
91
|
-
return segment
|
|
92
|
-
.split(/[_-]+/)
|
|
93
|
-
.filter(Boolean)
|
|
94
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
95
|
-
.join(' ')
|
|
96
|
-
}
|
|
97
|
-
|
|
98
91
|
const ICON_MAP: Record<string, LucideIcon> = {
|
|
99
92
|
bolt: Zap,
|
|
100
93
|
zap: Zap,
|
|
@@ -140,12 +133,6 @@ function resolveIcon(name?: string): LucideIcon | null {
|
|
|
140
133
|
return ICON_MAP[name.toLowerCase()] ?? null
|
|
141
134
|
}
|
|
142
135
|
|
|
143
|
-
function formatEntityId(entityId: string): string {
|
|
144
|
-
if (!entityId.includes(':')) return humanizeSegment(entityId)
|
|
145
|
-
const [module, entity] = entityId.split(':')
|
|
146
|
-
return `${humanizeSegment(module)} · ${humanizeSegment(entity)}`
|
|
147
|
-
}
|
|
148
|
-
|
|
149
136
|
export type GlobalSearchDialogProps = {
|
|
150
137
|
/** Whether embedding provider is configured for vector search */
|
|
151
138
|
embeddingConfigured: boolean
|
|
@@ -423,7 +410,7 @@ export function GlobalSearchDialog({
|
|
|
423
410
|
<div className="flex flex-wrap items-center gap-2">
|
|
424
411
|
<span className={cn('font-medium text-base whitespace-normal break-all', !hasLink && 'text-muted-foreground')}>{presenter?.title ?? result.recordId}</span>
|
|
425
412
|
<span className="rounded-full border border-muted-foreground/30 px-2 py-0.5 text-xs text-muted-foreground">
|
|
426
|
-
{
|
|
413
|
+
{resolveEntityTypeLabel(t, result.entityId)}
|
|
427
414
|
</span>
|
|
428
415
|
{!hasLink && (
|
|
429
416
|
<span className="rounded-full border border-status-warning-border bg-status-warning-bg px-2 py-0.5 text-xs text-status-warning-text">
|
|
@@ -11,6 +11,7 @@ import type { SearchResult, SearchStrategyId } from '@open-mercato/shared/module
|
|
|
11
11
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
12
12
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
13
13
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
14
|
+
import { resolveEntityTypeLabel } from '../lib/entityTypeLabel'
|
|
14
15
|
import {
|
|
15
16
|
getCurrentOrganizationScope,
|
|
16
17
|
subscribeOrganizationScopeChanged,
|
|
@@ -60,7 +61,7 @@ function createColumns(t: Translator): ColumnDef<Row>[] {
|
|
|
60
61
|
const title = resolveRowTitle(item)
|
|
61
62
|
const iconName = item.presenter?.icon
|
|
62
63
|
const Icon = iconName ? resolveIcon(iconName) : null
|
|
63
|
-
const typeLabel =
|
|
64
|
+
const typeLabel = resolveEntityTypeLabel(t, item.entityId)
|
|
64
65
|
const snapshot = item.presenter?.subtitle ?? extractSnapshot(item.metadata)
|
|
65
66
|
const links = normalizeLinks(item.links)
|
|
66
67
|
return (
|
|
@@ -160,22 +161,6 @@ function resolveIcon(name?: string): LucideIcon | null {
|
|
|
160
161
|
return null
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
function humanizeSegment(segment: string): string {
|
|
164
|
-
return segment
|
|
165
|
-
.split(/[_-]+/)
|
|
166
|
-
.filter(Boolean)
|
|
167
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
168
|
-
.join(' ')
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function formatEntityId(entityId: string): string {
|
|
172
|
-
if (!entityId.includes(':')) return humanizeSegment(entityId)
|
|
173
|
-
const [module, entity] = entityId.split(':')
|
|
174
|
-
const moduleLabel = humanizeSegment(module)
|
|
175
|
-
const entityLabel = humanizeSegment(entity)
|
|
176
|
-
return `${moduleLabel} · ${entityLabel}`
|
|
177
|
-
}
|
|
178
|
-
|
|
179
164
|
function resolveRowTitle(row: Row): string {
|
|
180
165
|
const presenterTitle = row.presenter?.title
|
|
181
166
|
if (typeof presenterTitle === 'string') {
|
|
@@ -49,6 +49,7 @@ import type { LucideIcon } from 'lucide-react'
|
|
|
49
49
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
50
50
|
import type { SearchResult, SearchResultLink } from '@open-mercato/shared/modules/search'
|
|
51
51
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
52
|
+
import { resolveEntityTypeLabel } from '../lib/entityTypeLabel'
|
|
52
53
|
import {
|
|
53
54
|
getCurrentOrganizationScope,
|
|
54
55
|
subscribeOrganizationScopeChanged,
|
|
@@ -84,14 +85,6 @@ function hasActiveOrganizationSelection(): boolean {
|
|
|
84
85
|
return !isAllOrganizationsSelection(cookieValue)
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
function humanizeSegment(segment: string): string {
|
|
88
|
-
return segment
|
|
89
|
-
.split(/[_-]+/)
|
|
90
|
-
.filter(Boolean)
|
|
91
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
92
|
-
.join(' ')
|
|
93
|
-
}
|
|
94
|
-
|
|
95
88
|
const ICON_MAP: Record<string, LucideIcon> = {
|
|
96
89
|
bolt: Zap,
|
|
97
90
|
zap: Zap,
|
|
@@ -137,12 +130,6 @@ function resolveIcon(name?: string): LucideIcon | null {
|
|
|
137
130
|
return ICON_MAP[name.toLowerCase()] ?? null
|
|
138
131
|
}
|
|
139
132
|
|
|
140
|
-
function formatEntityId(entityId: string): string {
|
|
141
|
-
if (!entityId.includes(':')) return humanizeSegment(entityId)
|
|
142
|
-
const [module, entity] = entityId.split(':')
|
|
143
|
-
return `${humanizeSegment(module)} · ${humanizeSegment(entity)}`
|
|
144
|
-
}
|
|
145
|
-
|
|
146
133
|
export type TopbarSearchInlineProps = {
|
|
147
134
|
/** Whether embedding provider is configured for vector search */
|
|
148
135
|
embeddingConfigured: boolean
|
|
@@ -521,7 +508,7 @@ export function TopbarSearchInline({
|
|
|
521
508
|
</span>
|
|
522
509
|
</span>
|
|
523
510
|
<span className="flex items-center gap-2 text-overline text-muted-foreground">
|
|
524
|
-
<span className="truncate">{
|
|
511
|
+
<span className="truncate">{resolveEntityTypeLabel(t, result.entityId)}</span>
|
|
525
512
|
{presenter?.subtitle ? (
|
|
526
513
|
<>
|
|
527
514
|
<span aria-hidden="true">·</span>
|
|
@@ -79,6 +79,8 @@ type VectorDriverStatus = {
|
|
|
79
79
|
name: string
|
|
80
80
|
configured: boolean
|
|
81
81
|
implemented: boolean
|
|
82
|
+
available?: boolean | null
|
|
83
|
+
unavailableReason?: string | null
|
|
82
84
|
envVars: VectorDriverEnvVar[]
|
|
83
85
|
}
|
|
84
86
|
|
|
@@ -447,6 +449,31 @@ export function VectorSearchSection({
|
|
|
447
449
|
const isEmbeddingConfigured = embeddingSettings?.configuredProviders?.includes(savedProvider)
|
|
448
450
|
const providerOptions: EmbeddingProviderId[] = ['openai', 'google', 'mistral', 'cohere', 'bedrock', 'ollama']
|
|
449
451
|
|
|
452
|
+
const activeDriver = vectorStoreConfig?.drivers.find((driver) => driver.id === vectorStoreConfig.currentDriver)
|
|
453
|
+
const isStoreUnavailable = activeDriver?.available === false
|
|
454
|
+
const storeUnavailableReason = activeDriver?.unavailableReason ?? null
|
|
455
|
+
|
|
456
|
+
const storeUnavailableBanner = isStoreUnavailable ? (
|
|
457
|
+
<div className="p-4 rounded-md bg-status-warning-bg border border-status-warning-border">
|
|
458
|
+
<div className="flex items-start gap-3">
|
|
459
|
+
<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">
|
|
460
|
+
<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" />
|
|
461
|
+
</svg>
|
|
462
|
+
<div>
|
|
463
|
+
<p className="text-sm font-medium text-status-warning-text">
|
|
464
|
+
{t('search.settings.vector.storeUnavailable', 'Vector store is not available')}
|
|
465
|
+
</p>
|
|
466
|
+
<p className="text-xs text-status-warning-text mt-1">
|
|
467
|
+
{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.')}
|
|
468
|
+
</p>
|
|
469
|
+
{storeUnavailableReason ? (
|
|
470
|
+
<p className="text-xs text-status-warning-text mt-1 font-mono">{storeUnavailableReason}</p>
|
|
471
|
+
) : null}
|
|
472
|
+
</div>
|
|
473
|
+
</div>
|
|
474
|
+
</div>
|
|
475
|
+
) : null
|
|
476
|
+
|
|
450
477
|
const autoIndexingChecked = embeddingSettings ? embeddingSettings.autoIndexingEnabled : true
|
|
451
478
|
const autoIndexingDisabled = embeddingLoading || embeddingSaving || Boolean(embeddingSettings?.autoIndexingLocked)
|
|
452
479
|
|
|
@@ -481,13 +508,14 @@ export function VectorSearchSection({
|
|
|
481
508
|
</div>
|
|
482
509
|
) : (
|
|
483
510
|
<div className="space-y-4">
|
|
511
|
+
{storeUnavailableBanner}
|
|
484
512
|
{/* Vector Store Driver Status */}
|
|
485
513
|
<div>
|
|
486
514
|
<h3 className="text-sm font-semibold mb-2">{t('search.settings.vector.store', 'Vector Store')}</h3>
|
|
487
515
|
<div className="grid gap-2 sm:grid-cols-3">
|
|
488
516
|
{vectorStoreConfig?.drivers.map((driver) => {
|
|
489
517
|
const isCurrent = driver.id === vectorStoreConfig.currentDriver
|
|
490
|
-
const isReady = driver.configured && driver.implemented
|
|
518
|
+
const isReady = driver.configured && driver.implemented && driver.available !== false
|
|
491
519
|
return (
|
|
492
520
|
<div
|
|
493
521
|
key={driver.id}
|
|
@@ -746,6 +774,8 @@ export function VectorSearchSection({
|
|
|
746
774
|
<Spinner size="sm" />
|
|
747
775
|
<span>{t('search.settings.loadingLabel', 'Loading settings...')}</span>
|
|
748
776
|
</div>
|
|
777
|
+
) : isStoreUnavailable ? (
|
|
778
|
+
storeUnavailableBanner
|
|
749
779
|
) : !isEmbeddingConfigured ? (
|
|
750
780
|
<div className="p-4 rounded-md bg-status-warning-bg border border-status-warning-border">
|
|
751
781
|
<div className="flex items-start gap-3">
|
|
@@ -979,7 +1009,7 @@ export function VectorSearchSection({
|
|
|
979
1009
|
<Button type="button" variant="outline" onClick={handleEmbeddingCancelChange} disabled={embeddingSaving}>
|
|
980
1010
|
{t('search.settings.actions.cancel', 'Cancel')}
|
|
981
1011
|
</Button>
|
|
982
|
-
<Button type="button" variant="destructive" onClick={handleEmbeddingConfirmChange} disabled={embeddingSaving}>
|
|
1012
|
+
<Button type="button" variant="destructive-solid" onClick={handleEmbeddingConfirmChange} disabled={embeddingSaving}>
|
|
983
1013
|
{embeddingSaving ? <Spinner size="sm" className="mr-2" /> : null}
|
|
984
1014
|
{t('search.settings.actions.confirm', 'Confirm')}
|
|
985
1015
|
</Button>
|
package/src/modules/search/frontend/components/sections/__tests__/VectorSearchSection.test.tsx
CHANGED
|
@@ -50,13 +50,14 @@ const vectorStoreConfig: NonNullable<VectorSearchSectionProps['vectorStoreConfig
|
|
|
50
50
|
|
|
51
51
|
function renderSection(
|
|
52
52
|
settings: NonNullable<VectorSearchSectionProps['embeddingSettings']> = embeddingSettings,
|
|
53
|
+
storeConfig: NonNullable<VectorSearchSectionProps['vectorStoreConfig']> = vectorStoreConfig,
|
|
53
54
|
) {
|
|
54
55
|
return render(
|
|
55
56
|
<I18nProvider locale="en" dict={{}}>
|
|
56
57
|
<VectorSearchSection
|
|
57
58
|
embeddingSettings={settings}
|
|
58
59
|
embeddingLoading={false}
|
|
59
|
-
vectorStoreConfig={
|
|
60
|
+
vectorStoreConfig={storeConfig}
|
|
60
61
|
vectorStoreConfigLoading={false}
|
|
61
62
|
vectorReindexLock={null}
|
|
62
63
|
onEmbeddingSettingsUpdate={jest.fn()}
|
|
@@ -113,3 +114,37 @@ describe('VectorSearchSection provider controls', () => {
|
|
|
113
114
|
expect(document.getElementById('provider-ollama-configuration')).toBeNull()
|
|
114
115
|
})
|
|
115
116
|
})
|
|
117
|
+
|
|
118
|
+
describe('VectorSearchSection store availability', () => {
|
|
119
|
+
beforeEach(() => {
|
|
120
|
+
Object.defineProperty(globalThis, 'fetch', {
|
|
121
|
+
configurable: true,
|
|
122
|
+
value: jest.fn(() => new Promise(() => undefined)),
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('warns that the vector store is unavailable and surfaces the driver reason', () => {
|
|
127
|
+
renderSection(embeddingSettings, {
|
|
128
|
+
...vectorStoreConfig,
|
|
129
|
+
drivers: [
|
|
130
|
+
{
|
|
131
|
+
...vectorStoreConfig.drivers[0],
|
|
132
|
+
available: false,
|
|
133
|
+
unavailableReason: 'extension "vector" is not available on this PostgreSQL server',
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
expect(screen.getByText('Vector store is not available')).toBeTruthy()
|
|
139
|
+
expect(screen.getByText(/extension "vector" is not available/)).toBeTruthy()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('keeps the banner hidden while the store is reachable', () => {
|
|
143
|
+
renderSection(embeddingSettings, {
|
|
144
|
+
...vectorStoreConfig,
|
|
145
|
+
drivers: [{ ...vectorStoreConfig.drivers[0], available: true, unavailableReason: null }],
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
expect(screen.queryByText('Vector store is not available')).toBeNull()
|
|
149
|
+
})
|
|
150
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { formatEntityId, resolveEntityTypeLabel } from '../entityTypeLabel'
|
|
2
|
+
import type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
3
|
+
|
|
4
|
+
describe('formatEntityId', () => {
|
|
5
|
+
it('humanizes module · entity', () => {
|
|
6
|
+
expect(formatEntityId('customers:customer_person_profile')).toBe('Customers · Customer Person Profile')
|
|
7
|
+
})
|
|
8
|
+
it('humanizes a bare segment', () => {
|
|
9
|
+
expect(formatEntityId('messages')).toBe('Messages')
|
|
10
|
+
})
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('resolveEntityTypeLabel', () => {
|
|
14
|
+
const t: TranslateFn = (key, fallbackOrParams) =>
|
|
15
|
+
key === 'search.entityType.sales.sales_order'
|
|
16
|
+
? 'Order'
|
|
17
|
+
: (typeof fallbackOrParams === 'string' ? fallbackOrParams : key)
|
|
18
|
+
|
|
19
|
+
it('returns the translated label when a key exists', () => {
|
|
20
|
+
expect(resolveEntityTypeLabel(t, 'sales:sales_order')).toBe('Order')
|
|
21
|
+
})
|
|
22
|
+
it('falls back to the humanized string for unknown entity types', () => {
|
|
23
|
+
expect(resolveEntityTypeLabel(t, 'thirdparty:widget_thing')).toBe('Thirdparty · Widget Thing')
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
2
|
+
|
|
3
|
+
function humanizeSegment(segment: string): string {
|
|
4
|
+
return segment
|
|
5
|
+
.split(/[_-]+/)
|
|
6
|
+
.filter(Boolean)
|
|
7
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
8
|
+
.join(' ')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function formatEntityId(entityId: string): string {
|
|
12
|
+
if (!entityId.includes(':')) return humanizeSegment(entityId)
|
|
13
|
+
const [module, entity] = entityId.split(':')
|
|
14
|
+
return `${humanizeSegment(module)} · ${humanizeSegment(entity)}`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolveEntityTypeLabel(t: TranslateFn, entityId: string): string {
|
|
18
|
+
const fallback = formatEntityId(entityId)
|
|
19
|
+
if (!entityId.includes(':')) return fallback
|
|
20
|
+
const [module, entity] = entityId.split(':')
|
|
21
|
+
return t(`search.entityType.${module}.${entity}`, fallback)
|
|
22
|
+
}
|
|
@@ -50,6 +50,53 @@
|
|
|
50
50
|
"search.dialog.strategies.toggle": "Suchoptionen",
|
|
51
51
|
"search.dialog.title": "Globale Suche",
|
|
52
52
|
"search.dialog.warnings.configureLink": "Sucheinstellungen konfigurieren",
|
|
53
|
+
"search.entityType.catalog.catalog_offer": "Angebot",
|
|
54
|
+
"search.entityType.catalog.catalog_option_schema_template": "Optionsvorlage",
|
|
55
|
+
"search.entityType.catalog.catalog_price_kind": "Preistyp",
|
|
56
|
+
"search.entityType.catalog.catalog_product": "Produkt",
|
|
57
|
+
"search.entityType.catalog.catalog_product_category": "Kategorie",
|
|
58
|
+
"search.entityType.catalog.catalog_product_tag": "Tag",
|
|
59
|
+
"search.entityType.catalog.catalog_product_unit_conversion": "Einheitenumrechnung",
|
|
60
|
+
"search.entityType.catalog.catalog_product_variant": "Variante",
|
|
61
|
+
"search.entityType.customer_accounts.customer_role": "Kundenrolle",
|
|
62
|
+
"search.entityType.customer_accounts.customer_user": "Kundenbenutzer",
|
|
63
|
+
"search.entityType.customers.customer_activity": "Aktivität",
|
|
64
|
+
"search.entityType.customers.customer_comment": "Kommentar",
|
|
65
|
+
"search.entityType.customers.customer_company_profile": "Unternehmen",
|
|
66
|
+
"search.entityType.customers.customer_deal": "Deal",
|
|
67
|
+
"search.entityType.customers.customer_entity": "Kunde",
|
|
68
|
+
"search.entityType.customers.customer_person_profile": "Person",
|
|
69
|
+
"search.entityType.customers.customer_todo_link": "Aufgabe",
|
|
70
|
+
"search.entityType.inbox_ops.inbox_proposal": "Posteingang-Vorschlag",
|
|
71
|
+
"search.entityType.messages.message": "Nachricht",
|
|
72
|
+
"search.entityType.planner.planner_availability_rule_set": "Verfügbarkeitsregeln",
|
|
73
|
+
"search.entityType.resources.resources_resource": "Ressource",
|
|
74
|
+
"search.entityType.resources.resources_resource_type": "Ressourcentyp",
|
|
75
|
+
"search.entityType.sales.sales_channel": "Kanal",
|
|
76
|
+
"search.entityType.sales.sales_credit_memo": "Gutschrift",
|
|
77
|
+
"search.entityType.sales.sales_credit_memo_line": "Gutschriftsposition",
|
|
78
|
+
"search.entityType.sales.sales_delivery_window": "Lieferfenster",
|
|
79
|
+
"search.entityType.sales.sales_document_address": "Dokumentadresse",
|
|
80
|
+
"search.entityType.sales.sales_document_tag": "Dokument-Tag",
|
|
81
|
+
"search.entityType.sales.sales_invoice": "Rechnung",
|
|
82
|
+
"search.entityType.sales.sales_invoice_line": "Rechnungsposition",
|
|
83
|
+
"search.entityType.sales.sales_note": "Notiz",
|
|
84
|
+
"search.entityType.sales.sales_order": "Bestellung",
|
|
85
|
+
"search.entityType.sales.sales_order_adjustment": "Bestellanpassung",
|
|
86
|
+
"search.entityType.sales.sales_order_line": "Bestellposition",
|
|
87
|
+
"search.entityType.sales.sales_payment": "Zahlung",
|
|
88
|
+
"search.entityType.sales.sales_payment_allocation": "Zahlungszuordnung",
|
|
89
|
+
"search.entityType.sales.sales_payment_method": "Zahlungsmethode",
|
|
90
|
+
"search.entityType.sales.sales_quote": "Angebot",
|
|
91
|
+
"search.entityType.sales.sales_quote_adjustment": "Angebotsanpassung",
|
|
92
|
+
"search.entityType.sales.sales_quote_line": "Angebotsposition",
|
|
93
|
+
"search.entityType.sales.sales_shipment": "Sendung",
|
|
94
|
+
"search.entityType.sales.sales_shipment_item": "Sendungsposition",
|
|
95
|
+
"search.entityType.sales.sales_shipping_method": "Versandart",
|
|
96
|
+
"search.entityType.sales.sales_tax_rate": "Steuersatz",
|
|
97
|
+
"search.entityType.staff.staff_team": "Team",
|
|
98
|
+
"search.entityType.staff.staff_team_member": "Teammitglied",
|
|
99
|
+
"search.entityType.staff.staff_team_role": "Teamrolle",
|
|
53
100
|
"search.messages.missingConfig": "Die Suche erfordert die Konfiguration eines Embedding-Anbieters für die semantische Suche.",
|
|
54
101
|
"search.nav.hybridSearch": "Suche",
|
|
55
102
|
"search.scopeHint.currentOrg": "Bereich: aktuelle Organisation",
|
|
@@ -167,6 +214,8 @@
|
|
|
167
214
|
"search.settings.vector.sectionTitle": "Vektorsuche",
|
|
168
215
|
"search.settings.vector.setEnvVar": "Umgebungsvariable setzen",
|
|
169
216
|
"search.settings.vector.store": "Vektorspeicher",
|
|
217
|
+
"search.settings.vector.storeUnavailable": "Vektorspeicher ist nicht verfügbar",
|
|
218
|
+
"search.settings.vector.storeUnavailableHint": "Vektorindizierung und semantische Suche werden übersprungen, bis der Speicher erreichbar ist. Installieren Sie die pgvector-Erweiterung auf Ihrem PostgreSQL-Server; der Status wird etwa einmal pro Minute neu geprüft.",
|
|
170
219
|
"search.settings.vector.usingTenantConfig": "Dieser Mandant verwendet eigene Embedding-Einstellungen.",
|
|
171
220
|
"search.settings.vectorDocumentsLabel": "Vektordokumente",
|
|
172
221
|
"search.settings.vectorNotConfigured": "Vektorsuche ist nicht konfiguriert.",
|
|
@@ -50,6 +50,53 @@
|
|
|
50
50
|
"search.dialog.strategies.toggle": "Search options",
|
|
51
51
|
"search.dialog.title": "Global Search",
|
|
52
52
|
"search.dialog.warnings.configureLink": "Configure search settings",
|
|
53
|
+
"search.entityType.catalog.catalog_offer": "Offer",
|
|
54
|
+
"search.entityType.catalog.catalog_option_schema_template": "Option Template",
|
|
55
|
+
"search.entityType.catalog.catalog_price_kind": "Price Type",
|
|
56
|
+
"search.entityType.catalog.catalog_product": "Product",
|
|
57
|
+
"search.entityType.catalog.catalog_product_category": "Category",
|
|
58
|
+
"search.entityType.catalog.catalog_product_tag": "Tag",
|
|
59
|
+
"search.entityType.catalog.catalog_product_unit_conversion": "Unit Conversion",
|
|
60
|
+
"search.entityType.catalog.catalog_product_variant": "Variant",
|
|
61
|
+
"search.entityType.customer_accounts.customer_role": "Customer Role",
|
|
62
|
+
"search.entityType.customer_accounts.customer_user": "Customer User",
|
|
63
|
+
"search.entityType.customers.customer_activity": "Activity",
|
|
64
|
+
"search.entityType.customers.customer_comment": "Comment",
|
|
65
|
+
"search.entityType.customers.customer_company_profile": "Company",
|
|
66
|
+
"search.entityType.customers.customer_deal": "Deal",
|
|
67
|
+
"search.entityType.customers.customer_entity": "Customer",
|
|
68
|
+
"search.entityType.customers.customer_person_profile": "Person",
|
|
69
|
+
"search.entityType.customers.customer_todo_link": "Task",
|
|
70
|
+
"search.entityType.inbox_ops.inbox_proposal": "Inbox Proposal",
|
|
71
|
+
"search.entityType.messages.message": "Message",
|
|
72
|
+
"search.entityType.planner.planner_availability_rule_set": "Availability Rules",
|
|
73
|
+
"search.entityType.resources.resources_resource": "Resource",
|
|
74
|
+
"search.entityType.resources.resources_resource_type": "Resource Type",
|
|
75
|
+
"search.entityType.sales.sales_channel": "Channel",
|
|
76
|
+
"search.entityType.sales.sales_credit_memo": "Credit Memo",
|
|
77
|
+
"search.entityType.sales.sales_credit_memo_line": "Credit Memo Line",
|
|
78
|
+
"search.entityType.sales.sales_delivery_window": "Delivery Window",
|
|
79
|
+
"search.entityType.sales.sales_document_address": "Document Address",
|
|
80
|
+
"search.entityType.sales.sales_document_tag": "Document Tag",
|
|
81
|
+
"search.entityType.sales.sales_invoice": "Invoice",
|
|
82
|
+
"search.entityType.sales.sales_invoice_line": "Invoice Line",
|
|
83
|
+
"search.entityType.sales.sales_note": "Note",
|
|
84
|
+
"search.entityType.sales.sales_order": "Order",
|
|
85
|
+
"search.entityType.sales.sales_order_adjustment": "Order Adjustment",
|
|
86
|
+
"search.entityType.sales.sales_order_line": "Order Line",
|
|
87
|
+
"search.entityType.sales.sales_payment": "Payment",
|
|
88
|
+
"search.entityType.sales.sales_payment_allocation": "Payment Allocation",
|
|
89
|
+
"search.entityType.sales.sales_payment_method": "Payment Method",
|
|
90
|
+
"search.entityType.sales.sales_quote": "Quote",
|
|
91
|
+
"search.entityType.sales.sales_quote_adjustment": "Quote Adjustment",
|
|
92
|
+
"search.entityType.sales.sales_quote_line": "Quote Line",
|
|
93
|
+
"search.entityType.sales.sales_shipment": "Shipment",
|
|
94
|
+
"search.entityType.sales.sales_shipment_item": "Shipment Item",
|
|
95
|
+
"search.entityType.sales.sales_shipping_method": "Shipping Method",
|
|
96
|
+
"search.entityType.sales.sales_tax_rate": "Tax Rate",
|
|
97
|
+
"search.entityType.staff.staff_team": "Team",
|
|
98
|
+
"search.entityType.staff.staff_team_member": "Team Member",
|
|
99
|
+
"search.entityType.staff.staff_team_role": "Team Role",
|
|
53
100
|
"search.messages.missingConfig": "Search requires configuring an embedding provider for semantic search.",
|
|
54
101
|
"search.nav.hybridSearch": "Search",
|
|
55
102
|
"search.scopeHint.currentOrg": "Scoped to current organization",
|
|
@@ -167,6 +214,8 @@
|
|
|
167
214
|
"search.settings.vector.sectionTitle": "Vector Search",
|
|
168
215
|
"search.settings.vector.setEnvVar": "Set environment variable",
|
|
169
216
|
"search.settings.vector.store": "Vector store",
|
|
217
|
+
"search.settings.vector.storeUnavailable": "Vector store is not available",
|
|
218
|
+
"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.",
|
|
170
219
|
"search.settings.vector.usingTenantConfig": "This tenant is using its own embedding settings.",
|
|
171
220
|
"search.settings.vectorDocumentsLabel": "Vector documents",
|
|
172
221
|
"search.settings.vectorNotConfigured": "Vector search is not configured.",
|
|
@@ -50,6 +50,53 @@
|
|
|
50
50
|
"search.dialog.strategies.toggle": "Opciones de búsqueda",
|
|
51
51
|
"search.dialog.title": "Búsqueda Global",
|
|
52
52
|
"search.dialog.warnings.configureLink": "Configurar búsqueda",
|
|
53
|
+
"search.entityType.catalog.catalog_offer": "Oferta",
|
|
54
|
+
"search.entityType.catalog.catalog_option_schema_template": "Plantilla de opciones",
|
|
55
|
+
"search.entityType.catalog.catalog_price_kind": "Tipo de precio",
|
|
56
|
+
"search.entityType.catalog.catalog_product": "Producto",
|
|
57
|
+
"search.entityType.catalog.catalog_product_category": "Categoría",
|
|
58
|
+
"search.entityType.catalog.catalog_product_tag": "Etiqueta",
|
|
59
|
+
"search.entityType.catalog.catalog_product_unit_conversion": "Conversión de unidades",
|
|
60
|
+
"search.entityType.catalog.catalog_product_variant": "Variante",
|
|
61
|
+
"search.entityType.customer_accounts.customer_role": "Rol de cliente",
|
|
62
|
+
"search.entityType.customer_accounts.customer_user": "Usuario de cliente",
|
|
63
|
+
"search.entityType.customers.customer_activity": "Actividad",
|
|
64
|
+
"search.entityType.customers.customer_comment": "Comentario",
|
|
65
|
+
"search.entityType.customers.customer_company_profile": "Empresa",
|
|
66
|
+
"search.entityType.customers.customer_deal": "Oportunidad",
|
|
67
|
+
"search.entityType.customers.customer_entity": "Cliente",
|
|
68
|
+
"search.entityType.customers.customer_person_profile": "Persona",
|
|
69
|
+
"search.entityType.customers.customer_todo_link": "Tarea",
|
|
70
|
+
"search.entityType.inbox_ops.inbox_proposal": "Propuesta de bandeja",
|
|
71
|
+
"search.entityType.messages.message": "Mensaje",
|
|
72
|
+
"search.entityType.planner.planner_availability_rule_set": "Reglas de disponibilidad",
|
|
73
|
+
"search.entityType.resources.resources_resource": "Recurso",
|
|
74
|
+
"search.entityType.resources.resources_resource_type": "Tipo de recurso",
|
|
75
|
+
"search.entityType.sales.sales_channel": "Canal",
|
|
76
|
+
"search.entityType.sales.sales_credit_memo": "Nota de crédito",
|
|
77
|
+
"search.entityType.sales.sales_credit_memo_line": "Línea de nota de crédito",
|
|
78
|
+
"search.entityType.sales.sales_delivery_window": "Ventana de entrega",
|
|
79
|
+
"search.entityType.sales.sales_document_address": "Dirección del documento",
|
|
80
|
+
"search.entityType.sales.sales_document_tag": "Etiqueta de documento",
|
|
81
|
+
"search.entityType.sales.sales_invoice": "Factura",
|
|
82
|
+
"search.entityType.sales.sales_invoice_line": "Línea de factura",
|
|
83
|
+
"search.entityType.sales.sales_note": "Nota",
|
|
84
|
+
"search.entityType.sales.sales_order": "Pedido",
|
|
85
|
+
"search.entityType.sales.sales_order_adjustment": "Ajuste de pedido",
|
|
86
|
+
"search.entityType.sales.sales_order_line": "Línea de pedido",
|
|
87
|
+
"search.entityType.sales.sales_payment": "Pago",
|
|
88
|
+
"search.entityType.sales.sales_payment_allocation": "Asignación de pago",
|
|
89
|
+
"search.entityType.sales.sales_payment_method": "Método de pago",
|
|
90
|
+
"search.entityType.sales.sales_quote": "Presupuesto",
|
|
91
|
+
"search.entityType.sales.sales_quote_adjustment": "Ajuste de presupuesto",
|
|
92
|
+
"search.entityType.sales.sales_quote_line": "Línea de presupuesto",
|
|
93
|
+
"search.entityType.sales.sales_shipment": "Envío",
|
|
94
|
+
"search.entityType.sales.sales_shipment_item": "Artículo de envío",
|
|
95
|
+
"search.entityType.sales.sales_shipping_method": "Método de envío",
|
|
96
|
+
"search.entityType.sales.sales_tax_rate": "Tasa de impuesto",
|
|
97
|
+
"search.entityType.staff.staff_team": "Equipo",
|
|
98
|
+
"search.entityType.staff.staff_team_member": "Miembro del equipo",
|
|
99
|
+
"search.entityType.staff.staff_team_role": "Rol del equipo",
|
|
53
100
|
"search.messages.missingConfig": "La búsqueda requiere configurar un proveedor de embeddings para la búsqueda semántica.",
|
|
54
101
|
"search.nav.hybridSearch": "Búsqueda",
|
|
55
102
|
"search.scopeHint.currentOrg": "Alcance: organización actual",
|
|
@@ -167,6 +214,8 @@
|
|
|
167
214
|
"search.settings.vector.sectionTitle": "Búsqueda vectorial",
|
|
168
215
|
"search.settings.vector.setEnvVar": "Configurar variable de entorno",
|
|
169
216
|
"search.settings.vector.store": "Almacén vectorial",
|
|
217
|
+
"search.settings.vector.storeUnavailable": "El almacén vectorial no está disponible",
|
|
218
|
+
"search.settings.vector.storeUnavailableHint": "La indexación vectorial y la búsqueda semántica se omiten hasta que el almacén esté disponible. Instala la extensión pgvector en tu servidor PostgreSQL; el estado se vuelve a comprobar aproximadamente cada minuto.",
|
|
170
219
|
"search.settings.vector.usingTenantConfig": "Este inquilino usa su propia configuración de incrustaciones.",
|
|
171
220
|
"search.settings.vectorDocumentsLabel": "Documentos vectoriales",
|
|
172
221
|
"search.settings.vectorNotConfigured": "La búsqueda vectorial no está configurada.",
|