@open-mercato/core 0.6.1-develop.3227.1.2b7b8ab258 → 0.6.1-develop.3256.1.fe3dec2464

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.
Files changed (34) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/catalog/ai-agents.js +4 -0
  3. package/dist/modules/catalog/ai-agents.js.map +2 -2
  4. package/dist/modules/customers/ai-agents.js.map +2 -2
  5. package/dist/modules/customers/api/companies/route.js +11 -1
  6. package/dist/modules/customers/api/companies/route.js.map +2 -2
  7. package/dist/modules/customers/api/people/route.js +11 -1
  8. package/dist/modules/customers/api/people/route.js.map +2 -2
  9. package/dist/modules/customers/backend/customers/companies/page.js +78 -35
  10. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  11. package/dist/modules/customers/backend/customers/deals/page.js +74 -30
  12. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  13. package/dist/modules/customers/backend/customers/people/page.js +78 -35
  14. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  15. package/dist/modules/customers/commands/people.js +20 -0
  16. package/dist/modules/customers/commands/people.js.map +2 -2
  17. package/dist/modules/entities/components/EncryptionManager.js +40 -17
  18. package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
  19. package/dist/modules/inbox_ops/lib/llmProvider.js.map +2 -2
  20. package/package.json +5 -5
  21. package/src/modules/catalog/ai-agents.ts +8 -49
  22. package/src/modules/customers/ai-agents.ts +4 -51
  23. package/src/modules/customers/api/companies/route.ts +10 -0
  24. package/src/modules/customers/api/people/route.ts +10 -0
  25. package/src/modules/customers/backend/customers/companies/page.tsx +95 -35
  26. package/src/modules/customers/backend/customers/deals/page.tsx +91 -30
  27. package/src/modules/customers/backend/customers/people/page.tsx +96 -35
  28. package/src/modules/customers/commands/people.ts +30 -0
  29. package/src/modules/customers/i18n/de.json +8 -0
  30. package/src/modules/customers/i18n/en.json +8 -0
  31. package/src/modules/customers/i18n/es.json +8 -0
  32. package/src/modules/customers/i18n/pl.json +8 -0
  33. package/src/modules/entities/components/EncryptionManager.tsx +50 -21
  34. package/src/modules/inbox_ops/lib/llmProvider.ts +10 -0
@@ -3,6 +3,7 @@
3
3
  import * as React from 'react'
4
4
  import { useQuery, useMutation } from '@tanstack/react-query'
5
5
  import { Button } from '@open-mercato/ui/primitives/button'
