@burdenoff/microfe-devportal 2026.629.11 → 2026.702.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.
- package/dist/devportal/components/index.js +1 -1
- package/dist/devportal/pages/AnalyticsDashboardPage.js +46 -46
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +1391 -712
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +313 -220
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApplicationPage.js +281 -228
- package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +200 -307
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +380 -284
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationDetailPage.js +12 -12
- package/dist/devportal/pages/RevenueDashboardPage.js +8 -8
- package/dist/devportal/pages/StoreSubmissionPage.js +368 -246
- package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +146 -146
- package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -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} 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 | \"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 Application = {\n id: string;\n name: string;\n slug: string;\n status: string;\n reviewStatus: string;\n iconUrl: string | null;\n metadata: Record<string, unknown> | null;\n};\n\nconst APP_QUERY = /* GraphQL */ `\n query DevportalAppForSubmission($id: ID!) {\n application(id: $id) {\n id\n name\n slug\n status\n reviewStatus\n iconUrl\n metadata\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\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 const rawStatus =\n typeof metadata.storeSubmissionStatus === \"string\"\n ? metadata.storeSubmissionStatus\n : null;\n const status: SubmissionStatus =\n rawStatus === \"PENDING_REVIEW\" ||\n rawStatus === \"PUBLISHED\" ||\n rawStatus === \"REJECTED\" ||\n rawStatus === \"DRAFT\"\n ? rawStatus\n : \"UNKNOWN\";\n return {\n productId,\n status,\n submittedAt:\n typeof metadata.storeSubmissionAt === \"string\"\n ? metadata.storeSubmissionAt\n : null,\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 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 [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 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 submission = inferSubmission(app);\n const statusMeta = submission\n ? statusLabels[submission.status]\n : statusLabels.UNKNOWN;\n const StatusIcon = statusMeta.icon;\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 <div className=\"mx-auto max-w-3xl px-6 py-6\">\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 {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 */}\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\">\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 {/* Timeline */}\n {submission && submission.status !== \"UNKNOWN\" && (\n <div className=\"rounded-lg border border-border-default bg-surface-raised p-4\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {tr(\n \"devportal.storeSubmission.timeline\",\n \"Submission timeline\",\n )}\n </h3>\n <ul className=\"space-y-2 text-sm\">\n {submission.submittedAt && (\n <li className=\"flex items-center gap-2\">\n <Clock\n className=\"h-4 w-4 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <span className=\"text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.submitted\",\n \"Submitted:\",\n )}\n </span>\n <span className=\"text-text-primary\">\n {new Date(submission.submittedAt).toLocaleString()}\n </span>\n </li>\n )}\n {submission.reviewedAt && (\n <li className=\"flex items-center gap-2\">\n <CheckCircle2\n className=\"h-4 w-4 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <span className=\"text-text-secondary\">\n {tr(\"devportal.storeSubmission.reviewed\", \"Reviewed:\")}\n </span>\n <span className=\"text-text-primary\">\n {new Date(submission.reviewedAt).toLocaleString()}\n </span>\n </li>\n )}\n {submission.publishedAt && (\n <li className=\"flex items-center gap-2\">\n <CheckCircle2\n className=\"h-4 w-4 text-status-success-text\"\n aria-hidden=\"true\"\n />\n <span className=\"text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.published\",\n \"Published:\",\n )}\n </span>\n <span className=\"text-text-primary\">\n {new Date(submission.publishedAt).toLocaleString()}\n </span>\n </li>\n )}\n </ul>\n {submission.reviewNotes && (\n <div className=\"mt-3 rounded-md bg-surface-muted p-3\">\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\n \"devportal.storeSubmission.reviewerNotes\",\n \"Reviewer notes\",\n )}\n </p>\n <p className=\"mt-1 text-sm text-text-primary\">\n {submission.reviewNotes}\n </p>\n </div>\n )}\n {submission.rejectedReason && (\n <div\n className=\"mt-3 rounded-md border border-border-danger bg-surface-danger-muted p-3\"\n role=\"alert\"\n >\n <p className=\"text-xs font-medium text-text-danger\">\n {tr(\n \"devportal.storeSubmission.rejectionReason\",\n \"Rejection reason\",\n )}\n </p>\n <p className=\"mt-1 text-sm text-text-primary\">\n {submission.rejectedReason}\n </p>\n </div>\n )}\n </div>\n )}\n\n {/* Submit / resubmit */}\n {(!submission ||\n submission.status === \"UNKNOWN\" ||\n submission.status === \"DRAFT\" ||\n submission.status === \"REJECTED\") && (\n <div className=\"rounded-lg border border-border-default bg-surface-raised p-4\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {submission?.status === \"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=\"mb-3 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 <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={submitting || !submitPerm.allowed}\n aria-disabled={submitting || !submitPerm.allowed}\n title={\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 {submission?.status === \"REJECTED\"\n ? tr(\"devportal.storeSubmission.resubmit\", \"Resubmit\")\n : tr(\n \"devportal.storeSubmission.submitForReview\",\n \"Submit for review\",\n )}\n </button>\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-dom\";\nimport {\n ArrowLeft,\n CheckCircle2,\n Clock,\n RefreshCw,\n XCircle,\n AlertTriangle,\n Store as StoreIcon,\n EyeOff,\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 Application = {\n id: string;\n name: string;\n slug: string;\n status: string;\n reviewStatus: string;\n reviewNotes: string | null;\n iconUrl: string | null;\n submittedAt: string | null;\n metadata: Record<string, unknown> | null;\n};\n\nconst APP_QUERY = /* GraphQL */ `\n query DevportalAppForSubmission($id: ID!) {\n application(id: $id) {\n id\n name\n slug\n status\n reviewStatus\n reviewNotes\n iconUrl\n submittedAt\n metadata\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\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 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 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\">\n <h3 className=\"mb-2 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=\"mb-3 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 <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={submitting || !submitPerm.allowed}\n aria-disabled={submitting || !submitPerm.allowed}\n title={\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 )}\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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"}
|
|
@@ -3,71 +3,71 @@ import { EmptyState as t } from "../components/EmptyState.js";
|
|
|
3
3
|
import { useDeleteWebhookMutation as n, useGetApplicationQuery as r, useGetWebhookDetailQuery as i, useTestWebhookMutation as ee, useUpdateWebhookMutation as a } from "../../generated/global-operations.js";
|
|
4
4
|
import { getErrorMessage as o } from "../hooks/useGraphQL.js";
|
|
5
5
|
import { PageLayout as s, PageSection as c } from "../components/PageLayout.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { nativeImpact as
|
|
9
|
-
import { SecretDisplay as
|
|
10
|
-
import { DataTable as
|
|
11
|
-
import { Alert as
|
|
12
|
-
import { DEVPORTAL_PERMISSIONS as
|
|
13
|
-
import { useDevportalPermission as
|
|
6
|
+
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";
|
|
12
|
+
import { DEVPORTAL_PERMISSIONS as p } from "../constants/permissions.js";
|
|
13
|
+
import { useDevportalPermission as m } from "../hooks/usePermission.js";
|
|
14
14
|
import "../hooks/index.js";
|
|
15
|
-
import
|
|
16
|
-
import { useEffect as
|
|
17
|
-
import { useI18n as
|
|
18
|
-
import { AlertCircle as
|
|
19
|
-
import { useParams as
|
|
20
|
-
import { jsx as
|
|
21
|
-
import { CTAOverflowMenu as
|
|
15
|
+
import h from "../../node_modules/date-fns/format.js";
|
|
16
|
+
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";
|
|
20
|
+
import { jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
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
|
-
var
|
|
24
|
-
let { appId:
|
|
25
|
-
let n =
|
|
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) => {
|
|
25
|
+
let n = F(e);
|
|
26
26
|
return n === e ? t : n;
|
|
27
|
-
}, { allowed:
|
|
28
|
-
variables: { id:
|
|
29
|
-
skip: !
|
|
27
|
+
}, { allowed: L } = m(p.WEBHOOK_UPDATE), { allowed: R } = m(p.WEBHOOK_DELETE), [z, B] = v(!1), [me, V] = v(!1), [H, U] = v(null), [he, W] = v(!1), { data: G } = r({
|
|
28
|
+
variables: { id: j ?? "" },
|
|
29
|
+
skip: !j,
|
|
30
30
|
fetchPolicy: "cache-first"
|
|
31
31
|
});
|
|
32
|
-
|
|
33
|
-
let e =
|
|
34
|
-
if (!
|
|
32
|
+
g(() => {
|
|
33
|
+
let e = G?.application?.name;
|
|
34
|
+
if (!j || !e) return;
|
|
35
35
|
let t = window.__breadcrumbRegistry;
|
|
36
|
-
return t?.register(
|
|
37
|
-
}, [
|
|
38
|
-
let { data:
|
|
36
|
+
return t?.register(j, e), () => t?.unregister(j);
|
|
37
|
+
}, [j, G?.application?.name]);
|
|
38
|
+
let { data: K, loading: ge, error: q, refetch: J } = i({
|
|
39
39
|
variables: {
|
|
40
|
-
id:
|
|
40
|
+
id: M || "",
|
|
41
41
|
limit: 20,
|
|
42
42
|
offset: 0
|
|
43
43
|
},
|
|
44
|
-
skip: !
|
|
44
|
+
skip: !M,
|
|
45
45
|
fetchPolicy: "cache-and-network"
|
|
46
|
-
}), [
|
|
47
|
-
|
|
46
|
+
}), [_e, { loading: Y }] = a(), [ve, { loading: ye }] = n(), [be, { loading: xe }] = ee(), X = K?.webhook;
|
|
47
|
+
g(() => {
|
|
48
48
|
let e = X?.name;
|
|
49
|
-
if (!
|
|
49
|
+
if (!M || !e) return;
|
|
50
50
|
let t = window.__breadcrumbRegistry;
|
|
51
|
-
return t?.register(
|
|
52
|
-
}, [
|
|
53
|
-
let Z =
|
|
51
|
+
return t?.register(M, e), () => t?.unregister(M);
|
|
52
|
+
}, [M, X?.name]);
|
|
53
|
+
let Z = K?.webhookDeliveries?.edges?.map((e) => e.node) ?? [], Q = j || X?.applicationId, Se = _(() => {
|
|
54
54
|
if (!X) return 0;
|
|
55
55
|
let e = (X.successCount ?? 0) + (X.failureCount ?? 0);
|
|
56
56
|
return e === 0 ? 100 : Math.round((X.successCount ?? 0) / e * 100);
|
|
57
57
|
}, [X]), Ce = (e) => {
|
|
58
|
-
|
|
58
|
+
N?.(`${P}${e}`);
|
|
59
59
|
}, $ = async () => {
|
|
60
|
-
X && (await
|
|
60
|
+
X && (await _e({ variables: {
|
|
61
61
|
id: X.id,
|
|
62
62
|
input: { isActive: !X.isActive }
|
|
63
|
-
} }), await
|
|
63
|
+
} }), await J());
|
|
64
64
|
}, we = async () => {
|
|
65
65
|
if (X) {
|
|
66
|
-
|
|
66
|
+
ne("medium");
|
|
67
67
|
try {
|
|
68
|
-
await ve({ variables: { id: X.id } }),
|
|
68
|
+
await ve({ variables: { id: X.id } }), d("success"), Ce(`/apps/${X.applicationId}/webhooks`);
|
|
69
69
|
} catch {
|
|
70
|
-
|
|
70
|
+
d("error");
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
}, Te = async () => {
|
|
@@ -77,173 +77,173 @@ var j = (e) => e ? _(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", M = () => {
|
|
|
77
77
|
eventType: X.events[0] || "application.updated"
|
|
78
78
|
} })).data?.testWebhook;
|
|
79
79
|
if (!e) {
|
|
80
|
-
|
|
80
|
+
W(!1), U("Test request did not return a response.");
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
W(e.success), U(e.success ? `Test delivery succeeded with HTTP ${e.statusCode ?? 200}.` : e.error || "Webhook test failed."), await J();
|
|
84
84
|
};
|
|
85
|
-
return
|
|
86
|
-
title:
|
|
87
|
-
description:
|
|
85
|
+
return ge && !X ? /* @__PURE__ */ T(s, {
|
|
86
|
+
title: I("devportal.webhook", "Webhook"),
|
|
87
|
+
description: I("devportal.loadingWebhookDetails", "Loading webhook details"),
|
|
88
88
|
backLink: Q ? `/apps/${Q}/webhooks` : "/webhooks",
|
|
89
|
-
backLabel:
|
|
90
|
-
children: /* @__PURE__ */
|
|
89
|
+
backLabel: I("devportal.backToWebhooks", "Back to Webhooks"),
|
|
90
|
+
children: /* @__PURE__ */ T("div", {
|
|
91
91
|
className: "flex items-center justify-center py-16 text-text-secondary",
|
|
92
|
-
children: /* @__PURE__ */
|
|
92
|
+
children: /* @__PURE__ */ T(le, { className: "h-8 w-8 animate-spin" })
|
|
93
93
|
})
|
|
94
|
-
}) :
|
|
95
|
-
title:
|
|
96
|
-
description:
|
|
94
|
+
}) : q || !X ? /* @__PURE__ */ T(s, {
|
|
95
|
+
title: I("devportal.webhook", "Webhook"),
|
|
96
|
+
description: I("devportal.webhookDetails", "Webhook details"),
|
|
97
97
|
backLink: Q ? `/apps/${Q}/webhooks` : "/webhooks",
|
|
98
|
-
backLabel:
|
|
99
|
-
children: /* @__PURE__ */
|
|
100
|
-
icon: /* @__PURE__ */
|
|
98
|
+
backLabel: I("devportal.backToWebhooks", "Back to Webhooks"),
|
|
99
|
+
children: /* @__PURE__ */ T(t, {
|
|
100
|
+
icon: /* @__PURE__ */ T(ce, {
|
|
101
101
|
size: 32,
|
|
102
102
|
className: "text-status-error-text"
|
|
103
103
|
}),
|
|
104
|
-
title:
|
|
105
|
-
description:
|
|
104
|
+
title: I("devportal.unableToLoadWebhook", "Unable to load webhook"),
|
|
105
|
+
description: q ? o(q) : I("devportal.webhookNotFound", "Webhook not found")
|
|
106
106
|
})
|
|
107
|
-
}) : /* @__PURE__ */
|
|
107
|
+
}) : /* @__PURE__ */ E(s, {
|
|
108
108
|
title: X.name,
|
|
109
109
|
description: X.description || "Webhook configuration and delivery history",
|
|
110
|
-
purpose:
|
|
110
|
+
purpose: I("devportal.webhookDetailPurpose", "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."),
|
|
111
111
|
backLink: `/apps/${Q}/webhooks`,
|
|
112
|
-
backLabel:
|
|
113
|
-
actions: /* @__PURE__ */
|
|
112
|
+
backLabel: I("devportal.backToWebhooks", "Back to Webhooks"),
|
|
113
|
+
actions: /* @__PURE__ */ T(D, {
|
|
114
114
|
primary: {
|
|
115
|
-
label:
|
|
116
|
-
icon: /* @__PURE__ */
|
|
115
|
+
label: I("devportal.test", "Test"),
|
|
116
|
+
icon: /* @__PURE__ */ T(w, { size: 16 }),
|
|
117
117
|
onSelect: () => {
|
|
118
|
-
|
|
118
|
+
U(null), V(!0);
|
|
119
119
|
}
|
|
120
120
|
},
|
|
121
121
|
actions: [{
|
|
122
|
-
label: X.isActive ?
|
|
123
|
-
icon: X.isActive ? /* @__PURE__ */
|
|
124
|
-
disabled:
|
|
122
|
+
label: X.isActive ? I("devportal.pauseWebhook", "Pause") : I("devportal.resumeWebhook", "Resume"),
|
|
123
|
+
icon: X.isActive ? /* @__PURE__ */ T(S, { size: 16 }) : /* @__PURE__ */ T(C, { size: 16 }),
|
|
124
|
+
disabled: Y || !L,
|
|
125
125
|
onSelect: () => void $()
|
|
126
126
|
}, {
|
|
127
|
-
label:
|
|
128
|
-
icon: /* @__PURE__ */
|
|
127
|
+
label: I("devportal.deleteWebhook", "Delete"),
|
|
128
|
+
icon: /* @__PURE__ */ T(ue, { size: 16 }),
|
|
129
129
|
intent: "destructive",
|
|
130
|
-
disabled: !
|
|
131
|
-
onSelect: () =>
|
|
130
|
+
disabled: !R,
|
|
131
|
+
onSelect: () => B(!0)
|
|
132
132
|
}]
|
|
133
133
|
}),
|
|
134
134
|
children: [
|
|
135
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(te, {
|
|
136
136
|
columns: 4,
|
|
137
137
|
children: [
|
|
138
|
-
/* @__PURE__ */
|
|
139
|
-
title:
|
|
138
|
+
/* @__PURE__ */ T(u, {
|
|
139
|
+
title: I("devportal.successRate", "Success Rate"),
|
|
140
140
|
value: `${Se}%`,
|
|
141
|
-
icon: /* @__PURE__ */
|
|
141
|
+
icon: /* @__PURE__ */ T(y, { size: 20 })
|
|
142
142
|
}),
|
|
143
|
-
/* @__PURE__ */
|
|
144
|
-
title:
|
|
143
|
+
/* @__PURE__ */ T(u, {
|
|
144
|
+
title: I("devportal.successfulDeliveries", "Successful Deliveries"),
|
|
145
145
|
value: (X.successCount ?? 0).toLocaleString(),
|
|
146
|
-
icon: /* @__PURE__ */
|
|
146
|
+
icon: /* @__PURE__ */ T(y, { size: 20 })
|
|
147
147
|
}),
|
|
148
|
-
/* @__PURE__ */
|
|
149
|
-
title:
|
|
148
|
+
/* @__PURE__ */ T(u, {
|
|
149
|
+
title: I("devportal.failedDeliveries", "Failed Deliveries"),
|
|
150
150
|
value: (X.failureCount ?? 0).toLocaleString(),
|
|
151
|
-
icon: /* @__PURE__ */
|
|
151
|
+
icon: /* @__PURE__ */ T(fe, { size: 20 })
|
|
152
152
|
}),
|
|
153
|
-
/* @__PURE__ */
|
|
154
|
-
title:
|
|
155
|
-
value: X.lastTriggeredAt ?
|
|
156
|
-
icon: /* @__PURE__ */
|
|
153
|
+
/* @__PURE__ */ T(u, {
|
|
154
|
+
title: I("devportal.lastTriggered", "Last Triggered"),
|
|
155
|
+
value: X.lastTriggeredAt ? h(new Date(X.lastTriggeredAt), "MMM d, HH:mm") : I("devportal.never", "Never"),
|
|
156
|
+
icon: /* @__PURE__ */ T(b, { size: 20 })
|
|
157
157
|
})
|
|
158
158
|
]
|
|
159
159
|
}) }),
|
|
160
|
-
/* @__PURE__ */
|
|
160
|
+
/* @__PURE__ */ T(k, {
|
|
161
161
|
storageKey: `devportal-webhook-${X.id}`,
|
|
162
162
|
steps: [{
|
|
163
163
|
id: "test",
|
|
164
|
-
label:
|
|
165
|
-
description:
|
|
166
|
-
icon: /* @__PURE__ */
|
|
164
|
+
label: I("devportal.sendTestEvent", "Send a test event"),
|
|
165
|
+
description: I("devportal.webhookStepTest", "Confirm your endpoint receives and verifies the payload"),
|
|
166
|
+
icon: /* @__PURE__ */ T(w, { size: 16 }),
|
|
167
167
|
onClick: () => {
|
|
168
|
-
|
|
168
|
+
U(null), V(!0);
|
|
169
169
|
}
|
|
170
170
|
}, {
|
|
171
171
|
id: "toggle",
|
|
172
|
-
label: X.isActive ?
|
|
173
|
-
description:
|
|
174
|
-
icon: X.isActive ? /* @__PURE__ */
|
|
172
|
+
label: X.isActive ? I("devportal.pauseWebhook", "Pause deliveries") : I("devportal.resumeWebhook", "Resume deliveries"),
|
|
173
|
+
description: I("devportal.webhookStepToggle", "Pause while your endpoint is down to avoid lost retries"),
|
|
174
|
+
icon: X.isActive ? /* @__PURE__ */ T(S, { size: 16 }) : /* @__PURE__ */ T(C, { size: 16 }),
|
|
175
175
|
onClick: () => void $()
|
|
176
176
|
}]
|
|
177
177
|
}),
|
|
178
|
-
/* @__PURE__ */
|
|
179
|
-
title:
|
|
180
|
-
children: /* @__PURE__ */
|
|
178
|
+
/* @__PURE__ */ T(c, {
|
|
179
|
+
title: I("devportal.configuration", "Configuration"),
|
|
180
|
+
children: /* @__PURE__ */ T(ae, { items: [
|
|
181
181
|
{
|
|
182
|
-
label:
|
|
183
|
-
value: /* @__PURE__ */
|
|
182
|
+
label: I("devportal.status", "Status"),
|
|
183
|
+
value: /* @__PURE__ */ T(l, {
|
|
184
184
|
status: X.status,
|
|
185
185
|
type: "webhook"
|
|
186
186
|
})
|
|
187
187
|
},
|
|
188
188
|
{
|
|
189
|
-
label:
|
|
190
|
-
value: /* @__PURE__ */
|
|
189
|
+
label: I("devportal.endpointUrl", "Endpoint URL"),
|
|
190
|
+
value: /* @__PURE__ */ E("span", {
|
|
191
191
|
className: "flex flex-wrap items-center gap-2 font-mono text-sm",
|
|
192
|
-
children: [/* @__PURE__ */
|
|
192
|
+
children: [/* @__PURE__ */ T("span", {
|
|
193
193
|
className: "break-all",
|
|
194
194
|
children: X.url
|
|
195
|
-
}), /* @__PURE__ */
|
|
195
|
+
}), /* @__PURE__ */ T("a", {
|
|
196
196
|
href: X.url,
|
|
197
197
|
target: "_blank",
|
|
198
198
|
rel: "noopener noreferrer",
|
|
199
199
|
className: "text-primary",
|
|
200
|
-
children: /* @__PURE__ */
|
|
200
|
+
children: /* @__PURE__ */ T(x, { size: 14 })
|
|
201
201
|
})]
|
|
202
202
|
})
|
|
203
203
|
},
|
|
204
204
|
{
|
|
205
|
-
label:
|
|
206
|
-
value: /* @__PURE__ */
|
|
205
|
+
label: I("devportal.signingSecret", "Signing Secret"),
|
|
206
|
+
value: /* @__PURE__ */ T(re, {
|
|
207
207
|
value: X.secret || "******",
|
|
208
208
|
label: ""
|
|
209
209
|
})
|
|
210
210
|
},
|
|
211
211
|
{
|
|
212
|
-
label:
|
|
213
|
-
value: /* @__PURE__ */
|
|
212
|
+
label: I("devportal.subscribedEvents", "Subscribed Events"),
|
|
213
|
+
value: /* @__PURE__ */ T("div", {
|
|
214
214
|
className: "flex flex-wrap gap-1",
|
|
215
|
-
children: X.events.map((e) => /* @__PURE__ */
|
|
215
|
+
children: X.events.map((e) => /* @__PURE__ */ T("code", {
|
|
216
216
|
className: "rounded bg-bg-sunken px-1.5 py-0.5 text-xs",
|
|
217
217
|
children: e
|
|
218
218
|
}, e))
|
|
219
219
|
})
|
|
220
220
|
},
|
|
221
221
|
{
|
|
222
|
-
label:
|
|
223
|
-
value: X.retryEnabled ?
|
|
222
|
+
label: I("devportal.retryPolicy", "Retry Policy"),
|
|
223
|
+
value: X.retryEnabled ? I("devportal.retryPolicyEnabled", `Up to ${X.maxRetries ?? 0} retries`) : I("devportal.retriesDisabled", "Retries disabled")
|
|
224
224
|
},
|
|
225
225
|
{
|
|
226
|
-
label:
|
|
227
|
-
value:
|
|
226
|
+
label: I("devportal.created", "Created"),
|
|
227
|
+
value: A(X.createdAt)
|
|
228
228
|
},
|
|
229
229
|
{
|
|
230
|
-
label:
|
|
231
|
-
value:
|
|
230
|
+
label: I("devportal.updated", "Updated"),
|
|
231
|
+
value: A(X.updatedAt)
|
|
232
232
|
}
|
|
233
233
|
] })
|
|
234
234
|
}),
|
|
235
|
-
/* @__PURE__ */
|
|
236
|
-
title:
|
|
237
|
-
children: Z.length > 0 ? /* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ T(c, {
|
|
236
|
+
title: I("devportal.recentDeliveries", "Recent Deliveries"),
|
|
237
|
+
children: Z.length > 0 ? /* @__PURE__ */ T(ie, {
|
|
238
238
|
data: Z,
|
|
239
239
|
keyField: "id",
|
|
240
240
|
columns: [
|
|
241
241
|
{
|
|
242
242
|
key: "status",
|
|
243
|
-
header:
|
|
244
|
-
render: (e) => /* @__PURE__ */
|
|
243
|
+
header: I("devportal.status", "Status"),
|
|
244
|
+
render: (e) => /* @__PURE__ */ T("div", {
|
|
245
245
|
className: "flex items-center gap-2",
|
|
246
|
-
children: /* @__PURE__ */
|
|
246
|
+
children: /* @__PURE__ */ T(l, {
|
|
247
247
|
status: e.status,
|
|
248
248
|
type: "delivery"
|
|
249
249
|
})
|
|
@@ -251,64 +251,64 @@ var j = (e) => e ? _(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", M = () => {
|
|
|
251
251
|
},
|
|
252
252
|
{
|
|
253
253
|
key: "eventType",
|
|
254
|
-
header:
|
|
255
|
-
render: (e) => /* @__PURE__ */
|
|
254
|
+
header: I("devportal.event", "Event"),
|
|
255
|
+
render: (e) => /* @__PURE__ */ T("code", {
|
|
256
256
|
className: "text-xs",
|
|
257
257
|
children: e.eventType
|
|
258
258
|
})
|
|
259
259
|
},
|
|
260
260
|
{
|
|
261
261
|
key: "responseStatus",
|
|
262
|
-
header:
|
|
262
|
+
header: I("devportal.response", "Response"),
|
|
263
263
|
render: (e) => e.responseStatus ?? "-"
|
|
264
264
|
},
|
|
265
265
|
{
|
|
266
266
|
key: "attemptNumber",
|
|
267
|
-
header:
|
|
267
|
+
header: I("devportal.attempts", "Attempts"),
|
|
268
268
|
render: (e) => e.attemptNumber
|
|
269
269
|
},
|
|
270
270
|
{
|
|
271
271
|
key: "createdAt",
|
|
272
|
-
header:
|
|
273
|
-
render: (e) =>
|
|
272
|
+
header: I("devportal.created", "Created"),
|
|
273
|
+
render: (e) => A(e.createdAt)
|
|
274
274
|
}
|
|
275
275
|
]
|
|
276
|
-
}) : /* @__PURE__ */
|
|
276
|
+
}) : /* @__PURE__ */ T(t, {
|
|
277
277
|
illustration: "empty-notifications",
|
|
278
|
-
icon: /* @__PURE__ */
|
|
279
|
-
title:
|
|
280
|
-
description:
|
|
278
|
+
icon: /* @__PURE__ */ T(de, { size: 24 }),
|
|
279
|
+
title: I("devportal.noDeliveriesYet", "No deliveries yet"),
|
|
280
|
+
description: I("devportal.noDeliveriesYetDescription", "Deliveries will appear here after the first matching event is sent to this endpoint.")
|
|
281
281
|
})
|
|
282
282
|
}),
|
|
283
|
-
/* @__PURE__ */
|
|
284
|
-
open:
|
|
285
|
-
onOpenChange:
|
|
283
|
+
/* @__PURE__ */ T(f, {
|
|
284
|
+
open: z,
|
|
285
|
+
onOpenChange: B,
|
|
286
286
|
onConfirm: () => void we(),
|
|
287
287
|
loading: ye,
|
|
288
|
-
title:
|
|
289
|
-
description:
|
|
290
|
-
confirmLabel:
|
|
288
|
+
title: I("devportal.deleteWebhookTitle", "Delete webhook"),
|
|
289
|
+
description: I("devportal.deleteWebhookDescription", "Deleting this webhook stops all future deliveries for this endpoint."),
|
|
290
|
+
confirmLabel: I("devportal.deleteWebhookConfirm", "Delete webhook"),
|
|
291
291
|
variant: "danger"
|
|
292
292
|
}),
|
|
293
|
-
/* @__PURE__ */
|
|
294
|
-
open:
|
|
293
|
+
/* @__PURE__ */ T(f, {
|
|
294
|
+
open: me,
|
|
295
295
|
onOpenChange: (e) => {
|
|
296
|
-
|
|
296
|
+
V(e), e || U(null);
|
|
297
297
|
},
|
|
298
298
|
onConfirm: () => void Te(),
|
|
299
299
|
loading: xe,
|
|
300
|
-
title:
|
|
301
|
-
description:
|
|
302
|
-
confirmLabel:
|
|
303
|
-
children:
|
|
304
|
-
variant:
|
|
305
|
-
children:
|
|
300
|
+
title: I("devportal.sendTestDelivery", "Send test delivery"),
|
|
301
|
+
description: I("devportal.sendTestDeliveryDescription", "A signed test event will be sent to the configured endpoint through the public gateway."),
|
|
302
|
+
confirmLabel: I("devportal.sendTestEvent", "Send test event"),
|
|
303
|
+
children: H && /* @__PURE__ */ T(oe, {
|
|
304
|
+
variant: he ? "success" : "error",
|
|
305
|
+
children: H
|
|
306
306
|
})
|
|
307
307
|
})
|
|
308
308
|
]
|
|
309
309
|
});
|
|
310
310
|
};
|
|
311
311
|
//#endregion
|
|
312
|
-
export {
|
|
312
|
+
export { j as WebhookDetailPage };
|
|
313
313
|
|
|
314
314
|
//# sourceMappingURL=WebhookDetailPage.js.map
|