@burdenoff/microfe-devportal 2026.728.2 → 2026.801.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/__tests__/utils/renderWithProviders.d.ts +1 -1
  2. package/dist/__tests__/utils/renderWithProviders.d.ts.map +1 -1
  3. package/dist/devportal/AdminDevportalRoutes.js +14 -14
  4. package/dist/devportal/AdminDevportalRoutes.js.map +1 -1
  5. package/dist/devportal/DevportalRoutes.js +115 -115
  6. package/dist/devportal/DevportalRoutes.js.map +1 -1
  7. package/dist/devportal/components/SectionNav.js +0 -1
  8. package/dist/devportal/components/SectionNav.js.map +1 -1
  9. package/dist/devportal/components/TelemetryErrorBoundary.js +7 -7
  10. package/dist/devportal/components/TelemetryErrorBoundary.js.map +1 -1
  11. package/dist/devportal/pages/AnalyticsDashboardPage.js +41 -41
  12. package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
  13. package/dist/devportal/pages/AppLogsPage.js +27 -27
  14. package/dist/devportal/pages/AppLogsPage.js.map +1 -1
  15. package/dist/devportal/pages/ApplicationDetailPage.js +929 -929
  16. package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
  17. package/dist/devportal/pages/ApplicationVersionsPage.js +39 -39
  18. package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
  19. package/dist/devportal/pages/CreateWebhookPage.js +55 -55
  20. package/dist/devportal/pages/CreateWebhookPage.js.map +1 -1
  21. package/dist/devportal/pages/EditApplicationPage.js +36 -36
  22. package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
  23. package/dist/devportal/pages/EditPhysicalItemPage.js +40 -40
  24. package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -1
  25. package/dist/devportal/pages/InvitationDecisionPage.js +25 -25
  26. package/dist/devportal/pages/InvitationDecisionPage.js.map +1 -1
  27. package/dist/devportal/pages/ListingPreviewPage.js +36 -36
  28. package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
  29. package/dist/devportal/pages/OAuthCallbackPage.js +10 -10
  30. package/dist/devportal/pages/OAuthCallbackPage.js.map +1 -1
  31. package/dist/devportal/pages/OAuthClientDetailPage.js +27 -27
  32. package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
  33. package/dist/devportal/pages/OrganizationDetailPage.js +329 -329
  34. package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
  35. package/dist/devportal/pages/OrganizationMembersPage.js +181 -181
  36. package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
  37. package/dist/devportal/pages/PublishPage.js +43 -43
  38. package/dist/devportal/pages/PublishPage.js.map +1 -1
  39. package/dist/devportal/pages/RevenueDashboardPage.js +65 -65
  40. package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -1
  41. package/dist/devportal/pages/StoreSubmissionPage.js +126 -126
  42. package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
  43. package/dist/devportal/pages/WebhookDetailPage.js +28 -28
  44. package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
  45. package/dist/devportal/pages/WebhooksPage.js +47 -47
  46. package/dist/devportal/pages/WebhooksPage.js.map +1 -1
  47. package/dist/devportal/providers/DevportalProvider.js +12 -12
  48. package/dist/devportal/providers/DevportalProvider.js.map +1 -1
  49. package/dist/node_modules/react-router/dist/production/lib/components.js +124 -0
  50. package/dist/node_modules/react-router/dist/production/lib/components.js.map +1 -0
  51. package/dist/node_modules/react-router/dist/production/lib/context.js +32 -0
  52. package/dist/node_modules/react-router/dist/production/lib/context.js.map +1 -0
  53. package/dist/node_modules/react-router/dist/production/lib/dom/dom.js +83 -0
  54. package/dist/node_modules/react-router/dist/production/lib/dom/dom.js.map +1 -0
  55. package/dist/node_modules/react-router/dist/production/lib/dom/lib.js +358 -0
  56. package/dist/node_modules/react-router/dist/production/lib/dom/lib.js.map +1 -0
  57. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/components.js +188 -0
  58. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/components.js.map +1 -0
  59. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/invariant.js +8 -0
  60. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/invariant.js.map +1 -0
  61. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/links.js +75 -0
  62. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/links.js.map +1 -0
  63. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/markup.js +15 -0
  64. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/markup.js.map +1 -0
  65. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/routeModules.js +18 -0
  66. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/routeModules.js.map +1 -0
  67. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/single-fetch.js +10 -0
  68. package/dist/node_modules/react-router/dist/production/lib/dom/ssr/single-fetch.js.map +1 -0
  69. package/dist/node_modules/react-router/dist/production/lib/errors.js +19 -0
  70. package/dist/node_modules/react-router/dist/production/lib/errors.js.map +1 -0
  71. package/dist/node_modules/react-router/dist/production/lib/hooks.js +296 -0
  72. package/dist/node_modules/react-router/dist/production/lib/hooks.js.map +1 -0
  73. package/dist/node_modules/react-router/dist/production/lib/router/history.js +166 -0
  74. package/dist/node_modules/react-router/dist/production/lib/router/history.js.map +1 -0
  75. package/dist/node_modules/react-router/dist/production/lib/router/router.js +33 -0
  76. package/dist/node_modules/react-router/dist/production/lib/router/router.js.map +1 -0
  77. package/dist/node_modules/react-router/dist/production/lib/router/url.js +9 -0
  78. package/dist/node_modules/react-router/dist/production/lib/router/url.js.map +1 -0
  79. package/dist/node_modules/react-router/dist/production/lib/router/utils.js +236 -0
  80. package/dist/node_modules/react-router/dist/production/lib/router/utils.js.map +1 -0
  81. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"StoreSubmissionPage.js","names":[],"sources":["../../../src/devportal/pages/StoreSubmissionPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useState } from \"react\";\nimport { useNavigate, useParams } from \"react-router-dom\";\nimport {\n ArrowLeft,\n CheckCircle2,\n Clock,\n RefreshCw,\n XCircle,\n AlertTriangle,\n Store as StoreIcon,\n EyeOff,\n Package,\n GitBranch,\n} from \"lucide-react\";\nimport { graphqlFetch } from \"@burdenoff/fe-libs/shared/graphql\";\nimport { IllustratedEmptyState, PagePurpose } from \"@burdenoff/fe-libs/ui\";\nimport { useDevportalEventBus } from \"../events\";\nimport { useTr } from \"../utils/i18n\";\nimport { useDevportalPermission } from \"../hooks\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\n\ntype SubmissionStatus =\n | \"DRAFT\"\n | \"PENDING_REVIEW\"\n | \"PUBLISHED\"\n | \"UNLISTED\"\n | \"REJECTED\"\n | \"UNKNOWN\";\n\ntype StoreSubmission = {\n productId: string;\n status: SubmissionStatus;\n submittedAt: string | null;\n reviewedAt: string | null;\n publishedAt: string | null;\n reviewNotes: string | null;\n rejectedReason: string | null;\n};\n\ntype ApplicationVersion = {\n id: string;\n version: string;\n status: string;\n releaseNotes: string | null;\n packageUrl: string | null;\n};\n\ntype Application = {\n id: string;\n name: string;\n slug: string;\n status: string;\n appType: string;\n reviewStatus: string;\n reviewNotes: string | null;\n iconUrl: string | null;\n submittedAt: string | null;\n metadata: Record<string, unknown> | null;\n versions: { edges: { node: ApplicationVersion }[] };\n};\n\nconst APP_QUERY = /* GraphQL */ `\n query DevportalAppForSubmission($id: ID!) {\n application(id: $id) {\n id\n name\n slug\n status\n appType\n reviewStatus\n reviewNotes\n iconUrl\n submittedAt\n metadata\n versions {\n edges {\n node {\n id\n version\n status\n releaseNotes\n packageUrl\n }\n }\n }\n }\n }\n`;\n\nconst SUBMIT_MUTATION = /* GraphQL */ `\n mutation DevportalSubmitForReview($id: ID!, $notes: String) {\n submitApplicationForReview(id: $id, notes: $notes) {\n id\n status\n submittedAt\n metadata\n }\n }\n`;\n\nconst UNLIST_MUTATION = /* GraphQL */ `\n mutation DevportalUnlistProduct($id: ID!) {\n unlistProduct(id: $id) {\n id\n status\n }\n }\n`;\n\nconst PUBLISH_VERSION_MUTATION = /* GraphQL */ `\n mutation DevportalPublishVersion($id: ID!) {\n publishApplicationVersion(id: $id) {\n id\n status\n }\n }\n`;\n\nfunction inferSubmission(app: Application | null): StoreSubmission | null {\n if (!app) return null;\n const metadata = (app.metadata ?? {}) as Record<string, unknown>;\n const productId =\n typeof metadata.storeProductId === \"string\" ? metadata.storeProductId : \"\";\n\n // Prefer metadata status (most accurate), but fall back to app.status so the\n // page reflects reality even when the metadata write failed after submission.\n const rawMetaStatus =\n typeof metadata.storeSubmissionStatus === \"string\"\n ? metadata.storeSubmissionStatus\n : null;\n\n let status: SubmissionStatus;\n if (\n rawMetaStatus === \"PENDING_REVIEW\" ||\n rawMetaStatus === \"PUBLISHED\" ||\n rawMetaStatus === \"REJECTED\" ||\n rawMetaStatus === \"DRAFT\"\n ) {\n status = rawMetaStatus;\n } else if (app.status === \"SUBMITTED\") {\n status = \"PENDING_REVIEW\";\n } else if (app.status === \"PUBLISHED\") {\n status = \"PUBLISHED\";\n } else if (app.status === \"REJECTED\") {\n status = \"REJECTED\";\n } else {\n status = \"UNKNOWN\";\n }\n\n const metaSubmittedAt =\n typeof metadata.storeSubmissionAt === \"string\"\n ? metadata.storeSubmissionAt\n : null;\n\n return {\n productId,\n status,\n submittedAt: metaSubmittedAt ?? app.submittedAt,\n reviewedAt: null,\n publishedAt: null,\n reviewNotes: null,\n rejectedReason: null,\n };\n}\n\ntype StatusMeta = {\n i18nKey: string;\n fallback: string;\n icon: typeof CheckCircle2;\n tone: string;\n};\n\nconst statusLabels: Record<SubmissionStatus, StatusMeta> = {\n DRAFT: {\n i18nKey: \"devportal.storeSubmission.status.draft\",\n fallback: \"Draft\",\n icon: Clock,\n tone: \"text-text-secondary\",\n },\n PENDING_REVIEW: {\n i18nKey: \"devportal.storeSubmission.status.pending\",\n fallback: \"Pending review\",\n icon: Clock,\n tone: \"text-status-warning-text\",\n },\n PUBLISHED: {\n i18nKey: \"devportal.storeSubmission.status.published\",\n fallback: \"Published\",\n icon: CheckCircle2,\n tone: \"text-status-success-text\",\n },\n UNLISTED: {\n i18nKey: \"devportal.storeSubmission.status.unlisted\",\n fallback: \"Unlisted\",\n icon: EyeOff,\n tone: \"text-text-secondary\",\n },\n REJECTED: {\n i18nKey: \"devportal.storeSubmission.status.rejected\",\n fallback: \"Rejected\",\n icon: XCircle,\n tone: \"text-status-danger-text\",\n },\n UNKNOWN: {\n i18nKey: \"devportal.storeSubmission.status.unknown\",\n fallback: \"Not yet submitted\",\n icon: AlertTriangle,\n tone: \"text-text-tertiary\",\n },\n};\n\nexport const StoreSubmissionPage: FC = () => {\n const { appId } = useParams<{ appId: string }>();\n const navigate = useNavigate();\n const bus = useDevportalEventBus();\n const tr = useTr();\n const submitPerm = useDevportalPermission(DEVPORTAL_PERMISSIONS.APP_SUBMIT);\n const [app, setApp] = useState<Application | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n const [unlisting, setUnlisting] = useState(false);\n const [showUnlistConfirm, setShowUnlistConfirm] = useState(false);\n const [overrideStatus, setOverrideStatus] = useState<SubmissionStatus | null>(\n null,\n );\n const [notes, setNotes] = useState(\"\");\n const load = async () => {\n if (!appId) return;\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{ application: Application | null }>({\n gateway: \"global\",\n query: APP_QUERY,\n variables: { id: appId },\n });\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr(\n \"devportal.storeSubmission.errors.loadFailed\",\n \"Failed to load application\",\n ),\n );\n }\n setApp(result.data?.application ?? null);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.storeSubmission.errors.loadFailed\",\n \"Failed to load application\",\n ),\n );\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void load();\n }, [appId]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n const name = app?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, app?.name]);\n\n const submit = async () => {\n if (!appId) return;\n setSubmitting(true);\n setError(null);\n try {\n // Auto-publish all draft versions alongside the app submission\n const draftVersions = (app?.versions.edges ?? [])\n .map((e) => e.node)\n .filter((v) => v.status === \"draft\");\n for (const v of draftVersions) {\n const vResult = await graphqlFetch({\n gateway: \"global\",\n query: PUBLISH_VERSION_MUTATION,\n variables: { id: v.id },\n });\n if (vResult.errors?.length) {\n throw new Error(\n vResult.errors[0]?.message ??\n `Failed to submit version ${v.version}`,\n );\n }\n }\n\n const result = await graphqlFetch({\n gateway: \"global\",\n query: SUBMIT_MUTATION,\n variables: { id: appId, notes: notes || null },\n });\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr(\n \"devportal.storeSubmission.errors.submitFailed\",\n \"Submission failed\",\n ),\n );\n }\n bus.emit(\"devportal.app.submitted\", { appId });\n await load();\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.storeSubmission.errors.submitFailed\",\n \"Submission failed\",\n ),\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n const unlist = async () => {\n const productId = inferSubmission(app)?.productId;\n if (!productId) return;\n setUnlisting(true);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: \"global\",\n query: UNLIST_MUTATION,\n variables: { id: productId },\n });\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr(\n \"devportal.storeSubmission.errors.unlistFailed\",\n \"Failed to unlist\",\n ),\n );\n }\n setOverrideStatus(\"UNLISTED\");\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.storeSubmission.errors.unlistFailed\",\n \"Failed to unlist\",\n ),\n );\n } finally {\n setUnlisting(false);\n setShowUnlistConfirm(false);\n }\n };\n\n const submission = inferSubmission(app);\n const effectiveStatus = overrideStatus ?? submission?.status ?? \"UNKNOWN\";\n const statusMeta = statusLabels[effectiveStatus];\n const StatusIcon = statusMeta.icon;\n\n const allVersions = (app?.versions.edges ?? []).map((e) => e.node);\n const draftVersions = allVersions.filter((v) => v.status === \"draft\");\n const isPackageApp = app?.appType === \"NODE\" || app?.appType === \"WORKFLOW\";\n const noVersionsBlock = isPackageApp && allVersions.length === 0;\n\n return (\n <main\n className=\"min-h-screen bg-surface-default\"\n aria-labelledby=\"store-submission-heading\"\n >\n <div className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-3xl px-6 py-4\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n {tr(\"devportal.common.back\", \"Back\")}\n </button>\n <div className=\"flex items-center gap-2\">\n <StoreIcon\n className=\"h-5 w-5 text-text-primary\"\n aria-hidden=\"true\"\n />\n <h1\n id=\"store-submission-heading\"\n className=\"text-xl font-semibold text-text-primary\"\n >\n {tr(\"devportal.storeSubmission.title\", \"Store submission\")}\n </h1>\n </div>\n </div>\n </div>\n </div>\n\n {/* Unlist confirm dialog */}\n {showUnlistConfirm && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"unlist-dialog-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\"\n >\n <div className=\"w-full max-w-md rounded-lg border border-border-default bg-surface-raised p-6 shadow-xl\">\n <div className=\"mb-4 flex items-start gap-3\">\n <EyeOff\n className=\"mt-0.5 h-5 w-5 shrink-0 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <div>\n <h2\n id=\"unlist-dialog-title\"\n className=\"text-base font-semibold text-text-primary\"\n >\n {tr(\n \"devportal.storeSubmission.unlistTitle\",\n \"Unlist from marketplace?\",\n )}\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.unlistDescription\",\n \"Your app will no longer appear in the marketplace for new users. Existing installations are not affected — users who already have it installed can continue using it.\",\n )}\n </p>\n </div>\n </div>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setShowUnlistConfirm(false)}\n className=\"rounded-md border border-border-default px-4 py-2 text-sm font-medium text-text-secondary hover:bg-surface-hover\"\n >\n {tr(\"devportal.common.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void unlist()}\n disabled={unlisting}\n className=\"inline-flex items-center gap-2 rounded-md bg-surface-danger px-4 py-2 text-sm font-semibold text-text-on-danger hover:opacity-90 disabled:opacity-60\"\n >\n {unlisting && (\n <RefreshCw\n className=\"h-4 w-4 animate-spin\"\n aria-hidden=\"true\"\n />\n )}\n {tr(\"devportal.storeSubmission.confirmUnlist\", \"Unlist app\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <div className=\"mx-auto max-w-3xl px-6 py-6\">\n {(loading ||\n !submission ||\n effectiveStatus === \"UNKNOWN\" ||\n effectiveStatus === \"DRAFT\" ||\n effectiveStatus === \"REJECTED\") && (\n <PagePurpose className=\"mb-5\">\n {tr(\n \"devportal.storeSubmission.purpose\",\n \"Finalize the details the marketplace needs before your app can be reviewed — pricing, categories, and required assets. Completing this is what makes the app eligible to submit for publication.\",\n )}\n </PagePurpose>\n )}\n\n {error && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading && !app ? (\n <div\n className=\"py-16 text-center text-text-secondary\"\n role=\"status\"\n aria-live=\"polite\"\n >\n {tr(\"devportal.storeSubmission.loading\", \"Loading application…\")}\n </div>\n ) : !app ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\n \"devportal.storeSubmission.notFoundTitle\",\n \"Application not found\",\n )}\n description={tr(\n \"devportal.storeSubmission.notFound\",\n \"Application not found.\",\n )}\n />\n ) : (\n <div className=\"space-y-4\">\n {/* App header card */}\n <div className=\"rounded-lg border border-border-default bg-surface-raised p-4\">\n <div className=\"flex items-center gap-3\">\n {app.iconUrl ? (\n <img\n src={app.iconUrl}\n alt=\"\"\n className=\"h-14 w-14 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex h-14 w-14 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-text-secondary text-sm font-medium\">\n {app.name.slice(0, 2).toUpperCase()}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {app.name}\n </h2>\n <p className=\"font-mono text-xs text-text-tertiary\">\n {app.slug}\n </p>\n </div>\n <div\n className={`flex items-center gap-1.5 ${statusMeta.tone}`}\n role=\"status\"\n >\n <StatusIcon className=\"h-5 w-5\" aria-hidden=\"true\" />\n <span className=\"text-sm font-medium\">\n {tr(statusMeta.i18nKey, statusMeta.fallback)}\n </span>\n </div>\n </div>\n </div>\n\n {/* Pending review — what happens next */}\n {effectiveStatus === \"PENDING_REVIEW\" && (\n <div className=\"rounded-lg border border-primary/20 bg-action-primary-bg/5 p-5 space-y-4\">\n <div className=\"flex items-start gap-3\">\n <Clock\n className=\"h-5 w-5 mt-0.5 shrink-0 text-primary\"\n aria-hidden=\"true\"\n />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {tr(\n \"devportal.storeSubmission.underReview\",\n \"Your app is under review\",\n )}\n </p>\n {app.submittedAt && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.submittedOn\",\n \"Submitted on\",\n )}{\" \"}\n {new Date(app.submittedAt).toLocaleString()}\n </p>\n )}\n </div>\n </div>\n\n <div className=\"border-t border-border-default pt-4\">\n <p className=\"text-xs font-medium text-text-secondary mb-3\">\n {tr(\n \"devportal.storeSubmission.whatNext\",\n \"What happens next\",\n )}\n </p>\n <ol className=\"space-y-2.5 text-sm text-text-secondary\">\n <li className=\"flex items-start gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-surface-muted text-xs font-medium text-text-tertiary\">\n 1\n </span>\n Our team reviews your app's listing, assets, and\n compliance.\n </li>\n <li className=\"flex items-start gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-surface-muted text-xs font-medium text-text-tertiary\">\n 2\n </span>\n You'll receive a notification when a decision is made.\n </li>\n <li className=\"flex items-start gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-surface-muted text-xs font-medium text-text-tertiary\">\n 3\n </span>\n If approved, your app goes live in the marketplace\n automatically.\n </li>\n </ol>\n </div>\n\n {app.reviewNotes && (\n <div className=\"border-t border-border-default pt-4\">\n <p className=\"text-xs font-medium text-text-secondary mb-1\">\n {tr(\n \"devportal.storeSubmission.reviewerNotes\",\n \"Reviewer notes\",\n )}\n </p>\n <p className=\"text-sm text-text-primary\">\n {app.reviewNotes}\n </p>\n </div>\n )}\n </div>\n )}\n\n {/* Published */}\n {effectiveStatus === \"PUBLISHED\" && (\n <div className=\"rounded-lg border border-status-success-bg bg-status-success-bg-subtle p-5 space-y-4\">\n <div className=\"flex items-start gap-3\">\n <CheckCircle2\n className=\"h-5 w-5 mt-0.5 shrink-0 text-status-success-text\"\n aria-hidden=\"true\"\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-semibold text-status-success-text\">\n {tr(\n \"devportal.storeSubmission.publishedTitle\",\n \"Your app is live in the marketplace\",\n )}\n </p>\n {submission?.publishedAt && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.publishedOn\",\n \"Published on\",\n )}{\" \"}\n {new Date(submission.publishedAt).toLocaleString()}\n </p>\n )}\n </div>\n </div>\n {submission?.productId && (\n <div className=\"border-t border-status-success-bg pt-4\">\n <p className=\"mb-2 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.unlistHint\",\n \"Need to remove this app from the marketplace? Unlisting hides it from new users while keeping existing installations active.\",\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => setShowUnlistConfirm(true)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-raised px-3 py-1.5 text-sm font-medium text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <EyeOff className=\"h-4 w-4\" aria-hidden=\"true\" />\n {tr(\n \"devportal.storeSubmission.unlistButton\",\n \"Unlist from marketplace\",\n )}\n </button>\n </div>\n )}\n </div>\n )}\n\n {/* Unlisted */}\n {effectiveStatus === \"UNLISTED\" && (\n <div className=\"rounded-lg border border-border-default bg-surface-muted p-5 flex items-start gap-3\">\n <EyeOff\n className=\"h-5 w-5 mt-0.5 shrink-0 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {tr(\n \"devportal.storeSubmission.unlistedTitle\",\n \"Your app has been unlisted\",\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.unlistedDescription\",\n \"It no longer appears in the marketplace for new users. Existing installations remain active and unaffected.\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {/* Rejected */}\n {effectiveStatus === \"REJECTED\" && (\n <div className=\"rounded-lg border border-status-error-bg bg-status-error-bg-subtle p-5 space-y-3\">\n <div className=\"flex items-start gap-3\">\n <XCircle\n className=\"h-5 w-5 mt-0.5 shrink-0 text-status-error-text\"\n aria-hidden=\"true\"\n />\n <div>\n <p className=\"text-sm font-semibold text-status-error-text\">\n {tr(\n \"devportal.storeSubmission.rejectedTitle\",\n \"Submission was not approved\",\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.rejectedHint\",\n \"Address the feedback below and resubmit.\",\n )}\n </p>\n </div>\n </div>\n {app.reviewNotes && (\n <div className=\"rounded-md bg-surface-muted p-3\">\n <p className=\"text-xs font-medium text-text-secondary mb-1\">\n {tr(\n \"devportal.storeSubmission.reviewerFeedback\",\n \"Reviewer feedback\",\n )}\n </p>\n <p className=\"text-sm text-text-primary\">\n {app.reviewNotes}\n </p>\n </div>\n )}\n </div>\n )}\n\n {/* Submit / resubmit form */}\n {(effectiveStatus === \"UNKNOWN\" ||\n effectiveStatus === \"DRAFT\" ||\n effectiveStatus === \"REJECTED\") && (\n <div className=\"rounded-lg border border-border-default bg-surface-raised p-4 space-y-4\">\n <div>\n <h3 className=\"mb-1 text-sm font-semibold text-text-primary\">\n {effectiveStatus === \"REJECTED\"\n ? tr(\n \"devportal.storeSubmission.resubmitForReview\",\n \"Resubmit for review\",\n )\n : tr(\n \"devportal.storeSubmission.submitForReview\",\n \"Submit for review\",\n )}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.help\",\n \"Your application will be sent to the store's review queue. Whether it's auto-published or requires admin approval depends on the target store's configuration.\",\n )}\n </p>\n </div>\n\n {/* Versions included in this submission */}\n {isPackageApp && (\n <div className=\"rounded-md border border-border-default bg-surface-muted p-3 space-y-2\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-secondary\">\n <GitBranch className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {tr(\n \"devportal.storeSubmission.versionsIncluded\",\n \"Versions included in this submission\",\n )}\n </div>\n\n {noVersionsBlock ? (\n <div className=\"flex items-start gap-2 text-sm text-status-danger-text\">\n <AlertTriangle\n className=\"h-4 w-4 mt-0.5 shrink-0\"\n aria-hidden=\"true\"\n />\n <span>\n {tr(\n \"devportal.storeSubmission.noVersionsError\",\n \"You must create at least one version before submitting. Go to the Versions tab to create one.\",\n )}\n </span>\n </div>\n ) : draftVersions.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.noDraftVersions\",\n \"No draft versions — all versions are already submitted or published.\",\n )}\n </p>\n ) : (\n <ul className=\"space-y-1\">\n {draftVersions.map((v) => (\n <li\n key={v.id}\n className=\"flex items-center gap-2 text-sm\"\n >\n <Package\n className=\"h-3.5 w-3.5 text-text-secondary shrink-0\"\n aria-hidden=\"true\"\n />\n <span className=\"font-mono font-medium\">\n {v.version}\n </span>\n {v.releaseNotes && (\n <span className=\"text-xs text-text-secondary truncate\">\n — {v.releaseNotes}\n </span>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n )}\n\n <div>\n <label className=\"block\" htmlFor=\"store-submission-notes\">\n <span className=\"sr-only\">\n {tr(\n \"devportal.storeSubmission.notesLabel\",\n \"Notes for the reviewer (optional)\",\n )}\n </span>\n </label>\n <textarea\n id=\"store-submission-notes\"\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={3}\n placeholder={tr(\n \"devportal.storeSubmission.notesLabel\",\n \"Notes for the reviewer (optional)\",\n )}\n className=\"mb-3 w-full rounded-md border border-border-default bg-surface-default px-3 py-2 text-sm text-text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n />\n <button\n type=\"button\"\n onClick={() => void submit()}\n disabled={\n submitting || !submitPerm.allowed || noVersionsBlock\n }\n aria-disabled={\n submitting || !submitPerm.allowed || noVersionsBlock\n }\n title={\n noVersionsBlock\n ? tr(\n \"devportal.storeSubmission.noVersionsError\",\n \"Create a version first\",\n )\n : !submitPerm.allowed\n ? tr(\n \"devportal.common.noPermission\",\n \"You do not have permission to perform this action\",\n )\n : undefined\n }\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90 disabled:opacity-60 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n {submitting && (\n <RefreshCw\n className=\"h-4 w-4 animate-spin\"\n aria-hidden=\"true\"\n />\n )}\n {effectiveStatus === \"REJECTED\"\n ? tr(\"devportal.storeSubmission.resubmit\", \"Resubmit\")\n : tr(\n \"devportal.storeSubmission.submitForReview\",\n \"Submit for review\",\n )}\n </button>\n </div>\n </div>\n )}\n\n {submission?.productId && (\n <div className=\"rounded-lg border border-border-default bg-surface-raised p-4\">\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.storeProductId\",\n \"Store product ID\",\n )}\n </p>\n <code className=\"mt-1 block break-all font-mono text-xs text-text-primary\">\n {submission.productId}\n </code>\n </div>\n )}\n </div>\n )}\n </div>\n </main>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"StoreSubmissionPage.js","names":[],"sources":["../../../src/devportal/pages/StoreSubmissionPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useState } from \"react\";\nimport { useNavigate, useParams } from \"react-router\";\nimport {\n ArrowLeft,\n CheckCircle2,\n Clock,\n RefreshCw,\n XCircle,\n AlertTriangle,\n Store as StoreIcon,\n EyeOff,\n Package,\n GitBranch,\n} from \"lucide-react\";\nimport { graphqlFetch } from \"@burdenoff/fe-libs/shared/graphql\";\nimport { IllustratedEmptyState, PagePurpose } from \"@burdenoff/fe-libs/ui\";\nimport { useDevportalEventBus } from \"../events\";\nimport { useTr } from \"../utils/i18n\";\nimport { useDevportalPermission } from \"../hooks\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\n\ntype SubmissionStatus =\n | \"DRAFT\"\n | \"PENDING_REVIEW\"\n | \"PUBLISHED\"\n | \"UNLISTED\"\n | \"REJECTED\"\n | \"UNKNOWN\";\n\ntype StoreSubmission = {\n productId: string;\n status: SubmissionStatus;\n submittedAt: string | null;\n reviewedAt: string | null;\n publishedAt: string | null;\n reviewNotes: string | null;\n rejectedReason: string | null;\n};\n\ntype ApplicationVersion = {\n id: string;\n version: string;\n status: string;\n releaseNotes: string | null;\n packageUrl: string | null;\n};\n\ntype Application = {\n id: string;\n name: string;\n slug: string;\n status: string;\n appType: string;\n reviewStatus: string;\n reviewNotes: string | null;\n iconUrl: string | null;\n submittedAt: string | null;\n metadata: Record<string, unknown> | null;\n versions: { edges: { node: ApplicationVersion }[] };\n};\n\nconst APP_QUERY = /* GraphQL */ `\n query DevportalAppForSubmission($id: ID!) {\n application(id: $id) {\n id\n name\n slug\n status\n appType\n reviewStatus\n reviewNotes\n iconUrl\n submittedAt\n metadata\n versions {\n edges {\n node {\n id\n version\n status\n releaseNotes\n packageUrl\n }\n }\n }\n }\n }\n`;\n\nconst SUBMIT_MUTATION = /* GraphQL */ `\n mutation DevportalSubmitForReview($id: ID!, $notes: String) {\n submitApplicationForReview(id: $id, notes: $notes) {\n id\n status\n submittedAt\n metadata\n }\n }\n`;\n\nconst UNLIST_MUTATION = /* GraphQL */ `\n mutation DevportalUnlistProduct($id: ID!) {\n unlistProduct(id: $id) {\n id\n status\n }\n }\n`;\n\nconst PUBLISH_VERSION_MUTATION = /* GraphQL */ `\n mutation DevportalPublishVersion($id: ID!) {\n publishApplicationVersion(id: $id) {\n id\n status\n }\n }\n`;\n\nfunction inferSubmission(app: Application | null): StoreSubmission | null {\n if (!app) return null;\n const metadata = (app.metadata ?? {}) as Record<string, unknown>;\n const productId =\n typeof metadata.storeProductId === \"string\" ? metadata.storeProductId : \"\";\n\n // Prefer metadata status (most accurate), but fall back to app.status so the\n // page reflects reality even when the metadata write failed after submission.\n const rawMetaStatus =\n typeof metadata.storeSubmissionStatus === \"string\"\n ? metadata.storeSubmissionStatus\n : null;\n\n let status: SubmissionStatus;\n if (\n rawMetaStatus === \"PENDING_REVIEW\" ||\n rawMetaStatus === \"PUBLISHED\" ||\n rawMetaStatus === \"REJECTED\" ||\n rawMetaStatus === \"DRAFT\"\n ) {\n status = rawMetaStatus;\n } else if (app.status === \"SUBMITTED\") {\n status = \"PENDING_REVIEW\";\n } else if (app.status === \"PUBLISHED\") {\n status = \"PUBLISHED\";\n } else if (app.status === \"REJECTED\") {\n status = \"REJECTED\";\n } else {\n status = \"UNKNOWN\";\n }\n\n const metaSubmittedAt =\n typeof metadata.storeSubmissionAt === \"string\"\n ? metadata.storeSubmissionAt\n : null;\n\n return {\n productId,\n status,\n submittedAt: metaSubmittedAt ?? app.submittedAt,\n reviewedAt: null,\n publishedAt: null,\n reviewNotes: null,\n rejectedReason: null,\n };\n}\n\ntype StatusMeta = {\n i18nKey: string;\n fallback: string;\n icon: typeof CheckCircle2;\n tone: string;\n};\n\nconst statusLabels: Record<SubmissionStatus, StatusMeta> = {\n DRAFT: {\n i18nKey: \"devportal.storeSubmission.status.draft\",\n fallback: \"Draft\",\n icon: Clock,\n tone: \"text-text-secondary\",\n },\n PENDING_REVIEW: {\n i18nKey: \"devportal.storeSubmission.status.pending\",\n fallback: \"Pending review\",\n icon: Clock,\n tone: \"text-status-warning-text\",\n },\n PUBLISHED: {\n i18nKey: \"devportal.storeSubmission.status.published\",\n fallback: \"Published\",\n icon: CheckCircle2,\n tone: \"text-status-success-text\",\n },\n UNLISTED: {\n i18nKey: \"devportal.storeSubmission.status.unlisted\",\n fallback: \"Unlisted\",\n icon: EyeOff,\n tone: \"text-text-secondary\",\n },\n REJECTED: {\n i18nKey: \"devportal.storeSubmission.status.rejected\",\n fallback: \"Rejected\",\n icon: XCircle,\n tone: \"text-status-danger-text\",\n },\n UNKNOWN: {\n i18nKey: \"devportal.storeSubmission.status.unknown\",\n fallback: \"Not yet submitted\",\n icon: AlertTriangle,\n tone: \"text-text-tertiary\",\n },\n};\n\nexport const StoreSubmissionPage: FC = () => {\n const { appId } = useParams<{ appId: string }>();\n const navigate = useNavigate();\n const bus = useDevportalEventBus();\n const tr = useTr();\n const submitPerm = useDevportalPermission(DEVPORTAL_PERMISSIONS.APP_SUBMIT);\n const [app, setApp] = useState<Application | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n const [unlisting, setUnlisting] = useState(false);\n const [showUnlistConfirm, setShowUnlistConfirm] = useState(false);\n const [overrideStatus, setOverrideStatus] = useState<SubmissionStatus | null>(\n null,\n );\n const [notes, setNotes] = useState(\"\");\n const load = async () => {\n if (!appId) return;\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{ application: Application | null }>({\n gateway: \"global\",\n query: APP_QUERY,\n variables: { id: appId },\n });\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr(\n \"devportal.storeSubmission.errors.loadFailed\",\n \"Failed to load application\",\n ),\n );\n }\n setApp(result.data?.application ?? null);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.storeSubmission.errors.loadFailed\",\n \"Failed to load application\",\n ),\n );\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void load();\n }, [appId]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n const name = app?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, app?.name]);\n\n const submit = async () => {\n if (!appId) return;\n setSubmitting(true);\n setError(null);\n try {\n // Auto-publish all draft versions alongside the app submission\n const draftVersions = (app?.versions.edges ?? [])\n .map((e) => e.node)\n .filter((v) => v.status === \"draft\");\n for (const v of draftVersions) {\n const vResult = await graphqlFetch({\n gateway: \"global\",\n query: PUBLISH_VERSION_MUTATION,\n variables: { id: v.id },\n });\n if (vResult.errors?.length) {\n throw new Error(\n vResult.errors[0]?.message ??\n `Failed to submit version ${v.version}`,\n );\n }\n }\n\n const result = await graphqlFetch({\n gateway: \"global\",\n query: SUBMIT_MUTATION,\n variables: { id: appId, notes: notes || null },\n });\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr(\n \"devportal.storeSubmission.errors.submitFailed\",\n \"Submission failed\",\n ),\n );\n }\n bus.emit(\"devportal.app.submitted\", { appId });\n await load();\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.storeSubmission.errors.submitFailed\",\n \"Submission failed\",\n ),\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n const unlist = async () => {\n const productId = inferSubmission(app)?.productId;\n if (!productId) return;\n setUnlisting(true);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: \"global\",\n query: UNLIST_MUTATION,\n variables: { id: productId },\n });\n if (result.errors?.length) {\n throw new Error(\n result.errors[0]?.message ??\n tr(\n \"devportal.storeSubmission.errors.unlistFailed\",\n \"Failed to unlist\",\n ),\n );\n }\n setOverrideStatus(\"UNLISTED\");\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.storeSubmission.errors.unlistFailed\",\n \"Failed to unlist\",\n ),\n );\n } finally {\n setUnlisting(false);\n setShowUnlistConfirm(false);\n }\n };\n\n const submission = inferSubmission(app);\n const effectiveStatus = overrideStatus ?? submission?.status ?? \"UNKNOWN\";\n const statusMeta = statusLabels[effectiveStatus];\n const StatusIcon = statusMeta.icon;\n\n const allVersions = (app?.versions.edges ?? []).map((e) => e.node);\n const draftVersions = allVersions.filter((v) => v.status === \"draft\");\n const isPackageApp = app?.appType === \"NODE\" || app?.appType === \"WORKFLOW\";\n const noVersionsBlock = isPackageApp && allVersions.length === 0;\n\n return (\n <main\n className=\"min-h-screen bg-surface-default\"\n aria-labelledby=\"store-submission-heading\"\n >\n <div className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-3xl px-6 py-4\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n {tr(\"devportal.common.back\", \"Back\")}\n </button>\n <div className=\"flex items-center gap-2\">\n <StoreIcon\n className=\"h-5 w-5 text-text-primary\"\n aria-hidden=\"true\"\n />\n <h1\n id=\"store-submission-heading\"\n className=\"text-xl font-semibold text-text-primary\"\n >\n {tr(\"devportal.storeSubmission.title\", \"Store submission\")}\n </h1>\n </div>\n </div>\n </div>\n </div>\n\n {/* Unlist confirm dialog */}\n {showUnlistConfirm && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"unlist-dialog-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\"\n >\n <div className=\"w-full max-w-md rounded-lg border border-border-default bg-surface-raised p-6 shadow-xl\">\n <div className=\"mb-4 flex items-start gap-3\">\n <EyeOff\n className=\"mt-0.5 h-5 w-5 shrink-0 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <div>\n <h2\n id=\"unlist-dialog-title\"\n className=\"text-base font-semibold text-text-primary\"\n >\n {tr(\n \"devportal.storeSubmission.unlistTitle\",\n \"Unlist from marketplace?\",\n )}\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.unlistDescription\",\n \"Your app will no longer appear in the marketplace for new users. Existing installations are not affected — users who already have it installed can continue using it.\",\n )}\n </p>\n </div>\n </div>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setShowUnlistConfirm(false)}\n className=\"rounded-md border border-border-default px-4 py-2 text-sm font-medium text-text-secondary hover:bg-surface-hover\"\n >\n {tr(\"devportal.common.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void unlist()}\n disabled={unlisting}\n className=\"inline-flex items-center gap-2 rounded-md bg-surface-danger px-4 py-2 text-sm font-semibold text-text-on-danger hover:opacity-90 disabled:opacity-60\"\n >\n {unlisting && (\n <RefreshCw\n className=\"h-4 w-4 animate-spin\"\n aria-hidden=\"true\"\n />\n )}\n {tr(\"devportal.storeSubmission.confirmUnlist\", \"Unlist app\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <div className=\"mx-auto max-w-3xl px-6 py-6\">\n {(loading ||\n !submission ||\n effectiveStatus === \"UNKNOWN\" ||\n effectiveStatus === \"DRAFT\" ||\n effectiveStatus === \"REJECTED\") && (\n <PagePurpose className=\"mb-5\">\n {tr(\n \"devportal.storeSubmission.purpose\",\n \"Finalize the details the marketplace needs before your app can be reviewed — pricing, categories, and required assets. Completing this is what makes the app eligible to submit for publication.\",\n )}\n </PagePurpose>\n )}\n\n {error && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading && !app ? (\n <div\n className=\"py-16 text-center text-text-secondary\"\n role=\"status\"\n aria-live=\"polite\"\n >\n {tr(\"devportal.storeSubmission.loading\", \"Loading application…\")}\n </div>\n ) : !app ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\n \"devportal.storeSubmission.notFoundTitle\",\n \"Application not found\",\n )}\n description={tr(\n \"devportal.storeSubmission.notFound\",\n \"Application not found.\",\n )}\n />\n ) : (\n <div className=\"space-y-4\">\n {/* App header card */}\n <div className=\"rounded-lg border border-border-default bg-surface-raised p-4\">\n <div className=\"flex items-center gap-3\">\n {app.iconUrl ? (\n <img\n src={app.iconUrl}\n alt=\"\"\n className=\"h-14 w-14 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex h-14 w-14 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-text-secondary text-sm font-medium\">\n {app.name.slice(0, 2).toUpperCase()}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {app.name}\n </h2>\n <p className=\"font-mono text-xs text-text-tertiary\">\n {app.slug}\n </p>\n </div>\n <div\n className={`flex items-center gap-1.5 ${statusMeta.tone}`}\n role=\"status\"\n >\n <StatusIcon className=\"h-5 w-5\" aria-hidden=\"true\" />\n <span className=\"text-sm font-medium\">\n {tr(statusMeta.i18nKey, statusMeta.fallback)}\n </span>\n </div>\n </div>\n </div>\n\n {/* Pending review — what happens next */}\n {effectiveStatus === \"PENDING_REVIEW\" && (\n <div className=\"rounded-lg border border-primary/20 bg-action-primary-bg/5 p-5 space-y-4\">\n <div className=\"flex items-start gap-3\">\n <Clock\n className=\"h-5 w-5 mt-0.5 shrink-0 text-primary\"\n aria-hidden=\"true\"\n />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {tr(\n \"devportal.storeSubmission.underReview\",\n \"Your app is under review\",\n )}\n </p>\n {app.submittedAt && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.submittedOn\",\n \"Submitted on\",\n )}{\" \"}\n {new Date(app.submittedAt).toLocaleString()}\n </p>\n )}\n </div>\n </div>\n\n <div className=\"border-t border-border-default pt-4\">\n <p className=\"text-xs font-medium text-text-secondary mb-3\">\n {tr(\n \"devportal.storeSubmission.whatNext\",\n \"What happens next\",\n )}\n </p>\n <ol className=\"space-y-2.5 text-sm text-text-secondary\">\n <li className=\"flex items-start gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-surface-muted text-xs font-medium text-text-tertiary\">\n 1\n </span>\n Our team reviews your app's listing, assets, and\n compliance.\n </li>\n <li className=\"flex items-start gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-surface-muted text-xs font-medium text-text-tertiary\">\n 2\n </span>\n You'll receive a notification when a decision is made.\n </li>\n <li className=\"flex items-start gap-2\">\n <span className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-surface-muted text-xs font-medium text-text-tertiary\">\n 3\n </span>\n If approved, your app goes live in the marketplace\n automatically.\n </li>\n </ol>\n </div>\n\n {app.reviewNotes && (\n <div className=\"border-t border-border-default pt-4\">\n <p className=\"text-xs font-medium text-text-secondary mb-1\">\n {tr(\n \"devportal.storeSubmission.reviewerNotes\",\n \"Reviewer notes\",\n )}\n </p>\n <p className=\"text-sm text-text-primary\">\n {app.reviewNotes}\n </p>\n </div>\n )}\n </div>\n )}\n\n {/* Published */}\n {effectiveStatus === \"PUBLISHED\" && (\n <div className=\"rounded-lg border border-status-success-bg bg-status-success-bg-subtle p-5 space-y-4\">\n <div className=\"flex items-start gap-3\">\n <CheckCircle2\n className=\"h-5 w-5 mt-0.5 shrink-0 text-status-success-text\"\n aria-hidden=\"true\"\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-semibold text-status-success-text\">\n {tr(\n \"devportal.storeSubmission.publishedTitle\",\n \"Your app is live in the marketplace\",\n )}\n </p>\n {submission?.publishedAt && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.publishedOn\",\n \"Published on\",\n )}{\" \"}\n {new Date(submission.publishedAt).toLocaleString()}\n </p>\n )}\n </div>\n </div>\n {submission?.productId && (\n <div className=\"border-t border-status-success-bg pt-4\">\n <p className=\"mb-2 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.unlistHint\",\n \"Need to remove this app from the marketplace? Unlisting hides it from new users while keeping existing installations active.\",\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => setShowUnlistConfirm(true)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-raised px-3 py-1.5 text-sm font-medium text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <EyeOff className=\"h-4 w-4\" aria-hidden=\"true\" />\n {tr(\n \"devportal.storeSubmission.unlistButton\",\n \"Unlist from marketplace\",\n )}\n </button>\n </div>\n )}\n </div>\n )}\n\n {/* Unlisted */}\n {effectiveStatus === \"UNLISTED\" && (\n <div className=\"rounded-lg border border-border-default bg-surface-muted p-5 flex items-start gap-3\">\n <EyeOff\n className=\"h-5 w-5 mt-0.5 shrink-0 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {tr(\n \"devportal.storeSubmission.unlistedTitle\",\n \"Your app has been unlisted\",\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.unlistedDescription\",\n \"It no longer appears in the marketplace for new users. Existing installations remain active and unaffected.\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {/* Rejected */}\n {effectiveStatus === \"REJECTED\" && (\n <div className=\"rounded-lg border border-status-error-bg bg-status-error-bg-subtle p-5 space-y-3\">\n <div className=\"flex items-start gap-3\">\n <XCircle\n className=\"h-5 w-5 mt-0.5 shrink-0 text-status-error-text\"\n aria-hidden=\"true\"\n />\n <div>\n <p className=\"text-sm font-semibold text-status-error-text\">\n {tr(\n \"devportal.storeSubmission.rejectedTitle\",\n \"Submission was not approved\",\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.rejectedHint\",\n \"Address the feedback below and resubmit.\",\n )}\n </p>\n </div>\n </div>\n {app.reviewNotes && (\n <div className=\"rounded-md bg-surface-muted p-3\">\n <p className=\"text-xs font-medium text-text-secondary mb-1\">\n {tr(\n \"devportal.storeSubmission.reviewerFeedback\",\n \"Reviewer feedback\",\n )}\n </p>\n <p className=\"text-sm text-text-primary\">\n {app.reviewNotes}\n </p>\n </div>\n )}\n </div>\n )}\n\n {/* Submit / resubmit form */}\n {(effectiveStatus === \"UNKNOWN\" ||\n effectiveStatus === \"DRAFT\" ||\n effectiveStatus === \"REJECTED\") && (\n <div className=\"rounded-lg border border-border-default bg-surface-raised p-4 space-y-4\">\n <div>\n <h3 className=\"mb-1 text-sm font-semibold text-text-primary\">\n {effectiveStatus === \"REJECTED\"\n ? tr(\n \"devportal.storeSubmission.resubmitForReview\",\n \"Resubmit for review\",\n )\n : tr(\n \"devportal.storeSubmission.submitForReview\",\n \"Submit for review\",\n )}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.help\",\n \"Your application will be sent to the store's review queue. Whether it's auto-published or requires admin approval depends on the target store's configuration.\",\n )}\n </p>\n </div>\n\n {/* Versions included in this submission */}\n {isPackageApp && (\n <div className=\"rounded-md border border-border-default bg-surface-muted p-3 space-y-2\">\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-secondary\">\n <GitBranch className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {tr(\n \"devportal.storeSubmission.versionsIncluded\",\n \"Versions included in this submission\",\n )}\n </div>\n\n {noVersionsBlock ? (\n <div className=\"flex items-start gap-2 text-sm text-status-danger-text\">\n <AlertTriangle\n className=\"h-4 w-4 mt-0.5 shrink-0\"\n aria-hidden=\"true\"\n />\n <span>\n {tr(\n \"devportal.storeSubmission.noVersionsError\",\n \"You must create at least one version before submitting. Go to the Versions tab to create one.\",\n )}\n </span>\n </div>\n ) : draftVersions.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.noDraftVersions\",\n \"No draft versions — all versions are already submitted or published.\",\n )}\n </p>\n ) : (\n <ul className=\"space-y-1\">\n {draftVersions.map((v) => (\n <li\n key={v.id}\n className=\"flex items-center gap-2 text-sm\"\n >\n <Package\n className=\"h-3.5 w-3.5 text-text-secondary shrink-0\"\n aria-hidden=\"true\"\n />\n <span className=\"font-mono font-medium\">\n {v.version}\n </span>\n {v.releaseNotes && (\n <span className=\"text-xs text-text-secondary truncate\">\n — {v.releaseNotes}\n </span>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n )}\n\n <div>\n <label className=\"block\" htmlFor=\"store-submission-notes\">\n <span className=\"sr-only\">\n {tr(\n \"devportal.storeSubmission.notesLabel\",\n \"Notes for the reviewer (optional)\",\n )}\n </span>\n </label>\n <textarea\n id=\"store-submission-notes\"\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={3}\n placeholder={tr(\n \"devportal.storeSubmission.notesLabel\",\n \"Notes for the reviewer (optional)\",\n )}\n className=\"mb-3 w-full rounded-md border border-border-default bg-surface-default px-3 py-2 text-sm text-text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n />\n <button\n type=\"button\"\n onClick={() => void submit()}\n disabled={\n submitting || !submitPerm.allowed || noVersionsBlock\n }\n aria-disabled={\n submitting || !submitPerm.allowed || noVersionsBlock\n }\n title={\n noVersionsBlock\n ? tr(\n \"devportal.storeSubmission.noVersionsError\",\n \"Create a version first\",\n )\n : !submitPerm.allowed\n ? tr(\n \"devportal.common.noPermission\",\n \"You do not have permission to perform this action\",\n )\n : undefined\n }\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90 disabled:opacity-60 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n {submitting && (\n <RefreshCw\n className=\"h-4 w-4 animate-spin\"\n aria-hidden=\"true\"\n />\n )}\n {effectiveStatus === \"REJECTED\"\n ? tr(\"devportal.storeSubmission.resubmit\", \"Resubmit\")\n : tr(\n \"devportal.storeSubmission.submitForReview\",\n \"Submit for review\",\n )}\n </button>\n </div>\n </div>\n )}\n\n {submission?.productId && (\n <div className=\"rounded-lg border border-border-default bg-surface-raised p-4\">\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.storeProductId\",\n \"Store product ID\",\n )}\n </p>\n <code className=\"mt-1 block break-all font-mono text-xs text-text-primary\">\n {submission.productId}\n </code>\n </div>\n )}\n </div>\n )}\n </div>\n </main>\n );\n};\n"],"mappings":"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"}
