@burdenoff/microfe-channel 2026.520.1 → 2026.525.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/channel/modules/channels/pages/ChannelDashboardPage.js +226 -224
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +4 -4
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +2 -2
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +65 -57
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +43 -41
- package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +1 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +183 -179
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditChannelPage.js +2 -2
- package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js +165 -159
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/package.json +1 -1
- package/dist/channel/ChannelRoot.d.ts +0 -32
- package/dist/channel/ChannelRoot.d.ts.map +0 -1
- package/dist/channel/ChannelRoutes.d.ts +0 -10
- package/dist/channel/ChannelRoutes.d.ts.map +0 -1
- package/dist/channel/components/AdapterCredentialsForm.d.ts +0 -20
- package/dist/channel/components/AdapterCredentialsForm.d.ts.map +0 -1
- package/dist/channel/components/FeatureNotEnabled.d.ts +0 -13
- package/dist/channel/components/FeatureNotEnabled.d.ts.map +0 -1
- package/dist/channel/components/PermissionGuard.d.ts +0 -15
- package/dist/channel/components/PermissionGuard.d.ts.map +0 -1
- package/dist/channel/config/adapterCredentials.d.ts +0 -44
- package/dist/channel/config/adapterCredentials.d.ts.map +0 -1
- package/dist/channel/constants/permissions.d.ts +0 -32
- package/dist/channel/constants/permissions.d.ts.map +0 -1
- package/dist/channel/graphql/global-client.d.ts +0 -11
- package/dist/channel/graphql/global-client.d.ts.map +0 -1
- package/dist/channel/hooks/useChannelOrgSettings.d.ts +0 -22
- package/dist/channel/hooks/useChannelOrgSettings.d.ts.map +0 -1
- package/dist/channel/hooks/useChannelSubscription.d.ts +0 -36
- package/dist/channel/hooks/useChannelSubscription.d.ts.map +0 -1
- package/dist/channel/hooks/useChannels.d.ts +0 -228
- package/dist/channel/hooks/useChannels.d.ts.map +0 -1
- package/dist/channel/index.d.ts +0 -12
- package/dist/channel/index.d.ts.map +0 -1
- package/dist/channel/modules/channels/hooks/useExport.d.ts +0 -45
- package/dist/channel/modules/channels/hooks/useExport.d.ts.map +0 -1
- package/dist/channel/modules/channels/i18n.d.ts +0 -14
- package/dist/channel/modules/channels/i18n.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.d.ts +0 -14
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.d.ts +0 -7
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.d.ts +0 -7
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.d.ts +0 -7
- package/dist/channel/modules/channels/pages/ChannelUsersPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.d.ts +0 -7
- package/dist/channel/modules/channels/pages/ChannelsListPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.d.ts +0 -7
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.d.ts +0 -7
- package/dist/channel/modules/channels/pages/ConnectionsListPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/CreateChannelPage.d.ts +0 -12
- package/dist/channel/modules/channels/pages/CreateChannelPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/CreateConnectionPage.d.ts +0 -11
- package/dist/channel/modules/channels/pages/CreateConnectionPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.d.ts +0 -7
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/EditChannelPage.d.ts +0 -8
- package/dist/channel/modules/channels/pages/EditChannelPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/EditConnectionPage.d.ts +0 -10
- package/dist/channel/modules/channels/pages/EditConnectionPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/SendMessagePage.d.ts +0 -8
- package/dist/channel/modules/channels/pages/SendMessagePage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.d.ts +0 -7
- package/dist/channel/modules/channels/pages/WebhookEventsPage.d.ts.map +0 -1
- package/dist/channel/modules/channels/pages/index.d.ts +0 -18
- package/dist/channel/modules/channels/pages/index.d.ts.map +0 -1
- package/dist/channel/pages/ChannelLandingPage.d.ts +0 -9
- package/dist/channel/pages/ChannelLandingPage.d.ts.map +0 -1
- package/dist/channel/providers/ChannelProvider.d.ts +0 -16
- package/dist/channel/providers/ChannelProvider.d.ts.map +0 -1
- package/dist/channel/types/index.d.ts +0 -89
- package/dist/channel/types/index.d.ts.map +0 -1
- package/dist/channel/utils/shellBridge.d.ts +0 -59
- package/dist/channel/utils/shellBridge.d.ts.map +0 -1
- package/dist/dev/App.d.ts +0 -6
- package/dist/dev/App.d.ts.map +0 -1
- package/dist/dev/main.d.ts +0 -2
- package/dist/dev/main.d.ts.map +0 -1
- package/dist/generated/global-operations.d.ts +0 -1549
- package/dist/generated/global-operations.d.ts.map +0 -1
- package/dist/generated/global-types.d.ts +0 -23527
- package/dist/generated/global-types.d.ts.map +0 -1
- package/dist/generated/wspace-operations.d.ts +0 -2
- package/dist/generated/wspace-operations.d.ts.map +0 -1
- package/dist/generated/wspace-types.d.ts +0 -57851
- package/dist/generated/wspace-types.d.ts.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/translations/en.d.ts +0 -260
- package/dist/translations/en.d.ts.map +0 -1
- package/dist/utils/nativeBridge.d.ts +0 -3
- package/dist/utils/nativeBridge.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebhookEventsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/WebhookEventsPage.tsx"],"sourcesContent":["/**\n * Webhook Events Page\n *\n * Displays webhook event history for a specific channel\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ArrowLeft,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n Clock,\n RefreshCw,\n Eye,\n EyeOff,\n Download,\n ChevronLeft,\n ChevronRight,\n} from 'lucide-react';\nimport { useChannel, type WebhookEvent as WebhookEventType } from '../../../hooks/useChannels';\nimport type { WebhookProcessingStatus } from '../../../hooks/useChannels';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { emitShellEvent } from '../../../utils/shellBridge';\nimport {\n WEBHOOK_RECEIVED_SUBSCRIPTION,\n useChannelSubscription,\n} from '../../../hooks/useChannelSubscription';\nimport { useExportWebhookEvents } from '../hooks/useExport';\n\nconst STATUS_CONFIG: Record<\n WebhookProcessingStatus,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n PENDING: { label: 'Pending', color: 'text-muted-foreground bg-muted', icon: Clock },\n PROCESSING: {\n label: 'Processing',\n color: 'text-accent-primary bg-accent-primary/10',\n icon: RefreshCw,\n },\n COMPLETED: {\n label: 'Completed',\n color: 'text-status-success-text bg-status-success-bg-subtle',\n icon: CheckCircle2,\n },\n FAILED: {\n label: 'Failed',\n color: 'text-status-error-text bg-status-error-bg-subtle',\n icon: XCircle,\n },\n IGNORED: {\n label: 'Ignored',\n color: 'text-status-warning-text bg-status-warning-bg-subtle',\n icon: EyeOff,\n },\n};\n\nexport function WebhookEventsPage() {\n const { channelId } = useParams<{ channelId: string }>();\n const navigate = useNavigate();\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 { organizationId } = useChannelContext();\n const {\n triggerExport: exportWebhooks,\n isExporting: isExportingWebhooks,\n exportError: exportWebhooksError,\n } = useExportWebhookEvents(organizationId ?? undefined, channelId);\n const [isLoadingEvents, setIsLoadingEvents] = useState(true);\n const [events, setEvents] = useState<WebhookEventType[]>([]);\n const [expandedEvent, setExpandedEvent] = useState<string | null>(null);\n const [limit, setLimit] = useState(25);\n const [total, setTotal] = useState(0);\n const [cursorStack, setCursorStack] = useState<(string | undefined)[]>([undefined]);\n const [currentPageIdx, setCurrentPageIdx] = useState(0);\n\n const { channel, isLoading, error, fetchWebhookEvents } = useChannel(channelId);\n\n const loadEvents = useCallback(\n async (cursor?: string) => {\n if (!channelId) return;\n setIsLoadingEvents(true);\n try {\n const result = await fetchWebhookEvents(limit, cursor);\n setEvents(result.items);\n setTotal(result.total);\n return result.nextCursor;\n } finally {\n setIsLoadingEvents(false);\n }\n },\n [channelId, fetchWebhookEvents, limit]\n );\n\n useEffect(() => {\n void Promise.resolve().then(() => {\n setCursorStack([undefined]);\n setCurrentPageIdx(0);\n void loadEvents(undefined);\n });\n }, [limit]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n void Promise.resolve().then(() => loadEvents(cursorStack[currentPageIdx]));\n }, [currentPageIdx]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Initial load\n useEffect(() => {\n void Promise.resolve().then(() => loadEvents(undefined));\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleNextPage = useCallback(async () => {\n const nextCursor = await loadEvents(cursorStack[currentPageIdx]);\n if (nextCursor) {\n setCursorStack((prev) => {\n const next = [...prev];\n if (!next[currentPageIdx + 1]) {\n next[currentPageIdx + 1] = nextCursor;\n }\n return next;\n });\n }\n setCurrentPageIdx((p) => p + 1);\n }, [cursorStack, currentPageIdx, loadEvents]);\n\n const handlePrevPage = useCallback(() => {\n const prevIdx = currentPageIdx - 1;\n setCurrentPageIdx(prevIdx);\n void loadEvents(cursorStack[prevIdx]);\n }, [cursorStack, currentPageIdx, loadEvents]);\n\n const hasNextPage = events.length === limit;\n const hasPrevPage = currentPageIdx > 0;\n\n // Real-time webhook stream. Subscribes when the gateway exposes\n // `webhookReceived`; otherwise polls every 60s as a graceful fallback.\n useChannelSubscription({\n query: WEBHOOK_RECEIVED_SUBSCRIPTION,\n variables: { channelId },\n operationName: 'WebhookReceived',\n enabled: Boolean(channelId),\n onData: () => {\n emitShellEvent('webhook.received', { channelId });\n void loadEvents(cursorStack[currentPageIdx]);\n },\n pollFn: () => {\n void loadEvents(cursorStack[currentPageIdx]);\n },\n pollIntervalMs: 60_000,\n });\n\n const getStatusConfig = (status: WebhookProcessingStatus) => {\n return STATUS_CONFIG[status] || STATUS_CONFIG.PENDING;\n };\n\n // Loading state (only show full-screen for initial channel load)\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 size={32} className=\"animate-spin text-accent-primary\" />\n </div>\n );\n }\n\n // Error state\n if (error || !channel) {\n return (\n <div className=\"p-6\">\n <div className=\"flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <div>\n <p className=\"text-sm font-medium text-status-error-text\">\n {error || 'Channel not found'}\n </p>\n </div>\n <button\n onClick={() => navigate(`/channels/${channelId}`)}\n className=\"ml-auto px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n Go Back\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_WEBHOOK_READ}>\n <div className=\"space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n onClick={() => navigate(`/channels/${channelId}`)}\n aria-label={tr('channel.backToChannel', 'Back to channel')}\n className=\"p-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <ArrowLeft size={20} aria-hidden=\"true\" />\n </button>\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold text-foreground\">\n {tr('channel.webhookEvents', 'Webhook Events')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{channel.name} - Event history</p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <select\n value={limit}\n onChange={(e) => setLimit(Number(e.target.value))}\n className=\"px-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-foreground\"\n >\n <option value={10}>10 results</option>\n <option value={25}>25 results</option>\n <option value={50}>50 results</option>\n <option value={100}>100 results</option>\n </select>\n <button\n onClick={() => void loadEvents(cursorStack[currentPageIdx])}\n disabled={isLoadingEvents}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50\"\n >\n <RefreshCw size={16} className={isLoadingEvents ? 'animate-spin' : ''} />\n {tr('channel.refresh', 'Refresh')}\n </button>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_ADMIN}>\n <button\n onClick={() => void exportWebhooks()}\n disabled={isExportingWebhooks}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-fg border border-border hover:bg-bg-elevated rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tr('channel.exportCSV', 'Export to CSV')}\n >\n {isExportingWebhooks ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Download size={16} />\n )}\n {tr('channel.export', 'Export')}\n </button>\n </PermissionGuard>\n </div>\n </div>\n\n {/* Export Error Banner */}\n {exportWebhooksError && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg flex items-center gap-3\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <p className=\"text-sm text-status-error-text flex-1\">{exportWebhooksError}</p>\n </div>\n )}\n\n {/* Info Banner */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-sm text-muted-foreground\">\n Webhook events show all incoming webhooks received by this channel. Data is retained for\n 7 days.\n </p>\n </div>\n\n {/* Events List */}\n <div className=\"bg-surface border border-border rounded-lg overflow-hidden\">\n <div className=\"overflow-x-auto min-w-full\">\n <table className=\"w-full min-w-[600px]\">\n <thead className=\"bg-muted/50 border-b border-border\">\n <tr>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.status', 'Status')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.eventType', 'Event Type')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.duplicate', 'Duplicate')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.processedAt', 'Processed At')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.error', 'Error')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {events.length === 0 ? (\n <tr>\n <td colSpan={6} className=\"px-4 py-8 text-center text-muted-foreground\">\n {tr('channel.noWebhookEventsFound', 'No webhook events found')}\n </td>\n </tr>\n ) : (\n events.map((event) => {\n const statusConfig = getStatusConfig(event.status as WebhookProcessingStatus);\n const StatusIcon = statusConfig.icon;\n const isExpanded = expandedEvent === event.id;\n return (\n <>\n <tr key={event.id} className=\"hover:bg-muted/30 transition-colors\">\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <StatusIcon size={14} className={statusConfig.color.split(' ')[0]} />\n <span className={`text-sm font-medium ${statusConfig.color}`}>\n {statusConfig.label}\n </span>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm font-mono text-foreground\">\n {event.eventType}\n </td>\n <td className=\"px-4 py-3\">\n {event.isDuplicate ? (\n <span className=\"text-xs text-status-warning-text bg-status-warning-bg-subtle px-2 py-1 rounded\">\n Duplicate\n </span>\n ) : (\n <span className=\"text-xs text-muted-foreground\">No</span>\n )}\n </td>\n <td className=\"px-4 py-3 text-sm text-muted-foreground\">\n {event.processedAt ? new Date(event.processedAt).toLocaleString() : '-'}\n </td>\n <td className=\"px-4 py-3 text-sm text-status-error-text max-w-xs truncate\">\n {event.errorMessage || '-'}\n </td>\n <td className=\"px-4 py-3\">\n <button\n onClick={() => setExpandedEvent(isExpanded ? null : event.id)}\n aria-label={\n isExpanded\n ? tr('channel.hidePayload', 'Hide payload')\n : tr('channel.viewPayload', 'View payload')\n }\n aria-expanded={isExpanded}\n className=\"p-1 text-muted-foreground hover:text-foreground hover:bg-muted rounded transition-colors\"\n >\n {isExpanded ? (\n <EyeOff size={16} aria-hidden=\"true\" />\n ) : (\n <Eye size={16} aria-hidden=\"true\" />\n )}\n </button>\n </td>\n </tr>\n {isExpanded && (\n <tr key={`${event.id}-payload`}>\n <td colSpan={6} className=\"px-4 py-3 bg-muted/30\">\n <div className=\"text-xs\">\n <p className=\"text-muted-foreground mb-1\">\n {tr('channel.payload', 'Payload')}:\n </p>\n <pre className=\"bg-surface p-2 rounded border border-border overflow-auto\">\n {JSON.stringify(event.payload, null, 2)}\n </pre>\n </div>\n </td>\n </tr>\n )}\n </>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n </div>\n\n {/* Pagination footer */}\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-muted-foreground\">\n {total > 0 && (\n <>\n {tr('channel.page', 'Page')}{' '}\n <span className=\"font-medium\">{currentPageIdx + 1}</span>\n {' · '}\n {tr('channel.totalRecords', 'Total')}: <span className=\"font-medium\">{total}</span>\n </>\n )}\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handlePrevPage}\n disabled={!hasPrevPage || isLoadingEvents}\n className=\"flex items-center gap-1 px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n <ChevronLeft size={16} />\n {tr('channel.previous', 'Previous')}\n </button>\n <button\n onClick={() => void handleNextPage()}\n disabled={!hasNextPage || isLoadingEvents}\n className=\"flex items-center gap-1 px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.next', 'Next')}\n <ChevronRight size={16} />\n </button>\n </div>\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WebhookEventsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/WebhookEventsPage.tsx"],"sourcesContent":["/**\n * Webhook Events Page\n *\n * Displays webhook event history for a specific channel\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ArrowLeft,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n Clock,\n RefreshCw,\n Eye,\n EyeOff,\n Download,\n ChevronLeft,\n ChevronRight,\n} from 'lucide-react';\nimport { useChannel, type WebhookEvent as WebhookEventType } from '../../../hooks/useChannels';\nimport type { WebhookProcessingStatus } from '../../../hooks/useChannels';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { emitShellEvent } from '../../../utils/shellBridge';\nimport {\n WEBHOOK_RECEIVED_SUBSCRIPTION,\n useChannelSubscription,\n} from '../../../hooks/useChannelSubscription';\nimport { useExportWebhookEvents } from '../hooks/useExport';\n\nconst STATUS_CONFIG: Record<\n WebhookProcessingStatus,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n PENDING: { label: 'Pending', color: 'text-muted-foreground bg-muted', icon: Clock },\n PROCESSING: {\n label: 'Processing',\n color: 'text-accent-primary bg-accent-primary/10',\n icon: RefreshCw,\n },\n COMPLETED: {\n label: 'Completed',\n color: 'text-status-success-text bg-status-success-bg-subtle',\n icon: CheckCircle2,\n },\n FAILED: {\n label: 'Failed',\n color: 'text-status-error-text bg-status-error-bg-subtle',\n icon: XCircle,\n },\n IGNORED: {\n label: 'Ignored',\n color: 'text-status-warning-text bg-status-warning-bg-subtle',\n icon: EyeOff,\n },\n};\n\nexport function WebhookEventsPage() {\n const { channelId } = useParams<{ channelId: string }>();\n const navigate = useNavigate();\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 { organizationId } = useChannelContext();\n const {\n triggerExport: exportWebhooks,\n isExporting: isExportingWebhooks,\n exportError: exportWebhooksError,\n } = useExportWebhookEvents(organizationId ?? undefined, channelId);\n const [isLoadingEvents, setIsLoadingEvents] = useState(true);\n const [events, setEvents] = useState<WebhookEventType[]>([]);\n const [expandedEvent, setExpandedEvent] = useState<string | null>(null);\n const [limit, setLimit] = useState(25);\n const [total, setTotal] = useState(0);\n const [cursorStack, setCursorStack] = useState<(string | undefined)[]>([undefined]);\n const [currentPageIdx, setCurrentPageIdx] = useState(0);\n\n const { channel, isLoading, error, fetchWebhookEvents } = useChannel(channelId);\n\n const loadEvents = useCallback(\n async (cursor?: string) => {\n if (!channelId) return;\n setIsLoadingEvents(true);\n try {\n const result = await fetchWebhookEvents(limit, cursor);\n setEvents(result.items);\n setTotal(result.total);\n return result.nextCursor;\n } finally {\n setIsLoadingEvents(false);\n }\n },\n [channelId, fetchWebhookEvents, limit]\n );\n\n // Keep refs current after every render so pagination and subscription callbacks\n // always call the latest function/value without those variables in deps.\n const loadEventsRef = useRef(loadEvents);\n const cursorStackRef = useRef(cursorStack);\n const currentPageIdxRef = useRef(currentPageIdx);\n useEffect(() => {\n loadEventsRef.current = loadEvents;\n cursorStackRef.current = cursorStack;\n currentPageIdxRef.current = currentPageIdx;\n });\n // Tracks whether the [limit] effect has already fired on mount so the\n // [currentPageIdx] effect skips its first run (avoiding a double load).\n const isFirstPageRender = useRef(true);\n\n // Reset cursor stack and reload whenever page size changes (also fires on mount).\n useEffect(() => {\n void Promise.resolve().then(() => {\n setCursorStack([undefined]);\n setCurrentPageIdx(0);\n void loadEventsRef.current(undefined);\n });\n }, [limit]);\n\n // Navigate to a specific page; skip the very first render to avoid\n // a duplicate load alongside the [limit] effect above.\n useEffect(() => {\n if (isFirstPageRender.current) {\n isFirstPageRender.current = false;\n return;\n }\n void Promise.resolve().then(() =>\n loadEventsRef.current(cursorStackRef.current[currentPageIdx])\n );\n }, [currentPageIdx]);\n\n const handleNextPage = useCallback(async () => {\n const nextCursor = await loadEvents(cursorStack[currentPageIdx]);\n if (nextCursor) {\n setCursorStack((prev) => {\n const next = [...prev];\n if (!next[currentPageIdx + 1]) {\n next[currentPageIdx + 1] = nextCursor;\n }\n return next;\n });\n }\n setCurrentPageIdx((p) => p + 1);\n }, [cursorStack, currentPageIdx, loadEvents]);\n\n const handlePrevPage = useCallback(() => {\n const prevIdx = currentPageIdx - 1;\n setCurrentPageIdx(prevIdx);\n void loadEvents(cursorStack[prevIdx]);\n }, [cursorStack, currentPageIdx, loadEvents]);\n\n const hasNextPage = events.length === limit;\n const hasPrevPage = currentPageIdx > 0;\n\n // Real-time webhook stream. Subscribes when the gateway exposes\n // `webhookReceived`; otherwise polls every 60s as a graceful fallback.\n useChannelSubscription({\n query: WEBHOOK_RECEIVED_SUBSCRIPTION,\n variables: { channelId },\n operationName: 'WebhookReceived',\n enabled: Boolean(channelId),\n onData: useCallback(() => {\n emitShellEvent('webhook.received', { channelId });\n void loadEventsRef.current(cursorStackRef.current[currentPageIdxRef.current]);\n }, [channelId]),\n pollFn: useCallback(() => {\n void loadEventsRef.current(cursorStackRef.current[currentPageIdxRef.current]);\n }, []),\n pollIntervalMs: 60_000,\n });\n\n const getStatusConfig = (status: WebhookProcessingStatus) => {\n return STATUS_CONFIG[status] || STATUS_CONFIG.PENDING;\n };\n\n // Loading state (only show full-screen for initial channel load)\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 size={32} className=\"animate-spin text-accent-primary\" />\n </div>\n );\n }\n\n // Error state\n if (error || !channel) {\n return (\n <div className=\"p-6\">\n <div className=\"flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <div>\n <p className=\"text-sm font-medium text-status-error-text\">\n {error || 'Channel not found'}\n </p>\n </div>\n <button\n onClick={() => navigate(`/channels/${channelId}`)}\n className=\"ml-auto px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n Go Back\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_WEBHOOK_READ}>\n <div className=\"space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n onClick={() => navigate(`/channels/${channelId}`)}\n aria-label={tr('channel.backToChannel', 'Back to channel')}\n className=\"p-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <ArrowLeft size={20} aria-hidden=\"true\" />\n </button>\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold text-foreground\">\n {tr('channel.webhookEvents', 'Webhook Events')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{channel.name} - Event history</p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <select\n value={limit}\n onChange={(e) => setLimit(Number(e.target.value))}\n className=\"px-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-foreground\"\n >\n <option value={10}>10 results</option>\n <option value={25}>25 results</option>\n <option value={50}>50 results</option>\n <option value={100}>100 results</option>\n </select>\n <button\n onClick={() => void loadEvents(cursorStack[currentPageIdx])}\n disabled={isLoadingEvents}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50\"\n >\n <RefreshCw size={16} className={isLoadingEvents ? 'animate-spin' : ''} />\n {tr('channel.refresh', 'Refresh')}\n </button>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_ADMIN}>\n <button\n onClick={() => void exportWebhooks()}\n disabled={isExportingWebhooks}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium text-fg border border-border hover:bg-bg-elevated rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tr('channel.exportCSV', 'Export to CSV')}\n >\n {isExportingWebhooks ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Download size={16} />\n )}\n {tr('channel.export', 'Export')}\n </button>\n </PermissionGuard>\n </div>\n </div>\n\n {/* Export Error Banner */}\n {exportWebhooksError && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg flex items-center gap-3\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <p className=\"text-sm text-status-error-text flex-1\">{exportWebhooksError}</p>\n </div>\n )}\n\n {/* Info Banner */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-sm text-muted-foreground\">\n Webhook events show all incoming webhooks received by this channel. Data is retained for\n 7 days.\n </p>\n </div>\n\n {/* Events List */}\n <div className=\"bg-surface border border-border rounded-lg overflow-hidden\">\n <div className=\"overflow-x-auto min-w-full\">\n <table className=\"w-full min-w-[600px]\">\n <thead className=\"bg-muted/50 border-b border-border\">\n <tr>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.status', 'Status')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.eventType', 'Event Type')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.duplicate', 'Duplicate')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.processedAt', 'Processed At')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.error', 'Error')}\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider\">\n {tr('channel.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {events.length === 0 ? (\n <tr>\n <td colSpan={6} className=\"px-4 py-8 text-center text-muted-foreground\">\n {tr('channel.noWebhookEventsFound', 'No webhook events found')}\n </td>\n </tr>\n ) : (\n events.map((event) => {\n const statusConfig = getStatusConfig(event.status as WebhookProcessingStatus);\n const StatusIcon = statusConfig.icon;\n const isExpanded = expandedEvent === event.id;\n return (\n <>\n <tr key={event.id} className=\"hover:bg-muted/30 transition-colors\">\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <StatusIcon size={14} className={statusConfig.color.split(' ')[0]} />\n <span className={`text-sm font-medium ${statusConfig.color}`}>\n {statusConfig.label}\n </span>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm font-mono text-foreground\">\n {event.eventType}\n </td>\n <td className=\"px-4 py-3\">\n {event.isDuplicate ? (\n <span className=\"text-xs text-status-warning-text bg-status-warning-bg-subtle px-2 py-1 rounded\">\n Duplicate\n </span>\n ) : (\n <span className=\"text-xs text-muted-foreground\">No</span>\n )}\n </td>\n <td className=\"px-4 py-3 text-sm text-muted-foreground\">\n {event.processedAt ? new Date(event.processedAt).toLocaleString() : '-'}\n </td>\n <td className=\"px-4 py-3 text-sm text-status-error-text max-w-xs truncate\">\n {event.errorMessage || '-'}\n </td>\n <td className=\"px-4 py-3\">\n <button\n onClick={() => setExpandedEvent(isExpanded ? null : event.id)}\n aria-label={\n isExpanded\n ? tr('channel.hidePayload', 'Hide payload')\n : tr('channel.viewPayload', 'View payload')\n }\n aria-expanded={isExpanded}\n className=\"p-1 text-muted-foreground hover:text-foreground hover:bg-muted rounded transition-colors\"\n >\n {isExpanded ? (\n <EyeOff size={16} aria-hidden=\"true\" />\n ) : (\n <Eye size={16} aria-hidden=\"true\" />\n )}\n </button>\n </td>\n </tr>\n {isExpanded && (\n <tr key={`${event.id}-payload`}>\n <td colSpan={6} className=\"px-4 py-3 bg-muted/30\">\n <div className=\"text-xs\">\n <p className=\"text-muted-foreground mb-1\">\n {tr('channel.payload', 'Payload')}:\n </p>\n <pre className=\"bg-surface p-2 rounded border border-border overflow-auto\">\n {JSON.stringify(event.payload, null, 2)}\n </pre>\n </div>\n </td>\n </tr>\n )}\n </>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n </div>\n\n {/* Pagination footer */}\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-muted-foreground\">\n {total > 0 && (\n <>\n {tr('channel.page', 'Page')}{' '}\n <span className=\"font-medium\">{currentPageIdx + 1}</span>\n {' · '}\n {tr('channel.totalRecords', 'Total')}: <span className=\"font-medium\">{total}</span>\n </>\n )}\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handlePrevPage}\n disabled={!hasPrevPage || isLoadingEvents}\n className=\"flex items-center gap-1 px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n <ChevronLeft size={16} />\n {tr('channel.previous', 'Previous')}\n </button>\n <button\n onClick={() => void handleNextPage()}\n disabled={!hasNextPage || isLoadingEvents}\n className=\"flex items-center gap-1 px-3 py-1.5 text-sm border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.next', 'Next')}\n <ChevronRight size={16} />\n </button>\n </div>\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
2
|
-
import { ChannelRootProps } from './types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Root component for the Channel microfrontend.
|
|
6
|
-
*
|
|
7
|
-
* This is the main entry point that host applications import and render.
|
|
8
|
-
* It sets up the necessary providers and renders the internal routing.
|
|
9
|
-
* Automatically registers navigation items with the shell if registration functions are provided.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```tsx
|
|
13
|
-
* import { ChannelRoot, type ChannelRootProps } from '@burdenoff/microfe-channel';
|
|
14
|
-
*
|
|
15
|
-
* function App() {
|
|
16
|
-
* const props: ChannelRootProps = {
|
|
17
|
-
* basePath: '/channels',
|
|
18
|
-
* navigate: (path) => router.push(path),
|
|
19
|
-
* currentUser: { id: '1', email: 'user@example.com', firstName: 'John', lastName: 'Doe' },
|
|
20
|
-
* workspaceId: 'ws-123',
|
|
21
|
-
* organizationId: 'org-123',
|
|
22
|
-
* apiGatewayUrl: 'https://api.example.com/workspaces/graphql',
|
|
23
|
-
* authToken: 'token',
|
|
24
|
-
* registerNavItems: (items) => { return () => {}; },
|
|
25
|
-
* };
|
|
26
|
-
*
|
|
27
|
-
* return <ChannelRoot {...props} />;
|
|
28
|
-
* }
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export declare const ChannelRoot: FC<ChannelRootProps>;
|
|
32
|
-
//# sourceMappingURL=ChannelRoot.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelRoot.d.ts","sourceRoot":"","sources":["../../src/channel/ChannelRoot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAMhC,OAAO,KAAK,EAAW,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGzD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CA+F5C,CAAC"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal router for the Channel microfrontend.
|
|
3
|
-
*
|
|
4
|
-
* The shell mounts this at /channels/* so all paths here are relative to /channels/.
|
|
5
|
-
* Every concrete route is wrapped with a FeatureGate so individual surfaces can
|
|
6
|
-
* be rolled out or kill-switched independently. Flag names follow
|
|
7
|
-
* `channels.<route-name>` (see vibecontrols-channel-stability tracker).
|
|
8
|
-
*/
|
|
9
|
-
export declare function ChannelRoutes(): import("react/jsx-runtime").JSX.Element;
|
|
10
|
-
//# sourceMappingURL=ChannelRoutes.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelRoutes.d.ts","sourceRoot":"","sources":["../../src/channel/ChannelRoutes.tsx"],"names":[],"mappings":"AAmCA;;;;;;;GAOG;AACH,wBAAgB,aAAa,4CAoD5B"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { AdapterType } from '../hooks/useChannels';
|
|
2
|
-
|
|
3
|
-
interface AdapterCredentialsFormProps {
|
|
4
|
-
adapterType: AdapterType | undefined;
|
|
5
|
-
credentials: Record<string, unknown>;
|
|
6
|
-
onChange: (credentials: Record<string, unknown>) => void;
|
|
7
|
-
onValidationChange?: (isValid: boolean, errors: string[]) => void;
|
|
8
|
-
disabled?: boolean;
|
|
9
|
-
showPlatformMetadata?: boolean;
|
|
10
|
-
}
|
|
11
|
-
export declare function AdapterCredentialsForm({ adapterType, credentials, onChange, onValidationChange, disabled, showPlatformMetadata, }: AdapterCredentialsFormProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
/**
|
|
13
|
-
* Compact validation status indicator
|
|
14
|
-
*/
|
|
15
|
-
export declare function CredentialValidationStatus({ adapterType, credentials, }: {
|
|
16
|
-
adapterType: AdapterType | undefined;
|
|
17
|
-
credentials: Record<string, unknown>;
|
|
18
|
-
}): import("react/jsx-runtime").JSX.Element | null;
|
|
19
|
-
export {};
|
|
20
|
-
//# sourceMappingURL=AdapterCredentialsForm.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdapterCredentialsForm.d.ts","sourceRoot":"","sources":["../../../src/channel/components/AdapterCredentialsForm.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQxD,UAAU,2BAA2B;IACnC,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACzD,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CAAC,EACrC,WAAW,EACX,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,QAAgB,EAChB,oBAA2B,GAC5B,EAAE,2BAA2B,2CA0O7B;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,WAAW,EACX,WAAW,GACZ,EAAE;IACD,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC,kDA8BA"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
interface FeatureNotEnabledProps {
|
|
2
|
-
flag: string;
|
|
3
|
-
}
|
|
4
|
-
/**
|
|
5
|
-
* Default fallback rendered when a feature flag is disabled. Behaves as a
|
|
6
|
-
* "404-style" panel — never throws, never crashes the route.
|
|
7
|
-
*
|
|
8
|
-
* - The flag key is only shown to super admins (developer-facing detail).
|
|
9
|
-
* - The CTA links to Feature Previews so admins can enable the flag directly.
|
|
10
|
-
*/
|
|
11
|
-
export declare function FeatureNotEnabled({ flag }: FeatureNotEnabledProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
export {};
|
|
13
|
-
//# sourceMappingURL=FeatureNotEnabled.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FeatureNotEnabled.d.ts","sourceRoot":"","sources":["../../../src/channel/components/FeatureNotEnabled.tsx"],"names":[],"mappings":"AAGA,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE,sBAAsB,2CAkCjE"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { FC, ReactNode } from 'react';
|
|
2
|
-
import { ChannelPermission } from '../constants/permissions';
|
|
3
|
-
|
|
4
|
-
interface PermissionGuardProps {
|
|
5
|
-
permission: ChannelPermission;
|
|
6
|
-
children: ReactNode;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Wraps page content with a permission check.
|
|
10
|
-
* Shows a loading spinner while permissions resolve, then either
|
|
11
|
-
* renders children (if permitted) or an AccessDenied screen.
|
|
12
|
-
*/
|
|
13
|
-
export declare const PermissionGuard: FC<PermissionGuardProps>;
|
|
14
|
-
export {};
|
|
15
|
-
//# sourceMappingURL=PermissionGuard.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionGuard.d.ts","sourceRoot":"","sources":["../../../src/channel/components/PermissionGuard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAK3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,UAAU,oBAAoB;IAC5B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,EAAE,CAAC,oBAAoB,CA0BpD,CAAC"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { AdapterType } from '../hooks/useChannels';
|
|
2
|
-
|
|
3
|
-
export interface CredentialField {
|
|
4
|
-
key: string;
|
|
5
|
-
displayName: string;
|
|
6
|
-
description: string;
|
|
7
|
-
type: 'text' | 'password' | 'textarea' | 'number' | 'boolean' | 'select';
|
|
8
|
-
required: boolean;
|
|
9
|
-
placeholder?: string;
|
|
10
|
-
defaultValue?: string | number | boolean;
|
|
11
|
-
options?: {
|
|
12
|
-
value: string;
|
|
13
|
-
label: string;
|
|
14
|
-
}[];
|
|
15
|
-
validation?: {
|
|
16
|
-
pattern?: string;
|
|
17
|
-
minLength?: number;
|
|
18
|
-
maxLength?: number;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
export interface AdapterCredentialSchema {
|
|
22
|
-
displayName: string;
|
|
23
|
-
description: string;
|
|
24
|
-
docUrl?: string;
|
|
25
|
-
logoUrl?: string;
|
|
26
|
-
credentials: CredentialField[];
|
|
27
|
-
platformMetadata?: CredentialField[];
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Get credential schema for an adapter type
|
|
31
|
-
*/
|
|
32
|
-
export declare function getAdapterCredentialSchema(adapterType: AdapterType | undefined): AdapterCredentialSchema | null;
|
|
33
|
-
/**
|
|
34
|
-
* Validate credentials against adapter schema
|
|
35
|
-
*/
|
|
36
|
-
export declare function validateCredentials(adapterType: AdapterType | undefined, credentials: Record<string, unknown>): {
|
|
37
|
-
valid: boolean;
|
|
38
|
-
errors: string[];
|
|
39
|
-
};
|
|
40
|
-
/**
|
|
41
|
-
* Get default values for an adapter's credentials
|
|
42
|
-
*/
|
|
43
|
-
export declare function getDefaultCredentials(adapterType: AdapterType | undefined): Record<string, unknown>;
|
|
44
|
-
//# sourceMappingURL=adapterCredentials.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapterCredentials.d.ts","sourceRoot":"","sources":["../../../src/channel/config/adapterCredentials.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACzE,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;CACtC;AAwyBD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,WAAW,GAAG,SAAS,GACnC,uBAAuB,GAAG,IAAI,CAGhC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CA8CtC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,WAAW,GAAG,SAAS,GACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWzB"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RBAC permission constants for the Channel microfrontend.
|
|
3
|
-
* Mirrors the @rbac directives in global-channel-svc.
|
|
4
|
-
*/
|
|
5
|
-
export declare const CHANNEL_PERMISSIONS: {
|
|
6
|
-
readonly CHANNEL_READ: "channel:read";
|
|
7
|
-
readonly CHANNEL_CREATE: "channel:create";
|
|
8
|
-
readonly CHANNEL_UPDATE: "channel:update";
|
|
9
|
-
readonly CHANNEL_DELETE: "channel:delete";
|
|
10
|
-
readonly CHANNEL_ARCHIVE: "channel:archive";
|
|
11
|
-
readonly CHANNEL_EXECUTE: "channel:execute";
|
|
12
|
-
readonly CHANNEL_EXPORT: "channel:export";
|
|
13
|
-
/** Admin-level permission — required for export operations. */
|
|
14
|
-
readonly CHANNEL_ADMIN: "channel:update";
|
|
15
|
-
readonly CHANNEL_CONNECTION_READ: "channel-connection:read";
|
|
16
|
-
readonly CHANNEL_CONNECTION_CREATE: "channel-connection:create";
|
|
17
|
-
readonly CHANNEL_CONNECTION_UPDATE: "channel-connection:update";
|
|
18
|
-
readonly CHANNEL_CONNECTION_DELETE: "channel-connection:delete";
|
|
19
|
-
readonly CHANNEL_DELIVERY_READ: "channel-delivery:read";
|
|
20
|
-
readonly CHANNEL_DELIVERY_SEND: "channel-delivery:send";
|
|
21
|
-
readonly CHANNEL_DELIVERY_EXECUTE: "channel-delivery:execute";
|
|
22
|
-
readonly CHANNEL_DELIVERY_DELETE: "channel-delivery:delete";
|
|
23
|
-
readonly CHANNEL_WEBHOOK_READ: "channel-webhook:read";
|
|
24
|
-
readonly CHANNEL_WEBHOOK_EXECUTE: "channel-webhook:execute";
|
|
25
|
-
readonly CHANNEL_WEBHOOK_UPDATE: "channel-webhook:update";
|
|
26
|
-
readonly USER_PLATFORM_CONNECTION_READ: "user-platform-connection:read";
|
|
27
|
-
readonly USER_PLATFORM_CONNECTION_CREATE: "user-platform-connection:create";
|
|
28
|
-
readonly USER_PLATFORM_CONNECTION_UPDATE: "user-platform-connection:update";
|
|
29
|
-
readonly USER_PLATFORM_CONNECTION_DELETE: "user-platform-connection:delete";
|
|
30
|
-
};
|
|
31
|
-
export type ChannelPermission = (typeof CHANNEL_PERMISSIONS)[keyof typeof CHANNEL_PERMISSIONS];
|
|
32
|
-
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../src/channel/constants/permissions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;IAS9B,+DAA+D;;;;;;;;;;;;;;;;;CAyBvD,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { ApolloClient } from '@apollo/client/core';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Get or create the singleton Apollo client for global channel operations
|
|
5
|
-
*
|
|
6
|
-
* @param globalGatewayUrl - Global Gateway GraphQL URL (default: https://gw-global.local.burdenoff.com/global/graphql)
|
|
7
|
-
* @param authToken - Optional JWT auth token
|
|
8
|
-
* @param organizationId - Optional organization ID for RBAC checks
|
|
9
|
-
*/
|
|
10
|
-
export declare function getGlobalChannelClient(globalGatewayUrl?: string, authToken?: string, organizationId?: string): ApolloClient;
|
|
11
|
-
//# sourceMappingURL=global-client.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"global-client.d.ts","sourceRoot":"","sources":["../../../src/channel/graphql/global-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiC,MAAM,qBAAqB,CAAC;AA0ClF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,gBAAgB,CAAC,EAAE,MAAM,EACzB,SAAS,CAAC,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,MAAM,GACtB,YAAY,CAoGd"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ChannelOrgSettingsInput } from '../../generated/global-types';
|
|
2
|
-
|
|
3
|
-
export interface ChannelOrgSettings {
|
|
4
|
-
organizationId: string;
|
|
5
|
-
defaultPriority: number;
|
|
6
|
-
rateLimitPerMinute: number;
|
|
7
|
-
rateLimitPerHour: number;
|
|
8
|
-
healthCheckIntervalMinutes: number;
|
|
9
|
-
enableAuditLogging: boolean;
|
|
10
|
-
enableWebhookVerification: boolean;
|
|
11
|
-
sessionTimeoutMinutes: number;
|
|
12
|
-
updatedAt: string;
|
|
13
|
-
}
|
|
14
|
-
export type { ChannelOrgSettingsInput };
|
|
15
|
-
export declare function useChannelOrgSettings(): {
|
|
16
|
-
settings: ChannelOrgSettings | null;
|
|
17
|
-
isLoading: boolean;
|
|
18
|
-
error: string | null;
|
|
19
|
-
refetch: () => Promise<void>;
|
|
20
|
-
saveSettings: (input: ChannelOrgSettingsInput) => Promise<ChannelOrgSettings | null>;
|
|
21
|
-
};
|
|
22
|
-
//# sourceMappingURL=useChannelOrgSettings.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useChannelOrgSettings.d.ts","sourceRoot":"","sources":["../../../src/channel/hooks/useChannelOrgSettings.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5E,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,0BAA0B,EAAE,MAAM,CAAC;IACnC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,yBAAyB,EAAE,OAAO,CAAC;IACnC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,YAAY,EAAE,uBAAuB,EAAE,CAAC;AAMxC,wBAAgB,qBAAqB;;;;;0BAwCnB,uBAAuB,KAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;EAqB7E"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useChannelSubscription
|
|
3
|
-
*
|
|
4
|
-
* Defensive subscription wrapper. Probes the shell-provided graphql-ws client
|
|
5
|
-
* (`globalThis.__burdenoffShell.graphqlWsClient`); if present, opens a
|
|
6
|
-
* subscription and dispatches each payload to `onData`. If absent — or if the
|
|
7
|
-
* subscription errors with a clear "unknown field" / "not implemented" signal
|
|
8
|
-
* (the operation isn't on the gateway yet) — silently falls back to a polling
|
|
9
|
-
* loop driven by the caller's `pollFn` at `pollIntervalMs`.
|
|
10
|
-
*
|
|
11
|
-
* Per task 5: backends may grow `webhookReceived`, `connectionStatusChanged`
|
|
12
|
-
* etc. independently; this hook lets the FE wire all four pages without
|
|
13
|
-
* crashing when a subscription isn't yet on the federated schema.
|
|
14
|
-
*/
|
|
15
|
-
export interface UseChannelSubscriptionArgs<TData> {
|
|
16
|
-
/** GraphQL subscription document (string SDL). */
|
|
17
|
-
query: string;
|
|
18
|
-
/** Variables for the subscription. */
|
|
19
|
-
variables?: Record<string, unknown>;
|
|
20
|
-
/** Stable identifier for this subscription, used in error context. */
|
|
21
|
-
operationName: string;
|
|
22
|
-
/** Called for each payload received from the subscription. */
|
|
23
|
-
onData: (data: TData) => void;
|
|
24
|
-
/** Polling fallback invoked when subscription is unavailable / failed. */
|
|
25
|
-
pollFn: () => Promise<void> | void;
|
|
26
|
-
/** Polling interval in ms (default 15s). */
|
|
27
|
-
pollIntervalMs?: number;
|
|
28
|
-
/** Whether to enable the hook at all. */
|
|
29
|
-
enabled?: boolean;
|
|
30
|
-
}
|
|
31
|
-
export declare function useChannelSubscription<TData = unknown>({ query, variables, operationName, onData, pollFn, pollIntervalMs, enabled, }: UseChannelSubscriptionArgs<TData>): void;
|
|
32
|
-
export declare const CHANNEL_HEALTH_CHANGED_SUBSCRIPTION = "\n subscription ChannelHealthChanged($organizationId: ID!) {\n channelHealthChanged(organizationId: $organizationId) {\n channelId\n previousHealth\n newHealth\n changedAt\n reason\n }\n }\n";
|
|
33
|
-
export declare const DELIVERY_STATUS_UPDATE_SUBSCRIPTION = "\n subscription OnDeliveryStatusUpdate($notificationId: ID!, $organizationId: ID!) {\n onDeliveryStatusUpdate(notificationId: $notificationId, organizationId: $organizationId) {\n notificationId\n messageId\n status\n timestamp\n }\n }\n";
|
|
34
|
-
export declare const WEBHOOK_RECEIVED_SUBSCRIPTION = "\n subscription WebhookReceived($channelId: ID!) {\n webhookReceived(channelId: $channelId) {\n channelId\n organizationId\n webhookEventId\n receivedAt\n platform\n processingStatus\n }\n }\n";
|
|
35
|
-
export declare const CONNECTION_STATUS_CHANGED_SUBSCRIPTION = "\n subscription ConnectionStatusChanged($channelId: ID!) {\n connectionStatusChanged(channelId: $channelId) {\n channelId\n organizationId\n connectionId\n isActive\n isHealthy\n changedAt\n reason\n }\n }\n";
|
|
36
|
-
//# sourceMappingURL=useChannelSubscription.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useChannelSubscription.d.ts","sourceRoot":"","sources":["../../../src/channel/hooks/useChannelSubscription.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,MAAM,WAAW,0BAA0B,CAAC,KAAK;IAC/C,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;IAC9B,0EAA0E;IAC1E,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACnC,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAmBD,wBAAgB,sBAAsB,CAAC,KAAK,GAAG,OAAO,EAAE,EACtD,KAAK,EACL,SAAS,EACT,aAAa,EACb,MAAM,EACN,MAAM,EACN,cAAuB,EACvB,OAAc,GACf,EAAE,0BAA0B,CAAC,KAAK,CAAC,QAgInC;AAMD,eAAO,MAAM,mCAAmC,oOAU/C,CAAC;AAEF,eAAO,MAAM,mCAAmC,8QAS/C,CAAC;AAEF,eAAO,MAAM,6BAA6B,2OAWzC,CAAC;AAEF,eAAO,MAAM,sCAAsC,+PAYlD,CAAC"}
|