@burdenoff/microfe-devportal 2026.717.1 → 2026.720.2
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/PermissionGuard.d.ts +14 -1
- package/dist/devportal/components/PermissionGuard.d.ts.map +1 -1
- package/dist/devportal/components/PermissionGuard.js +16 -13
- package/dist/devportal/components/PermissionGuard.js.map +1 -1
- package/dist/devportal/pages/AdminReviewPage.js +9 -9
- package/dist/devportal/pages/ApiKeysPage.js +180 -181
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +3 -3
- package/dist/devportal/pages/ApplicationVersionsPage.js +4 -4
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +4 -4
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +1 -1
- package/dist/devportal/pages/CreateOAuthClientPage.js +1 -1
- package/dist/devportal/pages/CreateOrganizationPage.d.ts.map +1 -1
- package/dist/devportal/pages/CreateOrganizationPage.js +97 -98
- package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
- package/dist/devportal/pages/CreateWebhookPage.js +25 -25
- package/dist/devportal/pages/DashboardPage.d.ts.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +110 -111
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +4 -4
- package/dist/devportal/pages/ListingPreviewPage.d.ts.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +160 -161
- package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientDetailPage.d.ts.map +1 -1
- package/dist/devportal/pages/OAuthClientDetailPage.js +166 -167
- package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.d.ts.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js +121 -122
- package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationDetailPage.js +3 -3
- package/dist/devportal/pages/OrganizationMembersPage.js +4 -4
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationsPage.d.ts.map +1 -1
- package/dist/devportal/pages/OrganizationsPage.js +97 -98
- package/dist/devportal/pages/OrganizationsPage.js.map +1 -1
- package/dist/devportal/pages/PublishPage.js +246 -246
- package/dist/devportal/pages/PublishPage.js.map +1 -1
- package/dist/devportal/pages/PublisherProfilePage.js +4 -4
- package/dist/devportal/pages/PublisherProfilePage.js.map +1 -1
- package/dist/devportal/pages/StoreSubmissionPage.js +3 -3
- package/dist/devportal/pages/WebhookDetailPage.js +3 -3
- package/dist/devportal/pages/WebhooksPage.js +129 -129
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebhooksPage.js","names":[],"sources":["../../../src/devportal/pages/WebhooksPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState, useEffect } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n Plus,\n Search,\n Webhook,\n CheckCircle,\n XCircle,\n PauseCircle,\n AlertTriangle,\n Loader2,\n AlertCircle,\n AppWindow,\n} from \"lucide-react\";\nimport { useParams } from \"react-router-dom\";\nimport { EmphasisPanel, GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { AppListItem } from \"../components/AppCard\";\nimport { PermissionGuard } from \"../components\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport type {\n Application,\n Webhook as WebhookType,\n WebhookStatus,\n} from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport {\n useGetApplicationsQuery,\n useGetApplicationQuery,\n useGetWebhooksQuery,\n getErrorMessage,\n useDevportalPermission,\n type ApplicationFieldsFragment,\n type DevportalWebhookFieldsFragment,\n} from \"../hooks\";\n\n/**\n * Map GraphQL webhook to local Webhook type\n */\nfunction mapWebhook(webhook: DevportalWebhookFieldsFragment): WebhookType {\n return {\n id: webhook.id,\n applicationId: webhook.applicationId,\n name: webhook.name,\n description: webhook.description ?? undefined,\n url: webhook.url,\n secret: undefined,\n events: webhook.events ?? [],\n isActive: webhook.isActive,\n status: webhook.status as WebhookStatus,\n maxRetries: webhook.maxRetries ?? 3,\n retryDelay: webhook.retryDelay ?? 60,\n retryEnabled: webhook.retryEnabled ?? true,\n lastTriggeredAt: webhook.lastTriggeredAt ?? undefined,\n lastSuccessAt: webhook.lastSuccessAt ?? undefined,\n lastFailureAt: webhook.lastFailureAt ?? undefined,\n lastFailureReason: webhook.lastFailureReason ?? undefined,\n failureCount: webhook.failureCount ?? 0,\n successCount: webhook.successCount ?? 0,\n createdAt: webhook.createdAt,\n updatedAt: webhook.updatedAt,\n };\n}\n\n/**\n * Webhooks listing page\n *\n * Displays webhooks for a specific application.\n * Uses GraphQL to fetch real data from the DevPortal backend.\n */\nexport const WebhooksPage: FC = () => {\n const { appId } = useParams<{ appId: string }>();\n const { navigate, basePath, activeOrgId } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const createPerm = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.WEBHOOK_CREATE,\n );\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [statusFilter, setStatusFilter] = useState<WebhookStatus | \"\">(\"\");\n\n // Fetch app name for breadcrumb when viewing webhooks for a specific app\n const { data: singleAppData } = useGetApplicationQuery({\n variables: { id: appId ?? \"\" },\n skip: !appId,\n fetchPolicy: \"cache-first\",\n });\n\n useEffect(() => {\n const name = singleAppData?.application?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, singleAppData?.application?.name]);\n\n const {\n data: appsData,\n loading: appsLoading,\n error: appsError,\n refetch: refetchApps,\n } = useGetApplicationsQuery({\n variables: { limit: 100 },\n skip: !!appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n // Fetch webhooks from GraphQL API (only if we have an applicationId)\n const { data, loading, error, refetch } = useGetWebhooksQuery({\n variables: {\n applicationId: appId || \"\",\n },\n skip: !appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const webhooks = data?.webhooks?.map(mapWebhook) ?? [];\n const allApplications: Application[] =\n appsData?.applications?.edges?.map(\n (edge: { node: ApplicationFieldsFragment }) => edge.node as Application,\n ) ?? [];\n // Scope app picker to active org\n const applications = activeOrgId\n ? allApplications.filter((a) => a.developerOrganizationId === activeOrgId)\n : allApplications;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n // Filter webhooks\n const filteredWebhooks = webhooks.filter((webhook: WebhookType) => {\n const matchesSearch =\n !searchQuery ||\n webhook.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n webhook.url.toLowerCase().includes(searchQuery.toLowerCase());\n const matchesStatus = !statusFilter || webhook.status === statusFilter;\n return matchesSearch && matchesStatus;\n });\n\n const getStatusIcon = (status: WebhookStatus) => {\n switch (status) {\n case \"ACTIVE\":\n return <CheckCircle size={16} className=\"text-status-success-text\" />;\n case \"PAUSED\":\n return <PauseCircle size={16} className=\"text-status-warning-text\" />;\n case \"FAILED\":\n return <XCircle size={16} className=\"text-status-error-text\" />;\n case \"DISABLED\":\n return <AlertTriangle size={16} className=\"text-text-tertiary\" />;\n default:\n return null;\n }\n };\n\n const calculateSuccessRate = (webhook: WebhookType): number => {\n const total = webhook.successCount + webhook.failureCount;\n if (total === 0) return 100;\n return Math.round((webhook.successCount / total) * 100);\n };\n\n // If no applicationId, show prompt to select an application\n if (!appId) {\n const filteredApplications = applications.filter((application) => {\n if (!searchQuery) return true;\n\n const normalizedQuery = searchQuery.toLowerCase();\n return (\n application.name.toLowerCase().includes(normalizedQuery) ||\n (application.slug ?? \"\").toLowerCase().includes(normalizedQuery)\n );\n });\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.WEBHOOK_READ}>\n <PageLayout\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n description={tr(\n \"devportal.manageWebhooksApps\",\n \"Manage webhooks for your applications\",\n )}\n purpose={tr(\n \"devportal.webhooksPickerPurpose\",\n \"Webhooks push real-time events (installs, payments, app updates) to a URL you host, so your backend can react without polling. Pick an application to view, add, or test the webhook endpoints it delivers to.\",\n )}\n >\n <div className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.webhooksSelectAppHint\",\n \"Select an application below to view and manage its webhooks.\",\n )}\n </p>\n <div className=\"max-w-md\">\n <div className=\"relative\">\n <Search\n size={16}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"text\"\n placeholder=\"Search applications...\"\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.target.value)}\n className=\"w-full rounded-md border bg-bg-surface pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n </div>\n\n {appsLoading ? (\n <div className=\"flex items-center justify-center py-12 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n ) : appsError ? (\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center\">\n <AlertCircle className=\"mx-auto h-8 w-8 text-status-error-text\" />\n <h3 className=\"mt-2 font-medium text-status-error-text\">\n Failed to load applications\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {getErrorMessage(appsError)}\n </p>\n <button\n onClick={() => refetchApps()}\n className=\"mt-4 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n Try Again\n </button>\n </div>\n ) : filteredApplications.length > 0 ? (\n <div className=\"space-y-2\">\n {filteredApplications.map((application) => (\n <AppListItem\n key={application.id}\n app={application}\n onClick={() =>\n handleNavigate(`/apps/${application.id}/webhooks`)\n }\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration={searchQuery ? \"empty-search\" : \"empty-data\"}\n icon={<AppWindow size={32} />}\n title=\"No applications available\"\n description={\n searchQuery\n ? \"No applications matched your search.\"\n : \"Create an application before configuring webhooks.\"\n }\n action={\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n Create Application\n </button>\n }\n />\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n }\n\n // Loading state\n if (loading && !data) {\n return (\n <PageLayout\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n description={tr(\n \"devportal.manageWebhooksApp\",\n \"Manage webhooks for this application\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n <span className=\"ml-2 text-text-secondary\">\n {tr(\"devportal.loadingWebhooks\", \"Loading webhooks...\")}\n </span>\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (error) {\n return (\n <PageLayout\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n description={tr(\n \"devportal.manageWebhooksApp\",\n \"Manage webhooks for this application\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center\">\n <AlertCircle className=\"mx-auto h-8 w-8 text-status-error-text\" />\n <h3 className=\"mt-2 font-medium text-status-error-text\">\n {tr(\"devportal.failedToLoadWebhooks\", \"Failed to load webhooks\")}\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {getErrorMessage(error)}\n </p>\n <button\n onClick={() => refetch()}\n className=\"mt-4 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr(\"devportal.tryAgain\", \"Try Again\")}\n </button>\n </div>\n </PageLayout>\n );\n }\n\n const columns = [\n {\n key: \"name\",\n header: \"Webhook\",\n render: (webhook: WebhookType) => (\n <div className=\"flex items-center gap-3\">\n <div\n className={cn(\n \"flex h-10 w-10 items-center justify-center rounded-lg\",\n webhook.status === \"ACTIVE\"\n ? \"bg-status-success-bg-subtle text-status-success-text\"\n : webhook.status === \"FAILED\"\n ? \"bg-status-error-bg-subtle text-status-error-text\"\n : webhook.status === \"PAUSED\"\n ? \"bg-status-warning-bg-subtle text-status-warning-text\"\n : \"bg-bg-sunken text-text-tertiary\",\n )}\n >\n <Webhook size={20} />\n </div>\n <div>\n <p className=\"font-medium\">{webhook.name}</p>\n <p className=\"text-xs text-text-secondary truncate max-w-xs\">\n {webhook.url}\n </p>\n </div>\n </div>\n ),\n },\n {\n key: \"events\",\n header: \"Events\",\n render: (webhook: WebhookType) => (\n <div className=\"flex flex-wrap gap-1\">\n {webhook.events.slice(0, 2).map((event) => (\n <span\n key={event}\n className=\"inline-flex rounded bg-bg-sunken px-1.5 py-0.5 text-xs\"\n >\n {event.split(\".\")[0]}\n </span>\n ))}\n {webhook.events.length > 2 && (\n <span className=\"text-xs text-text-secondary\">\n +{webhook.events.length - 2} more\n </span>\n )}\n </div>\n ),\n },\n {\n key: \"status\",\n header: \"Status\",\n render: (webhook: WebhookType) => (\n <div className=\"flex items-center gap-2\">\n {getStatusIcon(webhook.status)}\n <StatusBadge status={webhook.status} type=\"webhook\" />\n </div>\n ),\n },\n {\n key: \"successRate\",\n header: \"Success Rate\",\n render: (webhook: WebhookType) => {\n const rate = calculateSuccessRate(webhook);\n return (\n <div className=\"w-24\">\n <div className=\"flex items-center justify-between text-xs mb-1\">\n <span\n className={cn(\n rate >= 95\n ? \"text-status-success-text\"\n : rate >= 80\n ? \"text-status-warning-text\"\n : \"text-status-error-text\",\n )}\n >\n {rate}%\n </span>\n </div>\n <div className=\"h-1.5 rounded-full bg-bg-sunken\">\n <div\n className={cn(\n \"h-full rounded-full transition-all\",\n rate >= 95\n ? \"bg-status-success-bg-subtle\"\n : rate >= 80\n ? \"bg-status-warning-bg-subtle\"\n : \"bg-status-error-bg-subtle\",\n )}\n style={{ width: `${rate}%` }}\n />\n </div>\n </div>\n );\n },\n },\n {\n key: \"deliveries\",\n header: \"Deliveries\",\n render: (webhook: WebhookType) => (\n <div className=\"text-sm\">\n <span className=\"text-status-success-text\">\n {webhook.successCount.toLocaleString()}\n </span>\n <span className=\"text-text-secondary\"> / </span>\n <span className=\"text-status-error-text\">\n {webhook.failureCount.toLocaleString()}\n </span>\n </div>\n ),\n },\n {\n key: \"lastTriggered\",\n header: \"Last Triggered\",\n render: (webhook: WebhookType) => (\n <span className=\"text-sm text-text-secondary\">\n {webhook.lastTriggeredAt\n ? formatRelativeTime(webhook.lastTriggeredAt)\n : \"Never\"}\n </span>\n ),\n },\n ];\n\n // Stats calculation\n const stats = {\n total: webhooks.length,\n active: webhooks.filter((w: WebhookType) => w.status === \"ACTIVE\").length,\n failed: webhooks.filter((w: WebhookType) => w.status === \"FAILED\").length,\n paused: webhooks.filter((w: WebhookType) => w.status === \"PAUSED\").length,\n };\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.WEBHOOK_READ}>\n <PageLayout\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n description={tr(\n \"devportal.manageWebhooksApp\",\n \"Manage webhooks for this application\",\n )}\n purpose={tr(\n \"devportal.webhooksAppPurpose\",\n \"These webhooks deliver this application's events to your endpoints. Add a URL, choose which events to subscribe to, and send a test delivery to confirm your server receives and verifies the signed payload.\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n actions={\n <button\n onClick={() => handleNavigate(`/apps/${appId}/webhooks/new`)}\n disabled={!createPerm.allowed}\n aria-disabled={!createPerm.allowed}\n title={\n !createPerm.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-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <Plus size={16} aria-hidden=\"true\" />\n {tr(\"devportal.createWebhook\", \"Create Webhook\")}\n </button>\n }\n >\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-1 items-center gap-4\">\n {/* Search */}\n <div className=\"relative flex-1 max-w-md\">\n <Search\n size={16}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"text\"\n placeholder=\"Search by name or URL...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-md border bg-bg-surface pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n {/* Status Filter */}\n <select\n value={statusFilter}\n onChange={(e) =>\n setStatusFilter(e.target.value as WebhookStatus | \"\")\n }\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"\">All Status</option>\n <option value=\"ACTIVE\">Active</option>\n <option value=\"PAUSED\">Paused</option>\n <option value=\"FAILED\">Failed</option>\n <option value=\"DISABLED\">Disabled</option>\n </select>\n </div>\n </div>\n\n {/* Summary Stats — primary emphasis zone */}\n <EmphasisPanel>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Webhook size={16} />\n <span className=\"text-sm\">Total Webhooks</span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {stats.total}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-status-success-text\">\n <CheckCircle size={16} />\n <span className=\"text-sm\">Active</span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {stats.active}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-status-warning-text\">\n <PauseCircle size={16} />\n <span className=\"text-sm\">Paused</span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {stats.paused}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-status-error-text\">\n <XCircle size={16} />\n <span className=\"text-sm\">Failed</span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {stats.failed}\n </p>\n </GlassCard>\n </div>\n </EmphasisPanel>\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n {filteredWebhooks.length} webhook\n {filteredWebhooks.length !== 1 ? \"s\" : \"\"} found\n </p>\n\n {/* Webhooks Table */}\n {filteredWebhooks.length > 0 ? (\n <DataTable\n data={filteredWebhooks}\n columns={columns}\n keyField=\"id\"\n onRowClick={(webhook) =>\n handleNavigate(`/apps/${appId}/webhooks/${webhook.id}`)\n }\n />\n ) : (\n <EmptyState\n illustration=\"empty-notifications\"\n title={tr(\"devportal.noWebhooksFound\", \"No webhooks found\")}\n description={\n searchQuery || statusFilter\n ? tr(\n \"devportal.tryAdjustingFilters\",\n \"Try adjusting your filters\",\n )\n : tr(\n \"devportal.createFirstWebhook\",\n \"Create your first webhook to receive real-time event notifications\",\n )\n }\n action={\n !searchQuery && !statusFilter ? (\n <button\n onClick={() => handleNavigate(`/apps/${appId}/webhooks/new`)}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createWebhook\", \"Create Webhook\")}\n </button>\n ) : undefined\n }\n />\n )}\n </PageLayout>\n </PermissionGuard>\n );\n};\n\nfunction formatRelativeTime(dateString: string): string {\n const date = new Date(dateString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMins / 60);\n const diffDays = Math.floor(diffHours / 24);\n\n if (diffMins < 1) return \"Just now\";\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WebhooksPage.js","names":[],"sources":["../../../src/devportal/pages/WebhooksPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState, useEffect } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n Plus,\n Search,\n Webhook,\n CheckCircle,\n XCircle,\n PauseCircle,\n AlertTriangle,\n Loader2,\n AlertCircle,\n AppWindow,\n} from \"lucide-react\";\nimport { useParams } from \"react-router-dom\";\nimport { EmphasisPanel, GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { AppListItem } from \"../components/AppCard\";\nimport { PermissionGuard } from \"../components\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport type {\n Application,\n Webhook as WebhookType,\n WebhookStatus,\n} from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport {\n useGetApplicationsQuery,\n useGetApplicationQuery,\n useGetWebhooksQuery,\n getErrorMessage,\n useDevportalPermission,\n type ApplicationFieldsFragment,\n type DevportalWebhookFieldsFragment,\n} from \"../hooks\";\n\n/**\n * Map GraphQL webhook to local Webhook type\n */\nfunction mapWebhook(webhook: DevportalWebhookFieldsFragment): WebhookType {\n return {\n id: webhook.id,\n applicationId: webhook.applicationId,\n name: webhook.name,\n description: webhook.description ?? undefined,\n url: webhook.url,\n secret: undefined,\n events: webhook.events ?? [],\n isActive: webhook.isActive,\n status: webhook.status as WebhookStatus,\n maxRetries: webhook.maxRetries ?? 3,\n retryDelay: webhook.retryDelay ?? 60,\n retryEnabled: webhook.retryEnabled ?? true,\n lastTriggeredAt: webhook.lastTriggeredAt ?? undefined,\n lastSuccessAt: webhook.lastSuccessAt ?? undefined,\n lastFailureAt: webhook.lastFailureAt ?? undefined,\n lastFailureReason: webhook.lastFailureReason ?? undefined,\n failureCount: webhook.failureCount ?? 0,\n successCount: webhook.successCount ?? 0,\n createdAt: webhook.createdAt,\n updatedAt: webhook.updatedAt,\n };\n}\n\n/**\n * Webhooks listing page\n *\n * Displays webhooks for a specific application.\n * Uses GraphQL to fetch real data from the DevPortal backend.\n */\nexport const WebhooksPage: FC = () => {\n const { appId } = useParams<{ appId: string }>();\n const { navigate, basePath, activeOrgId } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const createPerm = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.WEBHOOK_CREATE,\n );\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [statusFilter, setStatusFilter] = useState<WebhookStatus | \"\">(\"\");\n\n // Fetch app name for breadcrumb when viewing webhooks for a specific app\n const { data: singleAppData } = useGetApplicationQuery({\n variables: { id: appId ?? \"\" },\n skip: !appId,\n fetchPolicy: \"cache-first\",\n });\n\n useEffect(() => {\n const name = singleAppData?.application?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, singleAppData?.application?.name]);\n\n const {\n data: appsData,\n loading: appsLoading,\n error: appsError,\n refetch: refetchApps,\n } = useGetApplicationsQuery({\n variables: { limit: 100 },\n skip: !!appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n // Fetch webhooks from GraphQL API (only if we have an applicationId)\n const { data, loading, error, refetch } = useGetWebhooksQuery({\n variables: {\n applicationId: appId || \"\",\n },\n skip: !appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const webhooks = data?.webhooks?.map(mapWebhook) ?? [];\n const allApplications: Application[] =\n appsData?.applications?.edges?.map(\n (edge: { node: ApplicationFieldsFragment }) => edge.node as Application,\n ) ?? [];\n // Scope app picker to active org\n const applications = activeOrgId\n ? allApplications.filter((a) => a.developerOrganizationId === activeOrgId)\n : allApplications;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n // Filter webhooks\n const filteredWebhooks = webhooks.filter((webhook: WebhookType) => {\n const matchesSearch =\n !searchQuery ||\n webhook.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n webhook.url.toLowerCase().includes(searchQuery.toLowerCase());\n const matchesStatus = !statusFilter || webhook.status === statusFilter;\n return matchesSearch && matchesStatus;\n });\n\n const getStatusIcon = (status: WebhookStatus) => {\n switch (status) {\n case \"ACTIVE\":\n return <CheckCircle size={16} className=\"text-status-success-text\" />;\n case \"PAUSED\":\n return <PauseCircle size={16} className=\"text-status-warning-text\" />;\n case \"FAILED\":\n return <XCircle size={16} className=\"text-status-error-text\" />;\n case \"DISABLED\":\n return <AlertTriangle size={16} className=\"text-text-tertiary\" />;\n default:\n return null;\n }\n };\n\n const calculateSuccessRate = (webhook: WebhookType): number => {\n const total = webhook.successCount + webhook.failureCount;\n if (total === 0) return 100;\n return Math.round((webhook.successCount / total) * 100);\n };\n\n // If no applicationId, show prompt to select an application\n if (!appId) {\n const filteredApplications = applications.filter((application) => {\n if (!searchQuery) return true;\n\n const normalizedQuery = searchQuery.toLowerCase();\n return (\n application.name.toLowerCase().includes(normalizedQuery) ||\n (application.slug ?? \"\").toLowerCase().includes(normalizedQuery)\n );\n });\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n description={tr(\n \"devportal.manageWebhooksApps\",\n \"Manage webhooks for your applications\",\n )}\n purpose={tr(\n \"devportal.webhooksPickerPurpose\",\n \"Webhooks push real-time events (installs, payments, app updates) to a URL you host, so your backend can react without polling. Pick an application to view, add, or test the webhook endpoints it delivers to.\",\n )}\n >\n <div className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.webhooksSelectAppHint\",\n \"Select an application below to view and manage its webhooks.\",\n )}\n </p>\n <div className=\"max-w-md\">\n <div className=\"relative\">\n <Search\n size={16}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"text\"\n placeholder=\"Search applications...\"\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.target.value)}\n className=\"w-full rounded-md border bg-bg-surface pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n </div>\n\n {appsLoading ? (\n <div className=\"flex items-center justify-center py-12 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n ) : appsError ? (\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center\">\n <AlertCircle className=\"mx-auto h-8 w-8 text-status-error-text\" />\n <h3 className=\"mt-2 font-medium text-status-error-text\">\n Failed to load applications\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {getErrorMessage(appsError)}\n </p>\n <button\n onClick={() => refetchApps()}\n className=\"mt-4 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n Try Again\n </button>\n </div>\n ) : filteredApplications.length > 0 ? (\n <div className=\"space-y-2\">\n {filteredApplications.map((application) => (\n <AppListItem\n key={application.id}\n app={application}\n onClick={() =>\n handleNavigate(`/apps/${application.id}/webhooks`)\n }\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration={searchQuery ? \"empty-search\" : \"empty-data\"}\n icon={<AppWindow size={32} />}\n title=\"No applications available\"\n description={\n searchQuery\n ? \"No applications matched your search.\"\n : \"Create an application before configuring webhooks.\"\n }\n action={\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n Create Application\n </button>\n }\n />\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n }\n\n // Loading state\n if (loading && !data) {\n return (\n <PageLayout\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n description={tr(\n \"devportal.manageWebhooksApp\",\n \"Manage webhooks for this application\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n <span className=\"ml-2 text-text-secondary\">\n {tr(\"devportal.loadingWebhooks\", \"Loading webhooks...\")}\n </span>\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (error) {\n return (\n <PageLayout\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n description={tr(\n \"devportal.manageWebhooksApp\",\n \"Manage webhooks for this application\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center\">\n <AlertCircle className=\"mx-auto h-8 w-8 text-status-error-text\" />\n <h3 className=\"mt-2 font-medium text-status-error-text\">\n {tr(\"devportal.failedToLoadWebhooks\", \"Failed to load webhooks\")}\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {getErrorMessage(error)}\n </p>\n <button\n onClick={() => refetch()}\n className=\"mt-4 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr(\"devportal.tryAgain\", \"Try Again\")}\n </button>\n </div>\n </PageLayout>\n );\n }\n\n const columns = [\n {\n key: \"name\",\n header: \"Webhook\",\n render: (webhook: WebhookType) => (\n <div className=\"flex items-center gap-3\">\n <div\n className={cn(\n \"flex h-10 w-10 items-center justify-center rounded-lg\",\n webhook.status === \"ACTIVE\"\n ? \"bg-status-success-bg-subtle text-status-success-text\"\n : webhook.status === \"FAILED\"\n ? \"bg-status-error-bg-subtle text-status-error-text\"\n : webhook.status === \"PAUSED\"\n ? \"bg-status-warning-bg-subtle text-status-warning-text\"\n : \"bg-bg-sunken text-text-tertiary\",\n )}\n >\n <Webhook size={20} />\n </div>\n <div>\n <p className=\"font-medium\">{webhook.name}</p>\n <p className=\"text-xs text-text-secondary truncate max-w-xs\">\n {webhook.url}\n </p>\n </div>\n </div>\n ),\n },\n {\n key: \"events\",\n header: \"Events\",\n render: (webhook: WebhookType) => (\n <div className=\"flex flex-wrap gap-1\">\n {webhook.events.slice(0, 2).map((event) => (\n <span\n key={event}\n className=\"inline-flex rounded bg-bg-sunken px-1.5 py-0.5 text-xs\"\n >\n {event.split(\".\")[0]}\n </span>\n ))}\n {webhook.events.length > 2 && (\n <span className=\"text-xs text-text-secondary\">\n +{webhook.events.length - 2} more\n </span>\n )}\n </div>\n ),\n },\n {\n key: \"status\",\n header: \"Status\",\n render: (webhook: WebhookType) => (\n <div className=\"flex items-center gap-2\">\n {getStatusIcon(webhook.status)}\n <StatusBadge status={webhook.status} type=\"webhook\" />\n </div>\n ),\n },\n {\n key: \"successRate\",\n header: \"Success Rate\",\n render: (webhook: WebhookType) => {\n const rate = calculateSuccessRate(webhook);\n return (\n <div className=\"w-24\">\n <div className=\"flex items-center justify-between text-xs mb-1\">\n <span\n className={cn(\n rate >= 95\n ? \"text-status-success-text\"\n : rate >= 80\n ? \"text-status-warning-text\"\n : \"text-status-error-text\",\n )}\n >\n {rate}%\n </span>\n </div>\n <div className=\"h-1.5 rounded-full bg-bg-sunken\">\n <div\n className={cn(\n \"h-full rounded-full transition-all\",\n rate >= 95\n ? \"bg-status-success-bg-subtle\"\n : rate >= 80\n ? \"bg-status-warning-bg-subtle\"\n : \"bg-status-error-bg-subtle\",\n )}\n style={{ width: `${rate}%` }}\n />\n </div>\n </div>\n );\n },\n },\n {\n key: \"deliveries\",\n header: \"Deliveries\",\n render: (webhook: WebhookType) => (\n <div className=\"text-sm\">\n <span className=\"text-status-success-text\">\n {webhook.successCount.toLocaleString()}\n </span>\n <span className=\"text-text-secondary\"> / </span>\n <span className=\"text-status-error-text\">\n {webhook.failureCount.toLocaleString()}\n </span>\n </div>\n ),\n },\n {\n key: \"lastTriggered\",\n header: \"Last Triggered\",\n render: (webhook: WebhookType) => (\n <span className=\"text-sm text-text-secondary\">\n {webhook.lastTriggeredAt\n ? formatRelativeTime(webhook.lastTriggeredAt)\n : \"Never\"}\n </span>\n ),\n },\n ];\n\n // Stats calculation\n const stats = {\n total: webhooks.length,\n active: webhooks.filter((w: WebhookType) => w.status === \"ACTIVE\").length,\n failed: webhooks.filter((w: WebhookType) => w.status === \"FAILED\").length,\n paused: webhooks.filter((w: WebhookType) => w.status === \"PAUSED\").length,\n };\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n description={tr(\n \"devportal.manageWebhooksApp\",\n \"Manage webhooks for this application\",\n )}\n purpose={tr(\n \"devportal.webhooksAppPurpose\",\n \"These webhooks deliver this application's events to your endpoints. Add a URL, choose which events to subscribe to, and send a test delivery to confirm your server receives and verifies the signed payload.\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n actions={\n <button\n onClick={() => handleNavigate(`/apps/${appId}/webhooks/new`)}\n disabled={!createPerm.allowed}\n aria-disabled={!createPerm.allowed}\n title={\n !createPerm.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-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <Plus size={16} aria-hidden=\"true\" />\n {tr(\"devportal.createWebhook\", \"Create Webhook\")}\n </button>\n }\n >\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-1 items-center gap-4\">\n {/* Search */}\n <div className=\"relative flex-1 max-w-md\">\n <Search\n size={16}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"text\"\n placeholder=\"Search by name or URL...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-md border bg-bg-surface pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n {/* Status Filter */}\n <select\n value={statusFilter}\n onChange={(e) =>\n setStatusFilter(e.target.value as WebhookStatus | \"\")\n }\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"\">All Status</option>\n <option value=\"ACTIVE\">Active</option>\n <option value=\"PAUSED\">Paused</option>\n <option value=\"FAILED\">Failed</option>\n <option value=\"DISABLED\">Disabled</option>\n </select>\n </div>\n </div>\n\n {/* Summary Stats — primary emphasis zone */}\n <EmphasisPanel>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Webhook size={16} />\n <span className=\"text-sm\">Total Webhooks</span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {stats.total}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-status-success-text\">\n <CheckCircle size={16} />\n <span className=\"text-sm\">Active</span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {stats.active}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-status-warning-text\">\n <PauseCircle size={16} />\n <span className=\"text-sm\">Paused</span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {stats.paused}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-status-error-text\">\n <XCircle size={16} />\n <span className=\"text-sm\">Failed</span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {stats.failed}\n </p>\n </GlassCard>\n </div>\n </EmphasisPanel>\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n {filteredWebhooks.length} webhook\n {filteredWebhooks.length !== 1 ? \"s\" : \"\"} found\n </p>\n\n {/* Webhooks Table */}\n {filteredWebhooks.length > 0 ? (\n <DataTable\n data={filteredWebhooks}\n columns={columns}\n keyField=\"id\"\n onRowClick={(webhook) =>\n handleNavigate(`/apps/${appId}/webhooks/${webhook.id}`)\n }\n />\n ) : (\n <EmptyState\n illustration=\"empty-notifications\"\n title={tr(\"devportal.noWebhooksFound\", \"No webhooks found\")}\n description={\n searchQuery || statusFilter\n ? tr(\n \"devportal.tryAdjustingFilters\",\n \"Try adjusting your filters\",\n )\n : tr(\n \"devportal.createFirstWebhook\",\n \"Create your first webhook to receive real-time event notifications\",\n )\n }\n action={\n !searchQuery && !statusFilter ? (\n <button\n onClick={() => handleNavigate(`/apps/${appId}/webhooks/new`)}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createWebhook\", \"Create Webhook\")}\n </button>\n ) : undefined\n }\n />\n )}\n </PageLayout>\n </PermissionGuard>\n );\n};\n\nfunction formatRelativeTime(dateString: string): string {\n const date = new Date(dateString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMins / 60);\n const diffDays = Math.floor(diffHours / 24);\n\n if (diffMins < 1) return \"Just now\";\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n}\n"],"mappings":"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"}
|