@@ -1,30 +1,30 @@
1
- import { useDevportal as e } from "../providers/DevportalProvider.js";
2
- import { EmptyState as t } from "../components/EmptyState.js";
3
- import { useDeleteWebhookMutation as n, useGetApplicationQuery as r, useGetWebhookDetailQuery as i, useTestWebhookMutation as ee, useUpdateWebhookMutation as a } from "../../generated/global-operations.js";
1
+ import { useParams as e } from "../../node_modules/react-router/dist/production/lib/hooks.js";
2
+ import { useDevportal as t } from "../providers/DevportalProvider.js";
3
+ import { EmptyState as n } from "../components/EmptyState.js";
4
+ import { useDeleteWebhookMutation as r, useGetApplicationQuery as ee, useGetWebhookDetailQuery as te, useTestWebhookMutation as i, useUpdateWebhookMutation as a } from "../../generated/global-operations.js";
4
5
  import { getErrorMessage as o } from "../hooks/useGraphQL.js";
5
6
  import { PageLayout as s, PageSection as c } from "../components/PageLayout.js";
6
7
  import { StatusBadge as l } from "../components/StatusBadge.js";
7
- import { StatCard as u, StatCardGrid as te } from "../components/StatCard.js";
8
- import { nativeImpact as ne, nativeNotify as d } from "../../utils/nativeBridge.js";
9
- import { SecretDisplay as re } from "../components/SecretDisplay.js";
10
- import { DataTable as ie, DetailList as ae } from "../components/DataTable.js";
11
- import { Alert as oe, ConfirmDialog as f } from "../components/ConfirmDialog.js";
8
+ import { StatCard as u, StatCardGrid as ne } from "../components/StatCard.js";
9
+ import { nativeImpact as re, nativeNotify as d } from "../../utils/nativeBridge.js";
10
+ import { SecretDisplay as ie } from "../components/SecretDisplay.js";
11
+ import { DataTable as ae, DetailList as oe } from "../components/DataTable.js";
12
+ import { Alert as se, ConfirmDialog as f } from "../components/ConfirmDialog.js";
12
13
  import { useDevportalPermission as p } from "../hooks/usePermission.js";