6
+ import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
6
7
  import {
7
8
  Select,
8
9
  SelectContent,
@@ -14,13 +15,14 @@ import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
14
15
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
16
  import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
16
17
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
17
- import { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'
18
+ import { useCustomFieldDefs, type CustomFieldDefDto } from '@open-mercato/ui/backend/utils/customFieldDefs'
18
19
  import { Plus, Save, Trash2 } from 'lucide-react'
19
20
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
20
21
  import { useT } from '@open-mercato/shared/lib/i18n/context'
21
22
  import { getEntityFields } from '#generated/entity-fields-registry'
22
23
 
23
24
  type EntityOption = { entityId: string; label?: string; source?: string }
25
+ type FieldOption = { value: string; label: string }
24
26
 
25
27
  type EncryptionFieldRow = {
26
28
  id: string
@@ -36,6 +38,8 @@ type EncryptionMapResponse = {
36
38
 
37
39
  type CanonicalOption = { value: string; label?: string }
38
40
 
41
+ const EMPTY_FIELD_DEFS: CustomFieldDefDto[] = []
42
+
39
43
  function normalizeToken(value: string): string {
40
44
  return value
41
45
  .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
@@ -64,6 +68,24 @@ function normalizeFieldRows(raw: EncryptionMapResponse | undefined, options: Can
64
68
  }))
65
69
  }
66
70
 
71
+ function areFieldRowsEqual(a: EncryptionFieldRow[], b: EncryptionFieldRow[]): boolean {
72
+ if (a === b) return true
73
+ if (a.length !== b.length) return false
74
+ return a.every((row, idx) => {
75
+ const other = b[idx]
76
+ return !!other && row.id === other.id && row.field === other.field && (row.hashField ?? null) === (other.hashField ?? null)
77
+ })
78
+ }
79
+
80
+ function areFieldOptionsEqual(a: FieldOption[], b: FieldOption[]): boolean {
81
+ if (a === b) return true
82
+ if (a.length !== b.length) return false
83
+ return a.every((option, idx) => {
84
+ const other = b[idx]
85
+ return !!other && option.value === other.value && option.label === other.label
86
+ })
87
+ }
88
+
67
89
  function buildRowId(): string {
68
90
  if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID()
69
91
  return `row-${Math.random().toString(36).slice(2)}`
@@ -75,11 +97,12 @@ export function EncryptionManager() {
75
97
  const [selectedEntityId, setSelectedEntityId] = React.useState('')
76
98
  const [fields, setFields] = React.useState<EncryptionFieldRow[]>([])
77
99
  const [isActive, setIsActive] = React.useState(true)
78
- const [baseFieldOptions, setBaseFieldOptions] = React.useState<Array<{ value: string; label: string }>>([])
100
+ const [baseFieldOptions, setBaseFieldOptions] = React.useState<FieldOption[]>([])
79
101
  const [hasUserEdited, setHasUserEdited] = React.useState(false)
80
- const { data: fieldDefs = [], isLoading: loadingFieldDefs } = useCustomFieldDefs(selectedEntityId ? [selectedEntityId] : [], {
102
+ const { data: rawFieldDefs, isLoading: loadingFieldDefs } = useCustomFieldDefs(selectedEntityId ? [selectedEntityId] : [], {
81
103
  enabled: !!selectedEntityId,
82
104
  })
105
+ const fieldDefs = rawFieldDefs ?? EMPTY_FIELD_DEFS
83
106
  const canonicalOptions = React.useMemo<CanonicalOption[]>(() => {
84
107
  const options: CanonicalOption[] = []
85
108
  for (const option of baseFieldOptions) {
@@ -133,13 +156,13 @@ export function EncryptionManager() {
133
156
 
134
157
  React.useEffect(() => {
135
158
  if (!selectedEntityId) {
136
- setBaseFieldOptions([])
159
+ setBaseFieldOptions((prev) => (prev.length ? [] : prev))
137
160
  return
138
161
  }
139
162
  const parts = selectedEntityId.split(':')
140
163
  const entitySlug = parts[1]
141
164
  if (!entitySlug) {
142
- setBaseFieldOptions([])
165
+ setBaseFieldOptions((prev) => (prev.length ? [] : prev))
143
166
  return
144
167
  }
145
168
 
@@ -147,11 +170,11 @@ export function EncryptionManager() {
147
170
  const mod = getEntityFields(entitySlug)
148
171
  if (!mod) {
149
172
  console.warn('[encryption] No fields found for entity', entitySlug)
150
- setBaseFieldOptions([])
173
+ setBaseFieldOptions((prev) => (prev.length ? [] : prev))
151
174
  return
152
175
  }
153
176
 
154
- const options: Array<{ value: string; label: string }> = []
177
+ const options: FieldOption[] = []
155
178
  for (const raw of Object.values(mod)) {
156
179
  if (typeof raw !== 'string' || !raw.trim()) continue
157
180
  const value = raw.trim()
@@ -163,7 +186,7 @@ export function EncryptionManager() {
163
186
  .trim() || value
164
187
  options.push({ value, label })
165
188
  }
166
- setBaseFieldOptions(options)
189
+ setBaseFieldOptions((prev) => (areFieldOptionsEqual(prev, options) ? prev : options))
167
190
  }, [selectedEntityId])
168
191
 
169
192
  const {
@@ -188,14 +211,16 @@ export function EncryptionManager() {
188
211
  const isSameMap = signature === lastMapSignatureRef.current
189
212
  if (isSameMap && hasUserEdited) return
190
213
  if (!map) {
191
- setFields([])
192
- setIsActive(true)
214
+ setFields((prev) => (prev.length ? [] : prev))
215
+ setIsActive((prev) => (prev === true ? prev : true))
193
216
  setHasUserEdited(false)
194
217
  lastMapSignatureRef.current = signature
195
218
  return
196
219
  }
197
- setFields(normalizeFieldRows(map, canonicalOptions))
198
- setIsActive(map?.isActive !== false)
220
+ const nextFields = normalizeFieldRows(map, canonicalOptions)
221
+ setFields((prev) => (areFieldRowsEqual(prev, nextFields) ? prev : nextFields))
222
+ const nextIsActive = map?.isActive !== false
223
+ setIsActive((prev) => (prev === nextIsActive ? prev : nextIsActive))
199
224
  setHasUserEdited(false)
200
225
  lastMapSignatureRef.current = signature
201
226
  }, [mapSignature, map, canonicalOptions, hasUserEdited])
@@ -267,7 +292,7 @@ export function EncryptionManager() {
267
292
  <ErrorMessage
268
293
  label={t('entities.encryption.errors.loadMap', 'Failed to load encryption map')}
269
294
  action={(
270
- <Button variant="outline" size="sm" onClick={() => void refetchMap()}>
295
+ <Button type="button" variant="outline" size="sm" onClick={() => void refetchMap()}>
271
296
  {t('entities.encryption.actions.retry', 'Retry')}
272
297
  </Button>
273
298
  )}
@@ -348,6 +373,7 @@ export function EncryptionManager() {
348
373
  </td>
349
374
  <td className="px-3 py-2 align-top text-right">
350
375
  <Button
376
+ type="button"
351
377
  variant="ghost"
352
378
  size="sm"
353
379
  className="h-9 w-9 px-0"
@@ -398,7 +424,7 @@ export function EncryptionManager() {
398
424
  </SelectContent>
399
425
  </Select>
400
426
  {entitiesError ? (
401
- <p className="mt-1 text-xs text-red-600">
427
+ <p className="mt-1 text-xs text-status-error-text">
402
428
  {t('entities.encryption.errors.loadEntities', 'Failed to load entities')}
403
429
  </p>
404
430
  ) : (
@@ -407,14 +433,16 @@ export function EncryptionManager() {
407
433
  </p>
408
434
  )}
409
435
  </div>
410
- <label className="inline-flex items-center gap-2 text-sm">
411
- <input
412
- type="checkbox"
436
+ <div className="inline-flex items-center gap-2 text-sm">
437
+ <Checkbox
438
+ id="entities-encryption-active"
413
439
  checked={isActive}
414
- onChange={(event) => setIsActive(event.target.checked)}
440
+ onCheckedChange={(checked) => setIsActive(checked === true)}
415
441
  />
416
- {t('entities.encryption.active', 'Encryption enabled for this entity')}
417
- </label>
442
+ <label htmlFor="entities-encryption-active">
443
+ {t('entities.encryption.active', 'Encryption enabled for this entity')}
444
+ </label>
445
+ </div>
418
446
  </div>
419
447
  </div>
420
448
  <div className="rounded-lg border bg-background/80 p-4">
@@ -425,7 +453,7 @@ export function EncryptionManager() {
425
453
  {t('entities.encryption.fields.subtitle', 'List the attributes that should be encrypted with the tenant key.')}
426
454
  </p>
427
455
  </div>
428
- <Button variant="outline" size="sm" onClick={addField}>
456
+ <Button type="button" variant="outline" size="sm" onClick={addField}>
429
457
  <Plus className="mr-2 h-4 w-4" />
430
458
  {t('entities.encryption.actions.add', 'Add field')}
431
459
  </Button>
@@ -434,6 +462,7 @@ export function EncryptionManager() {
434
462
  </div>
435
463
  <div className="flex justify-end">
436
464
  <Button
465
+ type="button"
437
466
  onClick={() => mutation.mutate()}
438
467
  disabled={mutation.isPending || loadingEntities || !!entitiesError || !selectedEntityId}
439
468
  >
@@ -163,6 +163,16 @@ export async function runExtractionWithConfiguredProvider(input: {
163
163
  model = factoryResolution.model
164
164
  modelWithProvider = `${factoryResolution.providerId}/${factoryResolution.modelId}`
165
165
  } else {
166
+ // BC: Legacy OPENCODE_PROVIDER / OPENCODE_MODEL path. This branch only
167
+ // runs when createModelFactory throws AiModelFactoryError('no_provider_configured'),
168
+ // meaning none of the new-style provider env vars (ANTHROPIC_API_KEY,
169
+ // OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, OM_AI_PROVIDER, ...) is
170
+ // set. The OPENCODE_* envs are deliberately kept as BC fallbacks instead
171
+ // of being treated as canonical OM_AI_* values because they are scoped to
172
+ // the OpenCode Code Mode stack (spec
173
+ // 2026-04-27-ai-agents-provider-model-baseurl-overrides, R1 mitigation).
174
+ // Keeping this fallback alive preserves backward compatibility for existing
175
+ // inbox_ops deployments that have not yet migrated to the new env vars.
166
176
  const providerId = resolveExtractionProviderId()
167
177
  const apiKey = requireOpenCodeProviderApiKey(providerId)
168
178
  const modelConfig = resolveOpenCodeModel(providerId, {