@burdenoff/microfe-devportal 2026.609.4 → 2026.611.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/DevportalRoot.js +6 -1
- package/dist/devportal/DevportalRoot.js.map +1 -1
- package/dist/devportal/DevportalRoutes.js +169 -158
- package/dist/devportal/DevportalRoutes.js.map +1 -1
- package/dist/devportal/hooks/usePackageUpload.js +120 -0
- package/dist/devportal/hooks/usePackageUpload.js.map +1 -0
- package/dist/devportal/pages/AnalyticsDashboardPage.js +73 -62
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +89 -82
- package/dist/devportal/pages/AppLogsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +948 -367
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +56 -45
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +30 -32
- package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
- package/dist/devportal/pages/CreateApplicationPage.js +8 -3
- package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
- package/dist/devportal/pages/CreateOAuthClientPage.js +4 -4
- package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
- package/dist/devportal/pages/CreateOrganizationPage.js +233 -228
- package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
- package/dist/devportal/pages/CreateWebhookPage.js +159 -148
- package/dist/devportal/pages/CreateWebhookPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +1 -1
- package/dist/devportal/pages/DelegationsPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +25 -17
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +159 -153
- package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js +2 -2
- package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationDetailPage.js +158 -153
- package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationMembersPage.js +223 -212
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/PublishPage.js +172 -165
- package/dist/devportal/pages/PublishPage.js.map +1 -1
- package/dist/devportal/pages/ResourcesPage.js.map +1 -1
- package/dist/devportal/pages/RevenueDashboardPage.js +405 -0
- package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -0
- package/dist/devportal/pages/SetupPage.js +186 -181
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/pages/StoreSubmissionPage.js +76 -97
- package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +172 -154
- package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
- package/dist/devportal/pages/WebhooksPage.js +218 -200
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/providers/DevportalProvider.js +16 -13
- package/dist/devportal/providers/DevportalProvider.js.map +1 -1
- package/dist/devportal/types/index.js.map +1 -1
- package/dist/generated/global-operations.js +310 -225
- package/dist/generated/global-operations.js.map +1 -1
- package/package.json +1 -1
- package/dist/devportal/pages/RevenueRedirectPage.js +0 -14
- package/dist/devportal/pages/RevenueRedirectPage.js.map +0 -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 { useApolloClient } from \"@apollo/client/react\";\nimport { gql } from \"@apollo/client/core\";\nimport { graphqlFetch } from \"@burdenoff/fe-libs/shared/graphql\";\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 storeClient = useApolloClient();\n\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 if (!appId) return;\n const sub = storeClient.subscribe({\n query: gql`\n subscription ApplicationStoreStatusChanged($applicationId: ID!) {\n applicationStoreStatusChanged(applicationId: $applicationId) {\n applicationId\n status\n reviewNotes\n rejectedReason\n updatedAt\n }\n }\n `,\n variables: { applicationId: appId },\n });\n const subscription = sub.subscribe({\n next: () => {\n void load();\n },\n error: (err: unknown) => {\n console.warn(\"[StoreSubmissionPage] subscription error:\", err);\n },\n });\n return () => subscription.unsubscribe();\n }, [appId, storeClient]); // eslint-disable-line react-hooks/exhaustive-deps\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 {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 <div className=\"py-16 text-center text-text-secondary\">\n {tr(\"devportal.storeSubmission.notFound\", \"Application not found.\")}\n </div>\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} from \"lucide-react\";\nimport { graphqlFetch } from \"@burdenoff/fe-libs/shared/graphql\";\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 {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 <div className=\"py-16 text-center text-text-secondary\">\n {tr(\"devportal.storeSubmission.notFound\", \"Application not found.\")}\n </div>\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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"}
|
|
@@ -4,216 +4,234 @@ import { PageLayout as n, PageSection as r } from "../components/PageLayout.js";
|
|
|
4
4
|
import { StatCard as i, StatCardGrid as a } from "../components/StatCard.js";
|
|
5
5
|
import { StatusBadge as o } from "../components/StatusBadge.js";
|
|
6
6
|
import { nativeImpact as s, nativeNotify as c } from "../../utils/nativeBridge.js";
|
|
7
|
-
import { SecretDisplay as
|
|
8
|
-
import { DataTable as
|
|
9
|
-
import { Alert as
|
|
10
|
-
import { useDeleteWebhookMutation as
|
|
11
|
-
import { DEVPORTAL_PERMISSIONS as
|
|
12
|
-
import { getErrorMessage as
|
|
13
|
-
import { useDevportalPermission as
|
|
7
|
+
import { SecretDisplay as ee } from "../components/SecretDisplay.js";
|
|
8
|
+
import { DataTable as te, DetailList as l } from "../components/DataTable.js";
|
|
9
|
+
import { Alert as ne, ConfirmDialog as u } from "../components/ConfirmDialog.js";
|
|
10
|
+
import { useDeleteWebhookMutation as d, useGetApplicationQuery as f, useGetWebhookDetailQuery as p, useTestWebhookMutation as m, useUpdateWebhookMutation as h } from "../../generated/global-operations.js";
|
|
11
|
+
import { DEVPORTAL_PERMISSIONS as g } from "../constants/permissions.js";
|
|
12
|
+
import { getErrorMessage as _ } from "../hooks/useGraphQL.js";
|
|
13
|
+
import { useDevportalPermission as v } from "../hooks/usePermission.js";
|
|
14
14
|
import "../hooks/index.js";
|
|
15
|
-
import
|
|
16
|
-
import { useMemo as x, useState as S } from "react";
|
|
17
|
-
import { useI18n as
|
|
18
|
-
import { AlertCircle as
|
|
19
|
-
import { useParams as
|
|
20
|
-
import { jsx as
|
|
15
|
+
import y from "../../node_modules/date-fns/format.js";
|
|
16
|
+
import { useEffect as b, useMemo as x, useState as S } from "react";
|
|
17
|
+
import { useI18n as C } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
18
|
+
import { AlertCircle as w, CheckCircle as T, Clock as re, ExternalLink as ie, Loader2 as ae, Pause as oe, Play as se, Send as ce, Trash2 as le, Webhook as E, XCircle as D } from "lucide-react";
|
|
19
|
+
import { useParams as O } from "react-router-dom";
|
|
20
|
+
import { jsx as k, jsxs as A } from "react/jsx-runtime";
|
|
21
21
|
//#region src/devportal/pages/WebhookDetailPage.tsx
|
|
22
|
-
var
|
|
23
|
-
let { appId:
|
|
24
|
-
let n =
|
|
22
|
+
var j = (e) => e ? y(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", M = () => {
|
|
23
|
+
let { appId: M, webhookId: N } = O(), { navigate: P, basePath: F } = e(), { t: I } = C(), L = (e, t) => {
|
|
24
|
+
let n = I(e);
|
|
25
25
|
return n === e ? t : n;
|
|
26
|
-
}, { allowed:
|
|
26
|
+
}, { allowed: R } = v(g.WEBHOOK_UPDATE), { allowed: z } = v(g.WEBHOOK_DELETE), [B, V] = S(!1), [H, U] = S(!1), [W, G] = S(null), [ue, K] = S(!1), { data: q } = f({
|
|
27
|
+
variables: { id: M ?? "" },
|
|
28
|
+
skip: !M,
|
|
29
|
+
fetchPolicy: "cache-first"
|
|
30
|
+
});
|
|
31
|
+
b(() => {
|
|
32
|
+
let e = q?.application?.name;
|
|
33
|
+
if (!M || !e) return;
|
|
34
|
+
let t = window.__breadcrumbRegistry;
|
|
35
|
+
return t?.register(M, e), () => t?.unregister(M);
|
|
36
|
+
}, [M, q?.application?.name]);
|
|
37
|
+
let { data: J, loading: de, error: Y, refetch: X } = p({
|
|
27
38
|
variables: {
|
|
28
|
-
id:
|
|
39
|
+
id: N || "",
|
|
29
40
|
limit: 20,
|
|
30
41
|
offset: 0
|
|
31
42
|
},
|
|
32
|
-
skip: !
|
|
43
|
+
skip: !N,
|
|
33
44
|
fetchPolicy: "cache-and-network"
|
|
34
|
-
}), [
|
|
35
|
-
|
|
36
|
-
let e =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
|
|
45
|
+
}), [fe, { loading: pe }] = h(), [me, { loading: he }] = d(), [ge, { loading: _e }] = m(), Z = J?.webhook;
|
|
46
|
+
b(() => {
|
|
47
|
+
let e = Z?.name;
|
|
48
|
+
if (!N || !e) return;
|
|
49
|
+
let t = window.__breadcrumbRegistry;
|
|
50
|
+
return t?.register(N, e), () => t?.unregister(N);
|
|
51
|
+
}, [N, Z?.name]);
|
|
52
|
+
let Q = J?.webhookDeliveries?.edges?.map((e) => e.node) ?? [], $ = M || Z?.applicationId, ve = x(() => {
|
|
53
|
+
if (!Z) return 0;
|
|
54
|
+
let e = (Z.successCount ?? 0) + (Z.failureCount ?? 0);
|
|
55
|
+
return e === 0 ? 100 : Math.round((Z.successCount ?? 0) / e * 100);
|
|
56
|
+
}, [Z]), ye = (e) => {
|
|
57
|
+
P?.(`${F}${e}`);
|
|
58
|
+
}, be = async () => {
|
|
59
|
+
Z && (await fe({ variables: {
|
|
60
|
+
id: Z.id,
|
|
61
|
+
input: { isActive: !Z.isActive }
|
|
62
|
+
} }), await X());
|
|
63
|
+
}, xe = async () => {
|
|
64
|
+
if (Z) {
|
|
47
65
|
s("medium");
|
|
48
66
|
try {
|
|
49
|
-
await
|
|
67
|
+
await me({ variables: { id: Z.id } }), c("success"), ye(`/apps/${Z.applicationId}/webhooks`);
|
|
50
68
|
} catch {
|
|
51
69
|
c("error");
|
|
52
70
|
}
|
|
53
71
|
}
|
|
54
|
-
},
|
|
55
|
-
if (!
|
|
56
|
-
let e = (await
|
|
57
|
-
id:
|
|
58
|
-
eventType:
|
|
72
|
+
}, Se = async () => {
|
|
73
|
+
if (!Z) return;
|
|
74
|
+
let e = (await ge({ variables: {
|
|
75
|
+
id: Z.id,
|
|
76
|
+
eventType: Z.events[0] || "application.updated"
|
|
59
77
|
} })).data?.testWebhook;
|
|
60
78
|
if (!e) {
|
|
61
|
-
K(!1),
|
|
79
|
+
K(!1), G("Test request did not return a response.");
|
|
62
80
|
return;
|
|
63
81
|
}
|
|
64
|
-
K(e.success),
|
|
82
|
+
K(e.success), G(e.success ? `Test delivery succeeded with HTTP ${e.statusCode ?? 200}.` : e.error || "Webhook test failed."), await X();
|
|
65
83
|
};
|
|
66
|
-
return
|
|
67
|
-
title:
|
|
68
|
-
description:
|
|
69
|
-
backLink:
|
|
70
|
-
backLabel:
|
|
71
|
-
children: /* @__PURE__ */
|
|
84
|
+
return de && !Z ? /* @__PURE__ */ k(n, {
|
|
85
|
+
title: L("devportal.webhook", "Webhook"),
|
|
86
|
+
description: L("devportal.loadingWebhookDetails", "Loading webhook details"),
|
|
87
|
+
backLink: $ ? `/apps/${$}/webhooks` : "/webhooks",
|
|
88
|
+
backLabel: L("devportal.backToWebhooks", "Back to Webhooks"),
|
|
89
|
+
children: /* @__PURE__ */ k("div", {
|
|
72
90
|
className: "flex items-center justify-center py-16 text-muted-foreground",
|
|
73
|
-
children: /* @__PURE__ */
|
|
91
|
+
children: /* @__PURE__ */ k(ae, { className: "h-8 w-8 animate-spin" })
|
|
74
92
|
})
|
|
75
|
-
}) :
|
|
76
|
-
title:
|
|
77
|
-
description:
|
|
78
|
-
backLink:
|
|
79
|
-
backLabel:
|
|
80
|
-
children: /* @__PURE__ */
|
|
81
|
-
icon: /* @__PURE__ */
|
|
93
|
+
}) : Y || !Z ? /* @__PURE__ */ k(n, {
|
|
94
|
+
title: L("devportal.webhook", "Webhook"),
|
|
95
|
+
description: L("devportal.webhookDetails", "Webhook details"),
|
|
96
|
+
backLink: $ ? `/apps/${$}/webhooks` : "/webhooks",
|
|
97
|
+
backLabel: L("devportal.backToWebhooks", "Back to Webhooks"),
|
|
98
|
+
children: /* @__PURE__ */ k(t, {
|
|
99
|
+
icon: /* @__PURE__ */ k(w, {
|
|
82
100
|
size: 32,
|
|
83
101
|
className: "text-status-error-text"
|
|
84
102
|
}),
|
|
85
|
-
title:
|
|
86
|
-
description:
|
|
103
|
+
title: L("devportal.unableToLoadWebhook", "Unable to load webhook"),
|
|
104
|
+
description: Y ? _(Y) : L("devportal.webhookNotFound", "Webhook not found")
|
|
87
105
|
})
|
|
88
|
-
}) : /* @__PURE__ */
|
|
89
|
-
title:
|
|
90
|
-
description:
|
|
91
|
-
backLink: `/apps/${
|
|
92
|
-
backLabel:
|
|
93
|
-
actions: /* @__PURE__ */
|
|
106
|
+
}) : /* @__PURE__ */ A(n, {
|
|
107
|
+
title: Z.name,
|
|
108
|
+
description: Z.description || "Webhook configuration and delivery history",
|
|
109
|
+
backLink: `/apps/${$}/webhooks`,
|
|
110
|
+
backLabel: L("devportal.backToWebhooks", "Back to Webhooks"),
|
|
111
|
+
actions: /* @__PURE__ */ A("div", {
|
|
94
112
|
className: "flex flex-wrap gap-2",
|
|
95
113
|
children: [
|
|
96
|
-
/* @__PURE__ */
|
|
114
|
+
/* @__PURE__ */ A("button", {
|
|
97
115
|
type: "button",
|
|
98
116
|
onClick: () => {
|
|
99
|
-
|
|
117
|
+
G(null), U(!0);
|
|
100
118
|
},
|
|
101
119
|
className: "inline-flex items-center gap-2 rounded-md border px-3 py-2 text-sm font-medium hover:bg-accent",
|
|
102
|
-
children: [/* @__PURE__ */
|
|
120
|
+
children: [/* @__PURE__ */ k(ce, { size: 16 }), L("devportal.test", "Test")]
|
|
103
121
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ A("button", {
|
|
105
123
|
type: "button",
|
|
106
|
-
disabled:
|
|
107
|
-
"aria-disabled": !
|
|
108
|
-
onClick: () => void
|
|
124
|
+
disabled: pe || !R,
|
|
125
|
+
"aria-disabled": !R,
|
|
126
|
+
onClick: () => void be(),
|
|
109
127
|
className: "inline-flex items-center gap-2 rounded-md border px-3 py-2 text-sm font-medium hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50",
|
|
110
|
-
children: [
|
|
128
|
+
children: [Z.isActive ? /* @__PURE__ */ k(oe, { size: 16 }) : /* @__PURE__ */ k(se, { size: 16 }), Z.isActive ? L("devportal.pauseWebhook", "Pause") : L("devportal.resumeWebhook", "Resume")]
|
|
111
129
|
}),
|
|
112
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ A("button", {
|
|
113
131
|
type: "button",
|
|
114
|
-
onClick: () =>
|
|
115
|
-
disabled: !
|
|
116
|
-
"aria-disabled": !
|
|
132
|
+
onClick: () => V(!0),
|
|
133
|
+
disabled: !z,
|
|
134
|
+
"aria-disabled": !z,
|
|
117
135
|
className: "inline-flex items-center gap-2 rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-50",
|
|
118
|
-
children: [/* @__PURE__ */
|
|
136
|
+
children: [/* @__PURE__ */ k(le, { size: 16 }), L("devportal.deleteWebhook", "Delete")]
|
|
119
137
|
})
|
|
120
138
|
]
|
|
121
139
|
}),
|
|
122
140
|
children: [
|
|
123
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ A(a, {
|
|
124
142
|
columns: 4,
|
|
125
143
|
children: [
|
|
126
|
-
/* @__PURE__ */
|
|
127
|
-
title:
|
|
128
|
-
value: `${
|
|
129
|
-
icon: /* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ k(i, {
|
|
145
|
+
title: L("devportal.successRate", "Success Rate"),
|
|
146
|
+
value: `${ve}%`,
|
|
147
|
+
icon: /* @__PURE__ */ k(T, { size: 20 })
|
|
130
148
|
}),
|
|
131
|
-
/* @__PURE__ */
|
|
132
|
-
title:
|
|
133
|
-
value: (
|
|
134
|
-
icon: /* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ k(i, {
|
|
150
|
+
title: L("devportal.successfulDeliveries", "Successful Deliveries"),
|
|
151
|
+
value: (Z.successCount ?? 0).toLocaleString(),
|
|
152
|
+
icon: /* @__PURE__ */ k(T, { size: 20 })
|
|
135
153
|
}),
|
|
136
|
-
/* @__PURE__ */
|
|
137
|
-
title:
|
|
138
|
-
value: (
|
|
139
|
-
icon: /* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ k(i, {
|
|
155
|
+
title: L("devportal.failedDeliveries", "Failed Deliveries"),
|
|
156
|
+
value: (Z.failureCount ?? 0).toLocaleString(),
|
|
157
|
+
icon: /* @__PURE__ */ k(D, { size: 20 })
|
|
140
158
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
title:
|
|
143
|
-
value:
|
|
144
|
-
icon: /* @__PURE__ */
|
|
159
|
+
/* @__PURE__ */ k(i, {
|
|
160
|
+
title: L("devportal.lastTriggered", "Last Triggered"),
|
|
161
|
+
value: Z.lastTriggeredAt ? y(new Date(Z.lastTriggeredAt), "MMM d, HH:mm") : L("devportal.never", "Never"),
|
|
162
|
+
icon: /* @__PURE__ */ k(re, { size: 20 })
|
|
145
163
|
})
|
|
146
164
|
]
|
|
147
165
|
}),
|
|
148
|
-
/* @__PURE__ */
|
|
149
|
-
title:
|
|
150
|
-
children: /* @__PURE__ */
|
|
166
|
+
/* @__PURE__ */ k(r, {
|
|
167
|
+
title: L("devportal.configuration", "Configuration"),
|
|
168
|
+
children: /* @__PURE__ */ k(l, { items: [
|
|
151
169
|
{
|
|
152
|
-
label:
|
|
153
|
-
value: /* @__PURE__ */
|
|
154
|
-
status:
|
|
170
|
+
label: L("devportal.status", "Status"),
|
|
171
|
+
value: /* @__PURE__ */ k(o, {
|
|
172
|
+
status: Z.status,
|
|
155
173
|
type: "webhook"
|
|
156
174
|
})
|
|
157
175
|
},
|
|
158
176
|
{
|
|
159
|
-
label:
|
|
160
|
-
value: /* @__PURE__ */
|
|
177
|
+
label: L("devportal.endpointUrl", "Endpoint URL"),
|
|
178
|
+
value: /* @__PURE__ */ A("span", {
|
|
161
179
|
className: "flex flex-wrap items-center gap-2 font-mono text-sm",
|
|
162
|
-
children: [/* @__PURE__ */
|
|
180
|
+
children: [/* @__PURE__ */ k("span", {
|
|
163
181
|
className: "break-all",
|
|
164
|
-
children:
|
|
165
|
-
}), /* @__PURE__ */
|
|
166
|
-
href:
|
|
182
|
+
children: Z.url
|
|
183
|
+
}), /* @__PURE__ */ k("a", {
|
|
184
|
+
href: Z.url,
|
|
167
185
|
target: "_blank",
|
|
168
186
|
rel: "noopener noreferrer",
|
|
169
187
|
className: "text-primary",
|
|
170
|
-
children: /* @__PURE__ */
|
|
188
|
+
children: /* @__PURE__ */ k(ie, { size: 14 })
|
|
171
189
|
})]
|
|
172
190
|
})
|
|
173
191
|
},
|
|
174
192
|
{
|
|
175
|
-
label:
|
|
176
|
-
value: /* @__PURE__ */
|
|
177
|
-
value:
|
|
193
|
+
label: L("devportal.signingSecret", "Signing Secret"),
|
|
194
|
+
value: /* @__PURE__ */ k(ee, {
|
|
195
|
+
value: Z.secret || "******",
|
|
178
196
|
label: ""
|
|
179
197
|
})
|
|
180
198
|
},
|
|
181
199
|
{
|
|
182
|
-
label:
|
|
183
|
-
value: /* @__PURE__ */
|
|
200
|
+
label: L("devportal.subscribedEvents", "Subscribed Events"),
|
|
201
|
+
value: /* @__PURE__ */ k("div", {
|
|
184
202
|
className: "flex flex-wrap gap-1",
|
|
185
|
-
children:
|
|
203
|
+
children: Z.events.map((e) => /* @__PURE__ */ k("code", {
|
|
186
204
|
className: "rounded bg-muted px-1.5 py-0.5 text-xs",
|
|
187
205
|
children: e
|
|
188
206
|
}, e))
|
|
189
207
|
})
|
|
190
208
|
},
|
|
191
209
|
{
|
|
192
|
-
label:
|
|
193
|
-
value:
|
|
210
|
+
label: L("devportal.retryPolicy", "Retry Policy"),
|
|
211
|
+
value: Z.retryEnabled ? L("devportal.retryPolicyEnabled", `Up to ${Z.maxRetries ?? 0} retries`) : L("devportal.retriesDisabled", "Retries disabled")
|
|
194
212
|
},
|
|
195
213
|
{
|
|
196
|
-
label:
|
|
197
|
-
value:
|
|
214
|
+
label: L("devportal.created", "Created"),
|
|
215
|
+
value: j(Z.createdAt)
|
|
198
216
|
},
|
|
199
217
|
{
|
|
200
|
-
label:
|
|
201
|
-
value:
|
|
218
|
+
label: L("devportal.updated", "Updated"),
|
|
219
|
+
value: j(Z.updatedAt)
|
|
202
220
|
}
|
|
203
221
|
] })
|
|
204
222
|
}),
|
|
205
|
-
/* @__PURE__ */
|
|
206
|
-
title:
|
|
207
|
-
children:
|
|
208
|
-
data:
|
|
223
|
+
/* @__PURE__ */ k(r, {
|
|
224
|
+
title: L("devportal.recentDeliveries", "Recent Deliveries"),
|
|
225
|
+
children: Q.length > 0 ? /* @__PURE__ */ k(te, {
|
|
226
|
+
data: Q,
|
|
209
227
|
keyField: "id",
|
|
210
228
|
columns: [
|
|
211
229
|
{
|
|
212
230
|
key: "status",
|
|
213
|
-
header:
|
|
214
|
-
render: (e) => /* @__PURE__ */
|
|
231
|
+
header: L("devportal.status", "Status"),
|
|
232
|
+
render: (e) => /* @__PURE__ */ k("div", {
|
|
215
233
|
className: "flex items-center gap-2",
|
|
216
|
-
children: /* @__PURE__ */
|
|
234
|
+
children: /* @__PURE__ */ k(o, {
|
|
217
235
|
status: e.status,
|
|
218
236
|
type: "delivery"
|
|
219
237
|
})
|
|
@@ -221,63 +239,63 @@ var A = (e) => e ? b(new Date(e), "MMM d, yyyy HH:mm:ss") : "Never", j = () => {
|
|
|
221
239
|
},
|
|
222
240
|
{
|
|
223
241
|
key: "eventType",
|
|
224
|
-
header:
|
|
225
|
-
render: (e) => /* @__PURE__ */
|
|
242
|
+
header: L("devportal.event", "Event"),
|
|
243
|
+
render: (e) => /* @__PURE__ */ k("code", {
|
|
226
244
|
className: "text-xs",
|
|
227
245
|
children: e.eventType
|
|
228
246
|
})
|
|
229
247
|
},
|
|
230
248
|
{
|
|
231
249
|
key: "responseStatus",
|
|
232
|
-
header:
|
|
250
|
+
header: L("devportal.response", "Response"),
|
|
233
251
|
render: (e) => e.responseStatus ?? "-"
|
|
234
252
|
},
|
|
235
253
|
{
|
|
236
254
|
key: "attemptNumber",
|
|
237
|
-
header:
|
|
255
|
+
header: L("devportal.attempts", "Attempts"),
|
|
238
256
|
render: (e) => e.attemptNumber
|
|
239
257
|
},
|
|
240
258
|
{
|
|
241
259
|
key: "createdAt",
|
|
242
|
-
header:
|
|
243
|
-
render: (e) =>
|
|
260
|
+
header: L("devportal.created", "Created"),
|
|
261
|
+
render: (e) => j(e.createdAt)
|
|
244
262
|
}
|
|
245
263
|
]
|
|
246
|
-
}) : /* @__PURE__ */
|
|
247
|
-
icon: /* @__PURE__ */
|
|
248
|
-
title:
|
|
249
|
-
description:
|
|
264
|
+
}) : /* @__PURE__ */ k(t, {
|
|
265
|
+
icon: /* @__PURE__ */ k(E, { size: 24 }),
|
|
266
|
+
title: L("devportal.noDeliveriesYet", "No deliveries yet"),
|
|
267
|
+
description: L("devportal.noDeliveriesYetDescription", "Deliveries will appear here after the first matching event is sent to this endpoint.")
|
|
250
268
|
})
|
|
251
269
|
}),
|
|
252
|
-
/* @__PURE__ */
|
|
253
|
-
open:
|
|
254
|
-
onOpenChange:
|
|
255
|
-
onConfirm: () => void
|
|
256
|
-
loading:
|
|
257
|
-
title:
|
|
258
|
-
description:
|
|
259
|
-
confirmLabel:
|
|
270
|
+
/* @__PURE__ */ k(u, {
|
|
271
|
+
open: B,
|
|
272
|
+
onOpenChange: V,
|
|
273
|
+
onConfirm: () => void xe(),
|
|
274
|
+
loading: he,
|
|
275
|
+
title: L("devportal.deleteWebhookTitle", "Delete webhook"),
|
|
276
|
+
description: L("devportal.deleteWebhookDescription", "Deleting this webhook stops all future deliveries for this endpoint."),
|
|
277
|
+
confirmLabel: L("devportal.deleteWebhookConfirm", "Delete webhook"),
|
|
260
278
|
variant: "danger"
|
|
261
279
|
}),
|
|
262
|
-
/* @__PURE__ */
|
|
263
|
-
open:
|
|
280
|
+
/* @__PURE__ */ k(u, {
|
|
281
|
+
open: H,
|
|
264
282
|
onOpenChange: (e) => {
|
|
265
|
-
|
|
283
|
+
U(e), e || G(null);
|
|
266
284
|
},
|
|
267
|
-
onConfirm: () => void
|
|
268
|
-
loading:
|
|
269
|
-
title:
|
|
270
|
-
description:
|
|
271
|
-
confirmLabel:
|
|
272
|
-
children:
|
|
273
|
-
variant:
|
|
274
|
-
children:
|
|
285
|
+
onConfirm: () => void Se(),
|
|
286
|
+
loading: _e,
|
|
287
|
+
title: L("devportal.sendTestDelivery", "Send test delivery"),
|
|
288
|
+
description: L("devportal.sendTestDeliveryDescription", "A signed test event will be sent to the configured endpoint through the public gateway."),
|
|
289
|
+
confirmLabel: L("devportal.sendTestEvent", "Send test event"),
|
|
290
|
+
children: W && /* @__PURE__ */ k(ne, {
|
|
291
|
+
variant: ue ? "success" : "error",
|
|
292
|
+
children: W
|
|
275
293
|
})
|
|
276
294
|
})
|
|
277
295
|
]
|
|
278
296
|
});
|
|
279
297
|
};
|
|
280
298
|
//#endregion
|
|
281
|
-
export {
|
|
299
|
+
export { M as WebhookDetailPage };
|
|
282
300
|
|
|
283
301
|
//# sourceMappingURL=WebhookDetailPage.js.map
|