13
14
  import "../hooks/index.js";
14
15
  import { DEVPORTAL_PERMISSIONS as m } from "../constants/permissions.js";
15
16
  import h from "../../node_modules/date-fns/format.js";
16
17
  import { useEffect as g, useMemo as _, useState as v } from "react";
17
- import { useI18n as se } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
18
- import { AlertCircle as ce, CheckCircle as y, Clock as b, ExternalLink as x, Loader2 as le, Pause as S, Play as C, Send as w, Trash2 as ue, Webhook as de, XCircle as fe } from "lucide-react";
19
- import { useParams as pe } from "react-router-dom";
18
+ import { useI18n as ce } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
19
+ import { AlertCircle as le, CheckCircle as y, Clock as b, ExternalLink as x, Loader2 as ue, Pause as S, Play as C, Send as w, Trash2 as de, Webhook as fe, XCircle as pe } from "lucide-react";
20
20
  import { jsx as T, jsxs as E } from "react/jsx-runtime";
21
21
  import { CTAOverflowMenu as D, EmphasisPanel as O, NextSteps as k } from "@burdenoff/fe-libs/ui";
22
22
  //#region src/devportal/pages/WebhookDetailPage.tsx
23
23
  var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
24
- let { appId: j, webhookId: M } = pe(), { navigate: N, basePath: P } = e(), { t: F } = se(), I = (e, t) => {
24
+ let { appId: j, webhookId: M } = e(), { navigate: N, basePath: P } = t(), { t: F } = ce(), I = (e, t) => {
25
25
  let n = F(e);
26
26
  return n === e ? t : n;
27
- }, { allowed: L } = p(m.WEBHOOK_UPDATE), { allowed: R } = p(m.WEBHOOK_DELETE), [z, B] = v(!1), [me, V] = v(!1), [H, U] = v(null), [he, W] = v(!1), { data: G } = r({
27
+ }, { allowed: L } = p(m.WEBHOOK_UPDATE), { allowed: R } = p(m.WEBHOOK_DELETE), [z, B] = v(!1), [me, V] = v(!1), [H, U] = v(null), [he, W] = v(!1), { data: G } = ee({
28
28
  variables: { id: j ?? "" },
29
29
  skip: !j,
30
30
  fetchPolicy: "cache-first"
@@ -35,7 +35,7 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
35
35
  let t = window.__breadcrumbRegistry;
36
36
  return t?.register(j, e), () => t?.unregister(j);
37
37
  }, [j, G?.application?.name]);
38
- let { data: K, loading: ge, error: q, refetch: J } = i({
38
+ let { data: K, loading: ge, error: q, refetch: J } = te({
39
39
  variables: {
40
40
  id: M || "",
41
41
  limit: 20,
@@ -43,7 +43,7 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
43
43
  },
44
44
  skip: !M,
45
45
  fetchPolicy: "cache-and-network"
46
- }), [_e, { loading: Y }] = a(), [ve, { loading: ye }] = n(), [be, { loading: xe }] = ee(), X = K?.webhook;
46
+ }), [_e, { loading: Y }] = a(), [ve, { loading: ye }] = r(), [be, { loading: xe }] = i(), X = K?.webhook;
47
47
  g(() => {
48
48
  let e = X?.name;
49
49
  if (!M || !e) return;
@@ -63,7 +63,7 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
63
63
  } }), await J());
