@open-mercato/core 0.4.7-develop-78d7541539 → 0.4.7-develop-c89cca0193
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/AGENTS.md +1 -0
- package/dist/modules/catalog/api/bulk-delete/route.js +86 -0
- package/dist/modules/catalog/api/bulk-delete/route.js.map +7 -0
- package/dist/modules/catalog/api/prices/route.js +39 -6
- package/dist/modules/catalog/api/prices/route.js.map +2 -2
- package/dist/modules/catalog/api/products/route.js +6 -11
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +2 -0
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +9 -1
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/lib/bulkDelete.js +70 -0
- package/dist/modules/catalog/lib/bulkDelete.js.map +7 -0
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js +185 -0
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js.map +7 -0
- package/dist/modules/catalog/widgets/injection-table.js +9 -1
- package/dist/modules/catalog/widgets/injection-table.js.map +2 -2
- package/dist/modules/catalog/workers/catalog-product-bulk-delete.js +40 -0
- package/dist/modules/catalog/workers/catalog-product-bulk-delete.js.map +7 -0
- package/dist/modules/data_sync/api/options.js +52 -0
- package/dist/modules/data_sync/api/options.js.map +7 -0
- package/dist/modules/data_sync/api/run.js +30 -35
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +15 -30
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +109 -0
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/schedules/route.js +72 -0
- package/dist/modules/data_sync/api/schedules/route.js.map +7 -0
- package/dist/modules/data_sync/api/schedules/serialize.js +21 -0
- package/dist/modules/data_sync/api/schedules/serialize.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.js +656 -47
- 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 +116 -34
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +394 -0
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +7 -0
- package/dist/modules/data_sync/data/validators.js +32 -0
- package/dist/modules/data_sync/data/validators.js.map +2 -2
- package/dist/modules/data_sync/di.js +2 -0
- package/dist/modules/data_sync/di.js.map +2 -2
- package/dist/modules/data_sync/lib/id-mapping.js +24 -2
- package/dist/modules/data_sync/lib/id-mapping.js.map +2 -2
- package/dist/modules/data_sync/lib/start-run.js +57 -0
- package/dist/modules/data_sync/lib/start-run.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +93 -4
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +5 -1
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-schedule-service.js +138 -0
- package/dist/modules/data_sync/lib/sync-schedule-service.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-export.js +28 -2
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +28 -2
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-scheduled.js +5 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +5 -2
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/lib/field-definitions.js +3 -1
- package/dist/modules/entities/lib/field-definitions.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +14 -15
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +3 -3
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +148 -33
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/lib/state-service.js +15 -1
- package/dist/modules/integrations/lib/state-service.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +24 -22
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/progress/api/active/route.js +3 -1
- package/dist/modules/progress/api/active/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +1 -1
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +1 -1
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/progress/lib/events.js.map +1 -1
- package/dist/modules/progress/lib/progressService.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +42 -1
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/lib/document.js +35 -1
- package/dist/modules/query_index/lib/document.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +91 -4
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +2 -0
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/sales/api/adjustment-kinds/route.js +3 -9
- package/dist/modules/sales/api/adjustment-kinds/route.js.map +2 -2
- package/dist/modules/sales/api/channels/route.js +3 -10
- package/dist/modules/sales/api/channels/route.js.map +2 -2
- package/dist/modules/sales/api/delivery-windows/route.js +3 -10
- package/dist/modules/sales/api/delivery-windows/route.js.map +2 -2
- package/dist/modules/sales/api/payment-methods/route.js +3 -11
- package/dist/modules/sales/api/payment-methods/route.js.map +2 -2
- package/dist/modules/sales/api/price-kinds/route.js +3 -5
- package/dist/modules/sales/api/price-kinds/route.js.map +2 -2
- package/dist/modules/sales/api/shipping-methods/route.js +3 -11
- package/dist/modules/sales/api/shipping-methods/route.js.map +2 -2
- package/dist/modules/sales/api/tags/route.js +3 -9
- package/dist/modules/sales/api/tags/route.js.map +2 -2
- package/dist/modules/sales/api/tax-rates/route.js +3 -13
- package/dist/modules/sales/api/tax-rates/route.js.map +2 -2
- package/dist/modules/sales/api/utils.js +9 -0
- package/dist/modules/sales/api/utils.js.map +2 -2
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +3 -9
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +3 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +4 -3
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/serialize.js +25 -0
- package/dist/modules/workflows/api/definitions/serialize.js.map +7 -0
- package/package.json +3 -3
- package/src/modules/catalog/api/bulk-delete/route.ts +93 -0
- package/src/modules/catalog/api/prices/route.ts +53 -6
- package/src/modules/catalog/api/products/route.ts +6 -11
- package/src/modules/catalog/commands/products.ts +2 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -0
- package/src/modules/catalog/i18n/de.json +10 -0
- package/src/modules/catalog/i18n/en.json +10 -0
- package/src/modules/catalog/i18n/es.json +10 -0
- package/src/modules/catalog/i18n/pl.json +10 -0
- package/src/modules/catalog/lib/bulkDelete.ts +106 -0
- package/src/modules/catalog/widgets/injection/product-bulk-delete/widget.ts +242 -0
- package/src/modules/catalog/widgets/injection-table.ts +8 -0
- package/src/modules/catalog/workers/catalog-product-bulk-delete.ts +48 -0
- package/src/modules/data_sync/AGENTS.md +11 -3
- package/src/modules/data_sync/api/options.ts +58 -0
- package/src/modules/data_sync/api/run.ts +34 -36
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +2 -2
- package/src/modules/data_sync/api/runs/[id]/retry.ts +14 -31
- package/src/modules/data_sync/api/schedules/[id]/route.ts +130 -0
- package/src/modules/data_sync/api/schedules/route.ts +77 -0
- package/src/modules/data_sync/api/schedules/serialize.ts +31 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +756 -2
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +179 -53
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +512 -0
- package/src/modules/data_sync/data/validators.ts +35 -0
- package/src/modules/data_sync/di.ts +6 -0
- package/src/modules/data_sync/i18n/de.json +72 -0
- package/src/modules/data_sync/i18n/en.json +72 -0
- package/src/modules/data_sync/i18n/es.json +72 -0
- package/src/modules/data_sync/i18n/pl.json +72 -0
- package/src/modules/data_sync/lib/adapter.ts +4 -1
- package/src/modules/data_sync/lib/id-mapping.ts +32 -2
- package/src/modules/data_sync/lib/start-run.ts +90 -0
- package/src/modules/data_sync/lib/sync-engine.ts +111 -4
- package/src/modules/data_sync/lib/sync-run-service.ts +5 -1
- package/src/modules/data_sync/lib/sync-schedule-service.ts +207 -0
- package/src/modules/data_sync/workers/sync-export.ts +33 -2
- package/src/modules/data_sync/workers/sync-import.ts +33 -2
- package/src/modules/data_sync/workers/sync-scheduled.ts +7 -0
- package/src/modules/entities/api/definitions.ts +12 -2
- package/src/modules/entities/lib/field-definitions.ts +2 -0
- package/src/modules/integrations/AGENTS.md +16 -3
- package/src/modules/integrations/api/[id]/route.ts +14 -15
- package/src/modules/integrations/api/route.ts +3 -3
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +176 -54
- package/src/modules/integrations/lib/state-service.ts +25 -1
- package/src/modules/messages/api/[id]/route.ts +25 -22
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +3 -3
- package/src/modules/progress/api/active/route.ts +4 -1
- package/src/modules/progress/api/jobs/[id]/route.ts +1 -1
- package/src/modules/progress/api/jobs/route.ts +1 -1
- package/src/modules/progress/lib/events.ts +6 -0
- package/src/modules/progress/lib/progressService.ts +1 -0
- package/src/modules/progress/lib/progressServiceImpl.ts +47 -1
- package/src/modules/query_index/lib/document.ts +52 -1
- package/src/modules/query_index/lib/engine.ts +104 -4
- package/src/modules/query_index/lib/indexer.ts +2 -0
- package/src/modules/sales/api/adjustment-kinds/route.ts +3 -9
- package/src/modules/sales/api/channels/route.ts +3 -10
- package/src/modules/sales/api/delivery-windows/route.ts +3 -10
- package/src/modules/sales/api/payment-methods/route.ts +3 -11
- package/src/modules/sales/api/price-kinds/route.ts +3 -5
- package/src/modules/sales/api/shipping-methods/route.ts +3 -11
- package/src/modules/sales/api/tags/route.ts +3 -9
- package/src/modules/sales/api/tax-rates/route.ts +3 -13
- package/src/modules/sales/api/utils.ts +9 -0
- package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +3 -9
- package/src/modules/workflows/api/definitions/[id]/route.ts +3 -2
- package/src/modules/workflows/api/definitions/route.ts +4 -3
- package/src/modules/workflows/api/definitions/serialize.ts +23 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/integrations/backend/integrations/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { z } from 'zod'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { WebhookSetupGuide } from '@open-mercato/ui/backend/WebhookSetupGuide'\nimport { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Card, CardHeader, CardTitle, CardContent } from '@open-mercato/ui/primitives/card'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport { JsonDisplay } from '@open-mercato/ui/backend/JsonDisplay'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID, type CredentialFieldType, type IntegrationCredentialField } from '@open-mercato/shared/modules/integrations/types'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { Bell, ChevronDown, ChevronRight, CreditCard, HardDrive, MessageSquare, RefreshCw, Truck, Webhook, Zap } from 'lucide-react'\nimport {\n buildIntegrationDetailInjectedTabs,\n filterIntegrationDetailWidgetsByKind,\n resolveIntegrationDetailWidgetSpotId,\n resolveRequestedIntegrationDetailTab,\n} from '../detail-page-widgets'\n\ntype CredentialField = IntegrationCredentialField\ntype BuiltInIntegrationDetailTab = 'credentials' | 'version' | 'health' | 'logs'\ntype IntegrationDetailTab = BuiltInIntegrationDetailTab | string\n\nconst UNSUPPORTED_CREDENTIAL_FIELD_TYPES = new Set<CredentialFieldType>(['oauth', 'ssh_keypair'])\n\nfunction isEditableCredentialField(field: CredentialField): boolean {\n return !UNSUPPORTED_CREDENTIAL_FIELD_TYPES.has(field.type)\n}\n\ntype ApiVersion = {\n id: string\n label?: string\n status: 'stable' | 'deprecated' | 'experimental'\n sunsetAt?: string\n migrationGuide?: string\n}\n\ntype IntegrationDetail = {\n integration: {\n id: string\n title: string\n description?: string\n category?: string\n hub?: string\n bundleId?: string\n docsUrl?: string\n apiVersions?: ApiVersion[]\n detailPage?: {\n widgetSpotId?: string\n }\n credentials?: { fields: CredentialField[] }\n }\n bundle?: { id: string; title: string; credentials?: { fields: CredentialField[] } }\n state: {\n isEnabled: boolean\n apiVersion: string | null\n reauthRequired: boolean\n lastHealthStatus: string | null\n lastHealthCheckedAt: string | null\n }\n hasCredentials: boolean\n}\n\ntype LogEntry = {\n id: string\n runId?: string | null\n scopeEntityType?: string | null\n scopeEntityId?: string | null\n level: 'info' | 'warn' | 'error'\n message: string\n createdAt: string\n code?: string | null\n payload?: Record<string, unknown> | null\n}\n\ntype IntegrationDetailPageProps = {\n params?: {\n id?: string | string[]\n }\n}\n\ntype HealthCheckResponse = {\n status: 'healthy' | 'degraded' | 'unhealthy'\n message: string | null\n details: Record<string, unknown> | null\n checkedAt: string\n}\n\nconst LOG_LEVEL_STYLES: Record<string, string> = {\n info: 'bg-blue-100 text-blue-800',\n warn: 'bg-yellow-100 text-yellow-800',\n error: 'bg-red-100 text-red-800',\n}\n\nconst HEALTH_STATUS_STYLES: Record<string, string> = {\n healthy: 'bg-green-100 text-green-800',\n degraded: 'bg-yellow-100 text-yellow-800',\n unhealthy: 'bg-red-100 text-red-800',\n}\n\nconst CATEGORY_ICONS: Record<string, React.ElementType> = {\n payment: CreditCard,\n shipping: Truck,\n data_sync: RefreshCw,\n communication: MessageSquare,\n notification: Bell,\n storage: HardDrive,\n webhook: Webhook,\n}\n\nfunction resolveRouteId(value: string | string[] | undefined): string | undefined {\n if (Array.isArray(value)) return value[0]\n return value\n}\n\nfunction resolvePathnameId(pathname: string): string | undefined {\n const parts = pathname.split('/').filter(Boolean)\n const integrationId = parts.at(-1)\n if (!integrationId || integrationId === 'integrations' || integrationId === 'bundle') return undefined\n return decodeURIComponent(integrationId)\n}\n\nfunction buildCredentialFields(credFields: CredentialField[]): CrudField[] {\n return credFields.map((field) => {\n const shared = {\n id: field.key,\n label: field.label,\n description: field.helpDetails ? (\n <div className=\"space-y-1\">\n {field.helpText ? <div>{field.helpText}</div> : null}\n <WebhookSetupGuide guide={field.helpDetails} buttonLabel=\"Show details\" />\n </div>\n ) : field.helpText,\n placeholder: field.placeholder,\n required: field.required,\n }\n\n if (field.type === 'secret') {\n return {\n ...shared,\n type: 'custom' as const,\n component: ({ id, value, setValue, disabled }) => (\n <Input\n id={id}\n type=\"password\"\n placeholder={field.placeholder}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => setValue(event.target.value)}\n disabled={disabled}\n />\n ),\n }\n }\n\n if (field.type === 'select' && field.options) {\n return {\n ...shared,\n type: 'select' as const,\n options: field.options,\n }\n }\n\n if (field.type === 'boolean') {\n return {\n ...shared,\n type: 'checkbox' as const,\n }\n }\n\n return {\n ...shared,\n type: 'text' as const,\n }\n })\n}\n\nfunction isHealthLog(log: LogEntry): boolean {\n return log.message === 'Health check passed' || log.message.startsWith('Health check:')\n}\n\nfunction extractHealthDetails(payload: Record<string, unknown> | null | undefined): Record<string, unknown> {\n if (!payload) return {}\n return Object.fromEntries(\n Object.entries(payload).filter(([key, value]) => key !== 'status' && key !== 'message' && value !== undefined && value !== null),\n )\n}\n\nfunction formatHealthValue(value: unknown): string {\n if (typeof value === 'boolean') return value ? 'Yes' : 'No'\n if (typeof value === 'string') return value\n if (typeof value === 'number') return String(value)\n if (value instanceof Date) return value.toLocaleString()\n return JSON.stringify(value)\n}\n\nfunction formatTypeLabel(value: string): string {\n return value.split('_').filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(' ')\n}\n\nfunction isPrimitiveLogValue(value: unknown): value is string | number | boolean | null {\n return value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'\n}\n\nfunction formatLogDetailLabel(key: string): string {\n return key\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .split(' ')\n .filter(Boolean)\n .map((part) => part[0]?.toUpperCase() + part.slice(1))\n .join(' ')\n}\n\nfunction formatLogPrimitiveValue(value: string | number | boolean | null): string {\n if (value === null) return 'None'\n if (typeof value === 'boolean') return value ? 'Yes' : 'No'\n return String(value)\n}\n\nfunction splitLogPayload(payload: Record<string, unknown> | null | undefined) {\n if (!payload) {\n return {\n inlineEntries: [] as Array<[string, string | number | boolean | null]>,\n nestedEntries: [] as Array<[string, unknown]>,\n }\n }\n\n const inlineEntries: Array<[string, string | number | boolean | null]> = []\n const nestedEntries: Array<[string, unknown]> = []\n\n Object.entries(payload).forEach(([key, value]) => {\n if (isPrimitiveLogValue(value)) {\n inlineEntries.push([key, value])\n return\n }\n nestedEntries.push([key, value])\n })\n\n return { inlineEntries, nestedEntries }\n}\n\nexport default function IntegrationDetailPage({ params }: IntegrationDetailPageProps) {\n const pathname = usePathname()\n const router = useRouter()\n const searchParams = useSearchParams()\n const integrationId = resolveRouteId(params?.id) ?? resolvePathnameId(pathname)\n const t = useT()\n\n const [detail, setDetail] = React.useState<IntegrationDetail | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n const [credValues, setCredValues] = React.useState<Record<string, unknown>>({})\n const [credentialsFormKey, setCredentialsFormKey] = React.useState(0)\n const [isSavingCredentials, setIsSavingCredentials] = React.useState(false)\n\n const [logs, setLogs] = React.useState<LogEntry[]>([])\n const [logLevel, setLogLevel] = React.useState<string>('')\n const [isLoadingLogs, setIsLoadingLogs] = React.useState(false)\n const [expandedLogId, setExpandedLogId] = React.useState<string | null>(null)\n\n const [isCheckingHealth, setIsCheckingHealth] = React.useState(false)\n const [isTogglingState, setIsTogglingState] = React.useState(false)\n const [latestHealthResult, setLatestHealthResult] = React.useState<HealthCheckResponse | null>(null)\n const [activeTab, setActiveTab] = React.useState<IntegrationDetailTab>('credentials')\n\n const credentialsFormId = React.useId()\n\n const resolveCurrentIntegrationId = React.useCallback(() => {\n return integrationId ?? (\n typeof window !== 'undefined'\n ? resolvePathnameId(window.location.pathname)\n : undefined\n )\n }, [integrationId])\n\n const loadDetail = React.useCallback(async () => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) {\n setIsLoading(false)\n setError(t('integrations.detail.loadError', 'Failed to load integration'))\n return\n }\n setError(null)\n setIsLoading(true)\n try {\n const call = await apiCall<IntegrationDetail>(\n `/api/integrations/${encodeURIComponent(currentIntegrationId)}`,\n undefined,\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n setError(t('integrations.detail.loadError', 'Failed to load integration'))\n setIsLoading(false)\n return\n }\n setDetail(call.result)\n setIsLoading(false)\n } catch {\n setError(t('integrations.detail.loadError', 'Failed to load integration'))\n setIsLoading(false)\n }\n }, [resolveCurrentIntegrationId, t])\n\n const loadCredentials = React.useCallback(async () => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n const call = await apiCall<{ credentials: Record<string, unknown> }>(\n `/api/integrations/${encodeURIComponent(currentIntegrationId)}/credentials`,\n undefined,\n { fallback: null },\n )\n if (call.ok && call.result?.credentials) {\n setCredValues(call.result.credentials)\n setCredentialsFormKey((current) => current + 1)\n }\n }, [resolveCurrentIntegrationId])\n\n const loadLogs = React.useCallback(async () => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n setIsLoadingLogs(true)\n const params = new URLSearchParams({ integrationId: currentIntegrationId, pageSize: '50' })\n if (logLevel) params.set('level', logLevel)\n const call = await apiCall<{ items: LogEntry[] }>(\n `/api/integrations/logs?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n if (call.ok && call.result) {\n setLogs(call.result.items)\n }\n setIsLoadingLogs(false)\n }, [logLevel, resolveCurrentIntegrationId])\n\n const detailWidgetSpotId = React.useMemo(\n () => resolveIntegrationDetailWidgetSpotId(detail?.integration ?? null, LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID),\n [detail?.integration],\n )\n const mutationContextId = React.useMemo(\n () => `integrations.detail:${integrationId ?? 'unknown'}`,\n [integrationId],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: mutationContextId,\n spotId: detailWidgetSpotId,\n })\n const refreshDetail = React.useCallback(async () => {\n await loadDetail()\n await loadCredentials()\n }, [loadCredentials, loadDetail])\n const refreshLogs = React.useCallback(async () => {\n await loadLogs()\n }, [loadLogs])\n const injectionContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n integrationDetailWidgetSpotId: detailWidgetSpotId,\n resourceKind: 'integrations.integration',\n resourceId: integrationId ?? detail?.integration.id,\n integrationId: integrationId ?? detail?.integration.id,\n integration: detail?.integration ?? null,\n detail,\n state: detail?.state ?? null,\n credentialValues: credValues,\n latestHealthResult,\n activeTab,\n setActiveTab,\n refreshDetail,\n refreshLogs,\n retryLastMutation,\n }),\n [\n activeTab,\n credValues,\n detail,\n detailWidgetSpotId,\n integrationId,\n latestHealthResult,\n mutationContextId,\n refreshDetail,\n refreshLogs,\n retryLastMutation,\n ],\n )\n const { widgets: detailWidgets } = useInjectionWidgets(detailWidgetSpotId, {\n context: injectionContext,\n triggerOnLoad: true,\n })\n const stackedDetailWidgets = React.useMemo(\n () => filterIntegrationDetailWidgetsByKind(detailWidgets, 'stack'),\n [detailWidgets],\n )\n const groupedDetailWidgets = React.useMemo(\n () => filterIntegrationDetailWidgetsByKind(detailWidgets, 'group'),\n [detailWidgets],\n )\n const injectedTabs = React.useMemo(\n () => buildIntegrationDetailInjectedTabs(\n detailWidgets,\n (widget) => (\n widget.placement?.groupLabel\n ? t(widget.placement.groupLabel, widget.module.metadata.title ?? widget.widgetId)\n : (widget.module.metadata.title ?? widget.widgetId)\n ),\n ),\n [detailWidgets, t],\n )\n const customTabIds = React.useMemo(\n () => injectedTabs.map((tab) => tab.id),\n [injectedTabs],\n )\n const runMutationWithContext = React.useCallback(\n async <T,>({\n operation,\n mutationPayload,\n actionId,\n tabId,\n operationType = 'update',\n }: {\n operation: () => Promise<T>\n mutationPayload?: Record<string, unknown>\n actionId: string\n tabId?: string\n operationType?: 'create' | 'update' | 'delete'\n }): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: {\n ...injectionContext,\n operation: operationType,\n actionId,\n activeTab: tabId ?? activeTab,\n },\n })\n },\n [activeTab, injectionContext, runMutation],\n )\n\n React.useEffect(() => { void loadDetail() }, [loadDetail])\n React.useEffect(() => { void loadCredentials() }, [loadCredentials])\n React.useEffect(() => { void loadLogs() }, [loadLogs])\n React.useEffect(() => {\n setExpandedLogId((current) => (current && logs.some((log) => log.id === current) ? current : null))\n }, [logs])\n\n const handleToggleState = React.useCallback(async (enabled: boolean) => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n setIsTogglingState(true)\n try {\n const call = await runMutationWithContext({\n actionId: 'toggle-state',\n mutationPayload: { integrationId: currentIntegrationId, isEnabled: enabled },\n operation: () => apiCall(`/api/integrations/${encodeURIComponent(currentIntegrationId)}/state`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ isEnabled: enabled }),\n }, { fallback: null }),\n })\n if (call.ok) {\n setDetail((prev) => prev ? { ...prev, state: { ...prev.state, isEnabled: enabled } } : prev)\n flash(t('integrations.detail.stateUpdated'), 'success')\n } else {\n flash(t('integrations.detail.stateError'), 'error')\n }\n } catch {\n flash(t('integrations.detail.stateError'), 'error')\n } finally {\n setIsTogglingState(false)\n }\n }, [resolveCurrentIntegrationId, runMutationWithContext, t])\n\n const handleSaveCredentials = React.useCallback(async (values: Record<string, unknown>) => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n setIsSavingCredentials(true)\n try {\n const call = await runMutationWithContext({\n actionId: 'save-credentials',\n tabId: 'credentials',\n mutationPayload: { integrationId: currentIntegrationId, credentials: values },\n operation: () => apiCall(`/api/integrations/${encodeURIComponent(currentIntegrationId)}/credentials`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ credentials: values }),\n }, { fallback: null }),\n })\n\n if (call.ok) {\n setCredValues(values)\n setCredentialsFormKey((current) => current + 1)\n flash(t('integrations.detail.credentials.saved'), 'success')\n return\n }\n\n const result = call.result as {\n error?: string\n details?: { fieldErrors?: Record<string, string>; formErrors?: string[] }\n } | null\n throw createCrudFormError(\n result?.error ?? t('integrations.detail.credentials.saveError', 'Failed to save credentials'),\n result?.details?.fieldErrors,\n { details: result?.details },\n )\n } finally {\n setIsSavingCredentials(false)\n }\n }, [resolveCurrentIntegrationId, runMutationWithContext, t])\n\n const handleVersionChange = React.useCallback(async (version: string) => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n try {\n const call = await runMutationWithContext({\n actionId: 'change-version',\n tabId: 'version',\n mutationPayload: { integrationId: currentIntegrationId, apiVersion: version },\n operation: () => apiCall(`/api/integrations/${encodeURIComponent(currentIntegrationId)}/version`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiVersion: version }),\n }, { fallback: null }),\n })\n if (call.ok) {\n setDetail((prev) => prev ? { ...prev, state: { ...prev.state, apiVersion: version } } : prev)\n flash(t('integrations.detail.version.saved'), 'success')\n } else {\n flash(t('integrations.detail.version.saveError'), 'error')\n }\n } catch {\n flash(t('integrations.detail.version.saveError'), 'error')\n }\n }, [resolveCurrentIntegrationId, runMutationWithContext, t])\n\n const handleHealthCheck = React.useCallback(async () => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n setIsCheckingHealth(true)\n try {\n const call = await runMutationWithContext({\n actionId: 'run-health-check',\n tabId: 'health',\n mutationPayload: { integrationId: currentIntegrationId },\n operation: () => apiCall<HealthCheckResponse>(\n `/api/integrations/${encodeURIComponent(currentIntegrationId)}/health`,\n { method: 'POST' },\n { fallback: null },\n ),\n })\n const result = call.result\n if (call.ok && result) {\n setLatestHealthResult(result)\n setDetail((prev) => prev ? {\n ...prev,\n state: {\n ...prev.state,\n lastHealthStatus: result.status,\n lastHealthCheckedAt: result.checkedAt,\n },\n } : prev)\n void refreshLogs()\n } else {\n flash(t('integrations.detail.health.checkError'), 'error')\n }\n } catch {\n flash(t('integrations.detail.health.checkError'), 'error')\n } finally {\n setIsCheckingHealth(false)\n }\n }, [refreshLogs, resolveCurrentIntegrationId, runMutationWithContext, t])\n\n const hasVersions = Boolean(detail?.integration.apiVersions?.length)\n const integration = detail?.integration ?? null\n const state = detail?.state ?? null\n const editableCredentialFields = React.useMemo(\n () => (detail?.integration.credentials?.fields ?? detail?.bundle?.credentials?.fields ?? []).filter(isEditableCredentialField),\n [detail?.bundle?.credentials?.fields, detail?.integration.credentials?.fields],\n )\n const credentialFormFields = React.useMemo(\n () => buildCredentialFields(editableCredentialFields),\n [editableCredentialFields],\n )\n const credentialSchema = React.useMemo(() => (\n z.object({}).passthrough().superRefine((rawValues, ctx) => {\n const values = rawValues as Record<string, unknown>\n\n editableCredentialFields.forEach((field) => {\n const value = values[field.key]\n\n if (field.type === 'boolean') {\n if (value !== undefined && value !== null && typeof value !== 'boolean') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.boolean', 'Select a valid value.'),\n })\n }\n if (field.required && typeof value !== 'boolean') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.required', '{field} is required.', { field: field.label }),\n })\n }\n return\n }\n\n if (value !== undefined && value !== null && typeof value !== 'string') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.text', 'Enter a valid value.'),\n })\n return\n }\n\n const normalizedValue = typeof value === 'string' ? value : ''\n\n if (field.required && normalizedValue.trim().length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.required', '{field} is required.', { field: field.label }),\n })\n }\n\n if (normalizedValue.length > 20_000) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.tooLong', 'Value is too long.'),\n })\n }\n\n if (\n field.type === 'select'\n && normalizedValue\n && field.options\n && !field.options.some((option) => option.value === normalizedValue)\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.option', 'Select one of the available options.'),\n })\n }\n })\n })\n ) as z.ZodType<Record<string, unknown>>, [editableCredentialFields, t])\n const latestHealthLog = React.useMemo(() => logs.find(isHealthLog) ?? null, [logs])\n const healthMessage =\n latestHealthResult?.message ??\n (typeof latestHealthLog?.payload?.message === 'string' ? latestHealthLog.payload.message : null)\n const healthDetailsSource = latestHealthResult?.details ?? extractHealthDetails(latestHealthLog?.payload)\n const healthDetails = latestHealthLog?.code\n ? { ...healthDetailsSource, code: latestHealthLog.code }\n : healthDetailsSource\n const healthDetailEntries = Object.entries(healthDetails)\n const healthStatusDescription = state?.lastHealthStatus\n ? t(\n `integrations.detail.health.meaning.${state.lastHealthStatus}`,\n state.lastHealthStatus === 'healthy'\n ? 'The provider responded successfully using the current credentials.'\n : state.lastHealthStatus === 'degraded'\n ? 'The provider responded, but reported warnings or limited functionality.'\n : integration?.id === 'gateway_stripe'\n ? 'Stripe rejected the last check. This usually means the secret key is invalid, missing required permissions, revoked, or Stripe was temporarily unavailable.'\n : 'The last check failed. This usually means invalid credentials, missing permissions, or a provider outage.',\n )\n : null\n\n React.useEffect(() => {\n setActiveTab(resolveRequestedIntegrationDetailTab(searchParams?.get('tab'), hasVersions, customTabIds))\n }, [customTabIds, hasVersions, searchParams])\n\n const handleTabChange = React.useCallback((nextValue: string) => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n const nextTab = resolveRequestedIntegrationDetailTab(nextValue, hasVersions, customTabIds)\n setActiveTab(nextTab)\n if (!currentIntegrationId) return\n const basePath = `/backend/integrations/${encodeURIComponent(currentIntegrationId)}`\n router.replace(nextTab === 'credentials' ? basePath : `${basePath}?tab=${encodeURIComponent(nextTab)}`)\n }, [customTabIds, hasVersions, resolveCurrentIntegrationId, router])\n\n if (isLoading) return <Page><PageBody><LoadingMessage label={t('integrations.detail.title')} /></PageBody></Page>\n if (error || !detail) return <Page><PageBody><ErrorMessage label={error ?? t('integrations.detail.loadError')} /></PageBody></Page>\n\n const resolvedIntegration = detail.integration\n const resolvedState = detail.state\n const CategoryIcon = resolvedIntegration.category ? CATEGORY_ICONS[resolvedIntegration.category] : null\n\n const showCredentialActions = activeTab === 'credentials' && credentialFormFields.length > 0\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <FormHeader\n backHref=\"/backend/integrations\"\n title={resolvedIntegration.title}\n actions={{\n cancelHref: showCredentialActions ? '/backend/integrations' : undefined,\n submit: showCredentialActions\n ? {\n formId: credentialsFormId,\n pending: isSavingCredentials,\n label: t('integrations.detail.credentials.save', 'Save credentials'),\n pendingLabel: t('ui.forms.status.saving', 'Saving...'),\n }\n : undefined,\n }}\n />\n\n <div className=\"space-y-2\">\n {resolvedIntegration.description ? (\n <p className=\"text-sm text-muted-foreground\">{resolvedIntegration.description}</p>\n ) : null}\n <div className=\"flex flex-wrap items-center gap-4 text-sm text-muted-foreground\">\n {resolvedIntegration.category ? (\n <div className=\"flex items-center gap-2\">\n {CategoryIcon ? <CategoryIcon className=\"h-4 w-4\" /> : null}\n <span>{formatTypeLabel(resolvedIntegration.category)}</span>\n </div>\n ) : null}\n {resolvedIntegration.hub ? (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground/80\">\n {t('integrations.detail.hub.label', 'Hub')}\n </span>\n <span>{formatTypeLabel(resolvedIntegration.hub)}</span>\n </div>\n ) : null}\n </div>\n </div>\n\n <section className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"space-y-1\">\n <p className=\"text-[11px] uppercase tracking-wide text-muted-foreground\">\n {t('integrations.detail.state.label', 'State')}\n </p>\n <p className=\"text-sm font-medium\">\n {resolvedState.isEnabled\n ? t('integrations.detail.state.enabled', 'Enabled')\n : t('integrations.detail.state.disabled', 'Disabled')}\n </p>\n </div>\n <Switch\n checked={resolvedState.isEnabled}\n disabled={isTogglingState}\n onCheckedChange={(checked) => void handleToggleState(checked)}\n />\n </div>\n </section>\n\n {stackedDetailWidgets.length > 0 ? (\n <section className=\"space-y-4\">\n <InjectionSpot\n spotId={detailWidgetSpotId}\n context={injectionContext}\n data={detail}\n onDataChange={(next) => setDetail(next as IntegrationDetail)}\n widgetsOverride={stackedDetailWidgets}\n />\n </section>\n ) : null}\n\n {groupedDetailWidgets.length > 0 ? (\n <section className=\"grid gap-4 lg:grid-cols-2\">\n {groupedDetailWidgets.map((widget) => (\n <Card\n key={widget.widgetId}\n className={widget.placement?.column === 2 ? 'lg:col-start-2' : undefined}\n >\n <CardHeader>\n <CardTitle>\n {widget.placement?.groupLabel\n ? t(widget.placement.groupLabel, widget.module.metadata.title)\n : widget.module.metadata.title}\n </CardTitle>\n {widget.placement?.groupDescription ? (\n <p className=\"text-sm text-muted-foreground\">\n {widget.placement.groupDescription}\n </p>\n ) : null}\n </CardHeader>\n <CardContent>\n <widget.module.Widget\n context={injectionContext}\n data={detail}\n onDataChange={(next) => setDetail(next as IntegrationDetail)}\n />\n </CardContent>\n </Card>\n ))}\n </section>\n ) : null}\n\n <Tabs value={activeTab} onValueChange={handleTabChange} className=\"space-y-4\">\n <TabsList>\n <TabsTrigger value=\"credentials\">{t('integrations.detail.tabs.credentials')}</TabsTrigger>\n {hasVersions ? <TabsTrigger value=\"version\">{t('integrations.detail.tabs.version')}</TabsTrigger> : null}\n <TabsTrigger value=\"health\">{t('integrations.detail.tabs.health')}</TabsTrigger>\n <TabsTrigger value=\"logs\">{t('integrations.detail.tabs.logs')}</TabsTrigger>\n {injectedTabs.map((tab) => (\n <TabsTrigger key={tab.id} value={tab.id}>{tab.label}</TabsTrigger>\n ))}\n </TabsList>\n\n <TabsContent value=\"credentials\" className=\"mt-0\">\n <section className=\"space-y-4 rounded-lg border bg-card p-6\">\n {detail.bundle ? (\n <div className=\"rounded-lg border border-blue-200 bg-blue-50 p-3 text-sm text-blue-800\">\n {t('integrations.detail.credentials.bundleShared', { bundle: detail.bundle.title })}\n </div>\n ) : null}\n {credentialFormFields.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('integrations.detail.credentials.notConfigured')}\n </p>\n ) : (\n <CrudForm<Record<string, unknown>>\n key={`${resolvedIntegration.id}:${credentialsFormKey}`}\n formId={credentialsFormId}\n entityId=\"integrations.integration\"\n schema={credentialSchema}\n fields={credentialFormFields}\n initialValues={credValues}\n onSubmit={handleSaveCredentials}\n embedded\n hideFooterActions\n />\n )}\n </section>\n </TabsContent>\n\n {hasVersions ? (\n <TabsContent value=\"version\" className=\"mt-0 space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle>{t('integrations.detail.version.select')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {resolvedIntegration.apiVersions?.map((version) => {\n const stableVersion = resolvedIntegration.apiVersions?.find((item) => item.status === 'stable')?.id\n const isSelected = (resolvedState.apiVersion ?? stableVersion) === version.id\n return (\n <div\n key={version.id}\n className={`flex cursor-pointer items-center justify-between rounded-lg border p-3 transition-colors ${isSelected ? 'border-primary bg-primary/5' : 'hover:bg-muted/50'}`}\n onClick={() => void handleVersionChange(version.id)}\n >\n <div>\n <span className=\"text-sm font-medium\">{version.label ?? version.id}</span>\n <Badge\n variant={version.status === 'stable' ? 'default' : version.status === 'deprecated' ? 'destructive' : 'secondary'}\n className=\"ml-2\"\n >\n {t(`integrations.detail.version.${version.status}`)}\n </Badge>\n {version.status === 'deprecated' && version.sunsetAt ? (\n <span className=\"ml-2 text-xs text-muted-foreground\">\n {t('integrations.detail.version.sunsetAt', { date: new Date(version.sunsetAt).toLocaleDateString() })}\n </span>\n ) : null}\n </div>\n {isSelected ? <Badge variant=\"outline\">{t('integrations.detail.version.current')}</Badge> : null}\n </div>\n )\n })}\n </CardContent>\n </Card>\n </TabsContent>\n ) : null}\n\n <TabsContent value=\"health\" className=\"mt-0 space-y-4\">\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle>{t('integrations.detail.health.title')}</CardTitle>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => void handleHealthCheck()}\n disabled={isCheckingHealth}\n >\n {isCheckingHealth ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Zap className=\"mr-2 h-4 w-4\" />}\n {isCheckingHealth ? t('integrations.detail.health.checking') : t('integrations.detail.health.check')}\n </Button>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"text-sm font-medium\">{t('integrations.detail.health.title')}:</span>\n {resolvedState.lastHealthStatus ? (\n <Badge className={HEALTH_STATUS_STYLES[resolvedState.lastHealthStatus] ?? ''}>\n {t(`integrations.detail.health.${resolvedState.lastHealthStatus}`)}\n </Badge>\n ) : (\n <span className=\"text-sm text-muted-foreground\">\n {t('integrations.detail.health.unknown')}\n </span>\n )}\n </div>\n {healthStatusDescription ? (\n <p className=\"text-sm text-muted-foreground\">{healthStatusDescription}</p>\n ) : null}\n {healthMessage ? (\n <div className=\"rounded-lg border bg-muted/30 p-3\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('integrations.detail.health.lastResult', 'Last result')}\n </p>\n <p className=\"mt-1 text-sm\">{healthMessage}</p>\n </div>\n ) : null}\n {healthDetailEntries.length > 0 ? (\n <div className=\"rounded-lg border p-3\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('integrations.detail.health.details', 'Details')}\n </p>\n <dl className=\"mt-3 grid gap-3 sm:grid-cols-2\">\n {healthDetailEntries.map(([key, value]) => (\n <div key={key}>\n <dt className=\"text-xs font-medium text-muted-foreground\">{key}</dt>\n <dd className=\"mt-1 text-sm\">{formatHealthValue(value)}</dd>\n </div>\n ))}\n </dl>\n </div>\n ) : null}\n <p className=\"text-xs text-muted-foreground\">\n {resolvedState.lastHealthCheckedAt\n ? t('integrations.detail.health.lastChecked', { date: new Date(resolvedState.lastHealthCheckedAt).toLocaleString() })\n : t('integrations.detail.health.neverChecked')\n }\n </p>\n </CardContent>\n </Card>\n </TabsContent>\n\n <TabsContent value=\"logs\" className=\"mt-0 space-y-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"relative inline-flex\">\n <select\n className=\"h-11 min-w-40 appearance-none rounded-xl border border-border bg-card pl-4 pr-11 text-sm font-medium text-foreground shadow-sm transition-colors focus-visible:border-ring focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30\"\n value={logLevel}\n onChange={(event) => setLogLevel(event.target.value)}\n >\n <option value=\"\">{t('integrations.detail.logs.level.all')}</option>\n <option value=\"info\">{t('integrations.detail.logs.level.info')}</option>\n <option value=\"warn\">{t('integrations.detail.logs.level.warn')}</option>\n <option value=\"error\">{t('integrations.detail.logs.level.error')}</option>\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-4 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n </div>\n </div>\n {isLoadingLogs ? (\n <div className=\"flex justify-center py-8\"><Spinner /></div>\n ) : logs.length === 0 ? (\n <p className=\"py-4 text-sm text-muted-foreground\">{t('integrations.detail.logs.empty')}</p>\n ) : (\n <div className=\"rounded-lg border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/50\">\n <th className=\"px-4 py-2 text-left font-medium\">{t('integrations.detail.logs.columns.time')}</th>\n <th className=\"px-4 py-2 text-left font-medium\">{t('integrations.detail.logs.columns.level')}</th>\n <th className=\"px-4 py-2 text-left font-medium\">{t('integrations.detail.logs.columns.message')}</th>\n </tr>\n </thead>\n <tbody>\n {logs.map((log) => {\n const isExpanded = expandedLogId === log.id\n const metadataEntries = [\n ['Time', new Date(log.createdAt).toLocaleString()],\n ['Level', log.level],\n ['Code', log.code ?? null],\n ['Run ID', log.runId ?? null],\n ['Entity Type', log.scopeEntityType ?? null],\n ['Entity ID', log.scopeEntityId ?? null],\n ].filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].trim().length > 0)\n const { inlineEntries, nestedEntries } = splitLogPayload(log.payload)\n\n return (\n <React.Fragment key={log.id}>\n <tr className=\"border-b last:border-0\">\n <td className=\"whitespace-nowrap px-4 py-2 text-muted-foreground\">\n {new Date(log.createdAt).toLocaleString()}\n </td>\n <td className=\"px-4 py-2\">\n <Badge variant=\"secondary\" className={LOG_LEVEL_STYLES[log.level] ?? ''}>\n {log.level}\n </Badge>\n </td>\n <td className=\"px-4 py-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto w-full justify-start gap-2 px-0 py-0 text-left hover:bg-transparent\"\n onClick={() => setExpandedLogId((current) => (current === log.id ? null : log.id))}\n >\n {isExpanded ? <ChevronDown className=\"h-4 w-4 shrink-0\" /> : <ChevronRight className=\"h-4 w-4 shrink-0\" />}\n <span className=\"truncate\">{log.message}</span>\n </Button>\n </td>\n </tr>\n {isExpanded ? (\n <tr className=\"border-b bg-muted/20 last:border-0\">\n <td colSpan={3} className=\"px-4 py-4\">\n <div className=\"space-y-4 rounded-lg border bg-card p-4\">\n <div className=\"grid gap-4 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]\">\n <div className=\"space-y-4\">\n <section className=\"space-y-3\">\n <div>\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('integrations.detail.logs.details.summary', 'Summary')}\n </p>\n <p className=\"mt-1 text-sm font-medium\">{log.message}</p>\n </div>\n {metadataEntries.length > 0 ? (\n <dl className=\"grid gap-3 sm:grid-cols-2\">\n {metadataEntries.map(([label, value]) => (\n <div key={label} className=\"rounded-md border bg-muted/30 px-3 py-2\">\n <dt className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">\n {label}\n </dt>\n <dd className=\"mt-1 break-all text-sm\">{value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n </section>\n\n {inlineEntries.length > 0 ? (\n <section className=\"space-y-3\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('integrations.detail.logs.details.fields', 'Fields')}\n </p>\n <dl className=\"grid gap-3 sm:grid-cols-2\">\n {inlineEntries.map(([key, value]) => (\n <div key={key} className=\"rounded-md border bg-muted/30 px-3 py-2\">\n <dt className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">\n {formatLogDetailLabel(key)}\n </dt>\n <dd className=\"mt-1 break-words text-sm\">\n {formatLogPrimitiveValue(value)}\n </dd>\n </div>\n ))}\n </dl>\n </section>\n ) : null}\n </div>\n\n <div className=\"space-y-3\">\n {nestedEntries.map(([key, value]) => (\n <JsonDisplay\n key={key}\n data={value}\n title={formatLogDetailLabel(key)}\n defaultExpanded\n maxInitialDepth={1}\n theme=\"dark\"\n maxHeight=\"16rem\"\n className=\"p-4\"\n />\n ))}\n {log.payload && nestedEntries.length === 0 ? (\n <JsonDisplay\n data={log.payload}\n title={t('integrations.detail.logs.details.payload', 'Payload')}\n defaultExpanded\n maxInitialDepth={1}\n theme=\"dark\"\n maxHeight=\"16rem\"\n className=\"p-4\"\n />\n ) : null}\n {!log.payload ? (\n <div className=\"rounded-lg border border-dashed px-4 py-6 text-sm text-muted-foreground\">\n {t('integrations.detail.logs.details.noPayload', 'No structured payload was stored for this log entry.')}\n </div>\n ) : null}\n </div>\n </div>\n </div>\n </td>\n </tr>\n ) : null}\n </React.Fragment>\n )\n })}\n </tbody>\n </table>\n </div>\n )}\n </TabsContent>\n\n {injectedTabs.map((tab) => (\n <TabsContent key={tab.id} value={tab.id} className=\"mt-0 space-y-4\">\n <InjectionSpot\n spotId={detailWidgetSpotId}\n context={injectionContext}\n data={detail}\n onDataChange={(next) => setDetail(next as IntegrationDetail)}\n widgetsOverride={tab.widgets}\n />\n </TabsContent>\n ))}\n </Tabs>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport { z } from 'zod'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { WebhookSetupGuide } from '@open-mercato/ui/backend/WebhookSetupGuide'\nimport { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Card, CardHeader, CardTitle, CardContent } from '@open-mercato/ui/primitives/card'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'\nimport { JsonDisplay } from '@open-mercato/ui/backend/JsonDisplay'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID, type CredentialFieldType, type IntegrationCredentialField } from '@open-mercato/shared/modules/integrations/types'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { Activity, AlertTriangle, Bell, Calendar, CheckCircle2, ChevronDown, ChevronRight, CreditCard, FileText, HardDrive, Key, MessageSquare, RefreshCw, Settings, Truck, Webhook, XCircle, Zap } from 'lucide-react'\nimport { IntegrationScheduleTab } from '../../../../data_sync/components/IntegrationScheduleTab'\nimport {\n buildIntegrationDetailInjectedTabs,\n filterIntegrationDetailWidgetsByKind,\n type IntegrationDetailInjectedTab,\n resolveIntegrationDetailWidgetSpotId,\n resolveRequestedIntegrationDetailTab,\n} from '../detail-page-widgets'\n\ntype CredentialField = IntegrationCredentialField\ntype BuiltInIntegrationDetailTab = 'credentials' | 'version' | 'health' | 'logs' | 'data-sync-schedule'\ntype IntegrationDetailTab = BuiltInIntegrationDetailTab | string\n\nconst UNSUPPORTED_CREDENTIAL_FIELD_TYPES = new Set<CredentialFieldType>(['oauth', 'ssh_keypair'])\n\nfunction isEditableCredentialField(field: CredentialField): boolean {\n return !UNSUPPORTED_CREDENTIAL_FIELD_TYPES.has(field.type)\n}\n\ntype ApiVersion = {\n id: string\n label?: string\n status: 'stable' | 'deprecated' | 'experimental'\n sunsetAt?: string\n migrationGuide?: string\n}\n\ntype IntegrationDetail = {\n integration: {\n id: string\n title: string\n description?: string\n category?: string\n hub?: string\n providerKey?: string | null\n bundleId?: string\n docsUrl?: string\n apiVersions?: ApiVersion[]\n detailPage?: {\n widgetSpotId?: string\n }\n credentials?: { fields: CredentialField[] }\n }\n bundle?: { id: string; title: string; credentials?: { fields: CredentialField[] } }\n state: {\n isEnabled: boolean\n apiVersion: string | null\n reauthRequired: boolean\n lastHealthStatus: string | null\n lastHealthCheckedAt: string | null\n }\n hasCredentials: boolean\n}\n\ntype LogEntry = {\n id: string\n runId?: string | null\n scopeEntityType?: string | null\n scopeEntityId?: string | null\n level: 'info' | 'warn' | 'error'\n message: string\n createdAt: string\n code?: string | null\n payload?: Record<string, unknown> | null\n}\n\ntype IntegrationDetailPageProps = {\n params?: {\n id?: string | string[]\n }\n}\n\ntype HealthCheckResponse = {\n status: 'healthy' | 'degraded' | 'unhealthy'\n message: string | null\n details: Record<string, unknown> | null\n checkedAt: string\n}\n\nconst LOG_LEVEL_STYLES: Record<string, string> = {\n info: 'bg-blue-100 text-blue-800',\n warn: 'bg-yellow-100 text-yellow-800',\n error: 'bg-red-100 text-red-800',\n}\n\nconst HEALTH_STATUS_STYLES: Record<string, string> = {\n healthy: 'bg-green-100 text-green-800',\n degraded: 'bg-yellow-100 text-yellow-800',\n unhealthy: 'bg-red-100 text-red-800',\n}\n\nconst HEALTH_STATUS_ICONS: Record<string, React.ElementType> = {\n healthy: CheckCircle2,\n degraded: AlertTriangle,\n unhealthy: XCircle,\n}\n\nconst CATEGORY_ICONS: Record<string, React.ElementType> = {\n payment: CreditCard,\n shipping: Truck,\n data_sync: RefreshCw,\n communication: MessageSquare,\n notification: Bell,\n storage: HardDrive,\n webhook: Webhook,\n}\n\nfunction resolveRouteId(value: string | string[] | undefined): string | undefined {\n if (Array.isArray(value)) return value[0]\n return value\n}\n\nfunction resolvePathnameId(pathname: string): string | undefined {\n const parts = pathname.split('/').filter(Boolean)\n const integrationId = parts.at(-1)\n if (!integrationId || integrationId === 'integrations' || integrationId === 'bundle') return undefined\n return decodeURIComponent(integrationId)\n}\n\nfunction buildCredentialFields(credFields: CredentialField[]): CrudField[] {\n return credFields.map((field) => {\n const shared = {\n id: field.key,\n label: field.label,\n description: field.helpDetails ? (\n <div className=\"space-y-1\">\n {field.helpText ? <div>{field.helpText}</div> : null}\n <WebhookSetupGuide guide={field.helpDetails} buttonLabel=\"Show details\" />\n </div>\n ) : field.helpText,\n placeholder: field.placeholder,\n required: field.required,\n }\n\n if (field.type === 'secret') {\n return {\n ...shared,\n type: 'custom' as const,\n component: ({ id, value, setValue, disabled }) => (\n <Input\n id={id}\n type=\"password\"\n placeholder={field.placeholder}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => setValue(event.target.value)}\n disabled={disabled}\n />\n ),\n }\n }\n\n if (field.type === 'select' && field.options) {\n return {\n ...shared,\n type: 'select' as const,\n options: field.options,\n }\n }\n\n if (field.type === 'boolean') {\n return {\n ...shared,\n type: 'checkbox' as const,\n }\n }\n\n return {\n ...shared,\n type: 'text' as const,\n }\n })\n}\n\nfunction isHealthLog(log: LogEntry): boolean {\n return log.message === 'Health check passed' || log.message.startsWith('Health check:')\n}\n\nfunction extractHealthDetails(payload: Record<string, unknown> | null | undefined): Record<string, unknown> {\n if (!payload) return {}\n return Object.fromEntries(\n Object.entries(payload).filter(([key, value]) => key !== 'status' && key !== 'message' && value !== undefined && value !== null),\n )\n}\n\nfunction formatHealthValue(value: unknown): string {\n if (typeof value === 'boolean') return value ? 'Yes' : 'No'\n if (typeof value === 'string') return value\n if (typeof value === 'number') return String(value)\n if (value instanceof Date) return value.toLocaleString()\n return JSON.stringify(value)\n}\n\nfunction formatTypeLabel(value: string): string {\n return value.split('_').filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(' ')\n}\n\nfunction isPrimitiveLogValue(value: unknown): value is string | number | boolean | null {\n return value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'\n}\n\nfunction formatLogDetailLabel(key: string): string {\n return key\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .split(' ')\n .filter(Boolean)\n .map((part) => part[0]?.toUpperCase() + part.slice(1))\n .join(' ')\n}\n\nfunction formatLogPrimitiveValue(value: string | number | boolean | null): string {\n if (value === null) return 'None'\n if (typeof value === 'boolean') return value ? 'Yes' : 'No'\n return String(value)\n}\n\nfunction isAkeneoSettingsTab(tab: IntegrationDetailInjectedTab): boolean {\n return tab.id.includes('sync_akeneo') || tab.label.toLowerCase().includes('akeneo')\n}\n\nfunction splitLogPayload(payload: Record<string, unknown> | null | undefined) {\n if (!payload) {\n return {\n inlineEntries: [] as Array<[string, string | number | boolean | null]>,\n nestedEntries: [] as Array<[string, unknown]>,\n }\n }\n\n const inlineEntries: Array<[string, string | number | boolean | null]> = []\n const nestedEntries: Array<[string, unknown]> = []\n\n Object.entries(payload).forEach(([key, value]) => {\n if (isPrimitiveLogValue(value)) {\n inlineEntries.push([key, value])\n return\n }\n nestedEntries.push([key, value])\n })\n\n return { inlineEntries, nestedEntries }\n}\n\nexport default function IntegrationDetailPage({ params }: IntegrationDetailPageProps) {\n const pathname = usePathname()\n const router = useRouter()\n const searchParams = useSearchParams()\n const integrationId = resolveRouteId(params?.id) ?? resolvePathnameId(pathname)\n const t = useT()\n\n const [detail, setDetail] = React.useState<IntegrationDetail | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n const [credValues, setCredValues] = React.useState<Record<string, unknown>>({})\n const [credentialsFormKey, setCredentialsFormKey] = React.useState(0)\n const [isSavingCredentials, setIsSavingCredentials] = React.useState(false)\n\n const [logs, setLogs] = React.useState<LogEntry[]>([])\n const [logLevel, setLogLevel] = React.useState<string>('')\n const [isLoadingLogs, setIsLoadingLogs] = React.useState(false)\n const [expandedLogId, setExpandedLogId] = React.useState<string | null>(null)\n\n const [isCheckingHealth, setIsCheckingHealth] = React.useState(false)\n const [isTogglingState, setIsTogglingState] = React.useState(false)\n const [latestHealthResult, setLatestHealthResult] = React.useState<HealthCheckResponse | null>(null)\n const [activeTab, setActiveTab] = React.useState<IntegrationDetailTab>('credentials')\n\n const credentialsFormId = React.useId()\n\n const resolveCurrentIntegrationId = React.useCallback(() => {\n return integrationId ?? (\n typeof window !== 'undefined'\n ? resolvePathnameId(window.location.pathname)\n : undefined\n )\n }, [integrationId])\n\n const loadDetail = React.useCallback(async () => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) {\n setIsLoading(false)\n setError(t('integrations.detail.loadError', 'Failed to load integration'))\n return\n }\n setError(null)\n setIsLoading(true)\n try {\n const call = await apiCall<IntegrationDetail>(\n `/api/integrations/${encodeURIComponent(currentIntegrationId)}`,\n undefined,\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n setError(t('integrations.detail.loadError', 'Failed to load integration'))\n setIsLoading(false)\n return\n }\n setDetail(call.result)\n setIsLoading(false)\n } catch {\n setError(t('integrations.detail.loadError', 'Failed to load integration'))\n setIsLoading(false)\n }\n }, [resolveCurrentIntegrationId, t])\n\n const loadCredentials = React.useCallback(async () => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n const call = await apiCall<{ credentials: Record<string, unknown> }>(\n `/api/integrations/${encodeURIComponent(currentIntegrationId)}/credentials`,\n undefined,\n { fallback: null },\n )\n if (call.ok && call.result?.credentials) {\n setCredValues(call.result.credentials)\n setCredentialsFormKey((current) => current + 1)\n }\n }, [resolveCurrentIntegrationId])\n\n const loadLogs = React.useCallback(async () => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n setIsLoadingLogs(true)\n const params = new URLSearchParams({ integrationId: currentIntegrationId, pageSize: '50' })\n if (logLevel) params.set('level', logLevel)\n const call = await apiCall<{ items: LogEntry[] }>(\n `/api/integrations/logs?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n if (call.ok && call.result) {\n setLogs(call.result.items)\n }\n setIsLoadingLogs(false)\n }, [logLevel, resolveCurrentIntegrationId])\n\n const detailWidgetSpotId = React.useMemo(\n () => resolveIntegrationDetailWidgetSpotId(detail?.integration ?? null, LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID),\n [detail?.integration],\n )\n const mutationContextId = React.useMemo(\n () => `integrations.detail:${integrationId ?? 'unknown'}`,\n [integrationId],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: mutationContextId,\n spotId: detailWidgetSpotId,\n })\n const refreshDetail = React.useCallback(async () => {\n await loadDetail()\n await loadCredentials()\n }, [loadCredentials, loadDetail])\n const refreshLogs = React.useCallback(async () => {\n await loadLogs()\n }, [loadLogs])\n const injectionContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n integrationDetailWidgetSpotId: detailWidgetSpotId,\n resourceKind: 'integrations.integration',\n resourceId: integrationId ?? detail?.integration.id,\n integrationId: integrationId ?? detail?.integration.id,\n integration: detail?.integration ?? null,\n detail,\n state: detail?.state ?? null,\n credentialValues: credValues,\n latestHealthResult,\n activeTab,\n setActiveTab,\n refreshDetail,\n refreshLogs,\n retryLastMutation,\n }),\n [\n activeTab,\n credValues,\n detail,\n detailWidgetSpotId,\n integrationId,\n latestHealthResult,\n mutationContextId,\n refreshDetail,\n refreshLogs,\n retryLastMutation,\n ],\n )\n const { widgets: detailWidgets } = useInjectionWidgets(detailWidgetSpotId, {\n context: injectionContext,\n triggerOnLoad: true,\n })\n const stackedDetailWidgets = React.useMemo(\n () => filterIntegrationDetailWidgetsByKind(detailWidgets, 'stack'),\n [detailWidgets],\n )\n const groupedDetailWidgets = React.useMemo(\n () => filterIntegrationDetailWidgetsByKind(detailWidgets, 'group'),\n [detailWidgets],\n )\n const injectedTabs = React.useMemo(\n () => buildIntegrationDetailInjectedTabs(\n detailWidgets,\n (widget) => (\n widget.placement?.groupLabel\n ? t(widget.placement.groupLabel, widget.module.metadata.title ?? widget.widgetId)\n : (widget.module.metadata.title ?? widget.widgetId)\n ),\n ),\n [detailWidgets, t],\n )\n const hasDataSyncScheduleTab = Boolean(\n detail?.integration.hub === 'data_sync'\n && detail?.integration.providerKey\n && detail.integration.providerKey.trim().length > 0,\n )\n const customTabIds = React.useMemo(\n () => [\n ...(hasDataSyncScheduleTab ? ['data-sync-schedule'] : []),\n ...injectedTabs.map((tab) => tab.id),\n ],\n [hasDataSyncScheduleTab, injectedTabs],\n )\n const runMutationWithContext = React.useCallback(\n async <T,>({\n operation,\n mutationPayload,\n actionId,\n tabId,\n operationType = 'update',\n }: {\n operation: () => Promise<T>\n mutationPayload?: Record<string, unknown>\n actionId: string\n tabId?: string\n operationType?: 'create' | 'update' | 'delete'\n }): Promise<T> => {\n return runMutation({\n operation,\n mutationPayload,\n context: {\n ...injectionContext,\n operation: operationType,\n actionId,\n activeTab: tabId ?? activeTab,\n },\n })\n },\n [activeTab, injectionContext, runMutation],\n )\n\n React.useEffect(() => { void loadDetail() }, [loadDetail])\n React.useEffect(() => { void loadCredentials() }, [loadCredentials])\n React.useEffect(() => { void loadLogs() }, [loadLogs])\n React.useEffect(() => {\n setExpandedLogId((current) => (current && logs.some((log) => log.id === current) ? current : null))\n }, [logs])\n\n const handleToggleState = React.useCallback(async (enabled: boolean) => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n setIsTogglingState(true)\n try {\n const call = await runMutationWithContext({\n actionId: 'toggle-state',\n mutationPayload: { integrationId: currentIntegrationId, isEnabled: enabled },\n operation: () => apiCall(`/api/integrations/${encodeURIComponent(currentIntegrationId)}/state`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ isEnabled: enabled }),\n }, { fallback: null }),\n })\n if (call.ok) {\n setDetail((prev) => prev ? { ...prev, state: { ...prev.state, isEnabled: enabled } } : prev)\n flash(t('integrations.detail.stateUpdated'), 'success')\n } else {\n flash(t('integrations.detail.stateError'), 'error')\n }\n } catch {\n flash(t('integrations.detail.stateError'), 'error')\n } finally {\n setIsTogglingState(false)\n }\n }, [resolveCurrentIntegrationId, runMutationWithContext, t])\n\n const handleSaveCredentials = React.useCallback(async (values: Record<string, unknown>) => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n setIsSavingCredentials(true)\n try {\n const call = await runMutationWithContext({\n actionId: 'save-credentials',\n tabId: 'credentials',\n mutationPayload: { integrationId: currentIntegrationId, credentials: values },\n operation: () => apiCall(`/api/integrations/${encodeURIComponent(currentIntegrationId)}/credentials`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ credentials: values }),\n }, { fallback: null }),\n })\n\n if (call.ok) {\n setCredValues(values)\n setCredentialsFormKey((current) => current + 1)\n flash(t('integrations.detail.credentials.saved'), 'success')\n return\n }\n\n const result = call.result as {\n error?: string\n details?: { fieldErrors?: Record<string, string>; formErrors?: string[] }\n } | null\n throw createCrudFormError(\n result?.error ?? t('integrations.detail.credentials.saveError', 'Failed to save credentials'),\n result?.details?.fieldErrors,\n { details: result?.details },\n )\n } finally {\n setIsSavingCredentials(false)\n }\n }, [resolveCurrentIntegrationId, runMutationWithContext, t])\n\n const handleVersionChange = React.useCallback(async (version: string) => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n try {\n const call = await runMutationWithContext({\n actionId: 'change-version',\n tabId: 'version',\n mutationPayload: { integrationId: currentIntegrationId, apiVersion: version },\n operation: () => apiCall(`/api/integrations/${encodeURIComponent(currentIntegrationId)}/version`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiVersion: version }),\n }, { fallback: null }),\n })\n if (call.ok) {\n setDetail((prev) => prev ? { ...prev, state: { ...prev.state, apiVersion: version } } : prev)\n flash(t('integrations.detail.version.saved'), 'success')\n } else {\n flash(t('integrations.detail.version.saveError'), 'error')\n }\n } catch {\n flash(t('integrations.detail.version.saveError'), 'error')\n }\n }, [resolveCurrentIntegrationId, runMutationWithContext, t])\n\n const handleHealthCheck = React.useCallback(async () => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n if (!currentIntegrationId) return\n setIsCheckingHealth(true)\n try {\n const call = await runMutationWithContext({\n actionId: 'run-health-check',\n tabId: 'health',\n mutationPayload: { integrationId: currentIntegrationId },\n operation: () => apiCall<HealthCheckResponse>(\n `/api/integrations/${encodeURIComponent(currentIntegrationId)}/health`,\n { method: 'POST' },\n { fallback: null },\n ),\n })\n const result = call.result\n if (call.ok && result) {\n setLatestHealthResult(result)\n setDetail((prev) => prev ? {\n ...prev,\n state: {\n ...prev.state,\n lastHealthStatus: result.status,\n lastHealthCheckedAt: result.checkedAt,\n },\n } : prev)\n void refreshLogs()\n } else {\n flash(t('integrations.detail.health.checkError'), 'error')\n }\n } catch {\n flash(t('integrations.detail.health.checkError'), 'error')\n } finally {\n setIsCheckingHealth(false)\n }\n }, [refreshLogs, resolveCurrentIntegrationId, runMutationWithContext, t])\n\n const hasVersions = Boolean(detail?.integration.apiVersions?.length)\n const integration = detail?.integration ?? null\n const state = detail?.state ?? null\n const editableCredentialFields = React.useMemo(\n () => (detail?.integration.credentials?.fields ?? detail?.bundle?.credentials?.fields ?? []).filter(isEditableCredentialField),\n [detail?.bundle?.credentials?.fields, detail?.integration.credentials?.fields],\n )\n const credentialFormFields = React.useMemo(\n () => buildCredentialFields(editableCredentialFields),\n [editableCredentialFields],\n )\n const credentialSchema = React.useMemo(() => (\n z.object({}).passthrough().superRefine((rawValues, ctx) => {\n const values = rawValues as Record<string, unknown>\n\n editableCredentialFields.forEach((field) => {\n const value = values[field.key]\n\n if (field.type === 'boolean') {\n if (value !== undefined && value !== null && typeof value !== 'boolean') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.boolean', 'Select a valid value.'),\n })\n }\n if (field.required && typeof value !== 'boolean') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.required', '{field} is required.', { field: field.label }),\n })\n }\n return\n }\n\n if (value !== undefined && value !== null && typeof value !== 'string') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.text', 'Enter a valid value.'),\n })\n return\n }\n\n const normalizedValue = typeof value === 'string' ? value : ''\n\n if (field.required && normalizedValue.trim().length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.required', '{field} is required.', { field: field.label }),\n })\n }\n\n if (normalizedValue.length > 20_000) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.tooLong', 'Value is too long.'),\n })\n }\n\n if (\n field.type === 'select'\n && normalizedValue\n && field.options\n && !field.options.some((option) => option.value === normalizedValue)\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [field.key],\n message: t('integrations.detail.credentials.validation.option', 'Select one of the available options.'),\n })\n }\n })\n })\n ) as z.ZodType<Record<string, unknown>>, [editableCredentialFields, t])\n const latestHealthLog = React.useMemo(() => logs.find(isHealthLog) ?? null, [logs])\n const healthMessage =\n latestHealthResult?.message ??\n (typeof latestHealthLog?.payload?.message === 'string' ? latestHealthLog.payload.message : null)\n const healthDetailsSource = latestHealthResult?.details ?? extractHealthDetails(latestHealthLog?.payload)\n const healthDetails = latestHealthLog?.code\n ? { ...healthDetailsSource, code: latestHealthLog.code }\n : healthDetailsSource\n const healthDetailEntries = Object.entries(healthDetails)\n const healthStatusDescription = state?.lastHealthStatus\n ? t(\n `integrations.detail.health.meaning.${state.lastHealthStatus}`,\n state.lastHealthStatus === 'healthy'\n ? 'The provider responded successfully using the current credentials.'\n : state.lastHealthStatus === 'degraded'\n ? 'The provider responded, but reported warnings or limited functionality.'\n : integration?.id === 'gateway_stripe'\n ? 'Stripe rejected the last check. This usually means the secret key is invalid, missing required permissions, revoked, or Stripe was temporarily unavailable.'\n : 'The last check failed. This usually means invalid credentials, missing permissions, or a provider outage.',\n )\n : null\n\n React.useEffect(() => {\n setActiveTab(resolveRequestedIntegrationDetailTab(searchParams?.get('tab'), hasVersions, customTabIds))\n }, [customTabIds, hasVersions, searchParams])\n\n const handleTabChange = React.useCallback((nextValue: string) => {\n const currentIntegrationId = resolveCurrentIntegrationId()\n const nextTab = resolveRequestedIntegrationDetailTab(nextValue, hasVersions, customTabIds)\n setActiveTab(nextTab)\n if (!currentIntegrationId) return\n const basePath = `/backend/integrations/${encodeURIComponent(currentIntegrationId)}`\n router.replace(nextTab === 'credentials' ? basePath : `${basePath}?tab=${encodeURIComponent(nextTab)}`)\n }, [customTabIds, hasVersions, resolveCurrentIntegrationId, router])\n\n if (isLoading) return <Page><PageBody><LoadingMessage label={t('integrations.detail.title')} /></PageBody></Page>\n if (error || !detail) return <Page><PageBody><ErrorMessage label={error ?? t('integrations.detail.loadError')} /></PageBody></Page>\n\n const resolvedIntegration = detail.integration\n const resolvedState = detail.state\n const CategoryIcon = resolvedIntegration.category ? CATEGORY_ICONS[resolvedIntegration.category] : null\n const HealthStatusIcon = resolvedState.lastHealthStatus ? HEALTH_STATUS_ICONS[resolvedState.lastHealthStatus] : null\n const prioritizedInjectedTabs = resolvedIntegration.id === 'sync_akeneo'\n ? [...injectedTabs].sort((left, right) => {\n const leftPriority = isAkeneoSettingsTab(left) ? 1 : 0\n const rightPriority = isAkeneoSettingsTab(right) ? 1 : 0\n if (leftPriority !== rightPriority) return rightPriority - leftPriority\n return 0\n })\n : injectedTabs\n const leadingInjectedTab = resolvedIntegration.id === 'sync_akeneo'\n ? prioritizedInjectedTabs.find(isAkeneoSettingsTab) ?? null\n : null\n const trailingInjectedTabs = leadingInjectedTab\n ? prioritizedInjectedTabs.filter((tab) => tab.id !== leadingInjectedTab.id)\n : prioritizedInjectedTabs\n const StateIcon = resolvedState.isEnabled ? CheckCircle2 : XCircle\n const stateBadgeClass = resolvedState.isEnabled\n ? 'border-emerald-500/30 bg-emerald-500/10 text-emerald-300'\n : 'border-zinc-500/30 bg-zinc-500/10 text-zinc-300'\n\n const showCredentialActions = activeTab === 'credentials' && credentialFormFields.length > 0\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <FormHeader\n backHref=\"/backend/integrations\"\n title={resolvedIntegration.title}\n actions={{\n cancelHref: showCredentialActions ? '/backend/integrations' : undefined,\n submit: showCredentialActions\n ? {\n formId: credentialsFormId,\n pending: isSavingCredentials,\n label: t('integrations.detail.credentials.save', 'Save credentials'),\n pendingLabel: t('ui.forms.status.saving', 'Saving...'),\n }\n : undefined,\n }}\n />\n\n <div className=\"space-y-2\">\n {resolvedIntegration.description ? (\n <p className=\"text-sm text-muted-foreground\">{resolvedIntegration.description}</p>\n ) : null}\n <div className=\"flex flex-wrap items-center gap-4 text-sm text-muted-foreground\">\n {resolvedIntegration.category ? (\n <div className=\"flex items-center gap-2\">\n {CategoryIcon ? <CategoryIcon className=\"h-4 w-4\" /> : null}\n <span>{formatTypeLabel(resolvedIntegration.category)}</span>\n </div>\n ) : null}\n {resolvedIntegration.hub ? (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground/80\">\n {t('integrations.detail.hub.label', 'Hub')}\n </span>\n <span>{formatTypeLabel(resolvedIntegration.hub)}</span>\n </div>\n ) : null}\n </div>\n </div>\n\n <section className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"space-y-2\">\n <p className=\"text-[11px] uppercase tracking-wide text-muted-foreground\">\n {t('integrations.detail.state.label', 'State')}\n </p>\n <Badge variant=\"outline\" className={cn('gap-1.5 rounded-full px-3 py-1 text-xs font-medium', stateBadgeClass)}>\n <StateIcon className=\"h-3.5 w-3.5\" />\n {resolvedState.isEnabled\n ? t('integrations.detail.state.enabled', 'Enabled')\n : t('integrations.detail.state.disabled', 'Disabled')}\n </Badge>\n </div>\n <Switch\n checked={resolvedState.isEnabled}\n disabled={isTogglingState}\n onCheckedChange={(checked) => void handleToggleState(checked)}\n />\n </div>\n </section>\n\n {stackedDetailWidgets.length > 0 ? (\n <section className=\"space-y-4\">\n <InjectionSpot\n spotId={detailWidgetSpotId}\n context={injectionContext}\n data={detail}\n onDataChange={(next) => setDetail(next as IntegrationDetail)}\n widgetsOverride={stackedDetailWidgets}\n />\n </section>\n ) : null}\n\n {groupedDetailWidgets.length > 0 ? (\n <section className=\"grid gap-4 lg:grid-cols-2\">\n {groupedDetailWidgets.map((widget) => (\n <Card\n key={widget.widgetId}\n className={widget.placement?.column === 2 ? 'lg:col-start-2' : undefined}\n >\n <CardHeader>\n <CardTitle>\n {widget.placement?.groupLabel\n ? t(widget.placement.groupLabel, widget.module.metadata.title)\n : widget.module.metadata.title}\n </CardTitle>\n {widget.placement?.groupDescription ? (\n <p className=\"text-sm text-muted-foreground\">\n {widget.placement.groupDescription}\n </p>\n ) : null}\n </CardHeader>\n <CardContent>\n <widget.module.Widget\n context={injectionContext}\n data={detail}\n onDataChange={(next) => setDetail(next as IntegrationDetail)}\n />\n </CardContent>\n </Card>\n ))}\n </section>\n ) : null}\n\n <Tabs value={activeTab} onValueChange={handleTabChange} className=\"space-y-5\">\n <TabsList className=\"h-auto w-full justify-start overflow-x-auto rounded-none border-b border-border bg-transparent p-0\">\n <TabsTrigger\n value=\"credentials\"\n className=\"mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Key className=\"h-4 w-4\" />\n <span>{t('integrations.detail.tabs.credentials')}</span>\n </span>\n </TabsTrigger>\n {leadingInjectedTab ? (\n <TabsTrigger\n value={leadingInjectedTab.id}\n className=\"mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Settings className=\"h-4 w-4\" />\n <span>{leadingInjectedTab.label}</span>\n </span>\n </TabsTrigger>\n ) : null}\n {hasVersions ? (\n <TabsTrigger\n value=\"version\"\n className=\"mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <RefreshCw className=\"h-4 w-4\" />\n <span>{t('integrations.detail.tabs.version')}</span>\n </span>\n </TabsTrigger>\n ) : null}\n {hasDataSyncScheduleTab ? (\n <TabsTrigger\n value=\"data-sync-schedule\"\n className=\"mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Calendar className=\"h-4 w-4\" />\n <span>{t('data_sync.integrationTab.title', 'Sync schedules')}</span>\n </span>\n </TabsTrigger>\n ) : null}\n <TabsTrigger\n value=\"health\"\n className=\"mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Activity className=\"h-4 w-4\" />\n <span>{t('integrations.detail.tabs.health')}</span>\n </span>\n </TabsTrigger>\n <TabsTrigger\n value=\"logs\"\n className=\"mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <FileText className=\"h-4 w-4\" />\n <span>{t('integrations.detail.tabs.logs')}</span>\n </span>\n </TabsTrigger>\n {trailingInjectedTabs.map((tab) => (\n <TabsTrigger\n key={tab.id}\n value={tab.id}\n className=\"mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Settings className=\"h-4 w-4\" />\n <span>{tab.label}</span>\n </span>\n </TabsTrigger>\n ))}\n </TabsList>\n\n <TabsContent value=\"credentials\" className=\"mt-0\">\n <section className=\"space-y-4 rounded-lg border bg-card p-6\">\n {detail.bundle ? (\n <div className=\"rounded-lg border border-blue-200 bg-blue-50 p-3 text-sm text-blue-800\">\n {t('integrations.detail.credentials.bundleShared', { bundle: detail.bundle.title })}\n </div>\n ) : null}\n {credentialFormFields.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('integrations.detail.credentials.notConfigured')}\n </p>\n ) : (\n <CrudForm<Record<string, unknown>>\n key={`${resolvedIntegration.id}:${credentialsFormKey}`}\n formId={credentialsFormId}\n entityId=\"integrations.integration\"\n schema={credentialSchema}\n fields={credentialFormFields}\n initialValues={credValues}\n onSubmit={handleSaveCredentials}\n embedded\n hideFooterActions\n />\n )}\n </section>\n </TabsContent>\n\n {hasVersions ? (\n <TabsContent value=\"version\" className=\"mt-0 space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle>{t('integrations.detail.version.select')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {resolvedIntegration.apiVersions?.map((version) => {\n const stableVersion = resolvedIntegration.apiVersions?.find((item) => item.status === 'stable')?.id\n const isSelected = (resolvedState.apiVersion ?? stableVersion) === version.id\n return (\n <div\n key={version.id}\n className={`flex cursor-pointer items-center justify-between rounded-lg border p-3 transition-colors ${isSelected ? 'border-primary bg-primary/5' : 'hover:bg-muted/50'}`}\n onClick={() => void handleVersionChange(version.id)}\n >\n <div>\n <span className=\"text-sm font-medium\">{version.label ?? version.id}</span>\n <Badge\n variant={version.status === 'stable' ? 'default' : version.status === 'deprecated' ? 'destructive' : 'secondary'}\n className=\"ml-2\"\n >\n {t(`integrations.detail.version.${version.status}`)}\n </Badge>\n {version.status === 'deprecated' && version.sunsetAt ? (\n <span className=\"ml-2 text-xs text-muted-foreground\">\n {t('integrations.detail.version.sunsetAt', { date: new Date(version.sunsetAt).toLocaleDateString() })}\n </span>\n ) : null}\n </div>\n {isSelected ? <Badge variant=\"outline\">{t('integrations.detail.version.current')}</Badge> : null}\n </div>\n )\n })}\n </CardContent>\n </Card>\n </TabsContent>\n ) : null}\n\n {hasDataSyncScheduleTab ? (\n <TabsContent value=\"data-sync-schedule\" className=\"mt-0\">\n <IntegrationScheduleTab\n integrationId={resolvedIntegration.id}\n hasCredentials={detail.hasCredentials}\n isEnabled={resolvedState.isEnabled}\n />\n </TabsContent>\n ) : null}\n\n <TabsContent value=\"health\" className=\"mt-0 space-y-4\">\n <Card className=\"gap-4 py-4\">\n <CardHeader className=\"px-5\">\n <div className=\"flex items-center justify-between\">\n <CardTitle>{t('integrations.detail.health.title')}</CardTitle>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => void handleHealthCheck()}\n disabled={isCheckingHealth}\n >\n {isCheckingHealth ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Zap className=\"mr-2 h-4 w-4\" />}\n {isCheckingHealth ? t('integrations.detail.health.checking') : t('integrations.detail.health.check')}\n </Button>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3 px-5\">\n <div className=\"flex flex-wrap items-center gap-3 rounded-lg border bg-muted/20 px-4 py-3\">\n {resolvedState.lastHealthStatus ? (\n <Badge className={`gap-1.5 ${HEALTH_STATUS_STYLES[resolvedState.lastHealthStatus] ?? ''}`}>\n {HealthStatusIcon ? <HealthStatusIcon className=\"h-3.5 w-3.5\" /> : null}\n {t(`integrations.detail.health.${resolvedState.lastHealthStatus}`)}\n </Badge>\n ) : (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <AlertTriangle className=\"h-4 w-4\" />\n <span>{t('integrations.detail.health.unknown')}</span>\n </div>\n )}\n {healthStatusDescription ? (\n <p className=\"min-w-0 flex-1 text-sm text-muted-foreground\">{healthStatusDescription}</p>\n ) : null}\n <p className=\"text-xs text-muted-foreground md:ml-auto\">\n {resolvedState.lastHealthCheckedAt\n ? t('integrations.detail.health.lastChecked', { date: new Date(resolvedState.lastHealthCheckedAt).toLocaleString() })\n : t('integrations.detail.health.neverChecked')\n }\n </p>\n </div>\n {healthMessage || healthDetailEntries.length > 0 ? (\n <div className={`grid gap-3 ${healthMessage && healthDetailEntries.length > 0 ? 'xl:grid-cols-[minmax(0,1.25fr)_minmax(0,1fr)]' : ''}`}>\n {healthMessage ? (\n <div className=\"rounded-lg border px-4 py-3\">\n <p className=\"text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground\">\n {t('integrations.detail.health.lastResult', 'Last result')}\n </p>\n <p className=\"mt-1.5 text-sm\">{healthMessage}</p>\n </div>\n ) : null}\n {healthDetailEntries.length > 0 ? (\n <div className=\"rounded-lg border px-4 py-3\">\n <p className=\"text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground\">\n {t('integrations.detail.health.details', 'Details')}\n </p>\n <dl className=\"mt-2 grid gap-x-6 gap-y-2 sm:grid-cols-2\">\n {healthDetailEntries.map(([key, value]) => (\n <div key={key}>\n <dt className=\"text-xs font-medium text-muted-foreground\">{formatLogDetailLabel(key)}</dt>\n <dd className=\"mt-0.5 text-sm\">{formatHealthValue(value)}</dd>\n </div>\n ))}\n </dl>\n </div>\n ) : null}\n </div>\n ) : null}\n </CardContent>\n </Card>\n </TabsContent>\n\n <TabsContent value=\"logs\" className=\"mt-0 space-y-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"relative inline-flex\">\n <select\n className=\"h-11 min-w-40 appearance-none rounded-xl border border-border bg-card pl-4 pr-11 text-sm font-medium text-foreground shadow-sm transition-colors focus-visible:border-ring focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30\"\n value={logLevel}\n onChange={(event) => setLogLevel(event.target.value)}\n >\n <option value=\"\">{t('integrations.detail.logs.level.all')}</option>\n <option value=\"info\">{t('integrations.detail.logs.level.info')}</option>\n <option value=\"warn\">{t('integrations.detail.logs.level.warn')}</option>\n <option value=\"error\">{t('integrations.detail.logs.level.error')}</option>\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-4 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n </div>\n </div>\n {isLoadingLogs ? (\n <div className=\"flex justify-center py-8\"><Spinner /></div>\n ) : logs.length === 0 ? (\n <p className=\"py-4 text-sm text-muted-foreground\">{t('integrations.detail.logs.empty')}</p>\n ) : (\n <div className=\"rounded-lg border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/50\">\n <th className=\"px-4 py-2 text-left font-medium\">{t('integrations.detail.logs.columns.time')}</th>\n <th className=\"px-4 py-2 text-left font-medium\">{t('integrations.detail.logs.columns.level')}</th>\n <th className=\"px-4 py-2 text-left font-medium\">{t('integrations.detail.logs.columns.message')}</th>\n </tr>\n </thead>\n <tbody>\n {logs.map((log) => {\n const isExpanded = expandedLogId === log.id\n const metadataEntries = [\n ['Time', new Date(log.createdAt).toLocaleString()],\n ['Level', log.level],\n ['Code', log.code ?? null],\n ['Run ID', log.runId ?? null],\n ['Entity Type', log.scopeEntityType ?? null],\n ['Entity ID', log.scopeEntityId ?? null],\n ].filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].trim().length > 0)\n const { inlineEntries, nestedEntries } = splitLogPayload(log.payload)\n\n return (\n <React.Fragment key={log.id}>\n <tr className=\"border-b last:border-0\">\n <td className=\"whitespace-nowrap px-4 py-2 text-muted-foreground\">\n {new Date(log.createdAt).toLocaleString()}\n </td>\n <td className=\"px-4 py-2\">\n <Badge variant=\"secondary\" className={LOG_LEVEL_STYLES[log.level] ?? ''}>\n {log.level}\n </Badge>\n </td>\n <td className=\"px-4 py-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto w-full justify-start gap-2 px-0 py-0 text-left hover:bg-transparent\"\n onClick={() => setExpandedLogId((current) => (current === log.id ? null : log.id))}\n >\n {isExpanded ? <ChevronDown className=\"h-4 w-4 shrink-0\" /> : <ChevronRight className=\"h-4 w-4 shrink-0\" />}\n <span className=\"truncate\">{log.message}</span>\n </Button>\n </td>\n </tr>\n {isExpanded ? (\n <tr className=\"border-b bg-muted/20 last:border-0\">\n <td colSpan={3} className=\"px-4 py-4\">\n <div className=\"space-y-4 rounded-lg border bg-card p-4\">\n <div className=\"grid gap-4 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]\">\n <div className=\"space-y-4\">\n <section className=\"space-y-3\">\n <div>\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('integrations.detail.logs.details.summary', 'Summary')}\n </p>\n <p className=\"mt-1 text-sm font-medium\">{log.message}</p>\n </div>\n {metadataEntries.length > 0 ? (\n <dl className=\"grid gap-3 sm:grid-cols-2\">\n {metadataEntries.map(([label, value]) => (\n <div key={label} className=\"rounded-md border bg-muted/30 px-3 py-2\">\n <dt className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">\n {label}\n </dt>\n <dd className=\"mt-1 break-all text-sm\">{value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n </section>\n\n {inlineEntries.length > 0 ? (\n <section className=\"space-y-3\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('integrations.detail.logs.details.fields', 'Fields')}\n </p>\n <dl className=\"grid gap-3 sm:grid-cols-2\">\n {inlineEntries.map(([key, value]) => (\n <div key={key} className=\"rounded-md border bg-muted/30 px-3 py-2\">\n <dt className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">\n {formatLogDetailLabel(key)}\n </dt>\n <dd className=\"mt-1 break-words text-sm\">\n {formatLogPrimitiveValue(value)}\n </dd>\n </div>\n ))}\n </dl>\n </section>\n ) : null}\n </div>\n\n <div className=\"space-y-3\">\n {nestedEntries.map(([key, value]) => (\n <JsonDisplay\n key={key}\n data={value}\n title={formatLogDetailLabel(key)}\n defaultExpanded\n maxInitialDepth={1}\n theme=\"dark\"\n maxHeight=\"16rem\"\n className=\"p-4\"\n />\n ))}\n {log.payload && nestedEntries.length === 0 ? (\n <JsonDisplay\n data={log.payload}\n title={t('integrations.detail.logs.details.payload', 'Payload')}\n defaultExpanded\n maxInitialDepth={1}\n theme=\"dark\"\n maxHeight=\"16rem\"\n className=\"p-4\"\n />\n ) : null}\n {!log.payload ? (\n <div className=\"rounded-lg border border-dashed px-4 py-6 text-sm text-muted-foreground\">\n {t('integrations.detail.logs.details.noPayload', 'No structured payload was stored for this log entry.')}\n </div>\n ) : null}\n </div>\n </div>\n </div>\n </td>\n </tr>\n ) : null}\n </React.Fragment>\n )\n })}\n </tbody>\n </table>\n </div>\n )}\n </TabsContent>\n\n {injectedTabs.map((tab) => (\n <TabsContent key={tab.id} value={tab.id} className=\"mt-0 space-y-4\">\n <InjectionSpot\n spotId={detailWidgetSpotId}\n context={injectionContext}\n data={detail}\n onDataChange={(next) => setDetail(next as IntegrationDetail)}\n widgetsOverride={tab.widgets}\n />\n </TabsContent>\n ))}\n </Tabs>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAuJQ,SACoB,KADpB;AAtJR,YAAY,WAAW;AACvB,SAAS,aAAa,WAAW,uBAAuB;AACxD,SAAS,SAAS;AAClB,SAAS,MAAM,gBAAgB;AAC/B,SAAS,gBAAgC;AACzC,SAAS,yBAAyB;AAClC,SAAS,eAAe,2BAA2B;AACnD,SAAS,0BAA0B;AACnC,SAAS,kBAAkB;AAC3B,SAAS,MAAM,YAAY,WAAW,mBAAmB;AACzD,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,eAAe;AACxB,SAAS,MAAM,aAAa,UAAU,mBAAmB;AACzD,SAAS,mBAAmB;AAC5B,SAAS,eAAe;AACxB,SAAS,aAAa;AACtB,SAAS,2BAA2B;AACpC,SAAS,YAAY;AACrB,SAAS,UAAU;AACnB,SAAS,8CAAyG;AAClH,SAAS,gBAAgB,oBAAoB;AAC7C,SAAS,UAAU,eAAe,MAAM,UAAU,cAAc,aAAa,cAAc,YAAY,UAAU,WAAW,KAAK,eAAe,WAAW,UAAU,OAAO,SAAS,SAAS,WAAW;AACzM,SAAS,8BAA8B;AACvC;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACK;AAMP,MAAM,qCAAqC,oBAAI,IAAyB,CAAC,SAAS,aAAa,CAAC;AAEhG,SAAS,0BAA0B,OAAiC;AAClE,SAAO,CAAC,mCAAmC,IAAI,MAAM,IAAI;AAC3D;AA8DA,MAAM,mBAA2C;AAAA,EAC/C,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,MAAM,uBAA+C;AAAA,EACnD,SAAS;AAAA,EACT,UAAU;AAAA,EACV,WAAW;AACb;AAEA,MAAM,sBAAyD;AAAA,EAC7D,SAAS;AAAA,EACT,UAAU;AAAA,EACV,WAAW;AACb;AAEA,MAAM,iBAAoD;AAAA,EACxD,SAAS;AAAA,EACT,UAAU;AAAA,EACV,WAAW;AAAA,EACX,eAAe;AAAA,EACf,cAAc;AAAA,EACd,SAAS;AAAA,EACT,SAAS;AACX;AAEA,SAAS,eAAe,OAA0D;AAChF,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,CAAC;AACxC,SAAO;AACT;AAEA,SAAS,kBAAkB,UAAsC;AAC/D,QAAM,QAAQ,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AAChD,QAAM,gBAAgB,MAAM,GAAG,EAAE;AACjC,MAAI,CAAC,iBAAiB,kBAAkB,kBAAkB,kBAAkB,SAAU,QAAO;AAC7F,SAAO,mBAAmB,aAAa;AACzC;AAEA,SAAS,sBAAsB,YAA4C;AACzE,SAAO,WAAW,IAAI,CAAC,UAAU;AAC/B,UAAM,SAAS;AAAA,MACb,IAAI,MAAM;AAAA,MACV,OAAO,MAAM;AAAA,MACb,aAAa,MAAM,cACjB,qBAAC,SAAI,WAAU,aACZ;AAAA,cAAM,WAAW,oBAAC,SAAK,gBAAM,UAAS,IAAS;AAAA,QAChD,oBAAC,qBAAkB,OAAO,MAAM,aAAa,aAAY,gBAAe;AAAA,SAC1E,IACE,MAAM;AAAA,MACV,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM;AAAA,IAClB;AAEA,QAAI,MAAM,SAAS,UAAU;AAC3B,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,QACN,WAAW,CAAC,EAAE,IAAI,OAAO,UAAU,SAAS,MAC1C;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,MAAK;AAAA,YACL,aAAa,MAAM;AAAA,YACnB,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,YAC3C,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,YAChD;AAAA;AAAA,QACF;AAAA,MAEJ;AAAA,IACF;AAEA,QAAI,MAAM,SAAS,YAAY,MAAM,SAAS;AAC5C,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,QACN,SAAS,MAAM;AAAA,MACjB;AAAA,IACF;AAEA,QAAI,MAAM,SAAS,WAAW;AAC5B,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,MACR;AAAA,IACF;AAEA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,MAAM;AAAA,IACR;AAAA,EACF,CAAC;AACH;AAEA,SAAS,YAAY,KAAwB;AAC3C,SAAO,IAAI,YAAY,yBAAyB,IAAI,QAAQ,WAAW,eAAe;AACxF;AAEA,SAAS,qBAAqB,SAA8E;AAC1G,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,MAAM,QAAQ,YAAY,QAAQ,aAAa,UAAU,UAAa,UAAU,IAAI;AAAA,EACjI;AACF;AAEA,SAAS,kBAAkB,OAAwB;AACjD,MAAI,OAAO,UAAU,UAAW,QAAO,QAAQ,QAAQ;AACvD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK;AAClD,MAAI,iBAAiB,KAAM,QAAO,MAAM,eAAe;AACvD,SAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAS,gBAAgB,OAAuB;AAC9C,SAAO,MAAM,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,SAAS,KAAK,CAAC,GAAG,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,GAAG;AACxG;AAEA,SAAS,oBAAoB,OAA2D;AACtF,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU;AACtG;AAEA,SAAS,qBAAqB,KAAqB;AACjD,SAAO,IACJ,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,UAAU,GAAG,EACrB,MAAM,GAAG,EACT,OAAO,OAAO,EACd,IAAI,CAAC,SAAS,KAAK,CAAC,GAAG,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EACpD,KAAK,GAAG;AACb;AAEA,SAAS,wBAAwB,OAAiD;AAChF,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,OAAO,UAAU,UAAW,QAAO,QAAQ,QAAQ;AACvD,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,oBAAoB,KAA4C;AACvE,SAAO,IAAI,GAAG,SAAS,aAAa,KAAK,IAAI,MAAM,YAAY,EAAE,SAAS,QAAQ;AACpF;AAEA,SAAS,gBAAgB,SAAqD;AAC5E,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,MACL,eAAe,CAAC;AAAA,MAChB,eAAe,CAAC;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,gBAAmE,CAAC;AAC1E,QAAM,gBAA0C,CAAC;AAEjD,SAAO,QAAQ,OAAO,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAChD,QAAI,oBAAoB,KAAK,GAAG;AAC9B,oBAAc,KAAK,CAAC,KAAK,KAAK,CAAC;AAC/B;AAAA,IACF;AACA,kBAAc,KAAK,CAAC,KAAK,KAAK,CAAC;AAAA,EACjC,CAAC;AAED,SAAO,EAAE,eAAe,cAAc;AACxC;AAEe,SAAR,sBAAuC,EAAE,OAAO,GAA+B;AACpF,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,gBAAgB;AACrC,QAAM,gBAAgB,eAAe,QAAQ,EAAE,KAAK,kBAAkB,QAAQ;AAC9E,QAAM,IAAI,KAAK;AAEf,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAmC,IAAI;AACzE,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAE5D,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAkC,CAAC,CAAC;AAC9E,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAS,CAAC;AACpE,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,MAAM,SAAS,KAAK;AAE1E,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAqB,CAAC,CAAC;AACrD,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAiB,EAAE;AACzD,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAC9D,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAwB,IAAI;AAE5E,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,KAAK;AACpE,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,KAAK;AAClE,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAqC,IAAI;AACnG,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA+B,aAAa;AAEpF,QAAM,oBAAoB,MAAM,MAAM;AAEtC,QAAM,8BAA8B,MAAM,YAAY,MAAM;AAC1D,WAAO,kBACL,OAAO,WAAW,cACd,kBAAkB,OAAO,SAAS,QAAQ,IAC1C;AAAA,EAER,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,aAAa,MAAM,YAAY,YAAY;AAC/C,UAAM,uBAAuB,4BAA4B;AACzD,QAAI,CAAC,sBAAsB;AACzB,mBAAa,KAAK;AAClB,eAAS,EAAE,iCAAiC,4BAA4B,CAAC;AACzE;AAAA,IACF;AACA,aAAS,IAAI;AACb,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,qBAAqB,mBAAmB,oBAAoB,CAAC;AAAA,QAC7D;AAAA,QACA,EAAE,UAAU,KAAK;AAAA,MACnB;AACA,UAAI,CAAC,KAAK,MAAM,CAAC,KAAK,QAAQ;AAC5B,iBAAS,EAAE,iCAAiC,4BAA4B,CAAC;AACzE,qBAAa,KAAK;AAClB;AAAA,MACF;AACA,gBAAU,KAAK,MAAM;AACrB,mBAAa,KAAK;AAAA,IACpB,QAAQ;AACN,eAAS,EAAE,iCAAiC,4BAA4B,CAAC;AACzE,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,6BAA6B,CAAC,CAAC;AAEnC,QAAM,kBAAkB,MAAM,YAAY,YAAY;AACpD,UAAM,uBAAuB,4BAA4B;AACzD,QAAI,CAAC,qBAAsB;AAC3B,UAAM,OAAO,MAAM;AAAA,MACjB,qBAAqB,mBAAmB,oBAAoB,CAAC;AAAA,MAC7D;AAAA,MACA,EAAE,UAAU,KAAK;AAAA,IACnB;AACA,QAAI,KAAK,MAAM,KAAK,QAAQ,aAAa;AACvC,oBAAc,KAAK,OAAO,WAAW;AACrC,4BAAsB,CAAC,YAAY,UAAU,CAAC;AAAA,IAChD;AAAA,EACF,GAAG,CAAC,2BAA2B,CAAC;AAEhC,QAAM,WAAW,MAAM,YAAY,YAAY;AAC7C,UAAM,uBAAuB,4BAA4B;AACzD,QAAI,CAAC,qBAAsB;AAC3B,qBAAiB,IAAI;AACrB,UAAMA,UAAS,IAAI,gBAAgB,EAAE,eAAe,sBAAsB,UAAU,KAAK,CAAC;AAC1F,QAAI,SAAU,CAAAA,QAAO,IAAI,SAAS,QAAQ;AAC1C,UAAM,OAAO,MAAM;AAAA,MACjB,0BAA0BA,QAAO,SAAS,CAAC;AAAA,MAC3C;AAAA,MACA,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,IAC5B;AACA,QAAI,KAAK,MAAM,KAAK,QAAQ;AAC1B,cAAQ,KAAK,OAAO,KAAK;AAAA,IAC3B;AACA,qBAAiB,KAAK;AAAA,EACxB,GAAG,CAAC,UAAU,2BAA2B,CAAC;AAE1C,QAAM,qBAAqB,MAAM;AAAA,IAC/B,MAAM,qCAAqC,QAAQ,eAAe,MAAM,sCAAsC;AAAA,IAC9G,CAAC,QAAQ,WAAW;AAAA,EACtB;AACA,QAAM,oBAAoB,MAAM;AAAA,IAC9B,MAAM,uBAAuB,iBAAiB,SAAS;AAAA,IACvD,CAAC,aAAa;AAAA,EAChB;AACA,QAAM,EAAE,aAAa,kBAAkB,IAAI,mBAA4C;AAAA,IACrF,WAAW;AAAA,IACX,QAAQ;AAAA,EACV,CAAC;AACD,QAAM,gBAAgB,MAAM,YAAY,YAAY;AAClD,UAAM,WAAW;AACjB,UAAM,gBAAgB;AAAA,EACxB,GAAG,CAAC,iBAAiB,UAAU,CAAC;AAChC,QAAM,cAAc,MAAM,YAAY,YAAY;AAChD,UAAM,SAAS;AAAA,EACjB,GAAG,CAAC,QAAQ,CAAC;AACb,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,+BAA+B;AAAA,MAC/B,cAAc;AAAA,MACd,YAAY,iBAAiB,QAAQ,YAAY;AAAA,MACjD,eAAe,iBAAiB,QAAQ,YAAY;AAAA,MACpD,aAAa,QAAQ,eAAe;AAAA,MACpC;AAAA,MACA,OAAO,QAAQ,SAAS;AAAA,MACxB,kBAAkB;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,EAAE,SAAS,cAAc,IAAI,oBAAoB,oBAAoB;AAAA,IACzE,SAAS;AAAA,IACT,eAAe;AAAA,EACjB,CAAC;AACD,QAAM,uBAAuB,MAAM;AAAA,IACjC,MAAM,qCAAqC,eAAe,OAAO;AAAA,IACjE,CAAC,aAAa;AAAA,EAChB;AACA,QAAM,uBAAuB,MAAM;AAAA,IACjC,MAAM,qCAAqC,eAAe,OAAO;AAAA,IACjE,CAAC,aAAa;AAAA,EAChB;AACA,QAAM,eAAe,MAAM;AAAA,IACzB,MAAM;AAAA,MACJ;AAAA,MACA,CAAC,WACC,OAAO,WAAW,aACd,EAAE,OAAO,UAAU,YAAY,OAAO,OAAO,SAAS,SAAS,OAAO,QAAQ,IAC7E,OAAO,OAAO,SAAS,SAAS,OAAO;AAAA,IAEhD;AAAA,IACA,CAAC,eAAe,CAAC;AAAA,EACnB;AACA,QAAM,yBAAyB;AAAA,IAC7B,QAAQ,YAAY,QAAQ,eACvB,QAAQ,YAAY,eACpB,OAAO,YAAY,YAAY,KAAK,EAAE,SAAS;AAAA,EACtD;AACA,QAAM,eAAe,MAAM;AAAA,IACzB,MAAM;AAAA,MACJ,GAAI,yBAAyB,CAAC,oBAAoB,IAAI,CAAC;AAAA,MACvD,GAAG,aAAa,IAAI,CAAC,QAAQ,IAAI,EAAE;AAAA,IACrC;AAAA,IACA,CAAC,wBAAwB,YAAY;AAAA,EACvC;AACA,QAAM,yBAAyB,MAAM;AAAA,IACnC,OAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB;AAAA,IAClB,MAMkB;AAChB,aAAO,YAAY;AAAA,QACjB;AAAA,QACA;AAAA,QACA,SAAS;AAAA,UACP,GAAG;AAAA,UACH,WAAW;AAAA,UACX;AAAA,UACA,WAAW,SAAS;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC,WAAW,kBAAkB,WAAW;AAAA,EAC3C;AAEA,QAAM,UAAU,MAAM;AAAE,SAAK,WAAW;AAAA,EAAE,GAAG,CAAC,UAAU,CAAC;AACzD,QAAM,UAAU,MAAM;AAAE,SAAK,gBAAgB;AAAA,EAAE,GAAG,CAAC,eAAe,CAAC;AACnE,QAAM,UAAU,MAAM;AAAE,SAAK,SAAS;AAAA,EAAE,GAAG,CAAC,QAAQ,CAAC;AACrD,QAAM,UAAU,MAAM;AACpB,qBAAiB,CAAC,YAAa,WAAW,KAAK,KAAK,CAAC,QAAQ,IAAI,OAAO,OAAO,IAAI,UAAU,IAAK;AAAA,EACpG,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,oBAAoB,MAAM,YAAY,OAAO,YAAqB;AACtE,UAAM,uBAAuB,4BAA4B;AACzD,QAAI,CAAC,qBAAsB;AAC3B,uBAAmB,IAAI;AACvB,QAAI;AACF,YAAM,OAAO,MAAM,uBAAuB;AAAA,QACxC,UAAU;AAAA,QACV,iBAAiB,EAAE,eAAe,sBAAsB,WAAW,QAAQ;AAAA,QAC3E,WAAW,MAAM,QAAQ,qBAAqB,mBAAmB,oBAAoB,CAAC,UAAU;AAAA,UAC9F,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,WAAW,QAAQ,CAAC;AAAA,QAC7C,GAAG,EAAE,UAAU,KAAK,CAAC;AAAA,MACvB,CAAC;AACD,UAAI,KAAK,IAAI;AACX,kBAAU,CAAC,SAAS,OAAO,EAAE,GAAG,MAAM,OAAO,EAAE,GAAG,KAAK,OAAO,WAAW,QAAQ,EAAE,IAAI,IAAI;AAC3F,cAAM,EAAE,kCAAkC,GAAG,SAAS;AAAA,MACxD,OAAO;AACL,cAAM,EAAE,gCAAgC,GAAG,OAAO;AAAA,MACpD;AAAA,IACF,QAAQ;AACN,YAAM,EAAE,gCAAgC,GAAG,OAAO;AAAA,IACpD,UAAE;AACA,yBAAmB,KAAK;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,6BAA6B,wBAAwB,CAAC,CAAC;AAE3D,QAAM,wBAAwB,MAAM,YAAY,OAAO,WAAoC;AACzF,UAAM,uBAAuB,4BAA4B;AACzD,QAAI,CAAC,qBAAsB;AAC3B,2BAAuB,IAAI;AAC3B,QAAI;AACF,YAAM,OAAO,MAAM,uBAAuB;AAAA,QACxC,UAAU;AAAA,QACV,OAAO;AAAA,QACP,iBAAiB,EAAE,eAAe,sBAAsB,aAAa,OAAO;AAAA,QAC5E,WAAW,MAAM,QAAQ,qBAAqB,mBAAmB,oBAAoB,CAAC,gBAAgB;AAAA,UACpG,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,aAAa,OAAO,CAAC;AAAA,QAC9C,GAAG,EAAE,UAAU,KAAK,CAAC;AAAA,MACvB,CAAC;AAED,UAAI,KAAK,IAAI;AACX,sBAAc,MAAM;AACpB,8BAAsB,CAAC,YAAY,UAAU,CAAC;AAC9C,cAAM,EAAE,uCAAuC,GAAG,SAAS;AAC3D;AAAA,MACF;AAEA,YAAM,SAAS,KAAK;AAIpB,YAAM;AAAA,QACJ,QAAQ,SAAS,EAAE,6CAA6C,4BAA4B;AAAA,QAC5F,QAAQ,SAAS;AAAA,QACjB,EAAE,SAAS,QAAQ,QAAQ;AAAA,MAC7B;AAAA,IACF,UAAE;AACA,6BAAuB,KAAK;AAAA,IAC9B;AAAA,EACF,GAAG,CAAC,6BAA6B,wBAAwB,CAAC,CAAC;AAE3D,QAAM,sBAAsB,MAAM,YAAY,OAAO,YAAoB;AACvE,UAAM,uBAAuB,4BAA4B;AACzD,QAAI,CAAC,qBAAsB;AAC3B,QAAI;AACF,YAAM,OAAO,MAAM,uBAAuB;AAAA,QACxC,UAAU;AAAA,QACV,OAAO;AAAA,QACP,iBAAiB,EAAE,eAAe,sBAAsB,YAAY,QAAQ;AAAA,QAC5E,WAAW,MAAM,QAAQ,qBAAqB,mBAAmB,oBAAoB,CAAC,YAAY;AAAA,UAChG,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,YAAY,QAAQ,CAAC;AAAA,QAC9C,GAAG,EAAE,UAAU,KAAK,CAAC;AAAA,MACvB,CAAC;AACD,UAAI,KAAK,IAAI;AACX,kBAAU,CAAC,SAAS,OAAO,EAAE,GAAG,MAAM,OAAO,EAAE,GAAG,KAAK,OAAO,YAAY,QAAQ,EAAE,IAAI,IAAI;AAC5F,cAAM,EAAE,mCAAmC,GAAG,SAAS;AAAA,MACzD,OAAO;AACL,cAAM,EAAE,uCAAuC,GAAG,OAAO;AAAA,MAC3D;AAAA,IACF,QAAQ;AACN,YAAM,EAAE,uCAAuC,GAAG,OAAO;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,6BAA6B,wBAAwB,CAAC,CAAC;AAE3D,QAAM,oBAAoB,MAAM,YAAY,YAAY;AACtD,UAAM,uBAAuB,4BAA4B;AACzD,QAAI,CAAC,qBAAsB;AAC3B,wBAAoB,IAAI;AACxB,QAAI;AACF,YAAM,OAAO,MAAM,uBAAuB;AAAA,QACxC,UAAU;AAAA,QACV,OAAO;AAAA,QACP,iBAAiB,EAAE,eAAe,qBAAqB;AAAA,QACvD,WAAW,MAAM;AAAA,UACf,qBAAqB,mBAAmB,oBAAoB,CAAC;AAAA,UAC7D,EAAE,QAAQ,OAAO;AAAA,UACjB,EAAE,UAAU,KAAK;AAAA,QACnB;AAAA,MACF,CAAC;AACD,YAAM,SAAS,KAAK;AACpB,UAAI,KAAK,MAAM,QAAQ;AACrB,8BAAsB,MAAM;AAC5B,kBAAU,CAAC,SAAS,OAAO;AAAA,UACzB,GAAG;AAAA,UACH,OAAO;AAAA,YACL,GAAG,KAAK;AAAA,YACR,kBAAkB,OAAO;AAAA,YACzB,qBAAqB,OAAO;AAAA,UAC9B;AAAA,QACF,IAAI,IAAI;AACR,aAAK,YAAY;AAAA,MACnB,OAAO;AACL,cAAM,EAAE,uCAAuC,GAAG,OAAO;AAAA,MAC3D;AAAA,IACF,QAAQ;AACN,YAAM,EAAE,uCAAuC,GAAG,OAAO;AAAA,IAC3D,UAAE;AACA,0BAAoB,KAAK;AAAA,IAC3B;AAAA,EACF,GAAG,CAAC,aAAa,6BAA6B,wBAAwB,CAAC,CAAC;AAExE,QAAM,cAAc,QAAQ,QAAQ,YAAY,aAAa,MAAM;AACnE,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,2BAA2B,MAAM;AAAA,IACrC,OAAO,QAAQ,YAAY,aAAa,UAAU,QAAQ,QAAQ,aAAa,UAAU,CAAC,GAAG,OAAO,yBAAyB;AAAA,IAC7H,CAAC,QAAQ,QAAQ,aAAa,QAAQ,QAAQ,YAAY,aAAa,MAAM;AAAA,EAC/E;AACA,QAAM,uBAAuB,MAAM;AAAA,IACjC,MAAM,sBAAsB,wBAAwB;AAAA,IACpD,CAAC,wBAAwB;AAAA,EAC3B;AACA,QAAM,mBAAmB,MAAM,QAAQ,MACrC,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY,EAAE,YAAY,CAAC,WAAW,QAAQ;AACzD,UAAM,SAAS;AAEf,6BAAyB,QAAQ,CAAC,UAAU;AAC1C,YAAM,QAAQ,OAAO,MAAM,GAAG;AAE9B,UAAI,MAAM,SAAS,WAAW;AAC5B,YAAI,UAAU,UAAa,UAAU,QAAQ,OAAO,UAAU,WAAW;AACvE,cAAI,SAAS;AAAA,YACX,MAAM,EAAE,aAAa;AAAA,YACrB,MAAM,CAAC,MAAM,GAAG;AAAA,YAChB,SAAS,EAAE,sDAAsD,uBAAuB;AAAA,UAC1F,CAAC;AAAA,QACH;AACA,YAAI,MAAM,YAAY,OAAO,UAAU,WAAW;AAChD,cAAI,SAAS;AAAA,YACX,MAAM,EAAE,aAAa;AAAA,YACrB,MAAM,CAAC,MAAM,GAAG;AAAA,YAChB,SAAS,EAAE,uDAAuD,wBAAwB,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,UAClH,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAEA,UAAI,UAAU,UAAa,UAAU,QAAQ,OAAO,UAAU,UAAU;AACtE,YAAI,SAAS;AAAA,UACX,MAAM,EAAE,aAAa;AAAA,UACrB,MAAM,CAAC,MAAM,GAAG;AAAA,UAChB,SAAS,EAAE,mDAAmD,sBAAsB;AAAA,QACtF,CAAC;AACD;AAAA,MACF;AAEA,YAAM,kBAAkB,OAAO,UAAU,WAAW,QAAQ;AAE5D,UAAI,MAAM,YAAY,gBAAgB,KAAK,EAAE,WAAW,GAAG;AACzD,YAAI,SAAS;AAAA,UACX,MAAM,EAAE,aAAa;AAAA,UACrB,MAAM,CAAC,MAAM,GAAG;AAAA,UAChB,SAAS,EAAE,uDAAuD,wBAAwB,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,QAClH,CAAC;AAAA,MACH;AAEA,UAAI,gBAAgB,SAAS,KAAQ;AACnC,YAAI,SAAS;AAAA,UACX,MAAM,EAAE,aAAa;AAAA,UACrB,MAAM,CAAC,MAAM,GAAG;AAAA,UAChB,SAAS,EAAE,sDAAsD,oBAAoB;AAAA,QACvF,CAAC;AAAA,MACH;AAEA,UACE,MAAM,SAAS,YACZ,mBACA,MAAM,WACN,CAAC,MAAM,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,eAAe,GACnE;AACA,YAAI,SAAS;AAAA,UACX,MAAM,EAAE,aAAa;AAAA,UACrB,MAAM,CAAC,MAAM,GAAG;AAAA,UAChB,SAAS,EAAE,qDAAqD,sCAAsC;AAAA,QACxG,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH,CAAC,GACsC,CAAC,0BAA0B,CAAC,CAAC;AACtE,QAAM,kBAAkB,MAAM,QAAQ,MAAM,KAAK,KAAK,WAAW,KAAK,MAAM,CAAC,IAAI,CAAC;AAClF,QAAM,gBACJ,oBAAoB,YACnB,OAAO,iBAAiB,SAAS,YAAY,WAAW,gBAAgB,QAAQ,UAAU;AAC7F,QAAM,sBAAsB,oBAAoB,WAAW,qBAAqB,iBAAiB,OAAO;AACxG,QAAM,gBAAgB,iBAAiB,OACnC,EAAE,GAAG,qBAAqB,MAAM,gBAAgB,KAAK,IACrD;AACJ,QAAM,sBAAsB,OAAO,QAAQ,aAAa;AACxD,QAAM,0BAA0B,OAAO,mBACnC;AAAA,IACA,sCAAsC,MAAM,gBAAgB;AAAA,IAC5D,MAAM,qBAAqB,YACvB,uEACA,MAAM,qBAAqB,aACzB,4EACA,aAAa,OAAO,mBAClB,gKACA;AAAA,EACV,IACE;AAEJ,QAAM,UAAU,MAAM;AACpB,iBAAa,qCAAqC,cAAc,IAAI,KAAK,GAAG,aAAa,YAAY,CAAC;AAAA,EACxG,GAAG,CAAC,cAAc,aAAa,YAAY,CAAC;AAE5C,QAAM,kBAAkB,MAAM,YAAY,CAAC,cAAsB;AAC/D,UAAM,uBAAuB,4BAA4B;AACzD,UAAM,UAAU,qCAAqC,WAAW,aAAa,YAAY;AACzF,iBAAa,OAAO;AACpB,QAAI,CAAC,qBAAsB;AAC3B,UAAM,WAAW,yBAAyB,mBAAmB,oBAAoB,CAAC;AAClF,WAAO,QAAQ,YAAY,gBAAgB,WAAW,GAAG,QAAQ,QAAQ,mBAAmB,OAAO,CAAC,EAAE;AAAA,EACxG,GAAG,CAAC,cAAc,aAAa,6BAA6B,MAAM,CAAC;AAEnE,MAAI,UAAW,QAAO,oBAAC,QAAK,8BAAC,YAAS,8BAAC,kBAAe,OAAO,EAAE,2BAA2B,GAAG,GAAE,GAAW;AAC1G,MAAI,SAAS,CAAC,OAAQ,QAAO,oBAAC,QAAK,8BAAC,YAAS,8BAAC,gBAAa,OAAO,SAAS,EAAE,+BAA+B,GAAG,GAAE,GAAW;AAE5H,QAAM,sBAAsB,OAAO;AACnC,QAAM,gBAAgB,OAAO;AAC7B,QAAM,eAAe,oBAAoB,WAAW,eAAe,oBAAoB,QAAQ,IAAI;AACnG,QAAM,mBAAmB,cAAc,mBAAmB,oBAAoB,cAAc,gBAAgB,IAAI;AAChH,QAAM,0BAA0B,oBAAoB,OAAO,gBACvD,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,MAAM,UAAU;AACxC,UAAM,eAAe,oBAAoB,IAAI,IAAI,IAAI;AACrD,UAAM,gBAAgB,oBAAoB,KAAK,IAAI,IAAI;AACvD,QAAI,iBAAiB,cAAe,QAAO,gBAAgB;AAC3D,WAAO;AAAA,EACT,CAAC,IACC;AACJ,QAAM,qBAAqB,oBAAoB,OAAO,gBAClD,wBAAwB,KAAK,mBAAmB,KAAK,OACrD;AACJ,QAAM,uBAAuB,qBACzB,wBAAwB,OAAO,CAAC,QAAQ,IAAI,OAAO,mBAAmB,EAAE,IACxE;AACJ,QAAM,YAAY,cAAc,YAAY,eAAe;AAC3D,QAAM,kBAAkB,cAAc,YAClC,6DACA;AAEJ,QAAM,wBAAwB,cAAc,iBAAiB,qBAAqB,SAAS;AAE3F,SACE,oBAAC,QACC,+BAAC,YAAS,WAAU,aAClB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,UAAS;AAAA,QACT,OAAO,oBAAoB;AAAA,QAC3B,SAAS;AAAA,UACP,YAAY,wBAAwB,0BAA0B;AAAA,UAC9D,QAAQ,wBACJ;AAAA,YACA,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,OAAO,EAAE,wCAAwC,kBAAkB;AAAA,YACnE,cAAc,EAAE,0BAA0B,WAAW;AAAA,UACvD,IACE;AAAA,QACN;AAAA;AAAA,IACF;AAAA,IAEA,qBAAC,SAAI,WAAU,aACZ;AAAA,0BAAoB,cACnB,oBAAC,OAAE,WAAU,iCAAiC,8BAAoB,aAAY,IAC5E;AAAA,MACJ,qBAAC,SAAI,WAAU,mEACZ;AAAA,4BAAoB,WACnB,qBAAC,SAAI,WAAU,2BACZ;AAAA,yBAAe,oBAAC,gBAAa,WAAU,WAAU,IAAK;AAAA,UACvD,oBAAC,UAAM,0BAAgB,oBAAoB,QAAQ,GAAE;AAAA,WACvD,IACE;AAAA,QACH,oBAAoB,MACnB,qBAAC,SAAI,WAAU,2BACb;AAAA,8BAAC,UAAK,WAAU,wEACb,YAAE,iCAAiC,KAAK,GAC3C;AAAA,UACA,oBAAC,UAAM,0BAAgB,oBAAoB,GAAG,GAAE;AAAA,WAClD,IACE;AAAA,SACN;AAAA,OACF;AAAA,IAEA,oBAAC,aAAQ,WAAU,iCACjB,+BAAC,SAAI,WAAU,2CACb;AAAA,2BAAC,SAAI,WAAU,aACb;AAAA,4BAAC,OAAE,WAAU,6DACV,YAAE,mCAAmC,OAAO,GAC/C;AAAA,QACA,qBAAC,SAAM,SAAQ,WAAU,WAAW,GAAG,sDAAsD,eAAe,GAC1G;AAAA,8BAAC,aAAU,WAAU,eAAc;AAAA,UAClC,cAAc,YACX,EAAE,qCAAqC,SAAS,IAChD,EAAE,sCAAsC,UAAU;AAAA,WACxD;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,cAAc;AAAA,UACvB,UAAU;AAAA,UACV,iBAAiB,CAAC,YAAY,KAAK,kBAAkB,OAAO;AAAA;AAAA,MAC9D;AAAA,OACF,GACF;AAAA,IAEC,qBAAqB,SAAS,IAC7B,oBAAC,aAAQ,WAAU,aACjB;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM;AAAA,QACN,cAAc,CAAC,SAAS,UAAU,IAAyB;AAAA,QAC3D,iBAAiB;AAAA;AAAA,IACnB,GACF,IACE;AAAA,IAEH,qBAAqB,SAAS,IAC7B,oBAAC,aAAQ,WAAU,6BAChB,+BAAqB,IAAI,CAAC,WACzB;AAAA,MAAC;AAAA;AAAA,QAEC,WAAW,OAAO,WAAW,WAAW,IAAI,mBAAmB;AAAA,QAE/D;AAAA,+BAAC,cACC;AAAA,gCAAC,aACE,iBAAO,WAAW,aACf,EAAE,OAAO,UAAU,YAAY,OAAO,OAAO,SAAS,KAAK,IAC3D,OAAO,OAAO,SAAS,OAC7B;AAAA,YACC,OAAO,WAAW,mBACjB,oBAAC,OAAE,WAAU,iCACV,iBAAO,UAAU,kBACpB,IACE;AAAA,aACN;AAAA,UACA,oBAAC,eACC;AAAA,YAAC,OAAO,OAAO;AAAA,YAAd;AAAA,cACC,SAAS;AAAA,cACT,MAAM;AAAA,cACN,cAAc,CAAC,SAAS,UAAU,IAAyB;AAAA;AAAA,UAC7D,GACF;AAAA;AAAA;AAAA,MArBK,OAAO;AAAA,IAsBd,CACD,GACH,IACE;AAAA,IAEJ,qBAAC,QAAK,OAAO,WAAW,eAAe,iBAAiB,WAAU,aAChE;AAAA,2BAAC,YAAS,WAAU,sGAClB;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,WAAU;AAAA,YAEV,+BAAC,UAAK,WAAU,kCACd;AAAA,kCAAC,OAAI,WAAU,WAAU;AAAA,cACzB,oBAAC,UAAM,YAAE,sCAAsC,GAAE;AAAA,eACnD;AAAA;AAAA,QACF;AAAA,QACC,qBACC;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,mBAAmB;AAAA,YAC1B,WAAU;AAAA,YAEV,+BAAC,UAAK,WAAU,kCACd;AAAA,kCAAC,YAAS,WAAU,WAAU;AAAA,cAC9B,oBAAC,UAAM,6BAAmB,OAAM;AAAA,eAClC;AAAA;AAAA,QACF,IACE;AAAA,QACH,cACC;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,WAAU;AAAA,YAEV,+BAAC,UAAK,WAAU,kCACd;AAAA,kCAAC,aAAU,WAAU,WAAU;AAAA,cAC/B,oBAAC,UAAM,YAAE,kCAAkC,GAAE;AAAA,eAC/C;AAAA;AAAA,QACF,IACE;AAAA,QACH,yBACC;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,WAAU;AAAA,YAEV,+BAAC,UAAK,WAAU,kCACd;AAAA,kCAAC,YAAS,WAAU,WAAU;AAAA,cAC9B,oBAAC,UAAM,YAAE,kCAAkC,gBAAgB,GAAE;AAAA,eAC/D;AAAA;AAAA,QACF,IACE;AAAA,QACJ;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,WAAU;AAAA,YAEV,+BAAC,UAAK,WAAU,kCACd;AAAA,kCAAC,YAAS,WAAU,WAAU;AAAA,cAC9B,oBAAC,UAAM,YAAE,iCAAiC,GAAE;AAAA,eAC9C;AAAA;AAAA,QACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,WAAU;AAAA,YAEV,+BAAC,UAAK,WAAU,kCACd;AAAA,kCAAC,YAAS,WAAU,WAAU;AAAA,cAC9B,oBAAC,UAAM,YAAE,+BAA+B,GAAE;AAAA,eAC5C;AAAA;AAAA,QACF;AAAA,QACC,qBAAqB,IAAI,CAAC,QACzB;AAAA,UAAC;AAAA;AAAA,YAEC,OAAO,IAAI;AAAA,YACX,WAAU;AAAA,YAEV,+BAAC,UAAK,WAAU,kCACd;AAAA,kCAAC,YAAS,WAAU,WAAU;AAAA,cAC9B,oBAAC,UAAM,cAAI,OAAM;AAAA,eACnB;AAAA;AAAA,UAPK,IAAI;AAAA,QAQX,CACD;AAAA,SACH;AAAA,MAEA,oBAAC,eAAY,OAAM,eAAc,WAAU,QACzC,+BAAC,aAAQ,WAAU,2CAChB;AAAA,eAAO,SACN,oBAAC,SAAI,WAAU,0EACZ,YAAE,gDAAgD,EAAE,QAAQ,OAAO,OAAO,MAAM,CAAC,GACpF,IACE;AAAA,QACH,qBAAqB,WAAW,IAC/B,oBAAC,OAAE,WAAU,iCACV,YAAE,+CAA+C,GACpD,IAEA;AAAA,UAAC;AAAA;AAAA,YAEC,QAAQ;AAAA,YACR,UAAS;AAAA,YACT,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,eAAe;AAAA,YACf,UAAU;AAAA,YACV,UAAQ;AAAA,YACR,mBAAiB;AAAA;AAAA,UARZ,GAAG,oBAAoB,EAAE,IAAI,kBAAkB;AAAA,QAStD;AAAA,SAEJ,GACF;AAAA,MAEC,cACC,oBAAC,eAAY,OAAM,WAAU,WAAU,kBACrC,+BAAC,QACC;AAAA,4BAAC,cACC,8BAAC,aAAW,YAAE,oCAAoC,GAAE,GACtD;AAAA,QACA,oBAAC,eAAY,WAAU,aACpB,8BAAoB,aAAa,IAAI,CAAC,YAAY;AACjD,gBAAM,gBAAgB,oBAAoB,aAAa,KAAK,CAAC,SAAS,KAAK,WAAW,QAAQ,GAAG;AACjG,gBAAM,cAAc,cAAc,cAAc,mBAAmB,QAAQ;AAC3E,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,WAAW,4FAA4F,aAAa,gCAAgC,mBAAmB;AAAA,cACvK,SAAS,MAAM,KAAK,oBAAoB,QAAQ,EAAE;AAAA,cAElD;AAAA,qCAAC,SACC;AAAA,sCAAC,UAAK,WAAU,uBAAuB,kBAAQ,SAAS,QAAQ,IAAG;AAAA,kBACnE;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAS,QAAQ,WAAW,WAAW,YAAY,QAAQ,WAAW,eAAe,gBAAgB;AAAA,sBACrG,WAAU;AAAA,sBAET,YAAE,+BAA+B,QAAQ,MAAM,EAAE;AAAA;AAAA,kBACpD;AAAA,kBACC,QAAQ,WAAW,gBAAgB,QAAQ,WAC1C,oBAAC,UAAK,WAAU,sCACb,YAAE,wCAAwC,EAAE,MAAM,IAAI,KAAK,QAAQ,QAAQ,EAAE,mBAAmB,EAAE,CAAC,GACtG,IACE;AAAA,mBACN;AAAA,gBACC,aAAa,oBAAC,SAAM,SAAQ,WAAW,YAAE,qCAAqC,GAAE,IAAW;AAAA;AAAA;AAAA,YAlBvF,QAAQ;AAAA,UAmBf;AAAA,QAEJ,CAAC,GACH;AAAA,SACF,GACF,IACE;AAAA,MAEH,yBACC,oBAAC,eAAY,OAAM,sBAAqB,WAAU,QAChD;AAAA,QAAC;AAAA;AAAA,UACC,eAAe,oBAAoB;AAAA,UACnC,gBAAgB,OAAO;AAAA,UACvB,WAAW,cAAc;AAAA;AAAA,MAC3B,GACF,IACE;AAAA,MAEJ,oBAAC,eAAY,OAAM,UAAS,WAAU,kBACpC,+BAAC,QAAK,WAAU,cACd;AAAA,4BAAC,cAAW,WAAU,QACpB,+BAAC,SAAI,WAAU,qCACb;AAAA,8BAAC,aAAW,YAAE,kCAAkC,GAAE;AAAA,UAClD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,SAAS,MAAM,KAAK,kBAAkB;AAAA,cACtC,UAAU;AAAA,cAET;AAAA,mCAAmB,oBAAC,WAAQ,WAAU,gBAAe,IAAK,oBAAC,OAAI,WAAU,gBAAe;AAAA,gBACxF,mBAAmB,EAAE,qCAAqC,IAAI,EAAE,kCAAkC;AAAA;AAAA;AAAA,UACrG;AAAA,WACF,GACF;AAAA,QACA,qBAAC,eAAY,WAAU,kBACrB;AAAA,+BAAC,SAAI,WAAU,6EACZ;AAAA,0BAAc,mBACb,qBAAC,SAAM,WAAW,WAAW,qBAAqB,cAAc,gBAAgB,KAAK,EAAE,IACpF;AAAA,iCAAmB,oBAAC,oBAAiB,WAAU,eAAc,IAAK;AAAA,cAClE,EAAE,8BAA8B,cAAc,gBAAgB,EAAE;AAAA,eACnE,IAEA,qBAAC,SAAI,WAAU,yDACb;AAAA,kCAAC,iBAAc,WAAU,WAAU;AAAA,cACnC,oBAAC,UAAM,YAAE,oCAAoC,GAAE;AAAA,eACjD;AAAA,YAED,0BACC,oBAAC,OAAE,WAAU,gDAAgD,mCAAwB,IACnF;AAAA,YACJ,oBAAC,OAAE,WAAU,4CACV,wBAAc,sBACX,EAAE,0CAA0C,EAAE,MAAM,IAAI,KAAK,cAAc,mBAAmB,EAAE,eAAe,EAAE,CAAC,IAClH,EAAE,yCAAyC,GAEjD;AAAA,aACF;AAAA,UACC,iBAAiB,oBAAoB,SAAS,IAC7C,qBAAC,SAAI,WAAW,cAAc,iBAAiB,oBAAoB,SAAS,IAAI,kDAAkD,EAAE,IACjI;AAAA,4BACC,qBAAC,SAAI,WAAU,+BACb;AAAA,kCAAC,OAAE,WAAU,6EACV,YAAE,yCAAyC,aAAa,GAC3D;AAAA,cACA,oBAAC,OAAE,WAAU,kBAAkB,yBAAc;AAAA,eAC/C,IACE;AAAA,YACH,oBAAoB,SAAS,IAC5B,qBAAC,SAAI,WAAU,+BACb;AAAA,kCAAC,OAAE,WAAU,6EACV,YAAE,sCAAsC,SAAS,GACpD;AAAA,cACA,oBAAC,QAAG,WAAU,4CACX,8BAAoB,IAAI,CAAC,CAAC,KAAK,KAAK,MACnC,qBAAC,SACC;AAAA,oCAAC,QAAG,WAAU,6CAA6C,+BAAqB,GAAG,GAAE;AAAA,gBACrF,oBAAC,QAAG,WAAU,kBAAkB,4BAAkB,KAAK,GAAE;AAAA,mBAFjD,GAGV,CACD,GACH;AAAA,eACF,IACE;AAAA,aACN,IACE;AAAA,WACN;AAAA,SACF,GACF;AAAA,MAEA,qBAAC,eAAY,OAAM,QAAO,WAAU,kBAClC;AAAA,4BAAC,SAAI,WAAU,2BACb,+BAAC,SAAI,WAAU,wBACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,YAAY,MAAM,OAAO,KAAK;AAAA,cAEnD;AAAA,oCAAC,YAAO,OAAM,IAAI,YAAE,oCAAoC,GAAE;AAAA,gBAC1D,oBAAC,YAAO,OAAM,QAAQ,YAAE,qCAAqC,GAAE;AAAA,gBAC/D,oBAAC,YAAO,OAAM,QAAQ,YAAE,qCAAqC,GAAE;AAAA,gBAC/D,oBAAC,YAAO,OAAM,SAAS,YAAE,sCAAsC,GAAE;AAAA;AAAA;AAAA,UACnE;AAAA,UACA,oBAAC,eAAY,WAAU,+FAA8F;AAAA,WACvH,GACF;AAAA,QACC,gBACC,oBAAC,SAAI,WAAU,4BAA2B,8BAAC,WAAQ,GAAE,IACnD,KAAK,WAAW,IAClB,oBAAC,OAAE,WAAU,sCAAsC,YAAE,gCAAgC,GAAE,IAEvF,oBAAC,SAAI,WAAU,qBACb,+BAAC,WAAM,WAAU,kBACf;AAAA,8BAAC,WACC,+BAAC,QAAG,WAAU,wBACZ;AAAA,gCAAC,QAAG,WAAU,mCAAmC,YAAE,uCAAuC,GAAE;AAAA,YAC5F,oBAAC,QAAG,WAAU,mCAAmC,YAAE,wCAAwC,GAAE;AAAA,YAC7F,oBAAC,QAAG,WAAU,mCAAmC,YAAE,0CAA0C,GAAE;AAAA,aACjG,GACF;AAAA,UACA,oBAAC,WACE,eAAK,IAAI,CAAC,QAAQ;AACjB,kBAAM,aAAa,kBAAkB,IAAI;AACzC,kBAAM,kBAAkB;AAAA,cACtB,CAAC,QAAQ,IAAI,KAAK,IAAI,SAAS,EAAE,eAAe,CAAC;AAAA,cACjD,CAAC,SAAS,IAAI,KAAK;AAAA,cACnB,CAAC,QAAQ,IAAI,QAAQ,IAAI;AAAA,cACzB,CAAC,UAAU,IAAI,SAAS,IAAI;AAAA,cAC5B,CAAC,eAAe,IAAI,mBAAmB,IAAI;AAAA,cAC3C,CAAC,aAAa,IAAI,iBAAiB,IAAI;AAAA,YACzC,EAAE,OAAO,CAAC,UAAqC,OAAO,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC;AACzG,kBAAM,EAAE,eAAe,cAAc,IAAI,gBAAgB,IAAI,OAAO;AAEpE,mBACE,qBAAC,MAAM,UAAN,EACC;AAAA,mCAAC,QAAG,WAAU,0BACZ;AAAA,oCAAC,QAAG,WAAU,qDACX,cAAI,KAAK,IAAI,SAAS,EAAE,eAAe,GAC1C;AAAA,gBACA,oBAAC,QAAG,WAAU,aACZ,8BAAC,SAAM,SAAQ,aAAY,WAAW,iBAAiB,IAAI,KAAK,KAAK,IAClE,cAAI,OACP,GACF;AAAA,gBACA,oBAAC,QAAG,WAAU,aACZ;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAQ;AAAA,oBACR,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,SAAS,MAAM,iBAAiB,CAAC,YAAa,YAAY,IAAI,KAAK,OAAO,IAAI,EAAG;AAAA,oBAEhF;AAAA,mCAAa,oBAAC,eAAY,WAAU,oBAAmB,IAAK,oBAAC,gBAAa,WAAU,oBAAmB;AAAA,sBACxG,oBAAC,UAAK,WAAU,YAAY,cAAI,SAAQ;AAAA;AAAA;AAAA,gBAC1C,GACF;AAAA,iBACF;AAAA,cACC,aACC,oBAAC,QAAG,WAAU,sCACZ,8BAAC,QAAG,SAAS,GAAG,WAAU,aACxB,8BAAC,SAAI,WAAU,2CACb,+BAAC,SAAI,WAAU,6DACb;AAAA,qCAAC,SAAI,WAAU,aACb;AAAA,uCAAC,aAAQ,WAAU,aACjB;AAAA,yCAAC,SACC;AAAA,0CAAC,OAAE,WAAU,qEACV,YAAE,4CAA4C,SAAS,GAC1D;AAAA,sBACA,oBAAC,OAAE,WAAU,4BAA4B,cAAI,SAAQ;AAAA,uBACvD;AAAA,oBACC,gBAAgB,SAAS,IACxB,oBAAC,QAAG,WAAU,6BACX,0BAAgB,IAAI,CAAC,CAAC,OAAO,KAAK,MACjC,qBAAC,SAAgB,WAAU,2CACzB;AAAA,0CAAC,QAAG,WAAU,yEACX,iBACH;AAAA,sBACA,oBAAC,QAAG,WAAU,0BAA0B,iBAAM;AAAA,yBAJtC,KAKV,CACD,GACH,IACE;AAAA,qBACN;AAAA,kBAEC,cAAc,SAAS,IACtB,qBAAC,aAAQ,WAAU,aACjB;AAAA,wCAAC,OAAE,WAAU,qEACV,YAAE,2CAA2C,QAAQ,GACxD;AAAA,oBACA,oBAAC,QAAG,WAAU,6BACX,wBAAc,IAAI,CAAC,CAAC,KAAK,KAAK,MAC7B,qBAAC,SAAc,WAAU,2CACvB;AAAA,0CAAC,QAAG,WAAU,yEACX,+BAAqB,GAAG,GAC3B;AAAA,sBACA,oBAAC,QAAG,WAAU,4BACX,kCAAwB,KAAK,GAChC;AAAA,yBANQ,GAOV,CACD,GACH;AAAA,qBACF,IACE;AAAA,mBACN;AAAA,gBAEA,qBAAC,SAAI,WAAU,aACZ;AAAA,gCAAc,IAAI,CAAC,CAAC,KAAK,KAAK,MAC7B;AAAA,oBAAC;AAAA;AAAA,sBAEC,MAAM;AAAA,sBACN,OAAO,qBAAqB,GAAG;AAAA,sBAC/B,iBAAe;AAAA,sBACf,iBAAiB;AAAA,sBACjB,OAAM;AAAA,sBACN,WAAU;AAAA,sBACV,WAAU;AAAA;AAAA,oBAPL;AAAA,kBAQP,CACD;AAAA,kBACA,IAAI,WAAW,cAAc,WAAW,IACvC;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAM,IAAI;AAAA,sBACV,OAAO,EAAE,4CAA4C,SAAS;AAAA,sBAC9D,iBAAe;AAAA,sBACf,iBAAiB;AAAA,sBACjB,OAAM;AAAA,sBACN,WAAU;AAAA,sBACV,WAAU;AAAA;AAAA,kBACZ,IACE;AAAA,kBACH,CAAC,IAAI,UACJ,oBAAC,SAAI,WAAU,2EACZ,YAAE,8CAA8C,sDAAsD,GACzG,IACE;AAAA,mBACN;AAAA,iBACF,GACF,GACF,GACF,IACE;AAAA,iBAzGe,IAAI,EA0GzB;AAAA,UAEJ,CAAC,GACH;AAAA,WACF,GACF;AAAA,SAEJ;AAAA,MAEC,aAAa,IAAI,CAAC,QACjB,oBAAC,eAAyB,OAAO,IAAI,IAAI,WAAU,kBACjD;AAAA,QAAC;AAAA;AAAA,UACC,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,MAAM;AAAA,UACN,cAAc,CAAC,SAAS,UAAU,IAAyB;AAAA,UAC3D,iBAAiB,IAAI;AAAA;AAAA,MACvB,KAPgB,IAAI,EAQtB,CACD;AAAA,OACH;AAAA,KACF,GACF;AAEJ;",
|
|
6
6
|
"names": ["params"]
|
|
7
7
|
}
|
|
@@ -16,6 +16,20 @@ function createIntegrationStateService(em) {
|
|
|
16
16
|
scope
|
|
17
17
|
);
|
|
18
18
|
},
|
|
19
|
+
async resolveState(integrationId, scope) {
|
|
20
|
+
const state = await this.get(integrationId, scope);
|
|
21
|
+
return {
|
|
22
|
+
isEnabled: state?.isEnabled ?? false,
|
|
23
|
+
apiVersion: state?.apiVersion ?? null,
|
|
24
|
+
reauthRequired: state?.reauthRequired ?? false,
|
|
25
|
+
lastHealthStatus: state?.lastHealthStatus ?? null,
|
|
26
|
+
lastHealthCheckedAt: state?.lastHealthCheckedAt ?? null
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
async isEnabled(integrationId, scope) {
|
|
30
|
+
const state = await this.resolveState(integrationId, scope);
|
|
31
|
+
return state.isEnabled;
|
|
32
|
+
},
|
|
19
33
|
async upsert(integrationId, input, scope) {
|
|
20
34
|
const current = await this.get(integrationId, scope);
|
|
21
35
|
if (current) {
|
|
@@ -29,7 +43,7 @@ function createIntegrationStateService(em) {
|
|
|
29
43
|
}
|
|
30
44
|
const created = em.create(IntegrationState, {
|
|
31
45
|
integrationId,
|
|
32
|
-
isEnabled: input.isEnabled ??
|
|
46
|
+
isEnabled: input.isEnabled ?? false,
|
|
33
47
|
apiVersion: input.apiVersion,
|
|
34
48
|
reauthRequired: input.reauthRequired ?? false,
|
|
35
49
|
lastHealthStatus: input.lastHealthStatus,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/integrations/lib/state-service.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { IntegrationScope } from '@open-mercato/shared/modules/integrations/types'\nimport { IntegrationState } from '../data/entities'\n\nexport function createIntegrationStateService(em: EntityManager) {\n return {\n async get(integrationId: string, scope: IntegrationScope): Promise<IntegrationState | null> {\n return findOneWithDecryption(\n em,\n IntegrationState,\n {\n integrationId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async upsert(\n integrationId: string,\n input: Partial<Pick<IntegrationState, 'isEnabled' | 'apiVersion' | 'reauthRequired' | 'lastHealthStatus' | 'lastHealthCheckedAt'>>,\n scope: IntegrationScope,\n ): Promise<IntegrationState> {\n const current = await this.get(integrationId, scope)\n if (current) {\n if (input.isEnabled !== undefined) current.isEnabled = input.isEnabled\n if (input.apiVersion !== undefined) current.apiVersion = input.apiVersion\n if (input.reauthRequired !== undefined) current.reauthRequired = input.reauthRequired\n if (input.lastHealthStatus !== undefined) current.lastHealthStatus = input.lastHealthStatus\n if (input.lastHealthCheckedAt !== undefined) current.lastHealthCheckedAt = input.lastHealthCheckedAt\n await em.flush()\n return current\n }\n\n const created = em.create(IntegrationState, {\n integrationId,\n isEnabled: input.isEnabled ??
|
|
5
|
-
"mappings": "AACA,SAAS,6BAA6B;AAEtC,SAAS,wBAAwB;
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { IntegrationScope } from '@open-mercato/shared/modules/integrations/types'\nimport { IntegrationState } from '../data/entities'\n\nexport type ResolvedIntegrationState = {\n isEnabled: boolean\n apiVersion: string | null\n reauthRequired: boolean\n lastHealthStatus: string | null\n lastHealthCheckedAt: Date | null\n}\n\nexport function createIntegrationStateService(em: EntityManager) {\n return {\n async get(integrationId: string, scope: IntegrationScope): Promise<IntegrationState | null> {\n return findOneWithDecryption(\n em,\n IntegrationState,\n {\n integrationId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async resolveState(integrationId: string, scope: IntegrationScope): Promise<ResolvedIntegrationState> {\n const state = await this.get(integrationId, scope)\n return {\n isEnabled: state?.isEnabled ?? false,\n apiVersion: state?.apiVersion ?? null,\n reauthRequired: state?.reauthRequired ?? false,\n lastHealthStatus: state?.lastHealthStatus ?? null,\n lastHealthCheckedAt: state?.lastHealthCheckedAt ?? null,\n }\n },\n\n async isEnabled(integrationId: string, scope: IntegrationScope): Promise<boolean> {\n const state = await this.resolveState(integrationId, scope)\n return state.isEnabled\n },\n\n async upsert(\n integrationId: string,\n input: Partial<Pick<IntegrationState, 'isEnabled' | 'apiVersion' | 'reauthRequired' | 'lastHealthStatus' | 'lastHealthCheckedAt'>>,\n scope: IntegrationScope,\n ): Promise<IntegrationState> {\n const current = await this.get(integrationId, scope)\n if (current) {\n if (input.isEnabled !== undefined) current.isEnabled = input.isEnabled\n if (input.apiVersion !== undefined) current.apiVersion = input.apiVersion\n if (input.reauthRequired !== undefined) current.reauthRequired = input.reauthRequired\n if (input.lastHealthStatus !== undefined) current.lastHealthStatus = input.lastHealthStatus\n if (input.lastHealthCheckedAt !== undefined) current.lastHealthCheckedAt = input.lastHealthCheckedAt\n await em.flush()\n return current\n }\n\n const created = em.create(IntegrationState, {\n integrationId,\n isEnabled: input.isEnabled ?? false,\n apiVersion: input.apiVersion,\n reauthRequired: input.reauthRequired ?? false,\n lastHealthStatus: input.lastHealthStatus,\n lastHealthCheckedAt: input.lastHealthCheckedAt,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n await em.persistAndFlush(created)\n return created\n },\n\n async resolveApiVersion(integrationId: string, scope: IntegrationScope): Promise<string | undefined> {\n const state = await this.get(integrationId, scope)\n return state?.apiVersion ?? undefined\n },\n\n async setReauthRequired(integrationId: string, required: boolean, scope: IntegrationScope): Promise<IntegrationState> {\n return this.upsert(integrationId, { reauthRequired: required }, scope)\n },\n }\n}\n\nexport type IntegrationStateService = ReturnType<typeof createIntegrationStateService>\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,6BAA6B;AAEtC,SAAS,wBAAwB;AAU1B,SAAS,8BAA8B,IAAmB;AAC/D,SAAO;AAAA,IACL,MAAM,IAAI,eAAuB,OAA2D;AAC1F,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,UACE;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,aAAa,eAAuB,OAA4D;AACpG,YAAM,QAAQ,MAAM,KAAK,IAAI,eAAe,KAAK;AACjD,aAAO;AAAA,QACL,WAAW,OAAO,aAAa;AAAA,QAC/B,YAAY,OAAO,cAAc;AAAA,QACjC,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,kBAAkB,OAAO,oBAAoB;AAAA,QAC7C,qBAAqB,OAAO,uBAAuB;AAAA,MACrD;AAAA,IACF;AAAA,IAEA,MAAM,UAAU,eAAuB,OAA2C;AAChF,YAAM,QAAQ,MAAM,KAAK,aAAa,eAAe,KAAK;AAC1D,aAAO,MAAM;AAAA,IACf;AAAA,IAEA,MAAM,OACJ,eACA,OACA,OAC2B;AAC3B,YAAM,UAAU,MAAM,KAAK,IAAI,eAAe,KAAK;AACnD,UAAI,SAAS;AACX,YAAI,MAAM,cAAc,OAAW,SAAQ,YAAY,MAAM;AAC7D,YAAI,MAAM,eAAe,OAAW,SAAQ,aAAa,MAAM;AAC/D,YAAI,MAAM,mBAAmB,OAAW,SAAQ,iBAAiB,MAAM;AACvE,YAAI,MAAM,qBAAqB,OAAW,SAAQ,mBAAmB,MAAM;AAC3E,YAAI,MAAM,wBAAwB,OAAW,SAAQ,sBAAsB,MAAM;AACjF,cAAM,GAAG,MAAM;AACf,eAAO;AAAA,MACT;AAEA,YAAM,UAAU,GAAG,OAAO,kBAAkB;AAAA,QAC1C;AAAA,QACA,WAAW,MAAM,aAAa;AAAA,QAC9B,YAAY,MAAM;AAAA,QAClB,gBAAgB,MAAM,kBAAkB;AAAA,QACxC,kBAAkB,MAAM;AAAA,QACxB,qBAAqB,MAAM;AAAA,QAC3B,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,MAClB,CAAC;AACD,YAAM,GAAG,gBAAgB,OAAO;AAChC,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,kBAAkB,eAAuB,OAAsD;AACnG,YAAM,QAAQ,MAAM,KAAK,IAAI,eAAe,KAAK;AACjD,aAAO,OAAO,cAAc;AAAA,IAC9B;AAAA,IAEA,MAAM,kBAAkB,eAAuB,UAAmB,OAAoD;AACpH,aAAO,KAAK,OAAO,eAAe,EAAE,gBAAgB,SAAS,GAAG,KAAK;AAAA,IACvE;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -45,25 +45,8 @@ async function GET(req, { params }) {
|
|
|
45
45
|
if (!isSender && !isRecipient) {
|
|
46
46
|
return Response.json({ error: "Access denied" }, { status: 403 });
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
await commandBus.execute("messages.recipients.mark_read", {
|
|
51
|
-
input: {
|
|
52
|
-
messageId: params.id,
|
|
53
|
-
tenantId: scope.tenantId,
|
|
54
|
-
organizationId: scope.organizationId,
|
|
55
|
-
userId: scope.userId
|
|
56
|
-
},
|
|
57
|
-
ctx: {
|
|
58
|
-
container: ctx.container,
|
|
59
|
-
auth: ctx.auth ?? null,
|
|
60
|
-
organizationScope: null,
|
|
61
|
-
selectedOrganizationId: scope.organizationId,
|
|
62
|
-
organizationIds: scope.organizationId ? [scope.organizationId] : null,
|
|
63
|
-
request: req
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
48
|
+
const autoMarkRead = !skipMarkRead && recipient?.status === "unread";
|
|
49
|
+
const readAt = autoMarkRead ? /* @__PURE__ */ new Date() : recipient?.readAt ?? null;
|
|
67
50
|
const objects = await em.find(MessageObject, { messageId: params.id });
|
|
68
51
|
const objectPreviews = await Promise.all(
|
|
69
52
|
objects.map(async (item) => {
|
|
@@ -123,6 +106,25 @@ async function GET(req, { params }) {
|
|
|
123
106
|
const senderEmail = senderUser?.email ?? null;
|
|
124
107
|
const messageType = getMessageTypeOrDefault(message.type);
|
|
125
108
|
const resolvedActionData = buildResolvedMessageActions(message, objects);
|
|
109
|
+
if (autoMarkRead) {
|
|
110
|
+
const commandBus = ctx.container.resolve("commandBus");
|
|
111
|
+
await commandBus.execute("messages.recipients.mark_read", {
|
|
112
|
+
input: {
|
|
113
|
+
messageId: params.id,
|
|
114
|
+
tenantId: scope.tenantId,
|
|
115
|
+
organizationId: scope.organizationId,
|
|
116
|
+
userId: scope.userId
|
|
117
|
+
},
|
|
118
|
+
ctx: {
|
|
119
|
+
container: ctx.container,
|
|
120
|
+
auth: ctx.auth ?? null,
|
|
121
|
+
organizationScope: null,
|
|
122
|
+
selectedOrganizationId: scope.organizationId,
|
|
123
|
+
organizationIds: scope.organizationId ? [scope.organizationId] : null,
|
|
124
|
+
request: req
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
126
128
|
return Response.json({
|
|
127
129
|
id: message.id,
|
|
128
130
|
type: message.type,
|
|
@@ -162,8 +164,8 @@ async function GET(req, { params }) {
|
|
|
162
164
|
recipients: allRecipients.map((item) => ({
|
|
163
165
|
userId: item.recipientUserId,
|
|
164
166
|
type: item.recipientType,
|
|
165
|
-
status: item.status,
|
|
166
|
-
readAt: item.readAt
|
|
167
|
+
status: autoMarkRead && item.recipientUserId === scope.userId ? "read" : item.status,
|
|
168
|
+
readAt: autoMarkRead && item.recipientUserId === scope.userId ? readAt : item.readAt
|
|
167
169
|
})),
|
|
168
170
|
objects: objects.map((item, index) => ({
|
|
169
171
|
id: item.id,
|
|
@@ -189,7 +191,7 @@ async function GET(req, { params }) {
|
|
|
189
191
|
sentAt: threadMessage.sentAt
|
|
190
192
|
};
|
|
191
193
|
}),
|
|
192
|
-
isRead: recipient ? recipient.status !== "unread" : true
|
|
194
|
+
isRead: recipient ? autoMarkRead || recipient.status !== "unread" : true
|
|
193
195
|
});
|
|
194
196
|
}
|
|
195
197
|
async function PATCH(req, { params }) {
|