@open-mercato/webhooks 0.4.10-develop.1139.189d8acf38 → 0.4.10-develop.1149.dd15f06da6

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.
@@ -25,7 +25,7 @@ import {
25
25
  normalizeWebhookFormPayload
26
26
  } from "../../../components/webhook-form-config.js";
27
27
  import { useWebhookFeatureAccess } from "../useWebhookFeatureAccess.js";
28
- import { WebhookSecretPanel } from "../WebhookSecretPanel.js";
28
+ import { WebhookSecretPanel } from "../../../components/WebhookSecretPanel.js";
29
29
  const statusVariantMap = {
30
30
  delivered: "default",
31
31
  pending: "secondary",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/webhooks/backend/webhooks/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useParams, usePathname, useRouter } from 'next/navigation'\nimport { RotateCw } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\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 { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { Notice } from '@open-mercato/ui/primitives/Notice'\nimport {\n buildWebhookFormContentHeader,\n buildWebhookFormFields,\n buildWebhookFormGroups,\n createWebhookInitialValues,\n normalizeWebhookFormPayload,\n type WebhookFormValues,\n} from '../../../components/webhook-form-config'\nimport { useWebhookFeatureAccess } from '../useWebhookFeatureAccess'\nimport { WebhookSecretPanel } from '../WebhookSecretPanel'\n\ntype Webhook = {\n id: string\n name: string\n description: string | null\n url: string\n subscribedEvents: string[]\n httpMethod: 'POST' | 'PUT' | 'PATCH'\n isActive: boolean\n maxRetries: number\n timeoutMs: number\n rateLimitPerMinute: number\n autoDisableThreshold: number\n consecutiveFailures: number\n lastSuccessAt: string | null\n lastFailureAt: string | null\n customHeaders: Record<string, string> | null\n createdAt: string\n updatedAt: string\n maskedSecret: string\n previousSecretSetAt: string | null\n}\n\ntype DeliveryRow = {\n id: string\n webhookId: string\n eventType: string\n messageId: string\n status: string\n responseStatus: number | null\n errorMessage: string | null\n attemptNumber: number\n maxAttempts: number\n durationMs: number | null\n targetUrl: string\n enqueuedAt: string\n lastAttemptAt: string | null\n deliveredAt: string | null\n createdAt: string\n}\n\ntype DeliveryResponse = {\n items: DeliveryRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\ntype DeliveryDetail = DeliveryRow & {\n payload: Record<string, unknown>\n responseBody: string | null\n responseHeaders: Record<string, string> | null\n nextRetryAt: string | null\n updatedAt: string\n}\n\nconst statusVariantMap: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {\n delivered: 'default',\n pending: 'secondary',\n sending: 'outline',\n failed: 'destructive',\n expired: 'destructive',\n}\nconst DELIVERY_AUTO_REFRESH_INTERVAL_MS = 30000\n\nexport default function WebhookDetailPage() {\n const params = useParams()\n const pathname = usePathname()\n const router = useRouter()\n const t = useT()\n const webhookId = React.useMemo(() => resolveWebhookId(params?.id, pathname), [params?.id, pathname])\n\n const [webhook, setWebhook] = React.useState<Webhook | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isEditing, setIsEditing] = React.useState(false)\n\n const [deliveries, setDeliveries] = React.useState<DeliveryRow[]>([])\n const [deliveryPage, setDeliveryPage] = React.useState(1)\n const [deliveryTotal, setDeliveryTotal] = React.useState(0)\n const [deliveryTotalPages, setDeliveryTotalPages] = React.useState(1)\n const [deliveriesLoading, setDeliveriesLoading] = React.useState(false)\n const [isRefreshingDeliveries, setIsRefreshingDeliveries] = React.useState(false)\n const [testDelivery, setTestDelivery] = React.useState<DeliveryDetail | null>(null)\n const [selectedDelivery, setSelectedDelivery] = React.useState<DeliveryDetail | null>(null)\n const [selectedDeliveryLoading, setSelectedDeliveryLoading] = React.useState(false)\n const [revealedSecret, setRevealedSecret] = React.useState<string | null>(null)\n const refreshInFlightRef = React.useRef(false)\n const access = useWebhookFeatureAccess()\n\n const fetchWebhook = React.useCallback(async (options?: { silent?: boolean }) => {\n if (!webhookId) {\n setError(t('webhooks.errors.notFound'))\n setIsLoading(false)\n return\n }\n\n const silent = options?.silent === true\n if (!silent) {\n setIsLoading(true)\n }\n\n setError(null)\n\n try {\n const call = await apiCall<Webhook>(\n `/api/webhooks/${encodeURIComponent(webhookId)}`,\n undefined,\n { fallback: null },\n )\n\n if (call.ok && call.result) {\n setWebhook(call.result)\n return\n }\n\n setError(t('webhooks.errors.notFound'))\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError.message : t('webhooks.detail.loadError'))\n } finally {\n if (!silent) {\n setIsLoading(false)\n }\n }\n }, [t, webhookId])\n\n const fetchDeliveries = React.useCallback(async (options?: { silent?: boolean }) => {\n if (!webhookId) return\n\n const silent = options?.silent === true\n if (!silent) {\n setDeliveriesLoading(true)\n }\n\n try {\n const params = new URLSearchParams()\n params.set('webhookId', webhookId)\n params.set('page', String(deliveryPage))\n params.set('pageSize', '20')\n\n const fallback: DeliveryResponse = { items: [], total: 0, page: deliveryPage, pageSize: 20, totalPages: 1 }\n const call = await apiCall<DeliveryResponse>(\n `/api/webhooks/deliveries?${params.toString()}`,\n undefined,\n { fallback },\n )\n\n if (call.ok && call.result) {\n setDeliveries(call.result.items)\n setDeliveryTotal(call.result.total)\n setDeliveryTotalPages(call.result.totalPages)\n }\n } finally {\n if (!silent) {\n setDeliveriesLoading(false)\n }\n }\n }, [deliveryPage, webhookId])\n\n const fetchDeliveryDetail = React.useCallback(async (deliveryId: string): Promise<DeliveryDetail | null> => {\n const call = await apiCall<DeliveryDetail>(\n `/api/webhooks/deliveries/${encodeURIComponent(deliveryId)}`,\n undefined,\n { fallback: null },\n )\n\n if (!call.ok || !call.result) {\n return null\n }\n\n return call.result\n }, [])\n\n const refreshDeliveryState = React.useCallback(async () => {\n if (!webhookId || refreshInFlightRef.current) return\n\n refreshInFlightRef.current = true\n setIsRefreshingDeliveries(true)\n\n try {\n await Promise.all([\n fetchWebhook({ silent: true }),\n fetchDeliveries({ silent: true }),\n ])\n\n const detailIds = [selectedDelivery?.id, testDelivery?.id].filter(\n (value): value is string => typeof value === 'string' && value.length > 0,\n )\n\n if (detailIds.length > 0) {\n const details = await Promise.all(detailIds.map((deliveryId) => fetchDeliveryDetail(deliveryId)))\n const detailMap = new Map<string, DeliveryDetail>()\n\n for (const detail of details) {\n if (detail) {\n detailMap.set(detail.id, detail)\n }\n }\n\n if (selectedDelivery?.id) {\n setSelectedDelivery((current) => current?.id ? (detailMap.get(current.id) ?? current) : current)\n }\n\n if (testDelivery?.id) {\n setTestDelivery((current) => current?.id ? (detailMap.get(current.id) ?? current) : current)\n }\n }\n } finally {\n refreshInFlightRef.current = false\n setIsRefreshingDeliveries(false)\n }\n }, [fetchDeliveries, fetchDeliveryDetail, fetchWebhook, selectedDelivery?.id, testDelivery?.id, webhookId])\n\n React.useEffect(() => {\n void fetchWebhook()\n }, [fetchWebhook])\n\n React.useEffect(() => {\n if (!webhookId) return\n void fetchDeliveries()\n }, [fetchDeliveries, webhookId])\n\n useAppEvent('webhooks.delivery.*', (event) => {\n const eventWebhookId = typeof event.payload?.webhookId === 'string' ? event.payload.webhookId : null\n if (eventWebhookId !== webhookId) return\n void refreshDeliveryState()\n }, [refreshDeliveryState, webhookId])\n\n React.useEffect(() => {\n if (!webhookId || isEditing) return\n\n const intervalId = window.setInterval(() => {\n if (document.visibilityState !== 'visible') return\n void refreshDeliveryState()\n }, DELIVERY_AUTO_REFRESH_INTERVAL_MS)\n\n return () => {\n window.clearInterval(intervalId)\n }\n }, [isEditing, refreshDeliveryState, webhookId])\n\n const handleToggleActive = React.useCallback(async () => {\n if (!webhook) return\n try {\n const call = await apiCall<Webhook>(\n `/api/webhooks/${encodeURIComponent(webhook.id)}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isActive: !webhook.isActive }),\n },\n { fallback: null },\n )\n if (call.ok) {\n setWebhook((prev) => prev ? { ...prev, isActive: !prev.isActive } : prev)\n flash(t('webhooks.form.updateSuccess'), 'success')\n void refreshDeliveryState()\n }\n } catch {\n flash(t('webhooks.form.updateError'), 'error')\n }\n }, [refreshDeliveryState, webhook, t])\n\n const handleRotateSecret = React.useCallback(async () => {\n if (!webhook) return\n try {\n const call = await apiCall<{ secret: string }>(\n `/api/webhooks/${encodeURIComponent(webhook.id)}/rotate-secret`,\n { method: 'POST' },\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n flash(t('webhooks.detail.rotateError'), 'error')\n return\n }\n setRevealedSecret(call.result.secret)\n flash(t('webhooks.detail.rotateSuccess'), 'success')\n void refreshDeliveryState()\n } catch {\n flash(t('webhooks.detail.rotateError'), 'error')\n }\n }, [refreshDeliveryState, t, webhook])\n\n const handleTest = React.useCallback(async () => {\n if (!webhook) return\n try {\n const call = await apiCall<{ delivery: DeliveryDetail }>(\n `/api/webhooks/${encodeURIComponent(webhook.id)}/test`,\n { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({}) },\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n flash(t('webhooks.detail.testError'), 'error')\n return\n }\n const deliveryStatus = call.result.delivery.status\n setTestDelivery(call.result.delivery)\n flash(\n deliveryStatus === 'delivered' ? t('webhooks.detail.testSuccess') : t('webhooks.detail.testQueued'),\n deliveryStatus === 'delivered' ? 'success' : 'error',\n )\n void refreshDeliveryState()\n } catch {\n flash(t('webhooks.detail.testError'), 'error')\n }\n }, [refreshDeliveryState, t, webhook])\n\n const handleRetryDelivery = React.useCallback(async (deliveryId: string) => {\n try {\n const call = await apiCall(\n `/api/webhooks/deliveries/${encodeURIComponent(deliveryId)}/retry`,\n { method: 'POST' },\n { fallback: null },\n )\n if (!call.ok) {\n flash(t('webhooks.deliveries.retryError'), 'error')\n return\n }\n flash(t('webhooks.deliveries.retrySuccess'), 'success')\n void refreshDeliveryState()\n } catch {\n flash(t('webhooks.deliveries.retryError'), 'error')\n }\n }, [refreshDeliveryState, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!webhook) return\n try {\n await deleteCrud(`webhooks/${encodeURIComponent(webhook.id)}`, { fallbackResult: null })\n flash(t('webhooks.list.deleteSuccess'), 'success')\n router.push('/backend/webhooks')\n } catch {\n flash(t('webhooks.list.deleteError'), 'error')\n }\n }, [router, t, webhook])\n\n const handleDeliveryOpen = React.useCallback(async (deliveryId: string) => {\n setSelectedDeliveryLoading(true)\n try {\n const detail = await fetchDeliveryDetail(deliveryId)\n if (!detail) {\n flash(t('webhooks.deliveries.loadError'), 'error')\n return\n }\n setSelectedDelivery(detail)\n } catch {\n flash(t('webhooks.deliveries.loadError'), 'error')\n } finally {\n setSelectedDeliveryLoading(false)\n }\n }, [fetchDeliveryDetail, t])\n\n const deliveryColumns = React.useMemo<ColumnDef<DeliveryRow>[]>(() => [\n { accessorKey: 'eventType', header: t('webhooks.deliveries.columns.event') },\n {\n accessorKey: 'status',\n header: t('webhooks.deliveries.columns.status'),\n cell: ({ row }) => (\n <Badge variant={statusVariantMap[row.original.status] ?? 'secondary'}>\n {t(`webhooks.deliveries.status.${row.original.status}` as Parameters<typeof t>[0])}\n </Badge>\n ),\n },\n {\n accessorKey: 'responseStatus',\n header: t('webhooks.deliveries.columns.responseStatus'),\n cell: ({ row }) => row.original.responseStatus ?? '\u2014',\n },\n {\n accessorKey: 'attemptNumber',\n header: t('webhooks.deliveries.columns.attempts'),\n cell: ({ row }) => `${row.original.attemptNumber}/${row.original.maxAttempts}`,\n },\n {\n accessorKey: 'durationMs',\n header: t('webhooks.deliveries.columns.duration'),\n cell: ({ row }) => row.original.durationMs != null ? `${row.original.durationMs}ms` : '\u2014',\n },\n {\n accessorKey: 'createdAt',\n header: t('webhooks.deliveries.columns.enqueuedAt'),\n cell: ({ row }) => {\n try { return new Date(row.original.enqueuedAt).toLocaleString() }\n catch { return '\u2014' }\n },\n },\n ], [t])\n\n const fields = React.useMemo(() => buildWebhookFormFields(t), [t])\n const groups = React.useMemo(() => buildWebhookFormGroups(t), [t])\n const contentHeader = React.useMemo(() => buildWebhookFormContentHeader(t), [t])\n const menuActions = React.useMemo(() => {\n const items: Array<{ id: string; label: string; onSelect: () => void }> = []\n const isActive = webhook?.isActive ?? false\n\n if (access.canManage) {\n items.push(\n {\n id: 'edit',\n label: t('webhooks.list.actions.edit'),\n onSelect: () => setIsEditing(true),\n },\n {\n id: 'toggle-active',\n label: isActive ? t('webhooks.detail.actions.deactivate') : t('webhooks.detail.actions.activate'),\n onSelect: () => { void handleToggleActive() },\n },\n )\n }\n\n if (access.canSecrets) {\n items.push({\n id: 'rotate-secret',\n label: t('webhooks.detail.actions.rotateSecret'),\n onSelect: () => { void handleRotateSecret() },\n })\n }\n\n if (access.canTest) {\n items.push({\n id: 'test',\n label: t('webhooks.detail.actions.test'),\n onSelect: () => { void handleTest() },\n })\n }\n\n if (access.canManage) {\n items.push({\n id: 'delete',\n label: t('webhooks.list.actions.delete'),\n onSelect: () => { void handleDelete() },\n })\n }\n\n return items\n }, [access.canManage, access.canSecrets, access.canTest, handleDelete, handleRotateSecret, handleTest, handleToggleActive, t, webhook?.isActive])\n\n if (isLoading) return <Page><PageBody><LoadingMessage label={t('webhooks.detail.loading')} /></PageBody></Page>\n if (error || !webhook) return <Page><PageBody><ErrorMessage label={error ?? t('webhooks.errors.notFound')} /></PageBody></Page>\n\n if (isEditing) {\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={t('webhooks.form.title.edit')}\n backHref={`/backend/webhooks/${webhook.id}`}\n fields={fields}\n groups={groups}\n initialValues={createWebhookInitialValues(webhook)}\n submitLabel={t('common.save')}\n cancelHref={`/backend/webhooks/${webhook.id}`}\n contentHeader={contentHeader}\n onDelete={access.canManage ? handleDelete : undefined}\n onSubmit={async (values) => {\n const payload = normalizeWebhookFormPayload(values as WebhookFormValues, t)\n await updateCrud(`webhooks/${encodeURIComponent(webhook.id)}`, payload)\n flash(t('webhooks.form.updateSuccess'), 'success')\n setIsEditing(false)\n await refreshDeliveryState()\n }}\n />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n {revealedSecret ? (\n <WebhookSecretPanel secret={revealedSecret} onClose={() => setRevealedSecret(null)} />\n ) : null}\n <FormHeader\n mode=\"detail\"\n title={webhook.name}\n entityTypeLabel={t('webhooks.nav.title')}\n statusBadge={\n <Badge\n className={webhook.isActive\n ? 'border-transparent bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'\n : 'border-transparent bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'}\n >\n {webhook.isActive ? t('webhooks.list.status.active') : t('webhooks.list.status.inactive')}\n </Badge>\n }\n backHref=\"/backend/webhooks\"\n menuActions={menuActions}\n />\n\n <div className=\"mt-6 space-y-4\">\n {!access.isLoading && !access.canManage && !access.canSecrets && !access.canTest ? (\n <Notice compact>{t('webhooks.detail.readOnlyTip')}</Notice>\n ) : null}\n <div className=\"grid gap-3 lg:grid-cols-2\">\n <Notice compact>{t('webhooks.detail.deliveryTip')}</Notice>\n <Notice compact>{t('webhooks.detail.signatureTip')}</Notice>\n </div>\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.url')}:</span>\n <code className=\"ml-2 text-xs break-all\">{webhook.url}</code>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.httpMethod')}:</span>\n <span className=\"ml-2\">{webhook.httpMethod}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.subscribedEvents')}:</span>\n <span className=\"ml-2 text-xs\">{webhook.subscribedEvents.join(', ')}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.maxRetries')}:</span>\n <span className=\"ml-2\">{webhook.maxRetries}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.rateLimitPerMinute')}:</span>\n <span className=\"ml-2\">{webhook.rateLimitPerMinute}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.autoDisableThreshold')}:</span>\n <span className=\"ml-2\">{webhook.autoDisableThreshold}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.detail.consecutiveFailures')}:</span>\n <span className=\"ml-2\">{webhook.consecutiveFailures}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.secret')}:</span>\n <span className=\"ml-2 inline-flex items-center gap-2\">\n <span className=\"font-mono text-xs\">{webhook.maskedSecret}</span>\n {access.canSecrets ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => { void handleRotateSecret() }}\n >\n <RotateCw className=\"mr-1.5 size-3.5\" />\n {t('webhooks.detail.actions.rotateSecret')}\n </Button>\n ) : null}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.list.columns.lastDelivery')}:</span>\n <span className=\"ml-2\">{webhook.lastSuccessAt ?? webhook.lastFailureAt ?? '\u2014'}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.detail.previousSecretSetAt')}:</span>\n <span className=\"ml-2\">{webhook.previousSecretSetAt ?? '\u2014'}</span>\n </div>\n <div className=\"col-span-2\">\n <span className=\"text-muted-foreground\">{t('webhooks.form.customHeaders')}:</span>\n <pre className=\"mt-2 rounded border bg-muted/40 p-3 text-xs\">\n {webhook.customHeaders ? JSON.stringify(webhook.customHeaders, null, 2) : '\u2014'}\n </pre>\n </div>\n </div>\n </div>\n\n {testDelivery ? (\n <div className=\"mt-8 rounded-lg border bg-card p-4\">\n <h2 className=\"text-sm font-semibold\">{t('webhooks.detail.testResult')}</h2>\n <div className=\"mt-3 grid gap-2 text-sm\">\n <div>{t('webhooks.deliveries.columns.status')}: {testDelivery.status}</div>\n <div>{t('webhooks.deliveries.columns.responseStatus')}: {testDelivery.responseStatus ?? '\u2014'}</div>\n <div>{t('webhooks.deliveries.columns.duration')}: {testDelivery.durationMs != null ? `${testDelivery.durationMs}ms` : '\u2014'}</div>\n <pre className=\"overflow-auto rounded border bg-muted/40 p-3 text-xs\">\n {JSON.stringify(testDelivery.payload, null, 2)}\n </pre>\n </div>\n </div>\n ) : null}\n\n <div className=\"mt-8\">\n <DataTable\n title={t('webhooks.deliveries.title')}\n actions={(\n <div className=\"flex items-center gap-2\">\n <span className=\"hidden text-xs text-muted-foreground md:inline\">\n {t('webhooks.deliveries.autoRefreshHint')}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => { void refreshDeliveryState() }}\n disabled={isRefreshingDeliveries}\n >\n {isRefreshingDeliveries\n ? t('webhooks.deliveries.refreshing')\n : t('webhooks.deliveries.refresh')}\n </Button>\n </div>\n )}\n columns={deliveryColumns}\n data={deliveries}\n onRowClick={(row) => { void handleDeliveryOpen(row.id) }}\n rowActions={(row) => (\n access.canManage && (row.status === 'failed' || row.status === 'expired')\n ? (\n <RowActions\n items={[\n {\n id: 'retry',\n label: t('webhooks.deliveries.actions.retry'),\n onSelect: () => { void handleRetryDelivery(row.id) },\n },\n ]}\n />\n )\n : null\n )}\n perspective={{ tableId: 'webhooks.deliveries' }}\n pagination={{\n page: deliveryPage,\n pageSize: 20,\n total: deliveryTotal,\n totalPages: deliveryTotalPages,\n onPageChange: setDeliveryPage,\n }}\n isLoading={deliveriesLoading || isRefreshingDeliveries}\n />\n </div>\n\n {selectedDelivery || selectedDeliveryLoading ? (\n <div className=\"mt-6 rounded-lg border bg-card p-4\">\n <h2 className=\"text-sm font-semibold\">{t('webhooks.deliveries.detailTitle')}</h2>\n {selectedDeliveryLoading || !selectedDelivery ? (\n <div className=\"mt-3 text-sm text-muted-foreground\">{t('common.loading')}</div>\n ) : (\n <div className=\"mt-3 space-y-4 text-sm\">\n <div>{t('webhooks.deliveries.columns.status')}: {selectedDelivery.status}</div>\n <div>{t('webhooks.deliveries.columns.responseStatus')}: {selectedDelivery.responseStatus ?? '\u2014'}</div>\n <div>{t('webhooks.deliveries.columns.duration')}: {selectedDelivery.durationMs != null ? `${selectedDelivery.durationMs}ms` : '\u2014'}</div>\n <div>\n <div className=\"mb-2 font-medium\">{t('webhooks.deliveries.requestBody')}</div>\n <pre className=\"overflow-auto rounded border bg-muted/40 p-3 text-xs\">\n {JSON.stringify(selectedDelivery.payload, null, 2)}\n </pre>\n </div>\n <div>\n <div className=\"mb-2 font-medium\">{t('webhooks.deliveries.responseBody')}</div>\n <pre className=\"overflow-auto rounded border bg-muted/40 p-3 text-xs\">\n {selectedDelivery.responseBody ?? '\u2014'}\n </pre>\n </div>\n <div>\n <div className=\"mb-2 font-medium\">{t('webhooks.deliveries.responseHeaders')}</div>\n <pre className=\"overflow-auto rounded border bg-muted/40 p-3 text-xs\">\n {selectedDelivery.responseHeaders ? JSON.stringify(selectedDelivery.responseHeaders, null, 2) : '\u2014'}\n </pre>\n </div>\n </div>\n )}\n </div>\n ) : null}\n </PageBody>\n </Page>\n )\n}\n\nfunction resolveWebhookId(paramValue: string | string[] | undefined, pathname: string | null): string | null {\n if (typeof paramValue === 'string' && paramValue.trim().length > 0) {\n return paramValue\n }\n\n if (Array.isArray(paramValue)) {\n const first = paramValue.find((value) => typeof value === 'string' && value.trim().length > 0)\n if (first) return first\n }\n\n if (typeof pathname === 'string') {\n const match = pathname.match(/\\/backend\\/webhooks\\/([^/?#]+)/)\n if (match?.[1]) {\n return decodeURIComponent(match[1])\n }\n }\n\n return null\n}\n"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useParams, usePathname, useRouter } from 'next/navigation'\nimport { RotateCw } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\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 { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { Notice } from '@open-mercato/ui/primitives/Notice'\nimport {\n buildWebhookFormContentHeader,\n buildWebhookFormFields,\n buildWebhookFormGroups,\n createWebhookInitialValues,\n normalizeWebhookFormPayload,\n type WebhookFormValues,\n} from '../../../components/webhook-form-config'\nimport { useWebhookFeatureAccess } from '../useWebhookFeatureAccess'\nimport { WebhookSecretPanel } from '../../../components/WebhookSecretPanel'\n\ntype Webhook = {\n id: string\n name: string\n description: string | null\n url: string\n subscribedEvents: string[]\n httpMethod: 'POST' | 'PUT' | 'PATCH'\n isActive: boolean\n maxRetries: number\n timeoutMs: number\n rateLimitPerMinute: number\n autoDisableThreshold: number\n consecutiveFailures: number\n lastSuccessAt: string | null\n lastFailureAt: string | null\n customHeaders: Record<string, string> | null\n createdAt: string\n updatedAt: string\n maskedSecret: string\n previousSecretSetAt: string | null\n}\n\ntype DeliveryRow = {\n id: string\n webhookId: string\n eventType: string\n messageId: string\n status: string\n responseStatus: number | null\n errorMessage: string | null\n attemptNumber: number\n maxAttempts: number\n durationMs: number | null\n targetUrl: string\n enqueuedAt: string\n lastAttemptAt: string | null\n deliveredAt: string | null\n createdAt: string\n}\n\ntype DeliveryResponse = {\n items: DeliveryRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\ntype DeliveryDetail = DeliveryRow & {\n payload: Record<string, unknown>\n responseBody: string | null\n responseHeaders: Record<string, string> | null\n nextRetryAt: string | null\n updatedAt: string\n}\n\nconst statusVariantMap: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {\n delivered: 'default',\n pending: 'secondary',\n sending: 'outline',\n failed: 'destructive',\n expired: 'destructive',\n}\nconst DELIVERY_AUTO_REFRESH_INTERVAL_MS = 30000\n\nexport default function WebhookDetailPage() {\n const params = useParams()\n const pathname = usePathname()\n const router = useRouter()\n const t = useT()\n const webhookId = React.useMemo(() => resolveWebhookId(params?.id, pathname), [params?.id, pathname])\n\n const [webhook, setWebhook] = React.useState<Webhook | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isEditing, setIsEditing] = React.useState(false)\n\n const [deliveries, setDeliveries] = React.useState<DeliveryRow[]>([])\n const [deliveryPage, setDeliveryPage] = React.useState(1)\n const [deliveryTotal, setDeliveryTotal] = React.useState(0)\n const [deliveryTotalPages, setDeliveryTotalPages] = React.useState(1)\n const [deliveriesLoading, setDeliveriesLoading] = React.useState(false)\n const [isRefreshingDeliveries, setIsRefreshingDeliveries] = React.useState(false)\n const [testDelivery, setTestDelivery] = React.useState<DeliveryDetail | null>(null)\n const [selectedDelivery, setSelectedDelivery] = React.useState<DeliveryDetail | null>(null)\n const [selectedDeliveryLoading, setSelectedDeliveryLoading] = React.useState(false)\n const [revealedSecret, setRevealedSecret] = React.useState<string | null>(null)\n const refreshInFlightRef = React.useRef(false)\n const access = useWebhookFeatureAccess()\n\n const fetchWebhook = React.useCallback(async (options?: { silent?: boolean }) => {\n if (!webhookId) {\n setError(t('webhooks.errors.notFound'))\n setIsLoading(false)\n return\n }\n\n const silent = options?.silent === true\n if (!silent) {\n setIsLoading(true)\n }\n\n setError(null)\n\n try {\n const call = await apiCall<Webhook>(\n `/api/webhooks/${encodeURIComponent(webhookId)}`,\n undefined,\n { fallback: null },\n )\n\n if (call.ok && call.result) {\n setWebhook(call.result)\n return\n }\n\n setError(t('webhooks.errors.notFound'))\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError.message : t('webhooks.detail.loadError'))\n } finally {\n if (!silent) {\n setIsLoading(false)\n }\n }\n }, [t, webhookId])\n\n const fetchDeliveries = React.useCallback(async (options?: { silent?: boolean }) => {\n if (!webhookId) return\n\n const silent = options?.silent === true\n if (!silent) {\n setDeliveriesLoading(true)\n }\n\n try {\n const params = new URLSearchParams()\n params.set('webhookId', webhookId)\n params.set('page', String(deliveryPage))\n params.set('pageSize', '20')\n\n const fallback: DeliveryResponse = { items: [], total: 0, page: deliveryPage, pageSize: 20, totalPages: 1 }\n const call = await apiCall<DeliveryResponse>(\n `/api/webhooks/deliveries?${params.toString()}`,\n undefined,\n { fallback },\n )\n\n if (call.ok && call.result) {\n setDeliveries(call.result.items)\n setDeliveryTotal(call.result.total)\n setDeliveryTotalPages(call.result.totalPages)\n }\n } finally {\n if (!silent) {\n setDeliveriesLoading(false)\n }\n }\n }, [deliveryPage, webhookId])\n\n const fetchDeliveryDetail = React.useCallback(async (deliveryId: string): Promise<DeliveryDetail | null> => {\n const call = await apiCall<DeliveryDetail>(\n `/api/webhooks/deliveries/${encodeURIComponent(deliveryId)}`,\n undefined,\n { fallback: null },\n )\n\n if (!call.ok || !call.result) {\n return null\n }\n\n return call.result\n }, [])\n\n const refreshDeliveryState = React.useCallback(async () => {\n if (!webhookId || refreshInFlightRef.current) return\n\n refreshInFlightRef.current = true\n setIsRefreshingDeliveries(true)\n\n try {\n await Promise.all([\n fetchWebhook({ silent: true }),\n fetchDeliveries({ silent: true }),\n ])\n\n const detailIds = [selectedDelivery?.id, testDelivery?.id].filter(\n (value): value is string => typeof value === 'string' && value.length > 0,\n )\n\n if (detailIds.length > 0) {\n const details = await Promise.all(detailIds.map((deliveryId) => fetchDeliveryDetail(deliveryId)))\n const detailMap = new Map<string, DeliveryDetail>()\n\n for (const detail of details) {\n if (detail) {\n detailMap.set(detail.id, detail)\n }\n }\n\n if (selectedDelivery?.id) {\n setSelectedDelivery((current) => current?.id ? (detailMap.get(current.id) ?? current) : current)\n }\n\n if (testDelivery?.id) {\n setTestDelivery((current) => current?.id ? (detailMap.get(current.id) ?? current) : current)\n }\n }\n } finally {\n refreshInFlightRef.current = false\n setIsRefreshingDeliveries(false)\n }\n }, [fetchDeliveries, fetchDeliveryDetail, fetchWebhook, selectedDelivery?.id, testDelivery?.id, webhookId])\n\n React.useEffect(() => {\n void fetchWebhook()\n }, [fetchWebhook])\n\n React.useEffect(() => {\n if (!webhookId) return\n void fetchDeliveries()\n }, [fetchDeliveries, webhookId])\n\n useAppEvent('webhooks.delivery.*', (event) => {\n const eventWebhookId = typeof event.payload?.webhookId === 'string' ? event.payload.webhookId : null\n if (eventWebhookId !== webhookId) return\n void refreshDeliveryState()\n }, [refreshDeliveryState, webhookId])\n\n React.useEffect(() => {\n if (!webhookId || isEditing) return\n\n const intervalId = window.setInterval(() => {\n if (document.visibilityState !== 'visible') return\n void refreshDeliveryState()\n }, DELIVERY_AUTO_REFRESH_INTERVAL_MS)\n\n return () => {\n window.clearInterval(intervalId)\n }\n }, [isEditing, refreshDeliveryState, webhookId])\n\n const handleToggleActive = React.useCallback(async () => {\n if (!webhook) return\n try {\n const call = await apiCall<Webhook>(\n `/api/webhooks/${encodeURIComponent(webhook.id)}`,\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isActive: !webhook.isActive }),\n },\n { fallback: null },\n )\n if (call.ok) {\n setWebhook((prev) => prev ? { ...prev, isActive: !prev.isActive } : prev)\n flash(t('webhooks.form.updateSuccess'), 'success')\n void refreshDeliveryState()\n }\n } catch {\n flash(t('webhooks.form.updateError'), 'error')\n }\n }, [refreshDeliveryState, webhook, t])\n\n const handleRotateSecret = React.useCallback(async () => {\n if (!webhook) return\n try {\n const call = await apiCall<{ secret: string }>(\n `/api/webhooks/${encodeURIComponent(webhook.id)}/rotate-secret`,\n { method: 'POST' },\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n flash(t('webhooks.detail.rotateError'), 'error')\n return\n }\n setRevealedSecret(call.result.secret)\n flash(t('webhooks.detail.rotateSuccess'), 'success')\n void refreshDeliveryState()\n } catch {\n flash(t('webhooks.detail.rotateError'), 'error')\n }\n }, [refreshDeliveryState, t, webhook])\n\n const handleTest = React.useCallback(async () => {\n if (!webhook) return\n try {\n const call = await apiCall<{ delivery: DeliveryDetail }>(\n `/api/webhooks/${encodeURIComponent(webhook.id)}/test`,\n { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({}) },\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n flash(t('webhooks.detail.testError'), 'error')\n return\n }\n const deliveryStatus = call.result.delivery.status\n setTestDelivery(call.result.delivery)\n flash(\n deliveryStatus === 'delivered' ? t('webhooks.detail.testSuccess') : t('webhooks.detail.testQueued'),\n deliveryStatus === 'delivered' ? 'success' : 'error',\n )\n void refreshDeliveryState()\n } catch {\n flash(t('webhooks.detail.testError'), 'error')\n }\n }, [refreshDeliveryState, t, webhook])\n\n const handleRetryDelivery = React.useCallback(async (deliveryId: string) => {\n try {\n const call = await apiCall(\n `/api/webhooks/deliveries/${encodeURIComponent(deliveryId)}/retry`,\n { method: 'POST' },\n { fallback: null },\n )\n if (!call.ok) {\n flash(t('webhooks.deliveries.retryError'), 'error')\n return\n }\n flash(t('webhooks.deliveries.retrySuccess'), 'success')\n void refreshDeliveryState()\n } catch {\n flash(t('webhooks.deliveries.retryError'), 'error')\n }\n }, [refreshDeliveryState, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!webhook) return\n try {\n await deleteCrud(`webhooks/${encodeURIComponent(webhook.id)}`, { fallbackResult: null })\n flash(t('webhooks.list.deleteSuccess'), 'success')\n router.push('/backend/webhooks')\n } catch {\n flash(t('webhooks.list.deleteError'), 'error')\n }\n }, [router, t, webhook])\n\n const handleDeliveryOpen = React.useCallback(async (deliveryId: string) => {\n setSelectedDeliveryLoading(true)\n try {\n const detail = await fetchDeliveryDetail(deliveryId)\n if (!detail) {\n flash(t('webhooks.deliveries.loadError'), 'error')\n return\n }\n setSelectedDelivery(detail)\n } catch {\n flash(t('webhooks.deliveries.loadError'), 'error')\n } finally {\n setSelectedDeliveryLoading(false)\n }\n }, [fetchDeliveryDetail, t])\n\n const deliveryColumns = React.useMemo<ColumnDef<DeliveryRow>[]>(() => [\n { accessorKey: 'eventType', header: t('webhooks.deliveries.columns.event') },\n {\n accessorKey: 'status',\n header: t('webhooks.deliveries.columns.status'),\n cell: ({ row }) => (\n <Badge variant={statusVariantMap[row.original.status] ?? 'secondary'}>\n {t(`webhooks.deliveries.status.${row.original.status}` as Parameters<typeof t>[0])}\n </Badge>\n ),\n },\n {\n accessorKey: 'responseStatus',\n header: t('webhooks.deliveries.columns.responseStatus'),\n cell: ({ row }) => row.original.responseStatus ?? '\u2014',\n },\n {\n accessorKey: 'attemptNumber',\n header: t('webhooks.deliveries.columns.attempts'),\n cell: ({ row }) => `${row.original.attemptNumber}/${row.original.maxAttempts}`,\n },\n {\n accessorKey: 'durationMs',\n header: t('webhooks.deliveries.columns.duration'),\n cell: ({ row }) => row.original.durationMs != null ? `${row.original.durationMs}ms` : '\u2014',\n },\n {\n accessorKey: 'createdAt',\n header: t('webhooks.deliveries.columns.enqueuedAt'),\n cell: ({ row }) => {\n try { return new Date(row.original.enqueuedAt).toLocaleString() }\n catch { return '\u2014' }\n },\n },\n ], [t])\n\n const fields = React.useMemo(() => buildWebhookFormFields(t), [t])\n const groups = React.useMemo(() => buildWebhookFormGroups(t), [t])\n const contentHeader = React.useMemo(() => buildWebhookFormContentHeader(t), [t])\n const menuActions = React.useMemo(() => {\n const items: Array<{ id: string; label: string; onSelect: () => void }> = []\n const isActive = webhook?.isActive ?? false\n\n if (access.canManage) {\n items.push(\n {\n id: 'edit',\n label: t('webhooks.list.actions.edit'),\n onSelect: () => setIsEditing(true),\n },\n {\n id: 'toggle-active',\n label: isActive ? t('webhooks.detail.actions.deactivate') : t('webhooks.detail.actions.activate'),\n onSelect: () => { void handleToggleActive() },\n },\n )\n }\n\n if (access.canSecrets) {\n items.push({\n id: 'rotate-secret',\n label: t('webhooks.detail.actions.rotateSecret'),\n onSelect: () => { void handleRotateSecret() },\n })\n }\n\n if (access.canTest) {\n items.push({\n id: 'test',\n label: t('webhooks.detail.actions.test'),\n onSelect: () => { void handleTest() },\n })\n }\n\n if (access.canManage) {\n items.push({\n id: 'delete',\n label: t('webhooks.list.actions.delete'),\n onSelect: () => { void handleDelete() },\n })\n }\n\n return items\n }, [access.canManage, access.canSecrets, access.canTest, handleDelete, handleRotateSecret, handleTest, handleToggleActive, t, webhook?.isActive])\n\n if (isLoading) return <Page><PageBody><LoadingMessage label={t('webhooks.detail.loading')} /></PageBody></Page>\n if (error || !webhook) return <Page><PageBody><ErrorMessage label={error ?? t('webhooks.errors.notFound')} /></PageBody></Page>\n\n if (isEditing) {\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={t('webhooks.form.title.edit')}\n backHref={`/backend/webhooks/${webhook.id}`}\n fields={fields}\n groups={groups}\n initialValues={createWebhookInitialValues(webhook)}\n submitLabel={t('common.save')}\n cancelHref={`/backend/webhooks/${webhook.id}`}\n contentHeader={contentHeader}\n onDelete={access.canManage ? handleDelete : undefined}\n onSubmit={async (values) => {\n const payload = normalizeWebhookFormPayload(values as WebhookFormValues, t)\n await updateCrud(`webhooks/${encodeURIComponent(webhook.id)}`, payload)\n flash(t('webhooks.form.updateSuccess'), 'success')\n setIsEditing(false)\n await refreshDeliveryState()\n }}\n />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n {revealedSecret ? (\n <WebhookSecretPanel secret={revealedSecret} onClose={() => setRevealedSecret(null)} />\n ) : null}\n <FormHeader\n mode=\"detail\"\n title={webhook.name}\n entityTypeLabel={t('webhooks.nav.title')}\n statusBadge={\n <Badge\n className={webhook.isActive\n ? 'border-transparent bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'\n : 'border-transparent bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'}\n >\n {webhook.isActive ? t('webhooks.list.status.active') : t('webhooks.list.status.inactive')}\n </Badge>\n }\n backHref=\"/backend/webhooks\"\n menuActions={menuActions}\n />\n\n <div className=\"mt-6 space-y-4\">\n {!access.isLoading && !access.canManage && !access.canSecrets && !access.canTest ? (\n <Notice compact>{t('webhooks.detail.readOnlyTip')}</Notice>\n ) : null}\n <div className=\"grid gap-3 lg:grid-cols-2\">\n <Notice compact>{t('webhooks.detail.deliveryTip')}</Notice>\n <Notice compact>{t('webhooks.detail.signatureTip')}</Notice>\n </div>\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.url')}:</span>\n <code className=\"ml-2 text-xs break-all\">{webhook.url}</code>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.httpMethod')}:</span>\n <span className=\"ml-2\">{webhook.httpMethod}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.subscribedEvents')}:</span>\n <span className=\"ml-2 text-xs\">{webhook.subscribedEvents.join(', ')}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.maxRetries')}:</span>\n <span className=\"ml-2\">{webhook.maxRetries}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.rateLimitPerMinute')}:</span>\n <span className=\"ml-2\">{webhook.rateLimitPerMinute}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.autoDisableThreshold')}:</span>\n <span className=\"ml-2\">{webhook.autoDisableThreshold}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.detail.consecutiveFailures')}:</span>\n <span className=\"ml-2\">{webhook.consecutiveFailures}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.form.secret')}:</span>\n <span className=\"ml-2 inline-flex items-center gap-2\">\n <span className=\"font-mono text-xs\">{webhook.maskedSecret}</span>\n {access.canSecrets ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 px-2 text-xs\"\n onClick={() => { void handleRotateSecret() }}\n >\n <RotateCw className=\"mr-1.5 size-3.5\" />\n {t('webhooks.detail.actions.rotateSecret')}\n </Button>\n ) : null}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.list.columns.lastDelivery')}:</span>\n <span className=\"ml-2\">{webhook.lastSuccessAt ?? webhook.lastFailureAt ?? '\u2014'}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('webhooks.detail.previousSecretSetAt')}:</span>\n <span className=\"ml-2\">{webhook.previousSecretSetAt ?? '\u2014'}</span>\n </div>\n <div className=\"col-span-2\">\n <span className=\"text-muted-foreground\">{t('webhooks.form.customHeaders')}:</span>\n <pre className=\"mt-2 rounded border bg-muted/40 p-3 text-xs\">\n {webhook.customHeaders ? JSON.stringify(webhook.customHeaders, null, 2) : '\u2014'}\n </pre>\n </div>\n </div>\n </div>\n\n {testDelivery ? (\n <div className=\"mt-8 rounded-lg border bg-card p-4\">\n <h2 className=\"text-sm font-semibold\">{t('webhooks.detail.testResult')}</h2>\n <div className=\"mt-3 grid gap-2 text-sm\">\n <div>{t('webhooks.deliveries.columns.status')}: {testDelivery.status}</div>\n <div>{t('webhooks.deliveries.columns.responseStatus')}: {testDelivery.responseStatus ?? '\u2014'}</div>\n <div>{t('webhooks.deliveries.columns.duration')}: {testDelivery.durationMs != null ? `${testDelivery.durationMs}ms` : '\u2014'}</div>\n <pre className=\"overflow-auto rounded border bg-muted/40 p-3 text-xs\">\n {JSON.stringify(testDelivery.payload, null, 2)}\n </pre>\n </div>\n </div>\n ) : null}\n\n <div className=\"mt-8\">\n <DataTable\n title={t('webhooks.deliveries.title')}\n actions={(\n <div className=\"flex items-center gap-2\">\n <span className=\"hidden text-xs text-muted-foreground md:inline\">\n {t('webhooks.deliveries.autoRefreshHint')}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => { void refreshDeliveryState() }}\n disabled={isRefreshingDeliveries}\n >\n {isRefreshingDeliveries\n ? t('webhooks.deliveries.refreshing')\n : t('webhooks.deliveries.refresh')}\n </Button>\n </div>\n )}\n columns={deliveryColumns}\n data={deliveries}\n onRowClick={(row) => { void handleDeliveryOpen(row.id) }}\n rowActions={(row) => (\n access.canManage && (row.status === 'failed' || row.status === 'expired')\n ? (\n <RowActions\n items={[\n {\n id: 'retry',\n label: t('webhooks.deliveries.actions.retry'),\n onSelect: () => { void handleRetryDelivery(row.id) },\n },\n ]}\n />\n )\n : null\n )}\n perspective={{ tableId: 'webhooks.deliveries' }}\n pagination={{\n page: deliveryPage,\n pageSize: 20,\n total: deliveryTotal,\n totalPages: deliveryTotalPages,\n onPageChange: setDeliveryPage,\n }}\n isLoading={deliveriesLoading || isRefreshingDeliveries}\n />\n </div>\n\n {selectedDelivery || selectedDeliveryLoading ? (\n <div className=\"mt-6 rounded-lg border bg-card p-4\">\n <h2 className=\"text-sm font-semibold\">{t('webhooks.deliveries.detailTitle')}</h2>\n {selectedDeliveryLoading || !selectedDelivery ? (\n <div className=\"mt-3 text-sm text-muted-foreground\">{t('common.loading')}</div>\n ) : (\n <div className=\"mt-3 space-y-4 text-sm\">\n <div>{t('webhooks.deliveries.columns.status')}: {selectedDelivery.status}</div>\n <div>{t('webhooks.deliveries.columns.responseStatus')}: {selectedDelivery.responseStatus ?? '\u2014'}</div>\n <div>{t('webhooks.deliveries.columns.duration')}: {selectedDelivery.durationMs != null ? `${selectedDelivery.durationMs}ms` : '\u2014'}</div>\n <div>\n <div className=\"mb-2 font-medium\">{t('webhooks.deliveries.requestBody')}</div>\n <pre className=\"overflow-auto rounded border bg-muted/40 p-3 text-xs\">\n {JSON.stringify(selectedDelivery.payload, null, 2)}\n </pre>\n </div>\n <div>\n <div className=\"mb-2 font-medium\">{t('webhooks.deliveries.responseBody')}</div>\n <pre className=\"overflow-auto rounded border bg-muted/40 p-3 text-xs\">\n {selectedDelivery.responseBody ?? '\u2014'}\n </pre>\n </div>\n <div>\n <div className=\"mb-2 font-medium\">{t('webhooks.deliveries.responseHeaders')}</div>\n <pre className=\"overflow-auto rounded border bg-muted/40 p-3 text-xs\">\n {selectedDelivery.responseHeaders ? JSON.stringify(selectedDelivery.responseHeaders, null, 2) : '\u2014'}\n </pre>\n </div>\n </div>\n )}\n </div>\n ) : null}\n </PageBody>\n </Page>\n )\n}\n\nfunction resolveWebhookId(paramValue: string | string[] | undefined, pathname: string | null): string | null {\n if (typeof paramValue === 'string' && paramValue.trim().length > 0) {\n return paramValue\n }\n\n if (Array.isArray(paramValue)) {\n const first = paramValue.find((value) => typeof value === 'string' && value.trim().length > 0)\n if (first) return first\n }\n\n if (typeof pathname === 'string') {\n const match = pathname.match(/\\/backend\\/webhooks\\/([^/?#]+)/)\n if (match?.[1]) {\n return decodeURIComponent(match[1])\n }\n }\n\n return null\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["params"]
7
7
  }
@@ -14,7 +14,7 @@ import {
14
14
  createWebhookInitialValues,
15
15
  normalizeWebhookFormPayload
16
16
  } from "../../../components/webhook-form-config.js";
17
- import { WebhookSecretPanel } from "../WebhookSecretPanel.js";
17
+ import { WebhookSecretPanel } from "../../../components/WebhookSecretPanel.js";
18
18
  function CreateWebhookPage() {
19
19
  const router = useRouter();
20
20
  const t = useT();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/webhooks/backend/webhooks/create/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n buildWebhookFormContentHeader,\n buildWebhookFormFields,\n buildWebhookFormGroups,\n createWebhookInitialValues,\n normalizeWebhookFormPayload,\n type WebhookFormValues,\n} from '../../../components/webhook-form-config'\nimport { WebhookSecretPanel } from '../WebhookSecretPanel'\n\nexport default function CreateWebhookPage() {\n const router = useRouter()\n const t = useT()\n const [createdSecret, setCreatedSecret] = React.useState<string | null>(null)\n\n const fields = React.useMemo(() => buildWebhookFormFields(t), [t])\n const groups = React.useMemo(() => buildWebhookFormGroups(t), [t])\n const contentHeader = React.useMemo(() => buildWebhookFormContentHeader(t), [t])\n\n if (createdSecret) {\n return (\n <Page>\n <PageBody>\n <WebhookSecretPanel secret={createdSecret} onClose={() => router.push('/backend/webhooks')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={t('webhooks.form.title.create')}\n backHref=\"/backend/webhooks\"\n fields={fields}\n groups={groups}\n initialValues={createWebhookInitialValues()}\n submitLabel={t('common.create')}\n cancelHref=\"/backend/webhooks\"\n contentHeader={contentHeader}\n onSubmit={async (values) => {\n const payload = normalizeWebhookFormPayload(values as WebhookFormValues, t)\n const { result } = await createCrud<{ id?: string; secret?: string }>('webhooks', payload)\n const secret = typeof result?.secret === 'string' ? result.secret : null\n if (!secret) {\n throw new Error(t('webhooks.form.secretMissing'))\n }\n setCreatedSecret(secret)\n flash(t('webhooks.form.createSuccess'), 'success')\n }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n buildWebhookFormContentHeader,\n buildWebhookFormFields,\n buildWebhookFormGroups,\n createWebhookInitialValues,\n normalizeWebhookFormPayload,\n type WebhookFormValues,\n} from '../../../components/webhook-form-config'\nimport { WebhookSecretPanel } from '../../../components/WebhookSecretPanel'\n\nexport default function CreateWebhookPage() {\n const router = useRouter()\n const t = useT()\n const [createdSecret, setCreatedSecret] = React.useState<string | null>(null)\n\n const fields = React.useMemo(() => buildWebhookFormFields(t), [t])\n const groups = React.useMemo(() => buildWebhookFormGroups(t), [t])\n const contentHeader = React.useMemo(() => buildWebhookFormContentHeader(t), [t])\n\n if (createdSecret) {\n return (\n <Page>\n <PageBody>\n <WebhookSecretPanel secret={createdSecret} onClose={() => router.push('/backend/webhooks')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={t('webhooks.form.title.create')}\n backHref=\"/backend/webhooks\"\n fields={fields}\n groups={groups}\n initialValues={createWebhookInitialValues()}\n submitLabel={t('common.create')}\n cancelHref=\"/backend/webhooks\"\n contentHeader={contentHeader}\n onSubmit={async (values) => {\n const payload = normalizeWebhookFormPayload(values as WebhookFormValues, t)\n const { result } = await createCrud<{ id?: string; secret?: string }>('webhooks', payload)\n const secret = typeof result?.secret === 'string' ? result.secret : null\n if (!secret) {\n throw new Error(t('webhooks.form.secretMissing'))\n }\n setCreatedSecret(secret)\n flash(t('webhooks.form.createSuccess'), 'success')\n }}\n />\n </PageBody>\n </Page>\n )\n}\n"],
5
5
  "mappings": ";AA+BU;AA9BV,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,MAAM,gBAAgB;AAC/B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,0BAA0B;AAEpB,SAAR,oBAAqC;AAC1C,QAAM,SAAS,UAAU;AACzB,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAwB,IAAI;AAE5E,QAAM,SAAS,MAAM,QAAQ,MAAM,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC;AACjE,QAAM,SAAS,MAAM,QAAQ,MAAM,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC;AACjE,QAAM,gBAAgB,MAAM,QAAQ,MAAM,8BAA8B,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/E,MAAI,eAAe;AACjB,WACE,oBAAC,QACC,8BAAC,YACC,8BAAC,sBAAmB,QAAQ,eAAe,SAAS,MAAM,OAAO,KAAK,mBAAmB,GAAG,GAC9F,GACF;AAAA,EAEJ;AAEA,SACE,oBAAC,QACC,8BAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,4BAA4B;AAAA,MACrC,UAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,eAAe,2BAA2B;AAAA,MAC1C,aAAa,EAAE,eAAe;AAAA,MAC9B,YAAW;AAAA,MACX;AAAA,MACA,UAAU,OAAO,WAAW;AAC1B,cAAM,UAAU,4BAA4B,QAA6B,CAAC;AAC1E,cAAM,EAAE,OAAO,IAAI,MAAM,WAA6C,YAAY,OAAO;AACzF,cAAM,SAAS,OAAO,QAAQ,WAAW,WAAW,OAAO,SAAS;AACpE,YAAI,CAAC,QAAQ;AACX,gBAAM,IAAI,MAAM,EAAE,6BAA6B,CAAC;AAAA,QAClD;AACA,yBAAiB,MAAM;AACvB,cAAM,EAAE,6BAA6B,GAAG,SAAS;AAAA,MACnD;AAAA;AAAA,EACF,GACF,GACF;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../../../src/modules/webhooks/backend/webhooks/WebhookSecretPanel.tsx"],
3
+ "sources": ["../../../../src/modules/webhooks/components/WebhookSecretPanel.tsx"],
4
4
  "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Check, Copy } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Notice } from '@open-mercato/ui/primitives/Notice'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype WebhookSecretPanelProps = {\n secret: string\n onClose?: () => void\n}\n\nexport function WebhookSecretPanel({ secret, onClose }: WebhookSecretPanelProps) {\n const t = useT()\n const [copied, setCopied] = React.useState(false)\n\n async function handleCopySecret() {\n await navigator.clipboard.writeText(secret)\n setCopied(true)\n flash(t('webhooks.form.secretCopied'), 'success')\n }\n\n React.useEffect(() => {\n if (!copied) return\n const timeout = window.setTimeout(() => setCopied(false), 2000)\n return () => window.clearTimeout(timeout)\n }, [copied])\n\n const CopyIcon = copied ? Check : Copy\n\n return (\n <div className=\"mx-auto w-full max-w-3xl rounded-xl border bg-card shadow-sm\">\n <div className=\"border-b p-6\">\n <h1 className=\"text-lg font-semibold leading-7\">{t('webhooks.form.secret')}</h1>\n <p className=\"mt-2 text-sm text-muted-foreground\">{t('webhooks.form.secretVisibleOnce')}</p>\n </div>\n <div className=\"space-y-4 p-6\">\n <Notice variant=\"warning\" compact>\n {t('webhooks.form.secretUsageTip')}\n </Notice>\n <div className=\"flex items-start gap-3 rounded-md border bg-muted/40 p-4\">\n <div className=\"min-w-0 flex-1 font-mono text-sm break-all\">\n {secret}\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"shrink-0\"\n aria-label={t('webhooks.form.secretCopy')}\n title={t('webhooks.form.secretCopy')}\n onClick={() => { void handleCopySecret() }}\n >\n <CopyIcon className={`h-4 w-4 ${copied ? 'text-green-600' : ''}`} />\n </Button>\n </div>\n <div className=\"grid gap-3 md:grid-cols-2\">\n <Notice compact>{t('webhooks.form.secretVerificationTip')}</Notice>\n <Notice compact>{t('webhooks.form.secretRotationTip')}</Notice>\n </div>\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { void handleCopySecret() }}>\n <CopyIcon className={`mr-2 h-4 w-4 ${copied ? 'text-green-600' : ''}`} />\n {t('webhooks.form.secretCopy')}\n </Button>\n {onClose ? (\n <Button type=\"button\" onClick={onClose}>\n {t('common.close')}\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n )\n}\n\nexport default WebhookSecretPanel\n"],
5
5
  "mappings": ";AAkCM,SACE,KADF;AAhCN,YAAY,WAAW;AACvB,SAAS,OAAO,YAAY;AAC5B,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,YAAY;AAOd,SAAS,mBAAmB,EAAE,QAAQ,QAAQ,GAA4B;AAC/E,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAEhD,iBAAe,mBAAmB;AAChC,UAAM,UAAU,UAAU,UAAU,MAAM;AAC1C,cAAU,IAAI;AACd,UAAM,EAAE,4BAA4B,GAAG,SAAS;AAAA,EAClD;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,OAAQ;AACb,UAAM,UAAU,OAAO,WAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAC9D,WAAO,MAAM,OAAO,aAAa,OAAO;AAAA,EAC1C,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,WAAW,SAAS,QAAQ;AAElC,SACE,qBAAC,SAAI,WAAU,gEACb;AAAA,yBAAC,SAAI,WAAU,gBACb;AAAA,0BAAC,QAAG,WAAU,mCAAmC,YAAE,sBAAsB,GAAE;AAAA,MAC3E,oBAAC,OAAE,WAAU,sCAAsC,YAAE,iCAAiC,GAAE;AAAA,OAC1F;AAAA,IACA,qBAAC,SAAI,WAAU,iBACb;AAAA,0BAAC,UAAO,SAAQ,WAAU,SAAO,MAC9B,YAAE,8BAA8B,GACnC;AAAA,MACA,qBAAC,SAAI,WAAU,4DACb;AAAA,4BAAC,SAAI,WAAU,8CACZ,kBACH;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,WAAU;AAAA,YACV,cAAY,EAAE,0BAA0B;AAAA,YACxC,OAAO,EAAE,0BAA0B;AAAA,YACnC,SAAS,MAAM;AAAE,mBAAK,iBAAiB;AAAA,YAAE;AAAA,YAEzC,8BAAC,YAAS,WAAW,WAAW,SAAS,mBAAmB,EAAE,IAAI;AAAA;AAAA,QACpE;AAAA,SACF;AAAA,MACA,qBAAC,SAAI,WAAU,6BACb;AAAA,4BAAC,UAAO,SAAO,MAAE,YAAE,qCAAqC,GAAE;AAAA,QAC1D,oBAAC,UAAO,SAAO,MAAE,YAAE,iCAAiC,GAAE;AAAA,SACxD;AAAA,MACA,qBAAC,SAAI,WAAU,oCACb;AAAA,6BAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,MAAM;AAAE,eAAK,iBAAiB;AAAA,QAAE,GAC/E;AAAA,8BAAC,YAAS,WAAW,gBAAgB,SAAS,mBAAmB,EAAE,IAAI;AAAA,UACtE,EAAE,0BAA0B;AAAA,WAC/B;AAAA,QACC,UACC,oBAAC,UAAO,MAAK,UAAS,SAAS,SAC5B,YAAE,cAAc,GACnB,IACE;AAAA,SACN;AAAA,OACF;AAAA,KACF;AAEJ;AAEA,IAAO,6BAAQ;",
6
6
  "names": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/webhooks",
3
- "version": "0.4.10-develop.1139.189d8acf38",
3
+ "version": "0.4.10-develop.1149.dd15f06da6",
4
4
  "description": "Webhooks module for Open Mercato — Standard Webhooks compliant outbound/inbound delivery",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -69,18 +69,18 @@
69
69
  }
70
70
  },
71
71
  "dependencies": {
72
- "@open-mercato/core": "0.4.10-develop.1139.189d8acf38",
73
- "@open-mercato/queue": "0.4.10-develop.1139.189d8acf38",
74
- "@open-mercato/ui": "0.4.10-develop.1139.189d8acf38"
72
+ "@open-mercato/core": "0.4.10-develop.1149.dd15f06da6",
73
+ "@open-mercato/queue": "0.4.10-develop.1149.dd15f06da6",
74
+ "@open-mercato/ui": "0.4.10-develop.1149.dd15f06da6"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "@mikro-orm/postgresql": "^6.6.2",
78
- "@open-mercato/shared": "0.4.10-develop.1139.189d8acf38",
78
+ "@open-mercato/shared": "0.4.10-develop.1149.dd15f06da6",
79
79
  "react": "^19.0.0",
80
80
  "react-dom": "^19.0.0"
81
81
  },
82
82
  "devDependencies": {
83
- "@open-mercato/shared": "0.4.10-develop.1139.189d8acf38",
83
+ "@open-mercato/shared": "0.4.10-develop.1149.dd15f06da6",
84
84
  "@types/jest": "^30.0.0",
85
85
  "esbuild": "^0.25.0",
86
86
  "glob": "^13.0.6",
@@ -26,7 +26,7 @@ import {
26
26
  type WebhookFormValues,
27
27
  } from '../../../components/webhook-form-config'
28
28
  import { useWebhookFeatureAccess } from '../useWebhookFeatureAccess'
29
- import { WebhookSecretPanel } from '../WebhookSecretPanel'
29
+ import { WebhookSecretPanel } from '../../../components/WebhookSecretPanel'
30
30
 
31
31
  type Webhook = {
32
32
  id: string
@@ -14,7 +14,7 @@ import {
14
14
  normalizeWebhookFormPayload,
15
15
  type WebhookFormValues,
16
16
  } from '../../../components/webhook-form-config'
17
- import { WebhookSecretPanel } from '../WebhookSecretPanel'
17
+ import { WebhookSecretPanel } from '../../../components/WebhookSecretPanel'
18
18
 
19
19
  export default function CreateWebhookPage() {
20
20
  const router = useRouter()