64
64
  }, we = async () => {
65
65
  if (X) {
66
- ne("medium");
66
+ re("medium");
67
67
  try {
68
68
  await ve({ variables: { id: X.id } }), d("success"), Ce(`/apps/${X.applicationId}/webhooks`);
69
69
  } catch {
@@ -89,15 +89,15 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
89
89
  backLabel: I("devportal.backToWebhooks", "Back to Webhooks"),
90
90
  children: /* @__PURE__ */ T("div", {
91
91
  className: "flex items-center justify-center py-16 text-text-secondary",
92
- children: /* @__PURE__ */ T(le, { className: "h-8 w-8 animate-spin" })
92
+ children: /* @__PURE__ */ T(ue, { className: "h-8 w-8 animate-spin" })
93
93
  })
94
94
  }) : q || !X ? /* @__PURE__ */ T(s, {
95
95
  title: I("devportal.webhook", "Webhook"),
96
96
  description: I("devportal.webhookDetails", "Webhook details"),
97
97
  backLink: Q ? `/apps/${Q}/webhooks` : "/webhooks",
98
98
  backLabel: I("devportal.backToWebhooks", "Back to Webhooks"),
99
- children: /* @__PURE__ */ T(t, {
100
- icon: /* @__PURE__ */ T(ce, {
99
+ children: /* @__PURE__ */ T(n, {
100
+ icon: /* @__PURE__ */ T(le, {
101
101
  size: 32,
102
102
  className: "text-status-error-text"
103
103
  }),
@@ -125,14 +125,14 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
125
125
  onSelect: () => void $()
126
126
  }, {
127
127
  label: I("devportal.deleteWebhook", "Delete"),
128
- icon: /* @__PURE__ */ T(ue, { size: 16 }),
128
+ icon: /* @__PURE__ */ T(de, { size: 16 }),
129
129
  intent: "destructive",
130
130
  disabled: !R,
131
131
  onSelect: () => B(!0)
132
132
  }]
133
133
  }),
134
134
  children: [
135
- /* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(te, {
135
+ /* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(ne, {
136
136
  columns: 4,
137
137
  children: [
138
138
  /* @__PURE__ */ T(u, {
@@ -148,7 +148,7 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
148
148
  /* @__PURE__ */ T(u, {
149
149
  title: I("devportal.failedDeliveries", "Failed Deliveries"),
150
150
  value: (X.failureCount ?? 0).toLocaleString(),
151
- icon: /* @__PURE__ */ T(fe, { size: 20 })
151
+ icon: /* @__PURE__ */ T(pe, { size: 20 })
152
152
  }),
153
153
  /* @__PURE__ */ T(u, {
154
154
  title: I("devportal.lastTriggered", "Last Triggered"),
@@ -177,7 +177,7 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
177
177
  }),
178
178
  /* @__PURE__ */ T(c, {
179
179
  title: I("devportal.configuration", "Configuration"),
180
- children: /* @__PURE__ */ T(ae, { items: [
180
+ children: /* @__PURE__ */ T(oe, { items: [
181
181
  {
182
182
  label: I("devportal.status", "Status"),
183
183
  value: /* @__PURE__ */ T(l, {
@@ -203,7 +203,7 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
203
203
  },
204
204
  {
205
205
  label: I("devportal.signingSecret", "Signing Secret"),
206
- value: /* @__PURE__ */ T(re, {
206
+ value: /* @__PURE__ */ T(ie, {
207
207
  value: X.secret || "******",
208
208
  label: ""
209
209
  })
@@ -234,7 +234,7 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
234
234
  }),
235
235
  /* @__PURE__ */ T(c, {
236
236
  title: I("devportal.recentDeliveries", "Recent Deliveries"),
237
- children: Z.length > 0 ? /* @__PURE__ */ T(ie, {
237
+ children: Z.length > 0 ? /* @__PURE__ */ T(ae, {
238
238
  data: Z,
239
239
  keyField: "id",
240
240
  columns: [
@@ -273,9 +273,9 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
273
273
  render: (e) => A(e.createdAt)
274
274
  }
275
275
  ]
276
- }) : /* @__PURE__ */ T(t, {
276
+ }) : /* @__PURE__ */ T(n, {
277
277
  illustration: "empty-notifications",
278
- icon: /* @__PURE__ */ T(de, { size: 24 }),
278
+ icon: /* @__PURE__ */ T(fe, { size: 24 }),
279
279
  title: I("devportal.noDeliveriesYet", "No deliveries yet"),
280
280
  description: I("devportal.noDeliveriesYetDescription", "Deliveries will appear here after the first matching event is sent to this endpoint.")
281
281
  })
@@ -300,7 +300,7 @@ var A = (e) => e ? h(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
300
300
  title: I("devportal.sendTestDelivery", "Send test delivery"),
301
301
  description: I("devportal.sendTestDeliveryDescription", "A signed test event will be sent to the configured endpoint through the public gateway."),
302
302
  confirmLabel: I("devportal.sendTestEvent", "Send test event"),
303
- children: H && /* @__PURE__ */ T(oe, {
303
+ children: H && /* @__PURE__ */ T(se, {
304
304
  variant: he ? "success" : "error",
305
305
  children: H
306
306
  })
@@ -1 +1 @@
1
- {"version":3,"file":"WebhookDetailPage.js","names":[],"sources":["../../../src/devportal/pages/WebhookDetailPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n CheckCircle,\n Clock,\n ExternalLink,\n Loader2,\n Pause,\n Play,\n Send,\n Trash2,\n Webhook,\n XCircle,\n AlertCircle,\n} from \"lucide-react\";\nimport { format } from \"date-fns\";\nimport {\n CTAOverflowMenu,\n EmphasisPanel,\n NextSteps,\n} from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { ConfirmDialog, Alert } from \"../components/ConfirmDialog\";\nimport { DataTable, DetailList } from \"../components/DataTable\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { SecretDisplay } from \"../components/SecretDisplay\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\nimport {\n type GetWebhookDetailQuery,\n getErrorMessage,\n useDeleteWebhookMutation,\n useGetApplicationQuery,\n useGetWebhookDetailQuery,\n useTestWebhookMutation,\n useUpdateWebhookMutation,\n} from \"../hooks\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\n\nconst formatTimestamp = (value?: string | null) => {\n if (!value) return \"Never\";\n return format(new Date(value), \"MMM d, yyyy HH:mm:ss\");\n};\n\ntype WebhookDeliveryEntry =\n GetWebhookDetailQuery[\"webhookDeliveries\"][\"edges\"][number][\"node\"];\n\nexport const WebhookDetailPage: FC = () => {\n const { appId, webhookId } = useParams<{\n appId: string;\n webhookId: string;\n }>();\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { allowed: canUpdate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.WEBHOOK_UPDATE,\n );\n const { allowed: canDelete } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.WEBHOOK_DELETE,\n );\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showTestDialog, setShowTestDialog] = useState(false);\n const [testMessage, setTestMessage] = useState<string | null>(null);\n const [testSuccess, setTestSuccess] = useState(false);\n\n const { data: appData } = useGetApplicationQuery({\n variables: { id: appId ?? \"\" },\n skip: !appId,\n fetchPolicy: \"cache-first\",\n });\n\n useEffect(() => {\n const name = appData?.application?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, appData?.application?.name]);\n\n const { data, loading, error, refetch } = useGetWebhookDetailQuery({\n variables: {\n id: webhookId || \"\",\n limit: 20,\n offset: 0,\n },\n skip: !webhookId,\n fetchPolicy: \"cache-and-network\",\n });\n const [updateWebhook, { loading: updateLoading }] =\n useUpdateWebhookMutation();\n const [deleteWebhook, { loading: deleteLoading }] =\n useDeleteWebhookMutation();\n const [testWebhook, { loading: testLoading }] = useTestWebhookMutation();\n\n const webhook = data?.webhook;\n\n useEffect(() => {\n const name = webhook?.name;\n if (!webhookId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(webhookId, name);\n return () => reg?.unregister(webhookId);\n }, [webhookId, webhook?.name]);\n\n const deliveries: WebhookDeliveryEntry[] =\n data?.webhookDeliveries?.edges?.map(\n (edge: GetWebhookDetailQuery[\"webhookDeliveries\"][\"edges\"][number]) =>\n edge.node,\n ) ?? [];\n const backAppId = appId || webhook?.applicationId;\n const successRate = useMemo(() => {\n if (!webhook) return 0;\n const total = (webhook.successCount ?? 0) + (webhook.failureCount ?? 0);\n if (total === 0) return 100;\n return Math.round(((webhook.successCount ?? 0) / total) * 100);\n }, [webhook]);\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleToggleActive = async () => {\n if (!webhook) return;\n\n await updateWebhook({\n variables: {\n id: webhook.id,\n input: {\n isActive: !webhook.isActive,\n },\n },\n });\n\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!webhook) return;\n void nativeImpact(\"medium\");\n\n try {\n await deleteWebhook({\n variables: {\n id: webhook.id,\n },\n });\n void nativeNotify(\"success\");\n handleNavigate(`/apps/${webhook.applicationId}/webhooks`);\n } catch {\n void nativeNotify(\"error\");\n }\n };\n\n const handleTest = async () => {\n if (!webhook) return;\n\n const result = await testWebhook({\n variables: {\n id: webhook.id,\n eventType: webhook.events[0] || \"application.updated\",\n },\n });\n\n const response = result.data?.testWebhook;\n if (!response) {\n setTestSuccess(false);\n setTestMessage(\"Test request did not return a response.\");\n return;\n }\n\n setTestSuccess(response.success);\n setTestMessage(\n response.success\n ? `Test delivery succeeded with HTTP ${response.statusCode ?? 200}.`\n : response.error || \"Webhook test failed.\",\n );\n\n await refetch();\n };\n\n if (loading && !webhook) {\n return (\n <PageLayout\n title={tr(\"devportal.webhook\", \"Webhook\")}\n description={tr(\n \"devportal.loadingWebhookDetails\",\n \"Loading webhook details\",\n )}\n backLink={backAppId ? `/apps/${backAppId}/webhooks` : \"/webhooks\"}\n backLabel={tr(\"devportal.backToWebhooks\", \"Back to Webhooks\")}\n >\n <div className=\"flex items-center justify-center py-16 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n </PageLayout>\n );\n }\n\n if (error || !webhook) {\n return (\n <PageLayout\n title={tr(\"devportal.webhook\", \"Webhook\")}\n description={tr(\"devportal.webhookDetails\", \"Webhook details\")}\n backLink={backAppId ? `/apps/${backAppId}/webhooks` : \"/webhooks\"}\n backLabel={tr(\"devportal.backToWebhooks\", \"Back to Webhooks\")}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\"devportal.unableToLoadWebhook\", \"Unable to load webhook\")}\n description={\n error\n ? getErrorMessage(error)\n : tr(\"devportal.webhookNotFound\", \"Webhook not found\")\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={webhook.name}\n description={\n webhook.description || \"Webhook configuration and delivery history\"\n }\n purpose={tr(\n \"devportal.webhookDetailPurpose\",\n \"This endpoint receives the events you subscribed to, signed with your secret. Check the delivery history to confirm your server is responding with 2xx, send a test event, and pause it if your endpoint is down so deliveries are not lost to repeated failures.\",\n )}\n backLink={`/apps/${backAppId}/webhooks`}\n backLabel={tr(\"devportal.backToWebhooks\", \"Back to Webhooks\")}\n actions={\n <CTAOverflowMenu\n primary={{\n label: tr(\"devportal.test\", \"Test\"),\n icon: <Send size={16} />,\n onSelect: () => {\n setTestMessage(null);\n setShowTestDialog(true);\n },\n }}\n actions={[\n {\n label: webhook.isActive\n ? tr(\"devportal.pauseWebhook\", \"Pause\")\n : tr(\"devportal.resumeWebhook\", \"Resume\"),\n icon: webhook.isActive ? <Pause size={16} /> : <Play size={16} />,\n disabled: updateLoading || !canUpdate,\n onSelect: () => void handleToggleActive(),\n },\n {\n label: tr(\"devportal.deleteWebhook\", \"Delete\"),\n icon: <Trash2 size={16} />,\n intent: \"destructive\",\n disabled: !canDelete,\n onSelect: () => setShowDeleteDialog(true),\n },\n ]}\n />\n }\n >\n <EmphasisPanel>\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.successRate\", \"Success Rate\")}\n value={`${successRate}%`}\n icon={<CheckCircle size={20} />}\n />\n <StatCard\n title={tr(\n \"devportal.successfulDeliveries\",\n \"Successful Deliveries\",\n )}\n value={(webhook.successCount ?? 0).toLocaleString()}\n icon={<CheckCircle size={20} />}\n />\n <StatCard\n title={tr(\"devportal.failedDeliveries\", \"Failed Deliveries\")}\n value={(webhook.failureCount ?? 0).toLocaleString()}\n icon={<XCircle size={20} />}\n />\n <StatCard\n title={tr(\"devportal.lastTriggered\", \"Last Triggered\")}\n value={\n webhook.lastTriggeredAt\n ? format(new Date(webhook.lastTriggeredAt), \"MMM d, HH:mm\")\n : tr(\"devportal.never\", \"Never\")\n }\n icon={<Clock size={20} />}\n />\n </StatCardGrid>\n </EmphasisPanel>\n\n {/* Guided next steps (dismissable) */}\n <NextSteps\n storageKey={`devportal-webhook-${webhook.id}`}\n steps={[\n {\n id: \"test\",\n label: tr(\"devportal.sendTestEvent\", \"Send a test event\"),\n description: tr(\n \"devportal.webhookStepTest\",\n \"Confirm your endpoint receives and verifies the payload\",\n ),\n icon: <Send size={16} />,\n onClick: () => {\n setTestMessage(null);\n setShowTestDialog(true);\n },\n },\n {\n id: \"toggle\",\n label: webhook.isActive\n ? tr(\"devportal.pauseWebhook\", \"Pause deliveries\")\n : tr(\"devportal.resumeWebhook\", \"Resume deliveries\"),\n description: tr(\n \"devportal.webhookStepToggle\",\n \"Pause while your endpoint is down to avoid lost retries\",\n ),\n icon: webhook.isActive ? <Pause size={16} /> : <Play size={16} />,\n onClick: () => void handleToggleActive(),\n },\n ]}\n />\n\n <PageSection title={tr(\"devportal.configuration\", \"Configuration\")}>\n <DetailList\n items={[\n {\n label: tr(\"devportal.status\", \"Status\"),\n value: <StatusBadge status={webhook.status} type=\"webhook\" />,\n },\n {\n label: tr(\"devportal.endpointUrl\", \"Endpoint URL\"),\n value: (\n <span className=\"flex flex-wrap items-center gap-2 font-mono text-sm\">\n <span className=\"break-all\">{webhook.url}</span>\n <a\n href={webhook.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-primary\"\n >\n <ExternalLink size={14} />\n </a>\n </span>\n ),\n },\n {\n label: tr(\"devportal.signingSecret\", \"Signing Secret\"),\n value: (\n <SecretDisplay value={webhook.secret || \"******\"} label=\"\" />\n ),\n },\n {\n label: tr(\"devportal.subscribedEvents\", \"Subscribed Events\"),\n value: (\n <div className=\"flex flex-wrap gap-1\">\n {webhook.events.map((eventName: string) => (\n <code\n key={eventName}\n className=\"rounded bg-bg-sunken px-1.5 py-0.5 text-xs\"\n >\n {eventName}\n </code>\n ))}\n </div>\n ),\n },\n {\n label: tr(\"devportal.retryPolicy\", \"Retry Policy\"),\n value: webhook.retryEnabled\n ? tr(\n \"devportal.retryPolicyEnabled\",\n `Up to ${webhook.maxRetries ?? 0} retries`,\n )\n : tr(\"devportal.retriesDisabled\", \"Retries disabled\"),\n },\n {\n label: tr(\"devportal.created\", \"Created\"),\n value: formatTimestamp(webhook.createdAt),\n },\n {\n label: tr(\"devportal.updated\", \"Updated\"),\n value: formatTimestamp(webhook.updatedAt),\n },\n ]}\n />\n </PageSection>\n\n <PageSection\n title={tr(\"devportal.recentDeliveries\", \"Recent Deliveries\")}\n >\n {deliveries.length > 0 ? (\n <DataTable\n data={deliveries}\n keyField=\"id\"\n columns={[\n {\n key: \"status\",\n header: tr(\"devportal.status\", \"Status\"),\n render: (delivery) => (\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={delivery.status} type=\"delivery\" />\n </div>\n ),\n },\n {\n key: \"eventType\",\n header: tr(\"devportal.event\", \"Event\"),\n render: (delivery) => (\n <code className=\"text-xs\">{delivery.eventType}</code>\n ),\n },\n {\n key: \"responseStatus\",\n header: tr(\"devportal.response\", \"Response\"),\n render: (delivery) => delivery.responseStatus ?? \"-\",\n },\n {\n key: \"attemptNumber\",\n header: tr(\"devportal.attempts\", \"Attempts\"),\n render: (delivery) => delivery.attemptNumber,\n },\n {\n key: \"createdAt\",\n header: tr(\"devportal.created\", \"Created\"),\n render: (delivery) => formatTimestamp(delivery.createdAt),\n },\n ]}\n />\n ) : (\n <EmptyState\n illustration=\"empty-notifications\"\n icon={<Webhook size={24} />}\n title={tr(\"devportal.noDeliveriesYet\", \"No deliveries yet\")}\n description={tr(\n \"devportal.noDeliveriesYetDescription\",\n \"Deliveries will appear here after the first matching event is sent to this endpoint.\",\n )}\n />\n )}\n </PageSection>\n\n <ConfirmDialog\n open={showDeleteDialog}\n onOpenChange={setShowDeleteDialog}\n onConfirm={() => void handleDelete()}\n loading={deleteLoading}\n title={tr(\"devportal.deleteWebhookTitle\", \"Delete webhook\")}\n description={tr(\n \"devportal.deleteWebhookDescription\",\n \"Deleting this webhook stops all future deliveries for this endpoint.\",\n )}\n confirmLabel={tr(\"devportal.deleteWebhookConfirm\", \"Delete webhook\")}\n variant=\"danger\"\n />\n\n <ConfirmDialog\n open={showTestDialog}\n onOpenChange={(open) => {\n setShowTestDialog(open);\n if (!open) {\n setTestMessage(null);\n }\n }}\n onConfirm={() => void handleTest()}\n loading={testLoading}\n title={tr(\"devportal.sendTestDelivery\", \"Send test delivery\")}\n description={tr(\n \"devportal.sendTestDeliveryDescription\",\n \"A signed test event will be sent to the configured endpoint through the public gateway.\",\n )}\n confirmLabel={tr(\"devportal.sendTestEvent\", \"Send test event\")}\n >\n {testMessage && (\n <Alert variant={testSuccess ? \"success\" : \"error\"}>\n {testMessage}\n </Alert>\n )}\n </ConfirmDialog>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"WebhookDetailPage.js","names":[],"sources":["../../../src/devportal/pages/WebhookDetailPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useParams } from \"react-router\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n CheckCircle,\n Clock,\n ExternalLink,\n Loader2,\n Pause,\n Play,\n Send,\n Trash2,\n Webhook,\n XCircle,\n AlertCircle,\n} from \"lucide-react\";\nimport { format } from \"date-fns\";\nimport {\n CTAOverflowMenu,\n EmphasisPanel,\n NextSteps,\n} from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { ConfirmDialog, Alert } from \"../components/ConfirmDialog\";\nimport { DataTable, DetailList } from \"../components/DataTable\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { SecretDisplay } from \"../components/SecretDisplay\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\nimport {\n type GetWebhookDetailQuery,\n getErrorMessage,\n useDeleteWebhookMutation,\n useGetApplicationQuery,\n useGetWebhookDetailQuery,\n useTestWebhookMutation,\n useUpdateWebhookMutation,\n} from \"../hooks\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\n\nconst formatTimestamp = (value?: string | null) => {\n if (!value) return \"Never\";\n return format(new Date(value), \"MMM d, yyyy HH:mm:ss\");\n};\n\ntype WebhookDeliveryEntry =\n GetWebhookDetailQuery[\"webhookDeliveries\"][\"edges\"][number][\"node\"];\n\nexport const WebhookDetailPage: FC = () => {\n const { appId, webhookId } = useParams<{\n appId: string;\n webhookId: string;\n }>();\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { allowed: canUpdate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.WEBHOOK_UPDATE,\n );\n const { allowed: canDelete } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.WEBHOOK_DELETE,\n );\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showTestDialog, setShowTestDialog] = useState(false);\n const [testMessage, setTestMessage] = useState<string | null>(null);\n const [testSuccess, setTestSuccess] = useState(false);\n\n const { data: appData } = useGetApplicationQuery({\n variables: { id: appId ?? \"\" },\n skip: !appId,\n fetchPolicy: \"cache-first\",\n });\n\n useEffect(() => {\n const name = appData?.application?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, appData?.application?.name]);\n\n const { data, loading, error, refetch } = useGetWebhookDetailQuery({\n variables: {\n id: webhookId || \"\",\n limit: 20,\n offset: 0,\n },\n skip: !webhookId,\n fetchPolicy: \"cache-and-network\",\n });\n const [updateWebhook, { loading: updateLoading }] =\n useUpdateWebhookMutation();\n const [deleteWebhook, { loading: deleteLoading }] =\n useDeleteWebhookMutation();\n const [testWebhook, { loading: testLoading }] = useTestWebhookMutation();\n\n const webhook = data?.webhook;\n\n useEffect(() => {\n const name = webhook?.name;\n if (!webhookId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(webhookId, name);\n return () => reg?.unregister(webhookId);\n }, [webhookId, webhook?.name]);\n\n const deliveries: WebhookDeliveryEntry[] =\n data?.webhookDeliveries?.edges?.map(\n (edge: GetWebhookDetailQuery[\"webhookDeliveries\"][\"edges\"][number]) =>\n edge.node,\n ) ?? [];\n const backAppId = appId || webhook?.applicationId;\n const successRate = useMemo(() => {\n if (!webhook) return 0;\n const total = (webhook.successCount ?? 0) + (webhook.failureCount ?? 0);\n if (total === 0) return 100;\n return Math.round(((webhook.successCount ?? 0) / total) * 100);\n }, [webhook]);\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleToggleActive = async () => {\n if (!webhook) return;\n\n await updateWebhook({\n variables: {\n id: webhook.id,\n input: {\n isActive: !webhook.isActive,\n },\n },\n });\n\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!webhook) return;\n void nativeImpact(\"medium\");\n\n try {\n await deleteWebhook({\n variables: {\n id: webhook.id,\n },\n });\n void nativeNotify(\"success\");\n handleNavigate(`/apps/${webhook.applicationId}/webhooks`);\n } catch {\n void nativeNotify(\"error\");\n }\n };\n\n const handleTest = async () => {\n if (!webhook) return;\n\n const result = await testWebhook({\n variables: {\n id: webhook.id,\n eventType: webhook.events[0] || \"application.updated\",\n },\n });\n\n const response = result.data?.testWebhook;\n if (!response) {\n setTestSuccess(false);\n setTestMessage(\"Test request did not return a response.\");\n return;\n }\n\n setTestSuccess(response.success);\n setTestMessage(\n response.success\n ? `Test delivery succeeded with HTTP ${response.statusCode ?? 200}.`\n : response.error || \"Webhook test failed.\",\n );\n\n await refetch();\n };\n\n if (loading && !webhook) {\n return (\n <PageLayout\n title={tr(\"devportal.webhook\", \"Webhook\")}\n description={tr(\n \"devportal.loadingWebhookDetails\",\n \"Loading webhook details\",\n )}\n backLink={backAppId ? `/apps/${backAppId}/webhooks` : \"/webhooks\"}\n backLabel={tr(\"devportal.backToWebhooks\", \"Back to Webhooks\")}\n >\n <div className=\"flex items-center justify-center py-16 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n </PageLayout>\n );\n }\n\n if (error || !webhook) {\n return (\n <PageLayout\n title={tr(\"devportal.webhook\", \"Webhook\")}\n description={tr(\"devportal.webhookDetails\", \"Webhook details\")}\n backLink={backAppId ? `/apps/${backAppId}/webhooks` : \"/webhooks\"}\n backLabel={tr(\"devportal.backToWebhooks\", \"Back to Webhooks\")}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\"devportal.unableToLoadWebhook\", \"Unable to load webhook\")}\n description={\n error\n ? getErrorMessage(error)\n : tr(\"devportal.webhookNotFound\", \"Webhook not found\")\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={webhook.name}\n description={\n webhook.description || \"Webhook configuration and delivery history\"\n }\n purpose={tr(\n \"devportal.webhookDetailPurpose\",\n \"This endpoint receives the events you subscribed to, signed with your secret. Check the delivery history to confirm your server is responding with 2xx, send a test event, and pause it if your endpoint is down so deliveries are not lost to repeated failures.\",\n )}\n backLink={`/apps/${backAppId}/webhooks`}\n backLabel={tr(\"devportal.backToWebhooks\", \"Back to Webhooks\")}\n actions={\n <CTAOverflowMenu\n primary={{\n label: tr(\"devportal.test\", \"Test\"),\n icon: <Send size={16} />,\n onSelect: () => {\n setTestMessage(null);\n setShowTestDialog(true);\n },\n }}\n actions={[\n {\n label: webhook.isActive\n ? tr(\"devportal.pauseWebhook\", \"Pause\")\n : tr(\"devportal.resumeWebhook\", \"Resume\"),\n icon: webhook.isActive ? <Pause size={16} /> : <Play size={16} />,\n disabled: updateLoading || !canUpdate,\n onSelect: () => void handleToggleActive(),\n },\n {\n label: tr(\"devportal.deleteWebhook\", \"Delete\"),\n icon: <Trash2 size={16} />,\n intent: \"destructive\",\n disabled: !canDelete,\n onSelect: () => setShowDeleteDialog(true),\n },\n ]}\n />\n }\n >\n <EmphasisPanel>\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.successRate\", \"Success Rate\")}\n value={`${successRate}%`}\n icon={<CheckCircle size={20} />}\n />\n <StatCard\n title={tr(\n \"devportal.successfulDeliveries\",\n \"Successful Deliveries\",\n )}\n value={(webhook.successCount ?? 0).toLocaleString()}\n icon={<CheckCircle size={20} />}\n />\n <StatCard\n title={tr(\"devportal.failedDeliveries\", \"Failed Deliveries\")}\n value={(webhook.failureCount ?? 0).toLocaleString()}\n icon={<XCircle size={20} />}\n />\n <StatCard\n title={tr(\"devportal.lastTriggered\", \"Last Triggered\")}\n value={\n webhook.lastTriggeredAt\n ? format(new Date(webhook.lastTriggeredAt), \"MMM d, HH:mm\")\n : tr(\"devportal.never\", \"Never\")\n }\n icon={<Clock size={20} />}\n />\n </StatCardGrid>\n </EmphasisPanel>\n\n {/* Guided next steps (dismissable) */}\n <NextSteps\n storageKey={`devportal-webhook-${webhook.id}`}\n steps={[\n {\n id: \"test\",\n label: tr(\"devportal.sendTestEvent\", \"Send a test event\"),\n description: tr(\n \"devportal.webhookStepTest\",\n \"Confirm your endpoint receives and verifies the payload\",\n ),\n icon: <Send size={16} />,\n onClick: () => {\n setTestMessage(null);\n setShowTestDialog(true);\n },\n },\n {\n id: \"toggle\",\n label: webhook.isActive\n ? tr(\"devportal.pauseWebhook\", \"Pause deliveries\")\n : tr(\"devportal.resumeWebhook\", \"Resume deliveries\"),\n description: tr(\n \"devportal.webhookStepToggle\",\n \"Pause while your endpoint is down to avoid lost retries\",\n ),\n icon: webhook.isActive ? <Pause size={16} /> : <Play size={16} />,\n onClick: () => void handleToggleActive(),\n },\n ]}\n />\n\n <PageSection title={tr(\"devportal.configuration\", \"Configuration\")}>\n <DetailList\n items={[\n {\n label: tr(\"devportal.status\", \"Status\"),\n value: <StatusBadge status={webhook.status} type=\"webhook\" />,\n },\n {\n label: tr(\"devportal.endpointUrl\", \"Endpoint URL\"),\n value: (\n <span className=\"flex flex-wrap items-center gap-2 font-mono text-sm\">\n <span className=\"break-all\">{webhook.url}</span>\n <a\n href={webhook.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-primary\"\n >\n <ExternalLink size={14} />\n </a>\n </span>\n ),\n },\n {\n label: tr(\"devportal.signingSecret\", \"Signing Secret\"),\n value: (\n <SecretDisplay value={webhook.secret || \"******\"} label=\"\" />\n ),\n },\n {\n label: tr(\"devportal.subscribedEvents\", \"Subscribed Events\"),\n value: (\n <div className=\"flex flex-wrap gap-1\">\n {webhook.events.map((eventName: string) => (\n <code\n key={eventName}\n className=\"rounded bg-bg-sunken px-1.5 py-0.5 text-xs\"\n >\n {eventName}\n </code>\n ))}\n </div>\n ),\n },\n {\n label: tr(\"devportal.retryPolicy\", \"Retry Policy\"),\n value: webhook.retryEnabled\n ? tr(\n \"devportal.retryPolicyEnabled\",\n `Up to ${webhook.maxRetries ?? 0} retries`,\n )\n : tr(\"devportal.retriesDisabled\", \"Retries disabled\"),\n },\n {\n label: tr(\"devportal.created\", \"Created\"),\n value: formatTimestamp(webhook.createdAt),\n },\n {\n label: tr(\"devportal.updated\", \"Updated\"),\n value: formatTimestamp(webhook.updatedAt),\n },\n ]}\n />\n </PageSection>\n\n <PageSection\n title={tr(\"devportal.recentDeliveries\", \"Recent Deliveries\")}\n >\n {deliveries.length > 0 ? (\n <DataTable\n data={deliveries}\n keyField=\"id\"\n columns={[\n {\n key: \"status\",\n header: tr(\"devportal.status\", \"Status\"),\n render: (delivery) => (\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={delivery.status} type=\"delivery\" />\n </div>\n ),\n },\n {\n key: \"eventType\",\n header: tr(\"devportal.event\", \"Event\"),\n render: (delivery) => (\n <code className=\"text-xs\">{delivery.eventType}</code>\n ),\n },\n {\n key: \"responseStatus\",\n header: tr(\"devportal.response\", \"Response\"),\n render: (delivery) => delivery.responseStatus ?? \"-\",\n },\n {\n key: \"attemptNumber\",\n header: tr(\"devportal.attempts\", \"Attempts\"),\n render: (delivery) => delivery.attemptNumber,\n },\n {\n key: \"createdAt\",\n header: tr(\"devportal.created\", \"Created\"),\n render: (delivery) => formatTimestamp(delivery.createdAt),\n },\n ]}\n />\n ) : (\n <EmptyState\n illustration=\"empty-notifications\"\n icon={<Webhook size={24} />}\n title={tr(\"devportal.noDeliveriesYet\", \"No deliveries yet\")}\n description={tr(\n \"devportal.noDeliveriesYetDescription\",\n \"Deliveries will appear here after the first matching event is sent to this endpoint.\",\n )}\n />\n )}\n </PageSection>\n\n <ConfirmDialog\n open={showDeleteDialog}\n onOpenChange={setShowDeleteDialog}\n onConfirm={() => void handleDelete()}\n loading={deleteLoading}\n title={tr(\"devportal.deleteWebhookTitle\", \"Delete webhook\")}\n description={tr(\n \"devportal.deleteWebhookDescription\",\n \"Deleting this webhook stops all future deliveries for this endpoint.\",\n )}\n confirmLabel={tr(\"devportal.deleteWebhookConfirm\", \"Delete webhook\")}\n variant=\"danger\"\n />\n\n <ConfirmDialog\n open={showTestDialog}\n onOpenChange={(open) => {\n setShowTestDialog(open);\n if (!open) {\n setTestMessage(null);\n }\n }}\n onConfirm={() => void handleTest()}\n loading={testLoading}\n title={tr(\"devportal.sendTestDelivery\", \"Send test delivery\")}\n description={tr(\n \"devportal.sendTestDeliveryDescription\",\n \"A signed test event will be sent to the configured endpoint through the public gateway.\",\n )}\n confirmLabel={tr(\"devportal.sendTestEvent\", \"Send test event\")}\n >\n {testMessage && (\n <Alert variant={testSuccess ? \"success\" : \"error\"}>\n {testMessage}\n </Alert>\n )}\n </ConfirmDialog>\n </PageLayout>\n );\n};\n"],"mappings":"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"}