@open-mercato/core 0.6.1-develop.3246.1.dbef9d7392 → 0.6.1-develop.3260.1.dc719cac84
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/dist/helpers/integration/authFixtures.js.map +1 -1
- package/dist/helpers/integration/authUi.js +10 -2
- package/dist/helpers/integration/authUi.js.map +2 -2
- package/dist/helpers/integration/salesUi.js +3 -1
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js +10 -1
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js.map +2 -2
- package/dist/modules/audit_logs/lib/display-helpers.js +28 -27
- package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +21 -3
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +6 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +4 -1
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +56 -2
- package/dist/modules/auth/backend/users/page.js.map +3 -3
- package/dist/modules/auth/commands/users.js +14 -0
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/validators.js +8 -0
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/catalog/ai-agents.js +4 -0
- package/dist/modules/catalog/ai-agents.js.map +2 -2
- package/dist/modules/customers/ai-agents.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +1 -1
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -9
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +26 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +1 -1
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +108 -99
- package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js +13 -5
- package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +10 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +15 -8
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/RolesSection.js +35 -22
- package/dist/modules/customers/components/detail/RolesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/TasksSection.js +2 -2
- package/dist/modules/customers/components/detail/TasksSection.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +73 -64
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js +11 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +2 -9
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +14 -29
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +10 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/entities/components/EncryptionManager.js +40 -17
- package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
- package/dist/modules/inbox_ops/lib/llmProvider.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +79 -95
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +49 -70
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRuleSetForm.js +1 -1
- package/dist/modules/planner/components/AvailabilityRuleSetForm.js.map +1 -1
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +19 -19
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/resources/components/ResourceCrudForm.js +1 -1
- package/dist/modules/resources/components/ResourceCrudForm.js.map +1 -1
- package/dist/modules/resources/components/ResourceTypeCrudForm.js +1 -1
- package/dist/modules/resources/components/ResourceTypeCrudForm.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnDialog.js +11 -8
- package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
- package/dist/modules/staff/backend/staff/profile/create/page.js +1 -1
- package/dist/modules/staff/backend/staff/profile/create/page.js.map +2 -2
- package/dist/modules/staff/components/TeamForm.js +1 -1
- package/dist/modules/staff/components/TeamForm.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -1
- package/dist/modules/staff/components/TeamMemberForm.js.map +1 -1
- package/dist/modules/staff/components/TeamRoleForm.js +1 -1
- package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
- package/package.json +5 -5
- package/src/helpers/integration/authFixtures.ts +1 -1
- package/src/helpers/integration/authUi.ts +11 -2
- package/src/helpers/integration/salesUi.ts +10 -1
- package/src/modules/api_docs/frontend/docs/api/Explorer.tsx +7 -3
- package/src/modules/audit_logs/lib/display-helpers.tsx +22 -14
- package/src/modules/auth/api/users/route.ts +22 -3
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +9 -1
- package/src/modules/auth/backend/users/create/page.tsx +6 -2
- package/src/modules/auth/backend/users/page.tsx +60 -3
- package/src/modules/auth/commands/users.ts +15 -0
- package/src/modules/auth/data/validators.ts +8 -0
- package/src/modules/auth/i18n/de.json +4 -0
- package/src/modules/auth/i18n/en.json +4 -0
- package/src/modules/auth/i18n/es.json +4 -0
- package/src/modules/auth/i18n/pl.json +4 -0
- package/src/modules/catalog/ai-agents.ts +8 -49
- package/src/modules/customers/ai-agents.ts +4 -51
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +1 -1
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +12 -13
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +17 -13
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +1 -1
- package/src/modules/customers/components/PipelineSettings.tsx +95 -95
- package/src/modules/customers/components/detail/AssignRoleDialog.tsx +7 -4
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +13 -8
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +16 -12
- package/src/modules/customers/components/detail/RolesSection.tsx +34 -31
- package/src/modules/customers/components/detail/TasksSection.tsx +16 -8
- package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +77 -51
- package/src/modules/customers/components/linking/LinkEntityDialog.tsx +7 -3
- package/src/modules/data_sync/backend/data-sync/page.tsx +4 -7
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +19 -54
- package/src/modules/dictionaries/components/DictionariesManager.tsx +7 -4
- package/src/modules/entities/components/EncryptionManager.tsx +50 -21
- package/src/modules/inbox_ops/lib/llmProvider.ts +10 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +101 -146
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +60 -103
- package/src/modules/planner/components/AvailabilityRuleSetForm.tsx +1 -1
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +17 -17
- package/src/modules/resources/components/ResourceCrudForm.tsx +1 -1
- package/src/modules/resources/components/ResourceTypeCrudForm.tsx +1 -1
- package/src/modules/sales/components/documents/ReturnDialog.tsx +10 -7
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
- package/src/modules/staff/backend/staff/profile/create/page.tsx +1 -1
- package/src/modules/staff/components/TeamForm.tsx +1 -1
- package/src/modules/staff/components/TeamMemberForm.tsx +1 -1
- package/src/modules/staff/components/TeamRoleForm.tsx +1 -1
|
@@ -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<
|
|
100
|
+
const [baseFieldOptions, setBaseFieldOptions] = React.useState<FieldOption[]>([])
|
|
79
101
|
const [hasUserEdited, setHasUserEdited] = React.useState(false)
|
|
80
|
-
const { data:
|
|
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:
|
|
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
|
-
|
|
198
|
-
|
|
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-
|
|
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
|
-
<
|
|
411
|
-
<
|
|
412
|
-
|
|
436
|
+
<div className="inline-flex items-center gap-2 text-sm">
|
|
437
|
+
<Checkbox
|
|
438
|
+
id="entities-encryption-active"
|
|
413
439
|
checked={isActive}
|
|
414
|
-
|
|
440
|
+
onCheckedChange={(checked) => setIsActive(checked === true)}
|
|
415
441
|
/>
|
|
416
|
-
|
|
417
|
-
|
|
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, {
|
|
@@ -35,7 +35,9 @@ import {
|
|
|
35
35
|
type IntegrationDetailBuiltInTab,
|
|
36
36
|
} from '@open-mercato/shared/modules/integrations/types'
|
|
37
37
|
import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
38
|
-
import {
|
|
38
|
+
import { LogList, type LogListEntry } from '@open-mercato/ui/backend/LogList'
|
|
39
|
+
import { Activity, AlertTriangle, Bell, Calendar, CheckCircle2, CreditCard, FileText, FileX, HardDrive, Key, MessageSquare, RefreshCw, Settings, Truck, Webhook, XCircle, Zap } from 'lucide-react'
|
|
40
|
+
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
39
41
|
import { IntegrationScheduleTab } from '../../../../data_sync/components/IntegrationScheduleTab'
|
|
40
42
|
import {
|
|
41
43
|
buildIntegrationDetailInjectedTabs,
|
|
@@ -129,12 +131,6 @@ type HealthCheckResponse = {
|
|
|
129
131
|
latencyMs: number | null
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
const LOG_LEVEL_STYLES: Record<string, string> = {
|
|
133
|
-
info: 'bg-blue-100 text-blue-800',
|
|
134
|
-
warn: 'bg-yellow-100 text-yellow-800',
|
|
135
|
-
error: 'bg-red-100 text-red-800',
|
|
136
|
-
}
|
|
137
|
-
|
|
138
134
|
const HEALTH_STATUS_STYLES: Record<string, string> = {
|
|
139
135
|
healthy: 'bg-green-100 text-green-800',
|
|
140
136
|
degraded: 'bg-yellow-100 text-yellow-800',
|
|
@@ -337,7 +333,6 @@ export default function IntegrationDetailPage({ params }: IntegrationDetailPageP
|
|
|
337
333
|
const [logs, setLogs] = React.useState<LogEntry[]>([])
|
|
338
334
|
const [logLevel, setLogLevel] = React.useState<string>('')
|
|
339
335
|
const [isLoadingLogs, setIsLoadingLogs] = React.useState(false)
|
|
340
|
-
const [expandedLogId, setExpandedLogId] = React.useState<string | null>(null)
|
|
341
336
|
|
|
342
337
|
const [isCheckingHealth, setIsCheckingHealth] = React.useState(false)
|
|
343
338
|
const [isTogglingState, setIsTogglingState] = React.useState(false)
|
|
@@ -522,9 +517,6 @@ export default function IntegrationDetailPage({ params }: IntegrationDetailPageP
|
|
|
522
517
|
React.useEffect(() => { void loadDetail() }, [loadDetail])
|
|
523
518
|
React.useEffect(() => { void loadCredentials() }, [loadCredentials])
|
|
524
519
|
React.useEffect(() => { void loadLogs() }, [loadLogs])
|
|
525
|
-
React.useEffect(() => {
|
|
526
|
-
setExpandedLogId((current) => (current && logs.some((log) => log.id === current) ? current : null))
|
|
527
|
-
}, [logs])
|
|
528
520
|
|
|
529
521
|
const handleToggleState = React.useCallback(async (enabled: boolean) => {
|
|
530
522
|
const currentIntegrationId = resolveCurrentIntegrationId()
|
|
@@ -1210,144 +1202,107 @@ export default function IntegrationDetailPage({ params }: IntegrationDetailPageP
|
|
|
1210
1202
|
</div>
|
|
1211
1203
|
{isLoadingLogs ? (
|
|
1212
1204
|
<div className="flex justify-center py-8"><Spinner /></div>
|
|
1213
|
-
) : logs.length === 0 ? (
|
|
1214
|
-
<p className="py-4 text-sm text-muted-foreground">{t('integrations.detail.logs.empty')}</p>
|
|
1215
1205
|
) : (
|
|
1216
|
-
<
|
|
1217
|
-
<
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
{t('integrations.detail.logs.details.summary', 'Summary')}
|
|
1272
|
-
</p>
|
|
1273
|
-
<p className="mt-1 text-sm font-medium">{log.message}</p>
|
|
1274
|
-
</div>
|
|
1275
|
-
{metadataEntries.length > 0 ? (
|
|
1276
|
-
<dl className="grid gap-3 sm:grid-cols-2">
|
|
1277
|
-
{metadataEntries.map(([label, value]) => (
|
|
1278
|
-
<div key={label} className="rounded-md border bg-muted/30 px-3 py-2">
|
|
1279
|
-
<dt className="text-overline font-medium uppercase tracking-wide text-muted-foreground">
|
|
1280
|
-
{label}
|
|
1281
|
-
</dt>
|
|
1282
|
-
<dd className="mt-1 break-all text-sm">{value}</dd>
|
|
1283
|
-
</div>
|
|
1284
|
-
))}
|
|
1285
|
-
</dl>
|
|
1286
|
-
) : null}
|
|
1287
|
-
</section>
|
|
1288
|
-
|
|
1289
|
-
{inlineEntries.length > 0 ? (
|
|
1290
|
-
<section className="space-y-3">
|
|
1291
|
-
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
1292
|
-
{t('integrations.detail.logs.details.fields', 'Fields')}
|
|
1293
|
-
</p>
|
|
1294
|
-
<dl className="grid gap-3 sm:grid-cols-2">
|
|
1295
|
-
{inlineEntries.map(([key, value]) => (
|
|
1296
|
-
<div key={key} className="rounded-md border bg-muted/30 px-3 py-2">
|
|
1297
|
-
<dt className="text-overline font-medium uppercase tracking-wide text-muted-foreground">
|
|
1298
|
-
{formatLogDetailLabel(key)}
|
|
1299
|
-
</dt>
|
|
1300
|
-
<dd className="mt-1 break-words text-sm">
|
|
1301
|
-
{formatLogPrimitiveValue(value)}
|
|
1302
|
-
</dd>
|
|
1303
|
-
</div>
|
|
1304
|
-
))}
|
|
1305
|
-
</dl>
|
|
1306
|
-
</section>
|
|
1307
|
-
) : null}
|
|
1308
|
-
</div>
|
|
1309
|
-
|
|
1310
|
-
<div className="space-y-3">
|
|
1311
|
-
{nestedEntries.map(([key, value]) => (
|
|
1312
|
-
<JsonDisplay
|
|
1313
|
-
key={key}
|
|
1314
|
-
data={value}
|
|
1315
|
-
title={formatLogDetailLabel(key)}
|
|
1316
|
-
defaultExpanded
|
|
1317
|
-
maxInitialDepth={1}
|
|
1318
|
-
theme="dark"
|
|
1319
|
-
maxHeight="16rem"
|
|
1320
|
-
className="p-4"
|
|
1321
|
-
/>
|
|
1322
|
-
))}
|
|
1323
|
-
{log.payload && nestedEntries.length === 0 ? (
|
|
1324
|
-
<JsonDisplay
|
|
1325
|
-
data={log.payload}
|
|
1326
|
-
title={t('integrations.detail.logs.details.payload', 'Payload')}
|
|
1327
|
-
defaultExpanded
|
|
1328
|
-
maxInitialDepth={1}
|
|
1329
|
-
theme="dark"
|
|
1330
|
-
maxHeight="16rem"
|
|
1331
|
-
className="p-4"
|
|
1332
|
-
/>
|
|
1333
|
-
) : null}
|
|
1334
|
-
{!log.payload ? (
|
|
1335
|
-
<div className="rounded-lg border border-dashed px-4 py-6 text-sm text-muted-foreground">
|
|
1336
|
-
{t('integrations.detail.logs.details.noPayload', 'No structured payload was stored for this log entry.')}
|
|
1337
|
-
</div>
|
|
1338
|
-
) : null}
|
|
1339
|
-
</div>
|
|
1206
|
+
<LogList
|
|
1207
|
+
entries={logs.map<LogListEntry>((log) => {
|
|
1208
|
+
const metadataEntries = [
|
|
1209
|
+
['Time', new Date(log.createdAt).toLocaleString()],
|
|
1210
|
+
['Level', log.level],
|
|
1211
|
+
['Code', log.code ?? null],
|
|
1212
|
+
['Run ID', log.runId ?? null],
|
|
1213
|
+
['Entity Type', log.scopeEntityType ?? null],
|
|
1214
|
+
['Entity ID', log.scopeEntityId ?? null],
|
|
1215
|
+
].filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].trim().length > 0)
|
|
1216
|
+
const { inlineEntries, nestedEntries } = splitLogPayload(log.payload)
|
|
1217
|
+
|
|
1218
|
+
return {
|
|
1219
|
+
id: log.id,
|
|
1220
|
+
time: new Date(log.createdAt).toLocaleString(),
|
|
1221
|
+
level: log.level,
|
|
1222
|
+
message: log.message,
|
|
1223
|
+
body: (
|
|
1224
|
+
<div className="grid gap-4 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]">
|
|
1225
|
+
<div className="space-y-4">
|
|
1226
|
+
<section className="space-y-3">
|
|
1227
|
+
<div>
|
|
1228
|
+
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
1229
|
+
{t('integrations.detail.logs.details.summary', 'Summary')}
|
|
1230
|
+
</p>
|
|
1231
|
+
<p className="mt-1 text-sm font-medium">{log.message}</p>
|
|
1232
|
+
</div>
|
|
1233
|
+
{metadataEntries.length > 0 ? (
|
|
1234
|
+
<dl className="grid gap-3 sm:grid-cols-2">
|
|
1235
|
+
{metadataEntries.map(([label, value]) => (
|
|
1236
|
+
<div key={label} className="rounded-md border bg-muted/30 px-3 py-2">
|
|
1237
|
+
<dt className="text-overline font-medium uppercase tracking-wide text-muted-foreground">
|
|
1238
|
+
{label}
|
|
1239
|
+
</dt>
|
|
1240
|
+
<dd className="mt-1 break-all text-sm">{value}</dd>
|
|
1241
|
+
</div>
|
|
1242
|
+
))}
|
|
1243
|
+
</dl>
|
|
1244
|
+
) : null}
|
|
1245
|
+
</section>
|
|
1246
|
+
|
|
1247
|
+
{inlineEntries.length > 0 ? (
|
|
1248
|
+
<section className="space-y-3">
|
|
1249
|
+
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
1250
|
+
{t('integrations.detail.logs.details.fields', 'Fields')}
|
|
1251
|
+
</p>
|
|
1252
|
+
<dl className="grid gap-3 sm:grid-cols-2">
|
|
1253
|
+
{inlineEntries.map(([key, value]) => (
|
|
1254
|
+
<div key={key} className="rounded-md border bg-muted/30 px-3 py-2">
|
|
1255
|
+
<dt className="text-overline font-medium uppercase tracking-wide text-muted-foreground">
|
|
1256
|
+
{formatLogDetailLabel(key)}
|
|
1257
|
+
</dt>
|
|
1258
|
+
<dd className="mt-1 break-words text-sm">
|
|
1259
|
+
{formatLogPrimitiveValue(value)}
|
|
1260
|
+
</dd>
|
|
1340
1261
|
</div>
|
|
1341
|
-
|
|
1342
|
-
</
|
|
1343
|
-
</
|
|
1262
|
+
))}
|
|
1263
|
+
</dl>
|
|
1264
|
+
</section>
|
|
1344
1265
|
) : null}
|
|
1345
|
-
</
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1266
|
+
</div>
|
|
1267
|
+
|
|
1268
|
+
<div className="space-y-3">
|
|
1269
|
+
{nestedEntries.map(([key, value]) => (
|
|
1270
|
+
<JsonDisplay
|
|
1271
|
+
key={key}
|
|
1272
|
+
data={value}
|
|
1273
|
+
title={formatLogDetailLabel(key)}
|
|
1274
|
+
defaultExpanded
|
|
1275
|
+
maxInitialDepth={1}
|
|
1276
|
+
theme="dark"
|
|
1277
|
+
maxHeight="16rem"
|
|
1278
|
+
className="p-4"
|
|
1279
|
+
/>
|
|
1280
|
+
))}
|
|
1281
|
+
{log.payload && nestedEntries.length === 0 ? (
|
|
1282
|
+
<JsonDisplay
|
|
1283
|
+
data={log.payload}
|
|
1284
|
+
title={t('integrations.detail.logs.details.payload', 'Payload')}
|
|
1285
|
+
defaultExpanded
|
|
1286
|
+
maxInitialDepth={1}
|
|
1287
|
+
theme="dark"
|
|
1288
|
+
maxHeight="16rem"
|
|
1289
|
+
className="p-4"
|
|
1290
|
+
/>
|
|
1291
|
+
) : null}
|
|
1292
|
+
{!log.payload ? (
|
|
1293
|
+
<EmptyState
|
|
1294
|
+
size="sm"
|
|
1295
|
+
icon={<FileX className="h-8 w-8" aria-hidden="true" />}
|
|
1296
|
+
title={t('integrations.detail.logs.details.noPayload', 'No structured payload was stored for this log entry.')}
|
|
1297
|
+
/>
|
|
1298
|
+
) : null}
|
|
1299
|
+
</div>
|
|
1300
|
+
</div>
|
|
1301
|
+
),
|
|
1302
|
+
}
|
|
1303
|
+
})}
|
|
1304
|
+
emptyMessage={t('integrations.detail.logs.empty')}
|
|
1305
|
+
/>
|
|
1351
1306
|
)}
|
|
1352
1307
|
</TabsContent>
|
|
1353
1308
|
) : null}
|