@open-mercato/core 0.4.6-develop-a88276bc52 → 0.4.6-develop-806a2ed6b9
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 +10 -0
- package/dist/generated/entities/integration_credentials/index.js +19 -0
- package/dist/generated/entities/integration_credentials/index.js.map +7 -0
- package/dist/generated/entities/integration_log/index.js +27 -0
- package/dist/generated/entities/integration_log/index.js.map +7 -0
- package/dist/generated/entities/integration_state/index.js +27 -0
- package/dist/generated/entities/integration_state/index.js.map +7 -0
- package/dist/generated/entities/sync_cursor/index.js +19 -0
- package/dist/generated/entities/sync_cursor/index.js.map +7 -0
- package/dist/generated/entities/sync_external_id_mapping/index.js +27 -0
- package/dist/generated/entities/sync_external_id_mapping/index.js.map +7 -0
- package/dist/generated/entities/sync_mapping/index.js +19 -0
- package/dist/generated/entities/sync_mapping/index.js.map +7 -0
- package/dist/generated/entities/sync_run/index.js +45 -0
- package/dist/generated/entities/sync_run/index.js.map +7 -0
- package/dist/generated/entities/sync_schedule/index.js +35 -0
- package/dist/generated/entities/sync_schedule/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +14 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +16 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/data_sync/acl.js +11 -0
- package/dist/modules/data_sync/acl.js.map +7 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js +137 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/mappings/route.js +132 -0
- package/dist/modules/data_sync/api/mappings/route.js.map +7 -0
- package/dist/modules/data_sync/api/run.js +87 -0
- package/dist/modules/data_sync/api/run.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +49 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js +93 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +7 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js +69 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/runs.js +66 -0
- package/dist/modules/data_sync/api/runs.js.map +7 -0
- package/dist/modules/data_sync/api/validate.js +66 -0
- package/dist/modules/data_sync/api/validate.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.js +216 -0
- package/dist/modules/data_sync/backend/data-sync/page.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.meta.js +25 -0
- package/dist/modules/data_sync/backend/data-sync/page.meta.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +178 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js +14 -0
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.meta.js.map +7 -0
- package/dist/modules/data_sync/data/entities.js +228 -0
- package/dist/modules/data_sync/data/entities.js.map +7 -0
- package/dist/modules/data_sync/data/validators.js +32 -0
- package/dist/modules/data_sync/data/validators.js.map +7 -0
- package/dist/modules/data_sync/di.js +26 -0
- package/dist/modules/data_sync/di.js.map +7 -0
- package/dist/modules/data_sync/events.js +16 -0
- package/dist/modules/data_sync/events.js.map +7 -0
- package/dist/modules/data_sync/index.js +9 -0
- package/dist/modules/data_sync/index.js.map +7 -0
- package/dist/modules/data_sync/lib/adapter-registry.js +16 -0
- package/dist/modules/data_sync/lib/adapter-registry.js.map +7 -0
- package/dist/modules/data_sync/lib/adapter.js +1 -0
- package/dist/modules/data_sync/lib/adapter.js.map +7 -0
- package/dist/modules/data_sync/lib/id-mapping.js +79 -0
- package/dist/modules/data_sync/lib/id-mapping.js.map +7 -0
- package/dist/modules/data_sync/lib/queue.js +17 -0
- package/dist/modules/data_sync/lib/queue.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +309 -0
- package/dist/modules/data_sync/lib/sync-engine.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-run-service.js +148 -0
- package/dist/modules/data_sync/lib/sync-run-service.js.map +7 -0
- package/dist/modules/data_sync/migrations/Migration20260304113737.js +17 -0
- package/dist/modules/data_sync/migrations/Migration20260304113737.js.map +7 -0
- package/dist/modules/data_sync/setup.js +13 -0
- package/dist/modules/data_sync/setup.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-export.js +14 -0
- package/dist/modules/data_sync/workers/sync-export.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-import.js +14 -0
- package/dist/modules/data_sync/workers/sync-import.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js +63 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +7 -0
- package/dist/modules/entities/lib/encryptionDefaults.js +4 -0
- package/dist/modules/entities/lib/encryptionDefaults.js.map +2 -2
- package/dist/modules/integrations/acl.js +4 -1
- package/dist/modules/integrations/acl.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +127 -0
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/health/route.js +46 -0
- package/dist/modules/integrations/api/[id]/health/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/route.js +65 -0
- package/dist/modules/integrations/api/[id]/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/state/route.js +109 -0
- package/dist/modules/integrations/api/[id]/state/route.js.map +7 -0
- package/dist/modules/integrations/api/[id]/version/route.js +117 -0
- package/dist/modules/integrations/api/[id]/version/route.js.map +7 -0
- package/dist/modules/integrations/api/guards.js +31 -0
- package/dist/modules/integrations/api/guards.js.map +7 -0
- package/dist/modules/integrations/api/logs/route.js +60 -0
- package/dist/modules/integrations/api/logs/route.js.map +7 -0
- package/dist/modules/integrations/api/openapi.js +25 -0
- package/dist/modules/integrations/api/openapi.js.map +7 -0
- package/dist/modules/integrations/api/route.js +68 -0
- package/dist/modules/integrations/api/route.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js +313 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.meta.js +15 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.meta.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +189 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js +15 -0
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.meta.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.js +212 -0
- package/dist/modules/integrations/backend/integrations/page.js.map +7 -0
- package/dist/modules/integrations/backend/integrations/page.meta.js +22 -0
- package/dist/modules/integrations/backend/integrations/page.meta.js.map +7 -0
- package/dist/modules/integrations/data/enrichers.js +27 -12
- package/dist/modules/integrations/data/enrichers.js.map +2 -2
- package/dist/modules/integrations/data/entities.js +136 -1
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/data/validators.js +36 -0
- package/dist/modules/integrations/data/validators.js.map +7 -0
- package/dist/modules/integrations/di.js +24 -0
- package/dist/modules/integrations/di.js.map +7 -0
- package/dist/modules/integrations/events.js +19 -0
- package/dist/modules/integrations/events.js.map +7 -0
- package/dist/modules/integrations/lib/credentials-service.js +159 -0
- package/dist/modules/integrations/lib/credentials-service.js.map +7 -0
- package/dist/modules/integrations/lib/health-service.js +37 -0
- package/dist/modules/integrations/lib/health-service.js.map +7 -0
- package/dist/modules/integrations/lib/log-service.js +66 -0
- package/dist/modules/integrations/lib/log-service.js.map +7 -0
- package/dist/modules/integrations/lib/registry-service.js +33 -0
- package/dist/modules/integrations/lib/registry-service.js.map +7 -0
- package/dist/modules/integrations/lib/state-service.js +55 -0
- package/dist/modules/integrations/lib/state-service.js.map +7 -0
- package/dist/modules/integrations/lib/types.js +1 -0
- package/dist/modules/integrations/lib/types.js.map +7 -0
- package/dist/modules/integrations/migrations/Migration20260304113737.js +19 -0
- package/dist/modules/integrations/migrations/Migration20260304113737.js.map +7 -0
- package/dist/modules/integrations/setup.js +2 -2
- package/dist/modules/integrations/setup.js.map +2 -2
- package/dist/modules/integrations/widgets/injection-table.js.map +1 -1
- package/dist/modules/integrations/workers/log-pruner.js +18 -0
- package/dist/modules/integrations/workers/log-pruner.js.map +7 -0
- package/generated/entities/integration_credentials/index.ts +8 -0
- package/generated/entities/integration_log/index.ts +12 -0
- package/generated/entities/integration_state/index.ts +12 -0
- package/generated/entities/sync_cursor/index.ts +8 -0
- package/generated/entities/sync_external_id_mapping/index.ts +12 -0
- package/generated/entities/sync_mapping/index.ts +8 -0
- package/generated/entities/sync_run/index.ts +21 -0
- package/generated/entities/sync_schedule/index.ts +16 -0
- package/generated/entities.ids.generated.ts +14 -0
- package/generated/entity-fields-registry.ts +16 -0
- package/package.json +2 -2
- package/src/modules/data_sync/AGENTS.md +157 -0
- package/src/modules/data_sync/acl.ts +7 -0
- package/src/modules/data_sync/api/mappings/[id]/route.ts +158 -0
- package/src/modules/data_sync/api/mappings/route.ts +144 -0
- package/src/modules/data_sync/api/run.ts +97 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +57 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +108 -0
- package/src/modules/data_sync/api/runs/[id]/route.ts +81 -0
- package/src/modules/data_sync/api/runs.ts +69 -0
- package/src/modules/data_sync/api/validate.ts +73 -0
- package/src/modules/data_sync/backend/data-sync/page.meta.ts +21 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +244 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.meta.ts +10 -0
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +278 -0
- package/src/modules/data_sync/data/entities.ts +180 -0
- package/src/modules/data_sync/data/validators.ts +35 -0
- package/src/modules/data_sync/di.ts +38 -0
- package/src/modules/data_sync/events.ts +12 -0
- package/src/modules/data_sync/i18n/de.json +48 -0
- package/src/modules/data_sync/i18n/en.json +48 -0
- package/src/modules/data_sync/i18n/es.json +48 -0
- package/src/modules/data_sync/i18n/pl.json +48 -0
- package/src/modules/data_sync/index.ts +5 -0
- package/src/modules/data_sync/lib/adapter-registry.ts +15 -0
- package/src/modules/data_sync/lib/adapter.ts +90 -0
- package/src/modules/data_sync/lib/id-mapping.ts +95 -0
- package/src/modules/data_sync/lib/queue.ts +19 -0
- package/src/modules/data_sync/lib/sync-engine.ts +375 -0
- package/src/modules/data_sync/lib/sync-run-service.ts +187 -0
- package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +653 -0
- package/src/modules/data_sync/migrations/Migration20260304113737.ts +19 -0
- package/src/modules/data_sync/setup.ts +11 -0
- package/src/modules/data_sync/workers/sync-export.ts +27 -0
- package/src/modules/data_sync/workers/sync-import.ts +27 -0
- package/src/modules/data_sync/workers/sync-scheduled.ts +84 -0
- package/src/modules/entities/lib/encryptionDefaults.ts +4 -0
- package/src/modules/integrations/AGENTS.md +160 -0
- package/src/modules/integrations/acl.ts +3 -0
- package/src/modules/integrations/api/[id]/credentials/route.ts +142 -0
- package/src/modules/integrations/api/[id]/health/route.ts +53 -0
- package/src/modules/integrations/api/[id]/route.ts +76 -0
- package/src/modules/integrations/api/[id]/state/route.ts +121 -0
- package/src/modules/integrations/api/[id]/version/route.ts +132 -0
- package/src/modules/integrations/api/guards.ts +59 -0
- package/src/modules/integrations/api/logs/route.ts +63 -0
- package/src/modules/integrations/api/openapi.ts +22 -0
- package/src/modules/integrations/api/route.ts +73 -0
- package/src/modules/integrations/backend/integrations/[id]/page.meta.ts +11 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +424 -0
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.meta.ts +11 -0
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +249 -0
- package/src/modules/integrations/backend/integrations/page.meta.ts +18 -0
- package/src/modules/integrations/backend/integrations/page.tsx +296 -0
- package/src/modules/integrations/data/enrichers.ts +35 -18
- package/src/modules/integrations/data/entities.ts +114 -5
- package/src/modules/integrations/data/validators.ts +41 -0
- package/src/modules/integrations/di.ts +31 -0
- package/src/modules/integrations/events.ts +17 -0
- package/src/modules/integrations/i18n/de.json +70 -0
- package/src/modules/integrations/i18n/en.json +70 -0
- package/src/modules/integrations/i18n/es.json +70 -0
- package/src/modules/integrations/i18n/pl.json +70 -0
- package/src/modules/integrations/lib/credentials-service.ts +204 -0
- package/src/modules/integrations/lib/health-service.ts +59 -0
- package/src/modules/integrations/lib/log-service.ts +84 -0
- package/src/modules/integrations/lib/registry-service.ts +42 -0
- package/src/modules/integrations/lib/state-service.ts +64 -0
- package/src/modules/integrations/lib/types.ts +4 -0
- package/src/modules/integrations/migrations/.snapshot-open-mercato.json +582 -0
- package/src/modules/integrations/migrations/Migration20260304113737.ts +21 -0
- package/src/modules/integrations/setup.ts +2 -2
- package/src/modules/integrations/widgets/injection-table.ts +1 -1
- package/src/modules/integrations/workers/log-pruner.ts +30 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/integrations/api/route.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CredentialsService } from '../lib/credentials-service'\nimport type { IntegrationStateService } from '../lib/state-service'\nimport { getAllBundles, getAllIntegrations } from '@open-mercato/shared/modules/integrations/types'\nimport { buildIntegrationsCrudOpenApi, createPagedListResponseSchema, integrationInfoSchema } from './openapi'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['integrations.view'] },\n}\n\nexport const openApi = buildIntegrationsCrudOpenApi({\n resourceName: 'Integration',\n pluralName: 'Integrations',\n listResponseSchema: createPagedListResponseSchema(integrationInfoSchema),\n querySchema: undefined,\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const credentialsService = container.resolve('integrationCredentialsService') as CredentialsService\n const stateService = container.resolve('integrationStateService') as IntegrationStateService\n\n const rows = await Promise.all(\n getAllIntegrations().map(async (integration) => {\n const [resolvedCredentials, state] = await Promise.all([\n credentialsService.resolve(integration.id, { organizationId: auth.orgId as string, tenantId: auth.tenantId as string }),\n stateService.get(integration.id, { organizationId: auth.orgId as string, tenantId: auth.tenantId as string }),\n ])\n\n return {\n id: integration.id,\n title: integration.title,\n category: integration.category ?? null,\n hub: integration.hub ?? null,\n providerKey: integration.providerKey ?? null,\n bundleId: integration.bundleId ?? null,\n hasCredentials: Boolean(resolvedCredentials),\n isEnabled: state?.isEnabled ?? true,\n apiVersion: state?.apiVersion ?? null,\n }\n }),\n )\n\n const bundles = getAllBundles().map((bundle) => {\n const bundleIntegrations = rows.filter((row) => row.bundleId === bundle.id)\n const enabledCount = bundleIntegrations.reduce((count, integration) => count + (integration.isEnabled ? 1 : 0), 0)\n\n return {\n id: bundle.id,\n title: bundle.title,\n description: bundle.description,\n icon: bundle.icon ?? null,\n integrationCount: bundleIntegrations.length,\n enabledCount,\n }\n })\n\n return NextResponse.json({\n items: rows,\n bundles,\n total: rows.length,\n page: 1,\n pageSize: 100,\n totalPages: 1,\n })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAGvC,SAAS,eAAe,0BAA0B;AAClD,SAAS,8BAA8B,+BAA+B,6BAA6B;AAE5F,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACnE;AAEO,MAAM,UAAU,6BAA6B;AAAA,EAClD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,oBAAoB,8BAA8B,qBAAqB;AAAA,EACvE,aAAa;AACf,CAAC;AAED,eAAsB,IAAI,KAAc;AACtC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,qBAAqB,UAAU,QAAQ,+BAA+B;AAC5E,QAAM,eAAe,UAAU,QAAQ,yBAAyB;AAEhE,QAAM,OAAO,MAAM,QAAQ;AAAA,IACzB,mBAAmB,EAAE,IAAI,OAAO,gBAAgB;AAC9C,YAAM,CAAC,qBAAqB,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,QACrD,mBAAmB,QAAQ,YAAY,IAAI,EAAE,gBAAgB,KAAK,OAAiB,UAAU,KAAK,SAAmB,CAAC;AAAA,QACtH,aAAa,IAAI,YAAY,IAAI,EAAE,gBAAgB,KAAK,OAAiB,UAAU,KAAK,SAAmB,CAAC;AAAA,MAC9G,CAAC;AAED,aAAO;AAAA,QACL,IAAI,YAAY;AAAA,QAChB,OAAO,YAAY;AAAA,QACnB,UAAU,YAAY,YAAY;AAAA,QAClC,KAAK,YAAY,OAAO;AAAA,QACxB,aAAa,YAAY,eAAe;AAAA,QACxC,UAAU,YAAY,YAAY;AAAA,QAClC,gBAAgB,QAAQ,mBAAmB;AAAA,QAC3C,WAAW,OAAO,aAAa;AAAA,QAC/B,YAAY,OAAO,cAAc;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,cAAc,EAAE,IAAI,CAAC,WAAW;AAC9C,UAAM,qBAAqB,KAAK,OAAO,CAAC,QAAQ,IAAI,aAAa,OAAO,EAAE;AAC1E,UAAM,eAAe,mBAAmB,OAAO,CAAC,OAAO,gBAAgB,SAAS,YAAY,YAAY,IAAI,IAAI,CAAC;AAEjH,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,MACX,OAAO,OAAO;AAAA,MACd,aAAa,OAAO;AAAA,MACpB,MAAM,OAAO,QAAQ;AAAA,MACrB,kBAAkB,mBAAmB;AAAA,MACrC;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,aAAa,KAAK;AAAA,IACvB,OAAO;AAAA,IACP;AAAA,IACA,OAAO,KAAK;AAAA,IACZ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,EACd,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { useParams } from "next/navigation";
|
|
6
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
7
|
+
import { Card, CardHeader, CardTitle, CardContent } from "@open-mercato/ui/primitives/card";
|
|
8
|
+
import { Badge } from "@open-mercato/ui/primitives/badge";
|
|
9
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
10
|
+
import { Switch } from "@open-mercato/ui/primitives/switch";
|
|
11
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
12
|
+
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
13
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@open-mercato/ui/primitives/tabs";
|
|
14
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
15
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
16
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
17
|
+
import { LoadingMessage } from "@open-mercato/ui/backend/detail";
|
|
18
|
+
import { ErrorMessage } from "@open-mercato/ui/backend/detail";
|
|
19
|
+
const LOG_LEVEL_STYLES = {
|
|
20
|
+
info: "bg-blue-100 text-blue-800",
|
|
21
|
+
warn: "bg-yellow-100 text-yellow-800",
|
|
22
|
+
error: "bg-red-100 text-red-800"
|
|
23
|
+
};
|
|
24
|
+
const HEALTH_STATUS_STYLES = {
|
|
25
|
+
healthy: "bg-green-100 text-green-800",
|
|
26
|
+
degraded: "bg-yellow-100 text-yellow-800",
|
|
27
|
+
unhealthy: "bg-red-100 text-red-800"
|
|
28
|
+
};
|
|
29
|
+
function IntegrationDetailPage() {
|
|
30
|
+
const params = useParams();
|
|
31
|
+
const integrationId = params.id;
|
|
32
|
+
const t = useT();
|
|
33
|
+
const [detail, setDetail] = React.useState(null);
|
|
34
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
35
|
+
const [error, setError] = React.useState(null);
|
|
36
|
+
const [credValues, setCredValues] = React.useState({});
|
|
37
|
+
const [isSavingCreds, setIsSavingCreds] = React.useState(false);
|
|
38
|
+
const [logs, setLogs] = React.useState([]);
|
|
39
|
+
const [logLevel, setLogLevel] = React.useState("");
|
|
40
|
+
const [isLoadingLogs, setIsLoadingLogs] = React.useState(false);
|
|
41
|
+
const [isCheckingHealth, setIsCheckingHealth] = React.useState(false);
|
|
42
|
+
const [isTogglingState, setIsTogglingState] = React.useState(false);
|
|
43
|
+
const loadDetail = React.useCallback(async () => {
|
|
44
|
+
setIsLoading(true);
|
|
45
|
+
setError(null);
|
|
46
|
+
const call = await apiCall(
|
|
47
|
+
`/api/integrations/${encodeURIComponent(integrationId)}`,
|
|
48
|
+
void 0,
|
|
49
|
+
{ fallback: null }
|
|
50
|
+
);
|
|
51
|
+
if (!call.ok || !call.result) {
|
|
52
|
+
setError(t("integrations.detail.loadError"));
|
|
53
|
+
setIsLoading(false);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
setDetail(call.result);
|
|
57
|
+
setIsLoading(false);
|
|
58
|
+
}, [integrationId, t]);
|
|
59
|
+
const loadCredentials = React.useCallback(async () => {
|
|
60
|
+
const call = await apiCall(
|
|
61
|
+
`/api/integrations/${encodeURIComponent(integrationId)}/credentials`,
|
|
62
|
+
void 0,
|
|
63
|
+
{ fallback: null }
|
|
64
|
+
);
|
|
65
|
+
if (call.ok && call.result?.credentials) {
|
|
66
|
+
setCredValues(call.result.credentials);
|
|
67
|
+
}
|
|
68
|
+
}, [integrationId]);
|
|
69
|
+
const loadLogs = React.useCallback(async () => {
|
|
70
|
+
setIsLoadingLogs(true);
|
|
71
|
+
const params2 = new URLSearchParams({ integrationId, pageSize: "50" });
|
|
72
|
+
if (logLevel) params2.set("level", logLevel);
|
|
73
|
+
const call = await apiCall(
|
|
74
|
+
`/api/integrations/logs?${params2.toString()}`,
|
|
75
|
+
void 0,
|
|
76
|
+
{ fallback: { items: [] } }
|
|
77
|
+
);
|
|
78
|
+
if (call.ok && call.result) {
|
|
79
|
+
setLogs(call.result.items);
|
|
80
|
+
}
|
|
81
|
+
setIsLoadingLogs(false);
|
|
82
|
+
}, [integrationId, logLevel]);
|
|
83
|
+
React.useEffect(() => {
|
|
84
|
+
void loadDetail();
|
|
85
|
+
}, [loadDetail]);
|
|
86
|
+
React.useEffect(() => {
|
|
87
|
+
void loadCredentials();
|
|
88
|
+
}, [loadCredentials]);
|
|
89
|
+
React.useEffect(() => {
|
|
90
|
+
void loadLogs();
|
|
91
|
+
}, [loadLogs]);
|
|
92
|
+
const handleToggleState = React.useCallback(async (enabled) => {
|
|
93
|
+
setIsTogglingState(true);
|
|
94
|
+
const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/state`, {
|
|
95
|
+
method: "PUT",
|
|
96
|
+
headers: { "Content-Type": "application/json" },
|
|
97
|
+
body: JSON.stringify({ isEnabled: enabled })
|
|
98
|
+
}, { fallback: null });
|
|
99
|
+
if (call.ok) {
|
|
100
|
+
setDetail((prev) => prev ? { ...prev, state: { ...prev.state, isEnabled: enabled } } : prev);
|
|
101
|
+
flash(t("integrations.detail.stateUpdated"), "success");
|
|
102
|
+
} else {
|
|
103
|
+
flash(t("integrations.detail.stateError"), "error");
|
|
104
|
+
}
|
|
105
|
+
setIsTogglingState(false);
|
|
106
|
+
}, [integrationId, t]);
|
|
107
|
+
const handleSaveCredentials = React.useCallback(async () => {
|
|
108
|
+
setIsSavingCreds(true);
|
|
109
|
+
const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/credentials`, {
|
|
110
|
+
method: "PUT",
|
|
111
|
+
headers: { "Content-Type": "application/json" },
|
|
112
|
+
body: JSON.stringify({ credentials: credValues })
|
|
113
|
+
}, { fallback: null });
|
|
114
|
+
if (call.ok) {
|
|
115
|
+
flash(t("integrations.detail.credentials.saved"), "success");
|
|
116
|
+
} else {
|
|
117
|
+
flash(t("integrations.detail.credentials.saveError"), "error");
|
|
118
|
+
}
|
|
119
|
+
setIsSavingCreds(false);
|
|
120
|
+
}, [integrationId, credValues, t]);
|
|
121
|
+
const handleVersionChange = React.useCallback(async (version) => {
|
|
122
|
+
const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/version`, {
|
|
123
|
+
method: "PUT",
|
|
124
|
+
headers: { "Content-Type": "application/json" },
|
|
125
|
+
body: JSON.stringify({ apiVersion: version })
|
|
126
|
+
}, { fallback: null });
|
|
127
|
+
if (call.ok) {
|
|
128
|
+
setDetail((prev) => prev ? { ...prev, state: { ...prev.state, apiVersion: version } } : prev);
|
|
129
|
+
flash(t("integrations.detail.version.saved"), "success");
|
|
130
|
+
} else {
|
|
131
|
+
flash(t("integrations.detail.version.saveError"), "error");
|
|
132
|
+
}
|
|
133
|
+
}, [integrationId, t]);
|
|
134
|
+
const handleHealthCheck = React.useCallback(async () => {
|
|
135
|
+
setIsCheckingHealth(true);
|
|
136
|
+
const call = await apiCall(
|
|
137
|
+
`/api/integrations/${encodeURIComponent(integrationId)}/health`,
|
|
138
|
+
{ method: "POST" },
|
|
139
|
+
{ fallback: null }
|
|
140
|
+
);
|
|
141
|
+
if (call.ok && call.result) {
|
|
142
|
+
setDetail((prev) => prev ? {
|
|
143
|
+
...prev,
|
|
144
|
+
state: {
|
|
145
|
+
...prev.state,
|
|
146
|
+
lastHealthStatus: call.result.status,
|
|
147
|
+
lastHealthCheckedAt: call.result.checkedAt
|
|
148
|
+
}
|
|
149
|
+
} : prev);
|
|
150
|
+
} else {
|
|
151
|
+
flash(t("integrations.detail.health.checkError"), "error");
|
|
152
|
+
}
|
|
153
|
+
setIsCheckingHealth(false);
|
|
154
|
+
}, [integrationId, t]);
|
|
155
|
+
if (isLoading) return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(LoadingMessage, { label: t("integrations.detail.title") }) }) });
|
|
156
|
+
if (error || !detail) return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(ErrorMessage, { label: error ?? t("integrations.detail.loadError") }) }) });
|
|
157
|
+
const { integration, state } = detail;
|
|
158
|
+
const credFields = integration.credentials?.fields ?? detail.bundle?.credentials?.fields ?? [];
|
|
159
|
+
const hasVersions = Boolean(integration.apiVersions?.length);
|
|
160
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsxs(PageBody, { className: "space-y-6", children: [
|
|
161
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Link, { href: "/backend/integrations", className: "text-sm text-muted-foreground hover:underline", children: t("integrations.detail.back") }) }),
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
163
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
164
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: integration.title }),
|
|
165
|
+
integration.description && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1", children: integration.description }),
|
|
166
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 mt-2", children: [
|
|
167
|
+
integration.category && /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: integration.category }),
|
|
168
|
+
integration.hub && /* @__PURE__ */ jsx(Badge, { variant: "outline", children: integration.hub })
|
|
169
|
+
] })
|
|
170
|
+
] }),
|
|
171
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
172
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: state.isEnabled ? t("integrations.detail.enable") : t("integrations.detail.disable") }),
|
|
173
|
+
/* @__PURE__ */ jsx(
|
|
174
|
+
Switch,
|
|
175
|
+
{
|
|
176
|
+
checked: state.isEnabled,
|
|
177
|
+
disabled: isTogglingState,
|
|
178
|
+
onCheckedChange: (checked) => void handleToggleState(checked)
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
] })
|
|
182
|
+
] }),
|
|
183
|
+
/* @__PURE__ */ jsxs(Tabs, { defaultValue: "credentials", children: [
|
|
184
|
+
/* @__PURE__ */ jsxs(TabsList, { children: [
|
|
185
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "credentials", children: t("integrations.detail.tabs.credentials") }),
|
|
186
|
+
hasVersions && /* @__PURE__ */ jsx(TabsTrigger, { value: "version", children: t("integrations.detail.tabs.version") }),
|
|
187
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "health", children: t("integrations.detail.tabs.health") }),
|
|
188
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "logs", children: t("integrations.detail.tabs.logs") })
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ jsxs(TabsContent, { value: "credentials", className: "space-y-4 mt-4", children: [
|
|
191
|
+
detail.bundle && /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-blue-200 bg-blue-50 p-3 text-sm text-blue-800", children: t("integrations.detail.credentials.bundleShared", { bundle: detail.bundle.title }) }),
|
|
192
|
+
credFields.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: t("integrations.detail.credentials.notConfigured") }) : /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "pt-6 space-y-4", children: [
|
|
193
|
+
credFields.filter((f) => f.type !== "oauth" && f.type !== "ssh_keypair").map((field) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
194
|
+
/* @__PURE__ */ jsxs("label", { className: "text-sm font-medium", children: [
|
|
195
|
+
field.label,
|
|
196
|
+
field.required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
|
|
197
|
+
] }),
|
|
198
|
+
field.type === "select" && field.options ? /* @__PURE__ */ jsxs(
|
|
199
|
+
"select",
|
|
200
|
+
{
|
|
201
|
+
className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm",
|
|
202
|
+
value: credValues[field.key] ?? "",
|
|
203
|
+
onChange: (e) => setCredValues((prev) => ({ ...prev, [field.key]: e.target.value })),
|
|
204
|
+
children: [
|
|
205
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014" }),
|
|
206
|
+
field.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
) : field.type === "boolean" ? /* @__PURE__ */ jsx(
|
|
210
|
+
Switch,
|
|
211
|
+
{
|
|
212
|
+
checked: Boolean(credValues[field.key]),
|
|
213
|
+
onCheckedChange: (checked) => setCredValues((prev) => ({ ...prev, [field.key]: checked }))
|
|
214
|
+
}
|
|
215
|
+
) : /* @__PURE__ */ jsx(
|
|
216
|
+
Input,
|
|
217
|
+
{
|
|
218
|
+
type: field.type === "secret" ? "password" : "text",
|
|
219
|
+
placeholder: field.placeholder,
|
|
220
|
+
value: credValues[field.key] ?? "",
|
|
221
|
+
onChange: (e) => setCredValues((prev) => ({ ...prev, [field.key]: e.target.value }))
|
|
222
|
+
}
|
|
223
|
+
)
|
|
224
|
+
] }, field.key)),
|
|
225
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", onClick: () => void handleSaveCredentials(), disabled: isSavingCreds, children: [
|
|
226
|
+
isSavingCreds ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 h-4 w-4" }) : null,
|
|
227
|
+
t("integrations.detail.credentials.save")
|
|
228
|
+
] })
|
|
229
|
+
] }) })
|
|
230
|
+
] }),
|
|
231
|
+
hasVersions && /* @__PURE__ */ jsx(TabsContent, { value: "version", className: "space-y-4 mt-4", children: /* @__PURE__ */ jsxs(Card, { children: [
|
|
232
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { children: t("integrations.detail.version.select") }) }),
|
|
233
|
+
/* @__PURE__ */ jsx(CardContent, { className: "space-y-3", children: integration.apiVersions.map((v) => {
|
|
234
|
+
const isSelected = (state.apiVersion ?? integration.apiVersions.find((x) => x.status === "stable")?.id) === v.id;
|
|
235
|
+
return /* @__PURE__ */ jsxs(
|
|
236
|
+
"div",
|
|
237
|
+
{
|
|
238
|
+
className: `flex items-center justify-between rounded-lg border p-3 cursor-pointer transition-colors ${isSelected ? "border-primary bg-primary/5" : "hover:bg-muted/50"}`,
|
|
239
|
+
onClick: () => void handleVersionChange(v.id),
|
|
240
|
+
children: [
|
|
241
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
242
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-sm", children: v.label ?? v.id }),
|
|
243
|
+
/* @__PURE__ */ jsx(
|
|
244
|
+
Badge,
|
|
245
|
+
{
|
|
246
|
+
variant: v.status === "stable" ? "default" : v.status === "deprecated" ? "destructive" : "secondary",
|
|
247
|
+
className: "ml-2",
|
|
248
|
+
children: t(`integrations.detail.version.${v.status}`)
|
|
249
|
+
}
|
|
250
|
+
),
|
|
251
|
+
v.status === "deprecated" && v.sunsetAt && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground ml-2", children: t("integrations.detail.version.sunsetAt", { date: new Date(v.sunsetAt).toLocaleDateString() }) })
|
|
252
|
+
] }),
|
|
253
|
+
isSelected && /* @__PURE__ */ jsx(Badge, { variant: "outline", children: t("integrations.detail.version.current") })
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
v.id
|
|
257
|
+
);
|
|
258
|
+
}) })
|
|
259
|
+
] }) }),
|
|
260
|
+
/* @__PURE__ */ jsx(TabsContent, { value: "health", className: "space-y-4 mt-4", children: /* @__PURE__ */ jsxs(Card, { children: [
|
|
261
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
262
|
+
/* @__PURE__ */ jsx(CardTitle, { children: t("integrations.detail.health.title") }),
|
|
263
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void handleHealthCheck(), disabled: isCheckingHealth, children: [
|
|
264
|
+
isCheckingHealth ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 h-4 w-4" }) : null,
|
|
265
|
+
isCheckingHealth ? t("integrations.detail.health.checking") : t("integrations.detail.health.check")
|
|
266
|
+
] })
|
|
267
|
+
] }) }),
|
|
268
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-3", children: [
|
|
269
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
270
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium", children: [
|
|
271
|
+
t("integrations.detail.health.title"),
|
|
272
|
+
":"
|
|
273
|
+
] }),
|
|
274
|
+
state.lastHealthStatus ? /* @__PURE__ */ jsx(Badge, { className: HEALTH_STATUS_STYLES[state.lastHealthStatus] ?? "", children: t(`integrations.detail.health.${state.lastHealthStatus}`) }) : /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: t("integrations.detail.health.unknown") })
|
|
275
|
+
] }),
|
|
276
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: state.lastHealthCheckedAt ? t("integrations.detail.health.lastChecked", { date: new Date(state.lastHealthCheckedAt).toLocaleString() }) : t("integrations.detail.health.neverChecked") })
|
|
277
|
+
] })
|
|
278
|
+
] }) }),
|
|
279
|
+
/* @__PURE__ */ jsxs(TabsContent, { value: "logs", className: "space-y-4 mt-4", children: [
|
|
280
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxs(
|
|
281
|
+
"select",
|
|
282
|
+
{
|
|
283
|
+
className: "flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm",
|
|
284
|
+
value: logLevel,
|
|
285
|
+
onChange: (e) => setLogLevel(e.target.value),
|
|
286
|
+
children: [
|
|
287
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("integrations.detail.logs.level.all") }),
|
|
288
|
+
/* @__PURE__ */ jsx("option", { value: "info", children: t("integrations.detail.logs.level.info") }),
|
|
289
|
+
/* @__PURE__ */ jsx("option", { value: "warn", children: t("integrations.detail.logs.level.warn") }),
|
|
290
|
+
/* @__PURE__ */ jsx("option", { value: "error", children: t("integrations.detail.logs.level.error") })
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
) }),
|
|
294
|
+
isLoadingLogs ? /* @__PURE__ */ jsx("div", { className: "flex justify-center py-8", children: /* @__PURE__ */ jsx(Spinner, {}) }) : logs.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm py-4", children: t("integrations.detail.logs.empty") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
295
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50", children: [
|
|
296
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-2 text-left font-medium", children: t("integrations.detail.logs.columns.time") }),
|
|
297
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-2 text-left font-medium", children: t("integrations.detail.logs.columns.level") }),
|
|
298
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-2 text-left font-medium", children: t("integrations.detail.logs.columns.message") })
|
|
299
|
+
] }) }),
|
|
300
|
+
/* @__PURE__ */ jsx("tbody", { children: logs.map((log) => /* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0", children: [
|
|
301
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-muted-foreground whitespace-nowrap", children: new Date(log.createdAt).toLocaleString() }),
|
|
302
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: LOG_LEVEL_STYLES[log.level] ?? "", children: log.level }) }),
|
|
303
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2", children: log.message })
|
|
304
|
+
] }, log.id)) })
|
|
305
|
+
] }) })
|
|
306
|
+
] })
|
|
307
|
+
] })
|
|
308
|
+
] }) });
|
|
309
|
+
}
|
|
310
|
+
export {
|
|
311
|
+
IntegrationDetailPage as default
|
|
312
|
+
};
|
|
313
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/integrations/backend/integrations/%5Bid%5D/page.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\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 { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { IntegrationCredentialField } from '@open-mercato/shared/modules/integrations/types'\nimport { LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { ErrorMessage } from '@open-mercato/ui/backend/detail'\n\ntype CredentialField = IntegrationCredentialField\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 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 level: 'info' | 'warn' | 'error'\n message: string\n createdAt: string\n code?: 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\nexport default function IntegrationDetailPage() {\n const params = useParams<{ id: string }>()\n const integrationId = params.id\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 [isSavingCreds, setIsSavingCreds] = 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\n const [isCheckingHealth, setIsCheckingHealth] = React.useState(false)\n const [isTogglingState, setIsTogglingState] = React.useState(false)\n\n const loadDetail = React.useCallback(async () => {\n setIsLoading(true)\n setError(null)\n const call = await apiCall<IntegrationDetail>(\n `/api/integrations/${encodeURIComponent(integrationId)}`,\n undefined,\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n setError(t('integrations.detail.loadError'))\n setIsLoading(false)\n return\n }\n setDetail(call.result)\n setIsLoading(false)\n }, [integrationId, t])\n\n const loadCredentials = React.useCallback(async () => {\n const call = await apiCall<{ credentials: Record<string, unknown> }>(\n `/api/integrations/${encodeURIComponent(integrationId)}/credentials`,\n undefined,\n { fallback: null },\n )\n if (call.ok && call.result?.credentials) {\n setCredValues(call.result.credentials)\n }\n }, [integrationId])\n\n const loadLogs = React.useCallback(async () => {\n setIsLoadingLogs(true)\n const params = new URLSearchParams({ integrationId, 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 }, [integrationId, logLevel])\n\n React.useEffect(() => { void loadDetail() }, [loadDetail])\n React.useEffect(() => { void loadCredentials() }, [loadCredentials])\n React.useEffect(() => { void loadLogs() }, [loadLogs])\n\n const handleToggleState = React.useCallback(async (enabled: boolean) => {\n setIsTogglingState(true)\n const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/state`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ isEnabled: enabled }),\n }, { fallback: null })\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 setIsTogglingState(false)\n }, [integrationId, t])\n\n const handleSaveCredentials = React.useCallback(async () => {\n setIsSavingCreds(true)\n const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/credentials`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ credentials: credValues }),\n }, { fallback: null })\n if (call.ok) {\n flash(t('integrations.detail.credentials.saved'), 'success')\n } else {\n flash(t('integrations.detail.credentials.saveError'), 'error')\n }\n setIsSavingCreds(false)\n }, [integrationId, credValues, t])\n\n const handleVersionChange = React.useCallback(async (version: string) => {\n const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/version`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiVersion: version }),\n }, { fallback: null })\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 }, [integrationId, t])\n\n const handleHealthCheck = React.useCallback(async () => {\n setIsCheckingHealth(true)\n const call = await apiCall<{ status: string; checkedAt: string }>(\n `/api/integrations/${encodeURIComponent(integrationId)}/health`,\n { method: 'POST' },\n { fallback: null },\n )\n if (call.ok && call.result) {\n setDetail((prev) => prev ? {\n ...prev,\n state: {\n ...prev.state,\n lastHealthStatus: call.result!.status,\n lastHealthCheckedAt: call.result!.checkedAt,\n },\n } : prev)\n } else {\n flash(t('integrations.detail.health.checkError'), 'error')\n }\n setIsCheckingHealth(false)\n }, [integrationId, t])\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 { integration, state } = detail\n const credFields = integration.credentials?.fields ?? detail.bundle?.credentials?.fields ?? []\n const hasVersions = Boolean(integration.apiVersions?.length)\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <div>\n <Link href=\"/backend/integrations\" className=\"text-sm text-muted-foreground hover:underline\">\n {t('integrations.detail.back')}\n </Link>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{integration.title}</h1>\n {integration.description && (\n <p className=\"text-muted-foreground mt-1\">{integration.description}</p>\n )}\n <div className=\"flex gap-2 mt-2\">\n {integration.category && <Badge variant=\"secondary\">{integration.category}</Badge>}\n {integration.hub && <Badge variant=\"outline\">{integration.hub}</Badge>}\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <span className=\"text-sm text-muted-foreground\">\n {state.isEnabled ? t('integrations.detail.enable') : t('integrations.detail.disable')}\n </span>\n <Switch\n checked={state.isEnabled}\n disabled={isTogglingState}\n onCheckedChange={(checked) => void handleToggleState(checked)}\n />\n </div>\n </div>\n\n <Tabs defaultValue=\"credentials\">\n <TabsList>\n <TabsTrigger value=\"credentials\">{t('integrations.detail.tabs.credentials')}</TabsTrigger>\n {hasVersions && <TabsTrigger value=\"version\">{t('integrations.detail.tabs.version')}</TabsTrigger>}\n <TabsTrigger value=\"health\">{t('integrations.detail.tabs.health')}</TabsTrigger>\n <TabsTrigger value=\"logs\">{t('integrations.detail.tabs.logs')}</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"credentials\" className=\"space-y-4 mt-4\">\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 )}\n {credFields.length === 0 ? (\n <p className=\"text-muted-foreground text-sm\">{t('integrations.detail.credentials.notConfigured')}</p>\n ) : (\n <Card>\n <CardContent className=\"pt-6 space-y-4\">\n {credFields.filter((f) => f.type !== 'oauth' && f.type !== 'ssh_keypair').map((field) => (\n <div key={field.key} className=\"space-y-1.5\">\n <label className=\"text-sm font-medium\">\n {field.label}{field.required && <span className=\"text-red-500 ml-0.5\">*</span>}\n </label>\n {field.type === 'select' && field.options ? (\n <select\n className=\"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm\"\n value={(credValues[field.key] as string) ?? ''}\n onChange={(e) => setCredValues((prev) => ({ ...prev, [field.key]: e.target.value }))}\n >\n <option value=\"\">\u2014</option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n ) : field.type === 'boolean' ? (\n <Switch\n checked={Boolean(credValues[field.key])}\n onCheckedChange={(checked) => setCredValues((prev) => ({ ...prev, [field.key]: checked }))}\n />\n ) : (\n <Input\n type={field.type === 'secret' ? 'password' : 'text'}\n placeholder={field.placeholder}\n value={(credValues[field.key] as string) ?? ''}\n onChange={(e) => setCredValues((prev) => ({ ...prev, [field.key]: e.target.value }))}\n />\n )}\n </div>\n ))}\n <Button type=\"button\" onClick={() => void handleSaveCredentials()} disabled={isSavingCreds}>\n {isSavingCreds ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('integrations.detail.credentials.save')}\n </Button>\n </CardContent>\n </Card>\n )}\n </TabsContent>\n\n {hasVersions && (\n <TabsContent value=\"version\" className=\"space-y-4 mt-4\">\n <Card>\n <CardHeader>\n <CardTitle>{t('integrations.detail.version.select')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {integration.apiVersions!.map((v) => {\n const isSelected = (state.apiVersion ?? integration.apiVersions!.find((x) => x.status === 'stable')?.id) === v.id\n return (\n <div\n key={v.id}\n className={`flex items-center justify-between rounded-lg border p-3 cursor-pointer transition-colors ${isSelected ? 'border-primary bg-primary/5' : 'hover:bg-muted/50'}`}\n onClick={() => void handleVersionChange(v.id)}\n >\n <div>\n <span className=\"font-medium text-sm\">{v.label ?? v.id}</span>\n <Badge\n variant={v.status === 'stable' ? 'default' : v.status === 'deprecated' ? 'destructive' : 'secondary'}\n className=\"ml-2\"\n >\n {t(`integrations.detail.version.${v.status}`)}\n </Badge>\n {v.status === 'deprecated' && v.sunsetAt && (\n <span className=\"text-xs text-muted-foreground ml-2\">\n {t('integrations.detail.version.sunsetAt', { date: new Date(v.sunsetAt).toLocaleDateString() })}\n </span>\n )}\n </div>\n {isSelected && <Badge variant=\"outline\">{t('integrations.detail.version.current')}</Badge>}\n </div>\n )\n })}\n </CardContent>\n </Card>\n </TabsContent>\n )}\n\n <TabsContent value=\"health\" className=\"space-y-4 mt-4\">\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle>{t('integrations.detail.health.title')}</CardTitle>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => void handleHealthCheck()} disabled={isCheckingHealth}>\n {isCheckingHealth ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\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 {state.lastHealthStatus ? (\n <Badge className={HEALTH_STATUS_STYLES[state.lastHealthStatus] ?? ''}>\n {t(`integrations.detail.health.${state.lastHealthStatus}`)}\n </Badge>\n ) : (\n <span className=\"text-sm text-muted-foreground\">{t('integrations.detail.health.unknown')}</span>\n )}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {state.lastHealthCheckedAt\n ? t('integrations.detail.health.lastChecked', { date: new Date(state.lastHealthCheckedAt).toLocaleString() })\n : t('integrations.detail.health.neverChecked')\n }\n </p>\n </CardContent>\n </Card>\n </TabsContent>\n\n <TabsContent value=\"logs\" className=\"space-y-4 mt-4\">\n <div className=\"flex items-center gap-3\">\n <select\n className=\"flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm\"\n value={logLevel}\n onChange={(e) => setLogLevel(e.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 </div>\n {isLoadingLogs ? (\n <div className=\"flex justify-center py-8\"><Spinner /></div>\n ) : logs.length === 0 ? (\n <p className=\"text-muted-foreground text-sm py-4\">{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 <tr key={log.id} className=\"border-b last:border-0\">\n <td className=\"px-4 py-2 text-muted-foreground whitespace-nowrap\">\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\">{log.message}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </TabsContent>\n </Tabs>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["params"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const metadata = {
|
|
2
|
+
requireAuth: true,
|
|
3
|
+
requireFeatures: ["integrations.view"],
|
|
4
|
+
pageContext: "settings",
|
|
5
|
+
pageTitle: "Integration Detail",
|
|
6
|
+
pageTitleKey: "integrations.detail.title",
|
|
7
|
+
navHidden: true,
|
|
8
|
+
breadcrumb: [
|
|
9
|
+
{ label: "Integrations", labelKey: "integrations.nav.title", href: "/backend/integrations" }
|
|
10
|
+
]
|
|
11
|
+
};
|
|
12
|
+
export {
|
|
13
|
+
metadata
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../src/modules/integrations/backend/integrations/%5Bid%5D/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['integrations.view'],\n pageContext: 'settings' as const,\n pageTitle: 'Integration Detail',\n pageTitleKey: 'integrations.detail.title',\n navHidden: true,\n breadcrumb: [\n { label: 'Integrations', labelKey: 'integrations.nav.title', href: '/backend/integrations' },\n ],\n}\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,mBAAmB;AAAA,EACrC,aAAa;AAAA,EACb,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AAAA,IACV,EAAE,OAAO,gBAAgB,UAAU,0BAA0B,MAAM,wBAAwB;AAAA,EAC7F;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { useParams } from "next/navigation";
|
|
6
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
7
|
+
import { Card, CardHeader, CardTitle, CardContent } from "@open-mercato/ui/primitives/card";
|
|
8
|
+
import { Badge } from "@open-mercato/ui/primitives/badge";
|
|
9
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
10
|
+
import { Switch } from "@open-mercato/ui/primitives/switch";
|
|
11
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
12
|
+
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
13
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
14
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
15
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
16
|
+
import { LoadingMessage } from "@open-mercato/ui/backend/detail";
|
|
17
|
+
import { ErrorMessage } from "@open-mercato/ui/backend/detail";
|
|
18
|
+
function BundleConfigPage() {
|
|
19
|
+
const params = useParams();
|
|
20
|
+
const bundleId = params.id;
|
|
21
|
+
const t = useT();
|
|
22
|
+
const [detail, setDetail] = React.useState(null);
|
|
23
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
24
|
+
const [error, setError] = React.useState(null);
|
|
25
|
+
const [credValues, setCredValues] = React.useState({});
|
|
26
|
+
const [isSavingCreds, setIsSavingCreds] = React.useState(false);
|
|
27
|
+
const [togglingIds, setTogglingIds] = React.useState(/* @__PURE__ */ new Set());
|
|
28
|
+
const load = React.useCallback(async () => {
|
|
29
|
+
setIsLoading(true);
|
|
30
|
+
setError(null);
|
|
31
|
+
const call = await apiCall(
|
|
32
|
+
`/api/integrations/${encodeURIComponent(bundleId)}`,
|
|
33
|
+
void 0,
|
|
34
|
+
{ fallback: null }
|
|
35
|
+
);
|
|
36
|
+
if (!call.ok || !call.result) {
|
|
37
|
+
setError(t("integrations.detail.loadError"));
|
|
38
|
+
setIsLoading(false);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setDetail(call.result);
|
|
42
|
+
const credCall = await apiCall(
|
|
43
|
+
`/api/integrations/${encodeURIComponent(bundleId)}/credentials`,
|
|
44
|
+
void 0,
|
|
45
|
+
{ fallback: null }
|
|
46
|
+
);
|
|
47
|
+
if (credCall.ok && credCall.result?.credentials) {
|
|
48
|
+
setCredValues(credCall.result.credentials);
|
|
49
|
+
}
|
|
50
|
+
setIsLoading(false);
|
|
51
|
+
}, [bundleId, t]);
|
|
52
|
+
React.useEffect(() => {
|
|
53
|
+
void load();
|
|
54
|
+
}, [load]);
|
|
55
|
+
const handleSaveCredentials = React.useCallback(async () => {
|
|
56
|
+
setIsSavingCreds(true);
|
|
57
|
+
const call = await apiCall(`/api/integrations/${encodeURIComponent(bundleId)}/credentials`, {
|
|
58
|
+
method: "PUT",
|
|
59
|
+
headers: { "Content-Type": "application/json" },
|
|
60
|
+
body: JSON.stringify({ credentials: credValues })
|
|
61
|
+
}, { fallback: null });
|
|
62
|
+
if (call.ok) {
|
|
63
|
+
flash(t("integrations.detail.credentials.saved"), "success");
|
|
64
|
+
} else {
|
|
65
|
+
flash(t("integrations.detail.credentials.saveError"), "error");
|
|
66
|
+
}
|
|
67
|
+
setIsSavingCreds(false);
|
|
68
|
+
}, [bundleId, credValues, t]);
|
|
69
|
+
const handleToggle = React.useCallback(async (integrationId, enabled) => {
|
|
70
|
+
setTogglingIds((prev) => new Set(prev).add(integrationId));
|
|
71
|
+
const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/state`, {
|
|
72
|
+
method: "PUT",
|
|
73
|
+
headers: { "Content-Type": "application/json" },
|
|
74
|
+
body: JSON.stringify({ isEnabled: enabled })
|
|
75
|
+
}, { fallback: null });
|
|
76
|
+
if (call.ok) {
|
|
77
|
+
setDetail((prev) => {
|
|
78
|
+
if (!prev) return prev;
|
|
79
|
+
return {
|
|
80
|
+
...prev,
|
|
81
|
+
bundleIntegrations: prev.bundleIntegrations.map(
|
|
82
|
+
(item) => item.id === integrationId ? { ...item, isEnabled: enabled } : item
|
|
83
|
+
)
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
flash(t("integrations.detail.stateError"), "error");
|
|
88
|
+
}
|
|
89
|
+
setTogglingIds((prev) => {
|
|
90
|
+
const next = new Set(prev);
|
|
91
|
+
next.delete(integrationId);
|
|
92
|
+
return next;
|
|
93
|
+
});
|
|
94
|
+
}, [t]);
|
|
95
|
+
const handleBulkToggle = React.useCallback(async (enabled) => {
|
|
96
|
+
if (!detail) return;
|
|
97
|
+
const targets = detail.bundleIntegrations.filter((item) => item.isEnabled !== enabled);
|
|
98
|
+
await Promise.all(targets.map((item) => handleToggle(item.id, enabled)));
|
|
99
|
+
}, [detail, handleToggle]);
|
|
100
|
+
if (isLoading) return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(LoadingMessage, { label: t("integrations.bundle.title") }) }) });
|
|
101
|
+
if (error || !detail?.bundle) return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(ErrorMessage, { label: error ?? t("integrations.detail.loadError") }) }) });
|
|
102
|
+
const credFields = detail.bundle.credentials?.fields ?? [];
|
|
103
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsxs(PageBody, { className: "space-y-6", children: [
|
|
104
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Link, { href: "/backend/integrations", className: "text-sm text-muted-foreground hover:underline", children: t("integrations.detail.back") }) }),
|
|
105
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
106
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: detail.bundle.title }),
|
|
107
|
+
detail.bundle.description && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1", children: detail.bundle.description })
|
|
108
|
+
] }),
|
|
109
|
+
credFields.length > 0 && /* @__PURE__ */ jsxs(Card, { children: [
|
|
110
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { children: t("integrations.bundle.sharedCredentials") }) }),
|
|
111
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
|
|
112
|
+
credFields.map((field) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
113
|
+
/* @__PURE__ */ jsxs("label", { className: "text-sm font-medium", children: [
|
|
114
|
+
field.label,
|
|
115
|
+
field.required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
|
|
116
|
+
] }),
|
|
117
|
+
field.type === "select" && field.options ? /* @__PURE__ */ jsxs(
|
|
118
|
+
"select",
|
|
119
|
+
{
|
|
120
|
+
className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm",
|
|
121
|
+
value: credValues[field.key] ?? "",
|
|
122
|
+
onChange: (e) => setCredValues((prev) => ({ ...prev, [field.key]: e.target.value })),
|
|
123
|
+
children: [
|
|
124
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014" }),
|
|
125
|
+
field.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
) : field.type === "boolean" ? /* @__PURE__ */ jsx(
|
|
129
|
+
Switch,
|
|
130
|
+
{
|
|
131
|
+
checked: Boolean(credValues[field.key]),
|
|
132
|
+
onCheckedChange: (checked) => setCredValues((prev) => ({ ...prev, [field.key]: checked }))
|
|
133
|
+
}
|
|
134
|
+
) : /* @__PURE__ */ jsx(
|
|
135
|
+
Input,
|
|
136
|
+
{
|
|
137
|
+
type: field.type === "secret" ? "password" : "text",
|
|
138
|
+
placeholder: field.placeholder,
|
|
139
|
+
value: credValues[field.key] ?? "",
|
|
140
|
+
onChange: (e) => setCredValues((prev) => ({ ...prev, [field.key]: e.target.value }))
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
] }, field.key)),
|
|
144
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", onClick: () => void handleSaveCredentials(), disabled: isSavingCreds, children: [
|
|
145
|
+
isSavingCreds ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 h-4 w-4" }) : null,
|
|
146
|
+
t("integrations.detail.credentials.save")
|
|
147
|
+
] })
|
|
148
|
+
] })
|
|
149
|
+
] }),
|
|
150
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
151
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
152
|
+
/* @__PURE__ */ jsx(CardTitle, { children: t("integrations.bundle.integrationToggles") }),
|
|
153
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
154
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void handleBulkToggle(true), children: t("integrations.marketplace.enableAll") }),
|
|
155
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void handleBulkToggle(false), children: t("integrations.marketplace.disableAll") })
|
|
156
|
+
] })
|
|
157
|
+
] }) }),
|
|
158
|
+
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("div", { className: "space-y-3", children: detail.bundleIntegrations.map((item) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-lg border p-3", children: [
|
|
159
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
160
|
+
/* @__PURE__ */ jsx(
|
|
161
|
+
Link,
|
|
162
|
+
{
|
|
163
|
+
href: `/backend/integrations/${encodeURIComponent(item.id)}`,
|
|
164
|
+
className: "text-sm font-medium hover:underline",
|
|
165
|
+
children: item.title
|
|
166
|
+
}
|
|
167
|
+
),
|
|
168
|
+
item.category && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "ml-2 text-xs", children: item.category }),
|
|
169
|
+
item.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: item.description })
|
|
170
|
+
] }),
|
|
171
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
172
|
+
/* @__PURE__ */ jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: /* @__PURE__ */ jsx(Link, { href: `/backend/integrations/${encodeURIComponent(item.id)}`, children: t("integrations.bundle.configureIntegration") }) }),
|
|
173
|
+
/* @__PURE__ */ jsx(
|
|
174
|
+
Switch,
|
|
175
|
+
{
|
|
176
|
+
checked: item.isEnabled,
|
|
177
|
+
disabled: togglingIds.has(item.id),
|
|
178
|
+
onCheckedChange: (checked) => void handleToggle(item.id, checked)
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
] })
|
|
182
|
+
] }, item.id)) }) })
|
|
183
|
+
] })
|
|
184
|
+
] }) });
|
|
185
|
+
}
|
|
186
|
+
export {
|
|
187
|
+
BundleConfigPage as default
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=page.js.map
|