@burdenoff/microfe-channel 2026.1013.2 → 2026.1107.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/channel/ChannelRoot.d.ts +0 -2
  2. package/dist/channel/ChannelRoot.js +9 -6
  3. package/dist/channel/ChannelRoot.js.map +1 -1
  4. package/dist/channel/ChannelRoutes.d.ts +1 -2
  5. package/dist/channel/ChannelRoutes.js +5 -6
  6. package/dist/channel/ChannelRoutes.js.map +1 -1
  7. package/dist/channel/components/AdapterCredentialsForm.d.ts +2 -4
  8. package/dist/channel/components/AdapterCredentialsForm.js +1 -2
  9. package/dist/channel/components/AdapterCredentialsForm.js.map +1 -1
  10. package/dist/channel/components/FeatureNotEnabled.d.ts +1 -2
  11. package/dist/channel/components/FeatureNotEnabled.js +1 -2
  12. package/dist/channel/components/FeatureNotEnabled.js.map +1 -1
  13. package/dist/channel/components/MarkdownEditor.d.ts +1 -2
  14. package/dist/channel/components/MarkdownEditor.js +2 -5
  15. package/dist/channel/components/MarkdownEditor.js.map +1 -1
  16. package/dist/channel/components/PermissionGuard.d.ts +0 -2
  17. package/dist/channel/components/PermissionGuard.js +1 -2
  18. package/dist/channel/components/PermissionGuard.js.map +1 -1
  19. package/dist/channel/config/adapterCredentials.d.ts +0 -2
  20. package/dist/channel/config/adapterCredentials.js +3 -7
  21. package/dist/channel/config/adapterCredentials.js.map +1 -1
  22. package/dist/channel/constants/permissions.d.ts +0 -1
  23. package/dist/channel/constants/permissions.js +1 -2
  24. package/dist/channel/constants/permissions.js.map +1 -1
  25. package/dist/channel/graphql/global-client.d.ts +0 -2
  26. package/dist/channel/graphql/global-client.js +5 -6
  27. package/dist/channel/graphql/global-client.js.map +1 -1
  28. package/dist/channel/hooks/useChannelOrgSettings.d.ts +0 -2
  29. package/dist/channel/hooks/useChannelOrgSettings.js +20 -21
  30. package/dist/channel/hooks/useChannelOrgSettings.js.map +1 -1
  31. package/dist/channel/hooks/useChannelSubscription.d.ts +0 -1
  32. package/dist/channel/hooks/useChannelSubscription.js +7 -12
  33. package/dist/channel/hooks/useChannelSubscription.js.map +1 -1
  34. package/dist/channel/hooks/useChannels.d.ts +20 -30
  35. package/dist/channel/hooks/useChannels.js +100 -101
  36. package/dist/channel/hooks/useChannels.js.map +1 -1
  37. package/dist/channel/hooks/useTr.d.ts +0 -1
  38. package/dist/channel/hooks/useTr.js +1 -2
  39. package/dist/channel/hooks/useTr.js.map +1 -1
  40. package/dist/channel/index.d.ts +0 -1
  41. package/dist/channel/index.js +3 -2
  42. package/dist/channel/modules/channels/hooks/useExport.d.ts +0 -2
  43. package/dist/channel/modules/channels/hooks/useExport.js +77 -80
  44. package/dist/channel/modules/channels/hooks/useExport.js.map +1 -1
  45. package/dist/channel/modules/channels/i18n.d.ts +0 -2
  46. package/dist/channel/modules/channels/i18n.js +1 -2
  47. package/dist/channel/modules/channels/i18n.js.map +1 -1
  48. package/dist/channel/modules/channels/pages/ChannelDashboardPage.d.ts +1 -2
  49. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +10 -11
  50. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
  51. package/dist/channel/modules/channels/pages/ChannelDetailsPage.d.ts +1 -2
  52. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +400 -401
  53. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
  54. package/dist/channel/modules/channels/pages/ChannelSettingsPage.d.ts +1 -2
  55. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +8 -7
  56. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
  57. package/dist/channel/modules/channels/pages/ChannelUsersPage.d.ts +1 -2
  58. package/dist/channel/modules/channels/pages/ChannelUsersPage.js +5 -10
  59. package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
  60. package/dist/channel/modules/channels/pages/ChannelsListPage.d.ts +1 -2
  61. package/dist/channel/modules/channels/pages/ChannelsListPage.js +287 -288
  62. package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
  63. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.d.ts +1 -2
  64. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +11 -12
  65. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
  66. package/dist/channel/modules/channels/pages/ConnectionsListPage.d.ts +1 -2
  67. package/dist/channel/modules/channels/pages/ConnectionsListPage.js +1 -2
  68. package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
  69. package/dist/channel/modules/channels/pages/CreateChannelPage.d.ts +1 -2
  70. package/dist/channel/modules/channels/pages/CreateChannelPage.js +4 -5
  71. package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
  72. package/dist/channel/modules/channels/pages/CreateConnectionPage.d.ts +1 -2
  73. package/dist/channel/modules/channels/pages/CreateConnectionPage.js +37 -38
  74. package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
  75. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.d.ts +1 -2
  76. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +7 -12
  77. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
  78. package/dist/channel/modules/channels/pages/EditChannelPage.d.ts +1 -2
  79. package/dist/channel/modules/channels/pages/EditChannelPage.js +105 -103
  80. package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
  81. package/dist/channel/modules/channels/pages/EditConnectionPage.d.ts +1 -2
  82. package/dist/channel/modules/channels/pages/EditConnectionPage.js +22 -23
  83. package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
  84. package/dist/channel/modules/channels/pages/SendMessagePage.d.ts +1 -2
  85. package/dist/channel/modules/channels/pages/SendMessagePage.js +180 -187
  86. package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
  87. package/dist/channel/modules/channels/pages/WebhookEventsPage.d.ts +1 -2
  88. package/dist/channel/modules/channels/pages/WebhookEventsPage.js +144 -149
  89. package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
  90. package/dist/channel/pages/ChannelLandingPage.d.ts +1 -2
  91. package/dist/channel/pages/ChannelLandingPage.js +20 -21
  92. package/dist/channel/pages/ChannelLandingPage.js.map +1 -1
  93. package/dist/channel/providers/ChannelProvider.d.ts +0 -2
  94. package/dist/channel/providers/ChannelProvider.js +1 -2
  95. package/dist/channel/providers/ChannelProvider.js.map +1 -1
  96. package/dist/channel/types/index.d.ts +0 -2
  97. package/dist/channel/utils/shellBridge.d.ts +0 -1
  98. package/dist/channel/utils/shellBridge.js +1 -2
  99. package/dist/channel/utils/shellBridge.js.map +1 -1
  100. package/dist/dev/App.d.ts +1 -2
  101. package/dist/dev/main.d.ts +0 -2
  102. package/dist/generated/global-operations.d.ts +885 -336
  103. package/dist/generated/global-operations.js +2 -3
  104. package/dist/generated/global-operations.js.map +1 -1
  105. package/dist/generated/global-types.d.ts +2 -22
  106. package/dist/generated/global-types.js +1 -2
  107. package/dist/generated/global-types.js.map +1 -1
  108. package/dist/generated/wspace-operations.d.ts +4 -2
  109. package/dist/generated/wspace-types.d.ts +4 -24
  110. package/dist/index.d.ts +0 -1
  111. package/dist/node_modules/@apollo/client/errors/CombinedGraphQLErrors.js +1 -2
  112. package/dist/node_modules/@apollo/client/errors/CombinedGraphQLErrors.js.map +1 -1
  113. package/dist/node_modules/@apollo/client/errors/utils.js +1 -2
  114. package/dist/node_modules/@apollo/client/errors/utils.js.map +1 -1
  115. package/dist/translations/en.d.ts +0 -1
  116. package/dist/utils/nativeBridge.d.ts +0 -1
  117. package/dist/utils/nativeBridge.js +1 -2
  118. package/package.json +65 -43
  119. package/dist/channel/ChannelRoot.d.ts.map +0 -1
  120. package/dist/channel/ChannelRoutes.d.ts.map +0 -1
  121. package/dist/channel/components/AdapterCredentialsForm.d.ts.map +0 -1
  122. package/dist/channel/components/FeatureNotEnabled.d.ts.map +0 -1
  123. package/dist/channel/components/MarkdownEditor.d.ts.map +0 -1
  124. package/dist/channel/components/PermissionGuard.d.ts.map +0 -1
  125. package/dist/channel/config/adapterCredentials.d.ts.map +0 -1
  126. package/dist/channel/constants/permissions.d.ts.map +0 -1
  127. package/dist/channel/graphql/global-client.d.ts.map +0 -1
  128. package/dist/channel/hooks/useChannelOrgSettings.d.ts.map +0 -1
  129. package/dist/channel/hooks/useChannelSubscription.d.ts.map +0 -1
  130. package/dist/channel/hooks/useChannels.d.ts.map +0 -1
  131. package/dist/channel/hooks/useTr.d.ts.map +0 -1
  132. package/dist/channel/index.d.ts.map +0 -1
  133. package/dist/channel/modules/channels/hooks/useExport.d.ts.map +0 -1
  134. package/dist/channel/modules/channels/i18n.d.ts.map +0 -1
  135. package/dist/channel/modules/channels/pages/ChannelDashboardPage.d.ts.map +0 -1
  136. package/dist/channel/modules/channels/pages/ChannelDetailsPage.d.ts.map +0 -1
  137. package/dist/channel/modules/channels/pages/ChannelSettingsPage.d.ts.map +0 -1
  138. package/dist/channel/modules/channels/pages/ChannelUsersPage.d.ts.map +0 -1
  139. package/dist/channel/modules/channels/pages/ChannelsListPage.d.ts.map +0 -1
  140. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.d.ts.map +0 -1
  141. package/dist/channel/modules/channels/pages/ConnectionsListPage.d.ts.map +0 -1
  142. package/dist/channel/modules/channels/pages/CreateChannelPage.d.ts.map +0 -1
  143. package/dist/channel/modules/channels/pages/CreateConnectionPage.d.ts.map +0 -1
  144. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.d.ts.map +0 -1
  145. package/dist/channel/modules/channels/pages/EditChannelPage.d.ts.map +0 -1
  146. package/dist/channel/modules/channels/pages/EditConnectionPage.d.ts.map +0 -1
  147. package/dist/channel/modules/channels/pages/SendMessagePage.d.ts.map +0 -1
  148. package/dist/channel/modules/channels/pages/WebhookEventsPage.d.ts.map +0 -1
  149. package/dist/channel/pages/ChannelLandingPage.d.ts.map +0 -1
  150. package/dist/channel/providers/ChannelProvider.d.ts.map +0 -1
  151. package/dist/channel/types/index.d.ts.map +0 -1
  152. package/dist/channel/utils/shellBridge.d.ts.map +0 -1
  153. package/dist/dev/App.d.ts.map +0 -1
  154. package/dist/dev/main.d.ts.map +0 -1
  155. package/dist/generated/global-operations.d.ts.map +0 -1
  156. package/dist/generated/global-types.d.ts.map +0 -1
  157. package/dist/generated/wspace-operations.d.ts.map +0 -1
  158. package/dist/generated/wspace-types.d.ts.map +0 -1
  159. package/dist/index.d.ts.map +0 -1
  160. package/dist/translations/en.d.ts.map +0 -1
  161. package/dist/utils/nativeBridge.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ConnectionsListPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ConnectionsListPage.tsx"],"sourcesContent":["/**\n * Connections List Page\n *\n * Displays all connections across channels with their status.\n */\n\nimport { useState, useEffect } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n ArrowLeft,\n Plus,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n RefreshCw,\n Settings,\n Shield,\n} from 'lucide-react';\nimport {\n EmphasisPanel,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useChannels } from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\n\nconst HEALTH_CONFIG: Record<string, { icon: typeof CheckCircle2; color: string; label: string }> = {\n true: { icon: CheckCircle2, color: 'text-status-success-text', label: 'Healthy' },\n false: { icon: XCircle, color: 'text-status-error-text', label: 'Unhealthy' },\n};\n\nexport function ConnectionsListPage() {\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n // Creating connections is admin-only (channel-connection:create) — hide otherwise.\n const canConnectionCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_CREATE);\n const { channels, isLoading, error, refetch } = useChannels();\n\n // Poll for connection status updates every 60 s.\n // connectionStatusChanged subscription is scoped to a single channelId;\n // this page shows all connections, so polling is used instead.\n useEffect(() => {\n const timer = setInterval(() => {\n void refetch();\n }, 60_000);\n return () => clearInterval(timer);\n }, [refetch]);\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\n // Filter state\n const [healthFilter, setHealthFilter] = useState<string>('all');\n const [platformFilter, setPlatformFilter] = useState<string>('all');\n const pageSize = 10;\n const [currentPage, setCurrentPage] = useState(0);\n\n // Get all connections from channels (single-pass; avoids filter().map() double traversal)\n const connections = channels.reduce<\n Array<{\n channelId: string;\n channelName: string;\n platform: (typeof channels)[number]['platform'];\n adapterType: (typeof channels)[number]['adapterType'];\n channelStatus: (typeof channels)[number]['status'];\n connectionId: string;\n isActive: boolean;\n isHealthy: boolean;\n needsTokenRefresh: boolean;\n lastTestedAt: string | null | undefined;\n authType: NonNullable<(typeof channels)[number]['connection']>['authType'];\n tagAssignments: NonNullable<\n NonNullable<(typeof channels)[number]['connection']>['tagAssignments']\n >;\n }>\n >((acc, ch) => {\n const conn = ch.connection;\n if (!conn) return acc;\n acc.push({\n channelId: ch.id,\n channelName: ch.name,\n platform: ch.platform,\n adapterType: ch.adapterType,\n channelStatus: ch.status,\n connectionId: conn.id,\n isActive: conn.isActive,\n isHealthy: conn.isHealthy,\n needsTokenRefresh: conn.needsTokenRefresh,\n lastTestedAt: conn.lastTestedAt,\n authType: conn.authType,\n tagAssignments: conn.tagAssignments ?? [],\n });\n return acc;\n }, []);\n\n // Get unique platforms\n const platforms = [...new Set(connections.map((c) => c.platform))];\n\n // Connections that are unhealthy or have a token expiring soon — the single\n // most important thing to act on, surfaced in the page's emphasis zone.\n const connectionsNeedingAttention = connections.filter(\n (c) => !c.isHealthy || c.needsTokenRefresh\n ).length;\n\n // Apply filters\n const filteredConnections = connections.filter((conn) => {\n if (healthFilter === 'healthy' && !conn.isHealthy) return false;\n if (healthFilter === 'unhealthy' && conn.isHealthy) return false;\n if (platformFilter !== 'all' && conn.platform !== platformFilter) return false;\n return true;\n });\n\n const totalPages = Math.max(1, Math.ceil(filteredConnections.length / pageSize));\n const pagedConnections = filteredConnections.slice(\n currentPage * pageSize,\n (currentPage + 1) * pageSize\n );\n\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 className=\"size-8 animate-spin text-text-secondary\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-screen gap-4\">\n <AlertCircle className=\"size-12 text-status-error-text\" />\n <h1 className=\"text-xl font-semibold\">\n {tr('channel.loadConnectionsFailed', 'Failed to load connections')}\n </h1>\n <p className=\"text-sm text-text-secondary\">{error}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr('channel.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_READ}>\n <div className=\"min-h-screen bg-bg-surface\">\n {/* Header */}\n <div className=\"border-b bg-bg-surface\" data-tour=\"channels-connections-header\">\n <div className=\"container mx-auto px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate('./list')}\n aria-label={tr('channel.backToChannels', 'Back to channels')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"size-5 \" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold\">{tr('channel.connections', 'Connections')}</h1>\n <p className=\"text-sm text-text-secondary\">\n {filteredConnections.length} connection\n {filteredConnections.length !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n {canConnectionCreate && (\n <button\n type=\"button\"\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors flex items-center gap-2\"\n >\n <Plus className=\"size-4 \" />\n {tr('channel.newConnection', 'New Connection')}\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Page purpose + emphasis */}\n <div className=\"container mx-auto space-y-4 px-6 pt-4\">\n <PagePurpose icon={<Shield size={15} aria-hidden=\"true\" />}>\n {tr(\n 'channel.connectionsPurpose',\n \"A connection is the authenticated link — the OAuth grant, API key or credentials — that lets a channel actually reach its provider. This page lists every channel's connection with its live health and token status. Use it to confirm channels are authenticated and to catch tokens that are about to expire before deliveries start failing.\"\n )}\n </PagePurpose>\n {connectionsNeedingAttention > 0 && (\n <EmphasisPanel className=\"flex items-center gap-3 p-4 shadow-[var(--shadow-glow)]\">\n <span className=\"rounded-lg bg-status-warning-bg-subtle p-2\">\n <AlertCircle size={18} className=\"text-status-warning-text\" aria-hidden=\"true\" />\n </span>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {connectionsNeedingAttention}{' '}\n {connectionsNeedingAttention === 1\n ? tr('channel.connectionNeedsAttention', 'connection needs attention')\n : tr('channel.connectionsNeedAttention', 'connections need attention')}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'channel.connectionsNeedAttentionHint',\n 'These connections are unhealthy or have tokens expiring soon. Refresh or re-authenticate them to keep deliveries flowing.'\n )}\n </p>\n </div>\n </EmphasisPanel>\n )}\n </div>\n\n {/* Filters */}\n <div className=\"container mx-auto px-6 py-4\" data-tour=\"channels-connections-filters\">\n <div className=\"flex items-center gap-4\">\n <select\n value={platformFilter}\n onChange={(e) => setPlatformFilter(e.target.value)}\n className=\"px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"all\">{tr('channel.allPlatforms', 'All Platforms')}</option>\n {platforms.map((p) => (\n <option key={p} value={p}>\n {p}\n </option>\n ))}\n </select>\n\n <select\n value={healthFilter}\n onChange={(e) => setHealthFilter(e.target.value)}\n className=\"px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"all\">{tr('channel.allStatus', 'All Status')}</option>\n <option value=\"healthy\">{tr('channel.healthy', 'Healthy')}</option>\n <option value=\"unhealthy\">{tr('channel.unhealthy', 'Unhealthy')}</option>\n </select>\n\n <button\n type=\"button\"\n onClick={() => refetch()}\n aria-label={tr('channel.refresh', 'Refresh')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <RefreshCw className=\"size-5 \" aria-hidden=\"true\" />\n </button>\n </div>\n </div>\n\n {/* Connections List */}\n <div className=\"container mx-auto px-6 pb-8\">\n {filteredConnections.length === 0 ? (\n <IllustratedEmptyState\n illustration={channels.length === 0 ? 'empty-data' : 'empty-search'}\n title={tr('channel.noConnectionsFound', 'No connections found')}\n description={\n channels.length === 0\n ? tr('channel.createChannelFirst', 'Create a channel first to add connections')\n : tr('channel.noConnectionsMatchFilter', 'No connections match your filters')\n }\n action={\n channels.length === 0 ? (\n canConnectionCreate ? (\n <button\n type=\"button\"\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr('channel.createConnection', 'Create Connection')}\n </button>\n ) : undefined\n ) : (\n <button\n type=\"button\"\n onClick={() => {\n setHealthFilter('all');\n setPlatformFilter('all');\n }}\n className=\"px-4 py-2 rounded-lg border hover:bg-bg-sunken\"\n >\n {tr('channel.clearFilters', 'Clear Filters')}\n </button>\n )\n }\n />\n ) : (\n <>\n <GlassCard\n treatment=\"plain\"\n className=\"border-border-seam rounded-lg p-0 divide-y divide-[var(--color-border-seam)] overflow-hidden\"\n >\n {pagedConnections.map((conn) => {\n const health = HEALTH_CONFIG[String(conn.isHealthy)] || HEALTH_CONFIG.false;\n const HealthIcon = health.icon;\n\n return (\n <div\n key={conn.connectionId}\n role=\"button\"\n tabIndex={0}\n className=\"p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n onClick={() => navigate(`./${conn.channelId}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(`./${conn.channelId}`);\n }\n }}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <div\n className={`${health.color} ${\n conn.isHealthy ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n aria-label={health.label}\n >\n <HealthIcon className=\"size-5 \" aria-hidden=\"true\" />\n </div>\n <div>\n <p className=\"font-medium\">{conn.channelName}</p>\n <p className=\"text-sm text-text-secondary\">\n {conn.platform} • {conn.adapterType} • {conn.authType}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm\">\n {conn.channelStatus === 'ACTIVE' ? (\n <span className=\"text-status-success-text\">\n {tr('channel.activeStatus', 'Active')}\n </span>\n ) : conn.channelStatus === 'INACTIVE' ? (\n <span className=\"text-text-secondary\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n ) : conn.channelStatus === 'ERROR' ? (\n <span className=\"text-status-error-text\">\n {tr('channel.errorStatus', 'Error')}\n </span>\n ) : conn.channelStatus === 'SUSPENDED' ? (\n <span className=\"text-status-warning-text\">\n {tr('channel.suspendedStatus', 'Suspended')}\n </span>\n ) : conn.channelStatus === 'ARCHIVED' ? (\n <span className=\"text-text-secondary\">\n {tr('channel.archivedStatus', 'Archived')}\n </span>\n ) : (\n <span className=\"text-text-secondary\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n )}\n </p>\n {conn.lastTestedAt && (\n <p className=\"text-xs text-text-secondary\">\n Last tested: {new Date(conn.lastTestedAt).toLocaleDateString()}\n </p>\n )}\n </div>\n\n {conn.needsTokenRefresh && (\n <span className=\"px-2 py-1 text-xs bg-status-warning-bg-subtle text-status-warning-text rounded\">\n Token Expires\n </span>\n )}\n\n {conn.tagAssignments.length > 0 && (\n <div className=\"flex flex-wrap gap-1\" aria-label=\"Connection tags\">\n {conn.tagAssignments.slice(0, 3).map((a) => (\n <TagBadge key={a.id} id={a.tagId} label={a.tagId} size=\"sm\" />\n ))}\n {conn.tagAssignments.length > 3 && (\n <span className=\"text-xs text-text-secondary\">\n +{conn.tagAssignments.length - 3}\n </span>\n )}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`/channels/${conn.channelId}`);\n }}\n aria-label={tr('channel.viewChannelSettings', 'View channel settings')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <Settings className=\"size-4 \" />\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </GlassCard>\n {/* Pagination */}\n {filteredConnections.length > pageSize && (\n <div className=\"flex items-center justify-between px-4 py-3 border border-border-seam rounded-lg mt-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium\">\n {currentPage * pageSize + 1}–\n {Math.min((currentPage + 1) * pageSize, filteredConnections.length)}\n </span>{' '}\n {tr('channel.of', 'of')}{' '}\n <span className=\"font-medium\">{filteredConnections.length}</span>\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.max(0, p - 1))}\n disabled={currentPage === 0}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {currentPage + 1} / {totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={currentPage >= totalPages - 1}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.next', 'Next')}\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ConnectionsListPage.js","names":["_Fragment","HEALTH_CONFIG","CheckCircle2","XCircle","ConnectionsListPage","navigate","useNavigate","usePermissions","canConnectionCreate","hasPermission","CHANNEL_PERMISSIONS","useChannels","timer","refetch","useI18n","tr","value","t","key","fallback","healthFilter","setHealthFilter","useState","platformFilter","setPlatformFilter","currentPage","setCurrentPage","connections","channels","conn","ch","acc","platforms","c","connectionsNeedingAttention","filteredConnections","totalPages","pagedConnections","isLoading","Loader2","error","AlertCircle","PermissionGuard","ArrowLeft","Plus","PagePurpose","Shield","EmphasisPanel","p","RefreshCw","IllustratedEmptyState","Fragment","GlassCard","health","HealthIcon","e","TagBadge","a","Settings"],"sources":["../../../../../src/channel/modules/channels/pages/ConnectionsListPage.tsx"],"sourcesContent":["/**\n * Connections List Page\n *\n * Displays all connections across channels with their status.\n */\n\nimport { useState, useEffect } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n ArrowLeft,\n Plus,\n Loader2,\n AlertCircle,\n CheckCircle2,\n XCircle,\n RefreshCw,\n Settings,\n Shield,\n} from 'lucide-react';\nimport {\n EmphasisPanel,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useChannels } from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\n\nconst HEALTH_CONFIG: Record<string, { icon: typeof CheckCircle2; color: string; label: string }> = {\n true: { icon: CheckCircle2, color: 'text-status-success-text', label: 'Healthy' },\n false: { icon: XCircle, color: 'text-status-error-text', label: 'Unhealthy' },\n};\n\nexport function ConnectionsListPage() {\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n // Creating connections is admin-only (channel-connection:create) — hide otherwise.\n const canConnectionCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_CREATE);\n const { channels, isLoading, error, refetch } = useChannels();\n\n // Poll for connection status updates every 60 s.\n // connectionStatusChanged subscription is scoped to a single channelId;\n // this page shows all connections, so polling is used instead.\n useEffect(() => {\n const timer = setInterval(() => {\n void refetch();\n }, 60_000);\n return () => clearInterval(timer);\n }, [refetch]);\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\n // Filter state\n const [healthFilter, setHealthFilter] = useState<string>('all');\n const [platformFilter, setPlatformFilter] = useState<string>('all');\n const pageSize = 10;\n const [currentPage, setCurrentPage] = useState(0);\n\n // Get all connections from channels (single-pass; avoids filter().map() double traversal)\n const connections = channels.reduce<\n Array<{\n channelId: string;\n channelName: string;\n platform: (typeof channels)[number]['platform'];\n adapterType: (typeof channels)[number]['adapterType'];\n channelStatus: (typeof channels)[number]['status'];\n connectionId: string;\n isActive: boolean;\n isHealthy: boolean;\n needsTokenRefresh: boolean;\n lastTestedAt: string | null | undefined;\n authType: NonNullable<(typeof channels)[number]['connection']>['authType'];\n tagAssignments: NonNullable<\n NonNullable<(typeof channels)[number]['connection']>['tagAssignments']\n >;\n }>\n >((acc, ch) => {\n const conn = ch.connection;\n if (!conn) return acc;\n acc.push({\n channelId: ch.id,\n channelName: ch.name,\n platform: ch.platform,\n adapterType: ch.adapterType,\n channelStatus: ch.status,\n connectionId: conn.id,\n isActive: conn.isActive,\n isHealthy: conn.isHealthy,\n needsTokenRefresh: conn.needsTokenRefresh,\n lastTestedAt: conn.lastTestedAt,\n authType: conn.authType,\n tagAssignments: conn.tagAssignments ?? [],\n });\n return acc;\n }, []);\n\n // Get unique platforms\n const platforms = [...new Set(connections.map((c) => c.platform))];\n\n // Connections that are unhealthy or have a token expiring soon — the single\n // most important thing to act on, surfaced in the page's emphasis zone.\n const connectionsNeedingAttention = connections.filter(\n (c) => !c.isHealthy || c.needsTokenRefresh\n ).length;\n\n // Apply filters\n const filteredConnections = connections.filter((conn) => {\n if (healthFilter === 'healthy' && !conn.isHealthy) return false;\n if (healthFilter === 'unhealthy' && conn.isHealthy) return false;\n if (platformFilter !== 'all' && conn.platform !== platformFilter) return false;\n return true;\n });\n\n const totalPages = Math.max(1, Math.ceil(filteredConnections.length / pageSize));\n const pagedConnections = filteredConnections.slice(\n currentPage * pageSize,\n (currentPage + 1) * pageSize\n );\n\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 className=\"size-8 animate-spin text-text-secondary\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-screen gap-4\">\n <AlertCircle className=\"size-12 text-status-error-text\" />\n <h1 className=\"text-xl font-semibold\">\n {tr('channel.loadConnectionsFailed', 'Failed to load connections')}\n </h1>\n <p className=\"text-sm text-text-secondary\">{error}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr('channel.retry', 'Retry')}\n </button>\n </div>\n );\n }\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_READ}>\n <div className=\"min-h-screen bg-bg-surface\">\n {/* Header */}\n <div className=\"border-b bg-bg-surface\" data-tour=\"channels-connections-header\">\n <div className=\"container mx-auto px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate('./list')}\n aria-label={tr('channel.backToChannels', 'Back to channels')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"size-5 \" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold\">{tr('channel.connections', 'Connections')}</h1>\n <p className=\"text-sm text-text-secondary\">\n {filteredConnections.length} connection\n {filteredConnections.length !== 1 ? 's' : ''}\n </p>\n </div>\n </div>\n {canConnectionCreate && (\n <button\n type=\"button\"\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors flex items-center gap-2\"\n >\n <Plus className=\"size-4 \" />\n {tr('channel.newConnection', 'New Connection')}\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Page purpose + emphasis */}\n <div className=\"container mx-auto space-y-4 px-6 pt-4\">\n <PagePurpose icon={<Shield size={15} aria-hidden=\"true\" />}>\n {tr(\n 'channel.connectionsPurpose',\n \"A connection is the authenticated link — the OAuth grant, API key or credentials — that lets a channel actually reach its provider. This page lists every channel's connection with its live health and token status. Use it to confirm channels are authenticated and to catch tokens that are about to expire before deliveries start failing.\"\n )}\n </PagePurpose>\n {connectionsNeedingAttention > 0 && (\n <EmphasisPanel className=\"flex items-center gap-3 p-4 shadow-[var(--shadow-glow)]\">\n <span className=\"rounded-lg bg-status-warning-bg-subtle p-2\">\n <AlertCircle size={18} className=\"text-status-warning-text\" aria-hidden=\"true\" />\n </span>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {connectionsNeedingAttention}{' '}\n {connectionsNeedingAttention === 1\n ? tr('channel.connectionNeedsAttention', 'connection needs attention')\n : tr('channel.connectionsNeedAttention', 'connections need attention')}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'channel.connectionsNeedAttentionHint',\n 'These connections are unhealthy or have tokens expiring soon. Refresh or re-authenticate them to keep deliveries flowing.'\n )}\n </p>\n </div>\n </EmphasisPanel>\n )}\n </div>\n\n {/* Filters */}\n <div className=\"container mx-auto px-6 py-4\" data-tour=\"channels-connections-filters\">\n <div className=\"flex items-center gap-4\">\n <select\n value={platformFilter}\n onChange={(e) => setPlatformFilter(e.target.value)}\n className=\"px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"all\">{tr('channel.allPlatforms', 'All Platforms')}</option>\n {platforms.map((p) => (\n <option key={p} value={p}>\n {p}\n </option>\n ))}\n </select>\n\n <select\n value={healthFilter}\n onChange={(e) => setHealthFilter(e.target.value)}\n className=\"px-4 py-2 rounded-lg border bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"all\">{tr('channel.allStatus', 'All Status')}</option>\n <option value=\"healthy\">{tr('channel.healthy', 'Healthy')}</option>\n <option value=\"unhealthy\">{tr('channel.unhealthy', 'Unhealthy')}</option>\n </select>\n\n <button\n type=\"button\"\n onClick={() => refetch()}\n aria-label={tr('channel.refresh', 'Refresh')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <RefreshCw className=\"size-5 \" aria-hidden=\"true\" />\n </button>\n </div>\n </div>\n\n {/* Connections List */}\n <div className=\"container mx-auto px-6 pb-8\">\n {filteredConnections.length === 0 ? (\n <IllustratedEmptyState\n illustration={channels.length === 0 ? 'empty-data' : 'empty-search'}\n title={tr('channel.noConnectionsFound', 'No connections found')}\n description={\n channels.length === 0\n ? tr('channel.createChannelFirst', 'Create a channel first to add connections')\n : tr('channel.noConnectionsMatchFilter', 'No connections match your filters')\n }\n action={\n channels.length === 0 ? (\n canConnectionCreate ? (\n <button\n type=\"button\"\n onClick={() => navigate('./create')}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n {tr('channel.createConnection', 'Create Connection')}\n </button>\n ) : undefined\n ) : (\n <button\n type=\"button\"\n onClick={() => {\n setHealthFilter('all');\n setPlatformFilter('all');\n }}\n className=\"px-4 py-2 rounded-lg border hover:bg-bg-sunken\"\n >\n {tr('channel.clearFilters', 'Clear Filters')}\n </button>\n )\n }\n />\n ) : (\n <>\n <GlassCard\n treatment=\"plain\"\n className=\"border-border-seam rounded-lg p-0 divide-y divide-[var(--color-border-seam)] overflow-hidden\"\n >\n {pagedConnections.map((conn) => {\n const health = HEALTH_CONFIG[String(conn.isHealthy)] || HEALTH_CONFIG.false;\n const HealthIcon = health.icon;\n\n return (\n <div\n key={conn.connectionId}\n role=\"button\"\n tabIndex={0}\n className=\"p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n onClick={() => navigate(`./${conn.channelId}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(`./${conn.channelId}`);\n }\n }}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <div\n className={`${health.color} ${\n conn.isHealthy ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n aria-label={health.label}\n >\n <HealthIcon className=\"size-5 \" aria-hidden=\"true\" />\n </div>\n <div>\n <p className=\"font-medium\">{conn.channelName}</p>\n <p className=\"text-sm text-text-secondary\">\n {conn.platform} • {conn.adapterType} • {conn.authType}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm\">\n {conn.channelStatus === 'ACTIVE' ? (\n <span className=\"text-status-success-text\">\n {tr('channel.activeStatus', 'Active')}\n </span>\n ) : conn.channelStatus === 'INACTIVE' ? (\n <span className=\"text-text-secondary\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n ) : conn.channelStatus === 'ERROR' ? (\n <span className=\"text-status-error-text\">\n {tr('channel.errorStatus', 'Error')}\n </span>\n ) : conn.channelStatus === 'SUSPENDED' ? (\n <span className=\"text-status-warning-text\">\n {tr('channel.suspendedStatus', 'Suspended')}\n </span>\n ) : conn.channelStatus === 'ARCHIVED' ? (\n <span className=\"text-text-secondary\">\n {tr('channel.archivedStatus', 'Archived')}\n </span>\n ) : (\n <span className=\"text-text-secondary\">\n {tr('channel.inactiveStatus', 'Inactive')}\n </span>\n )}\n </p>\n {conn.lastTestedAt && (\n <p className=\"text-xs text-text-secondary\">\n Last tested: {new Date(conn.lastTestedAt).toLocaleDateString()}\n </p>\n )}\n </div>\n\n {conn.needsTokenRefresh && (\n <span className=\"px-2 py-1 text-xs bg-status-warning-bg-subtle text-status-warning-text rounded\">\n Token Expires\n </span>\n )}\n\n {conn.tagAssignments.length > 0 && (\n <div className=\"flex flex-wrap gap-1\" aria-label=\"Connection tags\">\n {conn.tagAssignments.slice(0, 3).map((a) => (\n <TagBadge key={a.id} id={a.tagId} label={a.tagId} size=\"sm\" />\n ))}\n {conn.tagAssignments.length > 3 && (\n <span className=\"text-xs text-text-secondary\">\n +{conn.tagAssignments.length - 3}\n </span>\n )}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`/channels/${conn.channelId}`);\n }}\n aria-label={tr('channel.viewChannelSettings', 'View channel settings')}\n className=\"p-2 hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n <Settings className=\"size-4 \" />\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </GlassCard>\n {/* Pagination */}\n {filteredConnections.length > pageSize && (\n <div className=\"flex items-center justify-between px-4 py-3 border border-border-seam rounded-lg mt-3\">\n <p className=\"text-sm text-text-secondary\">\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium\">\n {currentPage * pageSize + 1}–\n {Math.min((currentPage + 1) * pageSize, filteredConnections.length)}\n </span>{' '}\n {tr('channel.of', 'of')}{' '}\n <span className=\"font-medium\">{filteredConnections.length}</span>\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.max(0, p - 1))}\n disabled={currentPage === 0}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {currentPage + 1} / {totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => setCurrentPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={currentPage >= totalPages - 1}\n className=\"px-3 py-1.5 text-sm border border-border-subtle rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.next', 'Next')}\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -8,5 +8,4 @@
8
8
  * organizationId is injected from shell context — not shown in form.
9
9
  * Credentials belong to createConnection mutation — not part of channel creation.
10
10
  */
11
- export declare function CreateChannelPage(): import("react").JSX.Element;
12
- //# sourceMappingURL=CreateChannelPage.d.ts.map
11
+ export declare function CreateChannelPage(): import('react').JSX.Element;
@@ -9,7 +9,7 @@ import { AlertCircle as c, ArrowLeft as l, Bell as u, CheckCircle2 as d, Globe a
9
9
  import { useNavigate as w } from "react-router";
10
10
  import { jsx as T, jsxs as E } from "react/jsx-runtime";
11
11
  import { EmphasisPanel as D, GlassCard as O, OptionalFields as k, PagePurpose as A } from "@burdenoff/fe-libs/ui";
12
- //#region src/channel/modules/channels/pages/CreateChannelPage.tsx
12
+ // src/channel/modules/channels/pages/CreateChannelPage.tsx
13
13
  function j() {
14
14
  let j = w(), { organizationId: M } = t(), { createChannel: N } = n(), P = e(), F = o(() => [
15
15
  {
@@ -237,7 +237,7 @@ function j() {
237
237
  }
238
238
  L(!0);
239
239
  try {
240
- let e = await N({
240
+ let e = {
241
241
  name: B.name.trim(),
242
242
  platform: B.platform,
243
243
  organizationId: M,
@@ -246,8 +246,8 @@ function j() {
246
246
  status: B.status,
247
247
  priority: B.priority,
248
248
  platformConfig: t
249
- });
250
- e ? j(`../${e.id}`) : z(P("channel.createFailed", "Failed to create channel"));
249
+ }, n = await N(e);
250
+ n ? j(`../${n.id}`) : z(P("channel.createFailed", "Failed to create channel"));
251
251
  } catch (e) {
252
252
  z(e instanceof Error ? e.message : P("channel.createFailed", "Failed to create channel"));
253
253
  } finally {
@@ -489,7 +489,6 @@ function j() {
489
489
  })
490
490
  });
491
491
  }
492
- //#endregion
493
492
  export { j as CreateChannelPage };
494
493
 
495
494
  //# sourceMappingURL=CreateChannelPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreateChannelPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/CreateChannelPage.tsx"],"sourcesContent":["/**\n * Create Channel Page\n *\n * Form fields match the exact CreateChannelInput from global-channel-svc:\n * name (required), platform (required), organizationId (auto), description,\n * adapterType, status, priority, platformConfig\n *\n * organizationId is injected from shell context — not shown in form.\n * Credentials belong to createConnection mutation — not part of channel creation.\n */\n\nimport { useState, useMemo } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useTr } from '../../../hooks/useTr';\nimport { EmphasisPanel, GlassCard, OptionalFields, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport {\n Radio,\n ArrowLeft,\n Save,\n Loader2,\n AlertCircle,\n Mail,\n Smartphone,\n Bell,\n Inbox,\n Globe,\n Hash,\n Users,\n MessageCircle,\n Send,\n MessageSquare,\n CheckCircle2,\n} from 'lucide-react';\nimport { useChannel } from '../../../providers/ChannelProvider';\nimport {\n useChannels,\n type ChannelPlatform,\n type AdapterType,\n type ChannelStatus,\n type CreateChannelInput,\n} from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { MarkdownEditor } from '../../../components/MarkdownEditor';\n\n// ============================================================================\n// PLATFORM CONFIG\n// ============================================================================\n\n// NOTE: AdapterType values MUST match the backend Prisma schema exactly.\n// See: global-channel-svc/prisma/schema/schema.prisma (enum AdapterType)\n\ntype PlatformOption = {\n value: ChannelPlatform;\n labelKey: string;\n labelFallback: string;\n descriptionKey: string;\n descriptionFallback: string;\n icon: typeof Mail;\n color: string;\n adapters: { value: AdapterType; labelKey: string; labelFallback: string }[];\n};\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport function CreateChannelPage() {\n const navigate = useNavigate();\n const { organizationId } = useChannel();\n const { createChannel } = useChannels();\n const tr = useTr();\n\n const PLATFORM_OPTIONS: PlatformOption[] = useMemo(\n () => [\n {\n value: 'EMAIL',\n labelKey: 'channel.platformLabel.email',\n labelFallback: 'Email',\n descriptionKey: 'channel.platformDescription.emailDescription',\n descriptionFallback: 'Send emails via various providers',\n icon: Mail,\n color: 'text-accent-blue bg-accent-blue-subtle border-accent-blue-subtle',\n adapters: [\n { value: 'AWS_SES', labelKey: 'channel.adapter.awsSes', labelFallback: 'AWS SES' },\n {\n value: 'AZURE_ACS',\n labelKey: 'channel.adapter.azureAcs',\n labelFallback: 'Azure Communication Services',\n },\n ],\n },\n {\n value: 'SMS',\n labelKey: 'channel.platformLabel.sms',\n labelFallback: 'SMS',\n descriptionKey: 'channel.platformDescription.smsDescription',\n descriptionFallback: 'Send text messages',\n icon: Smartphone,\n color:\n 'text-status-success-text bg-status-success-bg-subtle border-status-success-bg-subtle',\n adapters: [\n { value: 'AWS_SNS', labelKey: 'channel.adapter.awsSns', labelFallback: 'AWS SNS' },\n {\n value: 'TWILIO_SMS',\n labelKey: 'channel.adapter.twilioSms',\n labelFallback: 'Twilio SMS',\n },\n ],\n },\n {\n value: 'PUSH',\n labelKey: 'channel.platformLabel.push',\n labelFallback: 'Push',\n descriptionKey: 'channel.platformDescription.pushDescription',\n descriptionFallback: 'Mobile & web push',\n icon: Bell,\n color: 'text-accent-purple bg-accent-purple-subtle border-accent-purple-subtle',\n adapters: [\n {\n value: 'FCM',\n labelKey: 'channel.adapter.fcm',\n labelFallback: 'Firebase Cloud Messaging',\n },\n ],\n },\n {\n value: 'INAPP',\n labelKey: 'channel.platformLabel.inApp',\n labelFallback: 'In-App',\n descriptionKey: 'channel.platformDescription.inAppDescription',\n descriptionFallback: 'In-app notifications',\n icon: Inbox,\n color: 'text-accent-indigo bg-accent-indigo-subtle border-accent-indigo-subtle',\n adapters: [\n {\n value: 'INAPP_NATIVE',\n labelKey: 'channel.adapter.inAppNative',\n labelFallback: 'Native In-App',\n },\n ],\n },\n {\n value: 'SLACK',\n labelKey: 'channel.platformLabel.slack',\n labelFallback: 'Slack',\n descriptionKey: 'channel.platformDescription.slackDescription',\n descriptionFallback: 'Workspace notifications',\n icon: Hash,\n color: 'text-accent-pink bg-accent-pink-subtle border-accent-pink-subtle',\n adapters: [\n { value: 'SLACK_BOT', labelKey: 'channel.adapter.slackBot', labelFallback: 'Slack Bot' },\n ],\n },\n {\n value: 'TEAMS',\n labelKey: 'channel.platformLabel.teams',\n labelFallback: 'Teams',\n descriptionKey: 'channel.platformDescription.teamsDescription',\n descriptionFallback: 'Teams notifications',\n icon: Users,\n color: 'text-accent-cyan bg-accent-cyan-subtle border-accent-cyan-subtle',\n adapters: [\n { value: 'TEAMS_BOT', labelKey: 'channel.adapter.teamsBot', labelFallback: 'Teams Bot' },\n ],\n },\n {\n value: 'DISCORD',\n labelKey: 'channel.platformLabel.discord',\n labelFallback: 'Discord',\n descriptionKey: 'channel.platformDescription.discordDescription',\n descriptionFallback: 'Server notifications',\n icon: MessageCircle,\n color: 'text-accent-violet bg-accent-violet-subtle border-accent-violet-subtle',\n adapters: [\n {\n value: 'DISCORD_BOT',\n labelKey: 'channel.adapter.discordBot',\n labelFallback: 'Discord Bot',\n },\n ],\n },\n {\n value: 'WHATSAPP',\n labelKey: 'channel.platformLabel.whatsapp',\n labelFallback: 'WhatsApp',\n descriptionKey: 'channel.platformDescription.whatsappDescription',\n descriptionFallback: 'Business messaging',\n icon: Send,\n color: 'text-accent-emerald bg-accent-emerald-subtle border-accent-emerald-subtle',\n adapters: [\n {\n value: 'WHATSAPP_CLOUD',\n labelKey: 'channel.adapter.whatsappCloud',\n labelFallback: 'WhatsApp Cloud API',\n },\n {\n value: 'WHATSAPP_TWILIO',\n labelKey: 'channel.adapter.whatsappTwilio',\n labelFallback: 'WhatsApp via Twilio',\n },\n ],\n },\n {\n value: 'WEBCHAT',\n labelKey: 'channel.platformLabel.webchat',\n labelFallback: 'Webchat',\n descriptionKey: 'channel.platformDescription.webchatDescription',\n descriptionFallback: 'Chat widgets',\n icon: MessageSquare,\n color: 'text-accent-teal bg-accent-teal-subtle border-accent-teal-subtle',\n adapters: [], // No WEBCHAT adapters in backend Prisma schema yet\n },\n {\n value: 'WEBHOOK',\n labelKey: 'channel.platformLabel.webhook',\n labelFallback: 'Webhook',\n descriptionKey: 'channel.platformDescription.webhookDescription',\n descriptionFallback: 'HTTP webhooks',\n icon: Globe,\n color: 'text-accent-orange bg-accent-orange-subtle border-accent-orange-subtle',\n adapters: [\n {\n value: 'WEBHOOK_HTTP',\n labelKey: 'channel.adapter.webhookHttp',\n labelFallback: 'HTTP Webhook',\n },\n ],\n },\n ],\n []\n );\n\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Form state — matches CreateChannelInput exactly\n const [formData, setFormData] = useState<{\n name: string; // required\n platform: ChannelPlatform; // required\n description: string; // optional\n adapterType: AdapterType | ''; // required (auto-selected based on platform)\n status: ChannelStatus; // optional — default ACTIVE on server\n priority: number; // optional — default 100 on server\n platformConfig: string; // optional — JSON string\n }>({\n name: '',\n platform: 'EMAIL',\n description: '',\n adapterType: 'AWS_SES', // Default to AWS SES for Email platform\n status: 'ACTIVE',\n priority: 100,\n platformConfig: '',\n });\n\n const availableAdapters =\n PLATFORM_OPTIONS.find((p) => p.value === formData.platform)?.adapters || [];\n\n const selectedPlatform = PLATFORM_OPTIONS.find((p) => p.value === formData.platform);\n\n const handlePlatformChange = (platform: ChannelPlatform) => {\n setFormData((prev) => ({ ...prev, platform, adapterType: '' }));\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!formData.name.trim()) {\n setError(tr('channel.validation.nameRequired', 'Channel name is required'));\n return;\n }\n\n if (!organizationId) {\n setError(\n tr(\n 'channel.validation.organizationRequired',\n 'Organization context is required. Please ensure an organization is selected.'\n )\n );\n return;\n }\n\n // Validate platformConfig JSON if provided\n let parsedPlatformConfig: Record<string, unknown> | undefined;\n if (formData.platformConfig.trim()) {\n try {\n parsedPlatformConfig = JSON.parse(formData.platformConfig);\n } catch {\n setError(tr('channel.validation.platformConfigJson', 'Platform Config must be valid JSON'));\n return;\n }\n }\n\n setIsSubmitting(true);\n try {\n const input: CreateChannelInput = {\n name: formData.name.trim(),\n platform: formData.platform,\n organizationId,\n description: formData.description.trim() || undefined,\n adapterType: (formData.adapterType as AdapterType) || undefined,\n status: formData.status,\n priority: formData.priority,\n platformConfig: parsedPlatformConfig,\n };\n\n const result = await createChannel(input);\n if (result) {\n navigate(`../${result.id}`);\n } else {\n setError(tr('channel.createFailed', 'Failed to create channel'));\n }\n } catch (err: unknown) {\n setError(\n err instanceof Error ? err.message : tr('channel.createFailed', 'Failed to create channel')\n );\n } finally {\n setIsSubmitting(false);\n }\n };\n\n // ── Render ────────────────────────────────────────────────────────────\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CREATE}>\n <div className=\"p-3 sm:p-6 space-y-6 max-w-5xl mx-auto\">\n {/* ── Header ───────────────────────────────────────────────────── */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-surface-hover rounded-lg transition-colors\"\n >\n <ArrowLeft size={20} />\n </button>\n <div>\n <h1 className=\"text-xl font-bold text-text-primary flex items-center gap-2\">\n <Radio size={22} className=\"text-accent-primary\" />\n {tr('channel.createChannel', 'Create Channel')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {tr('channel.createChannelSubtitle', 'Set up a new communication channel')}\n </p>\n </div>\n </div>\n </div>\n\n {/* ── Page purpose ─────────────────────────────────────────────── */}\n <PagePurpose icon={<Radio size={15} aria-hidden=\"true\" />}>\n {tr(\n 'channel.createChannelPurpose',\n 'Set up a new way to reach people — for example a transactional email sender, an SMS gateway, a Slack workspace or a push provider. Name it, pick the platform and provider, and you will get a channel you can then authenticate and send through. Create one channel per provider account you want messages to flow through.'\n )}\n </PagePurpose>\n\n {/* ── Error ────────────────────────────────────────────────────── */}\n {error && (\n <div className=\"flex items-center gap-3 p-3 bg-error-500/10 border border-status-error-border/30 rounded-lg\">\n <AlertCircle size={16} className=\"text-status-error-text shrink-0\" />\n <p className=\"text-sm text-error-400\">{error}</p>\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* ── Row 1: Name + Description | Status + Provider (2-col) ─── */}\n <div className=\"grid gap-5 lg:grid-cols-2\">\n {/* Left: Name & Description — the primary identity of the channel */}\n <EmphasisPanel className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary uppercase tracking-wider\">\n {tr('channel.basicInformation', 'Basic Information')}\n </h2>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {tr('channel.name', 'Name')} <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n aria-label={tr('channel.namePlaceholder', 'e.g., Primary Email, SMS Alerts')}\n type=\"text\"\n value={formData.name}\n onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 placeholder:text-text-secondary\"\n placeholder={tr('channel.namePlaceholder', 'e.g., Primary Email, SMS Alerts')}\n />\n </div>\n\n <div>\n <label\n htmlFor=\"channel-create-description\"\n className=\"block text-sm font-medium text-text-primary mb-1.5\"\n >\n {tr('channel.description', 'Description')}\n </label>\n <MarkdownEditor\n id=\"channel-create-description\"\n value={formData.description}\n onChange={(next) => setFormData((prev) => ({ ...prev, description: next }))}\n aria-label={tr(\n 'channel.descriptionPlaceholder',\n \"Optional description of this channel's purpose\"\n )}\n placeholder={tr(\n 'channel.descriptionPlaceholder',\n \"Optional description of this channel's purpose\"\n )}\n rows={3}\n />\n </div>\n </EmphasisPanel>\n\n {/* Right: Status + Provider / Adapter */}\n <GlassCard treatment=\"plain\" className=\"p-5 border-border-seam space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary uppercase tracking-wider\">\n {tr('channel.providerStatus', 'Provider & Status')}\n </h2>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {tr('channel.providerAdapter', 'Provider / Adapter')}\n </label>\n <select\n value={formData.adapterType}\n onChange={(e) =>\n setFormData((prev) => ({\n ...prev,\n adapterType: e.target.value as AdapterType | '',\n }))\n }\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">\n {tr('channel.selectProvider', 'Select a provider (optional)')}\n </option>\n {availableAdapters.map((a) => (\n <option key={a.value} value={a.value}>\n {tr(a.labelKey, a.labelFallback)}\n </option>\n ))}\n </select>\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n {selectedPlatform\n ? tr(\n 'channel.providersAvailableForPlatform',\n '{{count}} providers available for {{platform}}',\n {\n count: availableAdapters.length,\n platform: tr(selectedPlatform.labelKey, selectedPlatform.labelFallback),\n }\n )\n : tr('channel.selectPlatformFirst', 'Select a platform first')}\n </p>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {tr('channel.status', 'Status')}\n </label>\n <select\n value={formData.status}\n onChange={(e) =>\n setFormData((prev) => ({ ...prev, status: e.target.value as ChannelStatus }))\n }\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"ACTIVE\">{tr('channel.statusLabel.active', 'Active')}</option>\n <option value=\"INACTIVE\">\n {tr('channel.statusLabel.inactive', 'Inactive')}\n </option>\n </select>\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n {formData.status === 'ACTIVE'\n ? tr('channel.statusHint.active', 'Ready to send immediately')\n : tr('channel.statusHint.inactive', 'Configure before activating')}\n </p>\n </div>\n\n <div>\n <label\n htmlFor=\"create-channel-priority\"\n className=\"block text-sm font-medium text-text-primary mb-1.5\"\n >\n {tr('channel.priority', 'Priority')}\n </label>\n <input\n id=\"create-channel-priority\"\n aria-label={tr('channel.priority', 'Priority')}\n type=\"number\"\n value={formData.priority}\n onChange={(e) =>\n setFormData((prev) => ({\n ...prev,\n priority: parseInt(e.target.value) || 0,\n }))\n }\n min={0}\n max={100}\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50\"\n />\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n {tr('channel.priorityHint', 'Lower = higher priority (0-100)')}\n </p>\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* ── Row 2: Platform selection (full width) ─────────────────── */}\n <GlassCard treatment=\"plain\" className=\"p-5 border-border-seam space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary uppercase tracking-wider\">\n {tr('channel.platform', 'Platform')} <span className=\"text-status-error-text\">*</span>\n </h2>\n\n <div className=\"grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-5\">\n {PLATFORM_OPTIONS.map((platform) => {\n const Icon = platform.icon;\n const isSelected = formData.platform === platform.value;\n return (\n <button\n key={platform.value}\n type=\"button\"\n onClick={() => handlePlatformChange(platform.value)}\n className={`relative flex flex-col items-center gap-1.5 p-3 rounded-lg border transition-all text-center ${\n isSelected\n ? `${platform.color} border-current ring-1 ring-current`\n : 'border-border-subtle hover:bg-surface-hover text-text-secondary hover:text-text-primary'\n }`}\n >\n {isSelected && (\n <CheckCircle2 size={14} className=\"absolute top-1.5 right-1.5 text-current\" />\n )}\n <Icon size={20} />\n <span className=\"text-xs font-semibold\">\n {tr(platform.labelKey, platform.labelFallback)}\n </span>\n <span className=\"text-[10px] leading-tight opacity-70\">\n {tr(platform.descriptionKey, platform.descriptionFallback)}\n </span>\n </button>\n );\n })}\n </div>\n </GlassCard>\n\n {/* ── Row 3: Advanced (collapsible) — webhook config / delivery / retry policy (platformConfig) */}\n <OptionalFields title={tr('channel.advancedOptions', 'Advanced settings')}>\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {tr('channel.platformConfig', 'Platform Config')}{' '}\n <span className=\"text-[10px] text-text-secondary\">\n {tr('channel.jsonMarker', '(JSON)')}\n </span>\n </label>\n <textarea\n aria-label={tr('channel.platformConfig', 'Platform Config')}\n value={formData.platformConfig}\n onChange={(e) =>\n setFormData((prev) => ({ ...prev, platformConfig: e.target.value }))\n }\n rows={3}\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 resize-none font-mono placeholder:text-text-secondary\"\n placeholder={tr(\n 'channel.platformConfigPlaceholder',\n '{\"fromEmail\": \"noreply@example.com\"}'\n )}\n />\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n {tr('channel.platformConfigHint', 'Platform-specific configuration as JSON')}\n </p>\n </div>\n </div>\n </OptionalFields>\n\n {/* ── Actions ────────────────────────────────────────────────── */}\n <div className=\"flex items-center justify-between pt-2\">\n <button\n type=\"button\"\n onClick={() => navigate('/channels/list')}\n className=\"px-4 py-2 text-sm text-text-secondary hover:text-text-primary hover:bg-surface-hover border border-border-subtle rounded-lg transition-colors\"\n >\n {tr('channel.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting || !formData.name.trim()}\n className=\"inline-flex items-center gap-2 px-5 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isSubmitting ? <Loader2 size={16} className=\"animate-spin\" /> : <Save size={16} />}\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n </div>\n </form>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAmEA,SAAgB,IAAoB;CAClC,IAAM,IAAW,GAAa,EACxB,EAAE,sBAAmB,GAAY,EACjC,EAAE,qBAAkB,GAAa,EACjC,IAAK,GAAO,EAEZ,IAAqC,QACnC;EACJ;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,CACR;IAAE,OAAO;IAAW,UAAU;IAA0B,eAAe;IAAW,EAClF;IACE,OAAO;IACP,UAAU;IACV,eAAe;IAChB,CACF;GACF;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OACE;GACF,UAAU,CACR;IAAE,OAAO;IAAW,UAAU;IAA0B,eAAe;IAAW,EAClF;IACE,OAAO;IACP,UAAU;IACV,eAAe;IAChB,CACF;GACF;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,CACR;IACE,OAAO;IACP,UAAU;IACV,eAAe;IAChB,CACF;GACF;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,CACR;IACE,OAAO;IACP,UAAU;IACV,eAAe;IAChB,CACF;GACF;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,CACR;IAAE,OAAO;IAAa,UAAU;IAA4B,eAAe;IAAa,CACzF;GACF;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,CACR;IAAE,OAAO;IAAa,UAAU;IAA4B,eAAe;IAAa,CACzF;GACF;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,CACR;IACE,OAAO;IACP,UAAU;IACV,eAAe;IAChB,CACF;GACF;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,CACR;IACE,OAAO;IACP,UAAU;IACV,eAAe;IAChB,EACD;IACE,OAAO;IACP,UAAU;IACV,eAAe;IAChB,CACF;GACF;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,EAAE;GACb;EACD;GACE,OAAO;GACP,UAAU;GACV,eAAe;GACf,gBAAgB;GAChB,qBAAqB;GACrB,MAAM;GACN,OAAO;GACP,UAAU,CACR;IACE,OAAO;IACP,UAAU;IACV,eAAe;IAChB,CACF;GACF;EACF,EACD,EAAE,CACH,EAEK,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,GAAO,KAAY,EAAwB,KAAK,EAGjD,CAAC,GAAU,KAAe,EAQ7B;EACD,MAAM;EACN,UAAU;EACV,aAAa;EACb,aAAa;EACb,QAAQ;EACR,UAAU;EACV,gBAAgB;EACjB,CAAC,EAEI,IACJ,EAAiB,MAAM,MAAM,EAAE,UAAU,EAAS,SAAS,EAAE,YAAY,EAAE,EAEvE,IAAmB,EAAiB,MAAM,MAAM,EAAE,UAAU,EAAS,SAAS,EAE9E,KAAwB,MAA8B;AAC1D,KAAa,OAAU;GAAE,GAAG;GAAM;GAAU,aAAa;GAAI,EAAE;;AA8DjE,QACE,kBAAC,GAAD;EAAiB,YAAY,EAAoB;YAC/C,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,GAAG;OAC3B,WAAU;iBAEV,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;OAChB,CAAA,EACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAd,CACE,kBAAC,GAAD;QAAO,MAAM;QAAI,WAAU;QAAwB,CAAA,EAClD,EAAG,yBAAyB,iBAAiB,CAC3C;UACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,iCAAiC,qCAAqC;OACxE,CAAA,CACA,EAAA,CAAA,CACF;;KACF,CAAA;IAGN,kBAAC,GAAD;KAAa,MAAM,kBAAC,GAAD;MAAO,MAAM;MAAI,eAAY;MAAS,CAAA;eACtD,EACC,gCACA,gUACD;KACW,CAAA;IAGb,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAa,MAAM;MAAI,WAAU;MAAoC,CAAA,EACrE,kBAAC,KAAD;MAAG,WAAU;gBAA0B;MAAU,CAAA,CAC7C;;IAGR,kBAAC,QAAD;KAAM,UApGS,OAAO,MAAuB;AAIjD,UAHA,EAAE,gBAAgB,EAClB,EAAS,KAAK,EAEV,CAAC,EAAS,KAAK,MAAM,EAAE;AACzB,SAAS,EAAG,mCAAmC,2BAA2B,CAAC;AAC3E;;AAGF,UAAI,CAAC,GAAgB;AACnB,SACE,EACE,2CACA,+EACD,CACF;AACD;;MAIF,IAAI;AACJ,UAAI,EAAS,eAAe,MAAM,CAChC,KAAI;AACF,WAAuB,KAAK,MAAM,EAAS,eAAe;cACpD;AACN,SAAS,EAAG,yCAAyC,qCAAqC,CAAC;AAC3F;;AAIJ,QAAgB,GAAK;AACrB,UAAI;OAYF,IAAM,IAAS,MAAM,EAXa;QAChC,MAAM,EAAS,KAAK,MAAM;QAC1B,UAAU,EAAS;QACnB;QACA,aAAa,EAAS,YAAY,MAAM,IAAI,KAAA;QAC5C,aAAc,EAAS,eAA+B,KAAA;QACtD,QAAQ,EAAS;QACjB,UAAU,EAAS;QACnB,gBAAgB;QACjB,CAEwC;AACzC,OAAI,IACF,EAAS,MAAM,EAAO,KAAK,GAE3B,EAAS,EAAG,wBAAwB,2BAA2B,CAAC;eAE3D,GAAc;AACrB,SACE,aAAe,QAAQ,EAAI,UAAU,EAAG,wBAAwB,2BAA2B,CAC5F;gBACO;AACR,SAAgB,GAAM;;;KA8CU,WAAU;eAAxC;MAEE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAEE,kBAAC,GAAD;QAAe,WAAU;kBAAzB;SACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,4BAA4B,oBAAoB;UACjD,CAAA;SAEL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UAAO,WAAU;oBAAjB;WACG,EAAG,gBAAgB,OAAO;WAAC;WAAC,kBAAC,QAAD;YAAM,WAAU;sBAAyB;YAAQ,CAAA;WACxE;aACR,kBAAC,SAAD;UACE,cAAY,EAAG,2BAA2B,kCAAkC;UAC5E,MAAK;UACL,OAAO,EAAS;UAChB,WAAW,MAAM,GAAa,OAAU;WAAE,GAAG;WAAM,MAAM,EAAE,OAAO;WAAO,EAAE;UAC3E,WAAU;UACV,aAAa,EAAG,2BAA2B,kCAAkC;UAC7E,CAAA,CACE,EAAA,CAAA;SAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UACE,SAAQ;UACR,WAAU;oBAET,EAAG,uBAAuB,cAAc;UACnC,CAAA,EACR,kBAAC,GAAD;UACE,IAAG;UACH,OAAO,EAAS;UAChB,WAAW,MAAS,GAAa,OAAU;WAAE,GAAG;WAAM,aAAa;WAAM,EAAE;UAC3E,cAAY,EACV,kCACA,iDACD;UACD,aAAa,EACX,kCACA,iDACD;UACD,MAAM;UACN,CAAA,CACE,EAAA,CAAA;SACQ;WAGhB,kBAAC,GAAD;QAAW,WAAU;QAAQ,WAAU;kBAAvC;SACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,0BAA0B,oBAAoB;UAC/C,CAAA;SAEL,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,SAAD;WAAO,WAAU;qBACd,EAAG,2BAA2B,qBAAqB;WAC9C,CAAA;UACR,kBAAC,UAAD;WACE,OAAO,EAAS;WAChB,WAAW,MACT,GAAa,OAAU;YACrB,GAAG;YACH,aAAa,EAAE,OAAO;YACvB,EAAE;WAEL,WAAU;qBARZ,CAUE,kBAAC,UAAD;YAAQ,OAAM;sBACX,EAAG,0BAA0B,+BAA+B;YACtD,CAAA,EACR,EAAkB,KAAK,MACtB,kBAAC,UAAD;YAAsB,OAAO,EAAE;sBAC5B,EAAG,EAAE,UAAU,EAAE,cAAc;YACzB,EAFI,EAAE,MAEN,CACT,CACK;;UACT,kBAAC,KAAD;WAAG,WAAU;qBACV,IACG,EACE,yCACA,kDACA;YACE,OAAO,EAAkB;YACzB,UAAU,EAAG,EAAiB,UAAU,EAAiB,cAAc;YACxE,CACF,GACD,EAAG,+BAA+B,0BAA0B;WAC9D,CAAA;UACA,EAAA,CAAA;SAEN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD,EAAA,UAAA;WACE,kBAAC,SAAD;YAAO,WAAU;sBACd,EAAG,kBAAkB,SAAS;YACzB,CAAA;WACR,kBAAC,UAAD;YACE,OAAO,EAAS;YAChB,WAAW,MACT,GAAa,OAAU;aAAE,GAAG;aAAM,QAAQ,EAAE,OAAO;aAAwB,EAAE;YAE/E,WAAU;sBALZ,CAOE,kBAAC,UAAD;aAAQ,OAAM;uBAAU,EAAG,8BAA8B,SAAS;aAAU,CAAA,EAC5E,kBAAC,UAAD;aAAQ,OAAM;uBACX,EAAG,gCAAgC,WAAW;aACxC,CAAA,CACF;;WACT,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAS,WAAW,WACjB,EAAG,6BAA6B,4BAA4B,GAC5D,EAAG,+BAA+B,8BAA8B;YAClE,CAAA;WACA,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA;WACE,kBAAC,SAAD;YACE,SAAQ;YACR,WAAU;sBAET,EAAG,oBAAoB,WAAW;YAC7B,CAAA;WACR,kBAAC,SAAD;YACE,IAAG;YACH,cAAY,EAAG,oBAAoB,WAAW;YAC9C,MAAK;YACL,OAAO,EAAS;YAChB,WAAW,MACT,GAAa,OAAU;aACrB,GAAG;aACH,UAAU,SAAS,EAAE,OAAO,MAAM,IAAI;aACvC,EAAE;YAEL,KAAK;YACL,KAAK;YACL,WAAU;YACV,CAAA;WACF,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAG,wBAAwB,kCAAkC;YAC5D,CAAA;WACA,EAAA,CAAA,CACF;;SACI;UACR;;MAGN,kBAAC,GAAD;OAAW,WAAU;OAAQ,WAAU;iBAAvC,CACE,kBAAC,MAAD;QAAI,WAAU;kBAAd;SACG,EAAG,oBAAoB,WAAW;SAAC;SAAC,kBAAC,QAAD;UAAM,WAAU;oBAAyB;UAAQ,CAAA;SACnF;WAEL,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAiB,KAAK,MAAa;SAClC,IAAM,IAAO,EAAS,MAChB,IAAa,EAAS,aAAa,EAAS;AAClD,gBACE,kBAAC,UAAD;UAEE,MAAK;UACL,eAAe,EAAqB,EAAS,MAAM;UACnD,WAAW,gGACT,IACI,GAAG,EAAS,MAAM,uCAClB;oBAPR;WAUG,KACC,kBAAC,GAAD;YAAc,MAAM;YAAI,WAAU;YAA4C,CAAA;WAEhF,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;WAClB,kBAAC,QAAD;YAAM,WAAU;sBACb,EAAG,EAAS,UAAU,EAAS,cAAc;YACzC,CAAA;WACP,kBAAC,QAAD;YAAM,WAAU;sBACb,EAAG,EAAS,gBAAgB,EAAS,oBAAoB;YACrD,CAAA;WACA;YAnBF,EAAS,MAmBP;UAEX;QACE,CAAA,CACI;;MAGZ,kBAAC,GAAD;OAAgB,OAAO,EAAG,2BAA2B,oBAAoB;iBACvE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD,EAAA,UAAA;SACE,kBAAC,SAAD;UAAO,WAAU;oBAAjB;WACG,EAAG,0BAA0B,kBAAkB;WAAE;WAClD,kBAAC,QAAD;YAAM,WAAU;sBACb,EAAG,sBAAsB,SAAS;YAC9B,CAAA;WACD;;SACR,kBAAC,YAAD;UACE,cAAY,EAAG,0BAA0B,kBAAkB;UAC3D,OAAO,EAAS;UAChB,WAAW,MACT,GAAa,OAAU;WAAE,GAAG;WAAM,gBAAgB,EAAE,OAAO;WAAO,EAAE;UAEtE,MAAM;UACN,WAAU;UACV,aAAa,EACX,qCACA,2CACD;UACD,CAAA;SACF,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAG,8BAA8B,0CAA0C;UAC1E,CAAA;SACA,EAAA,CAAA;QACF,CAAA;OACS,CAAA;MAGjB,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,iBAAiB;QACzC,WAAU;kBAET,EAAG,kBAAkB,SAAS;QACxB,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,UAAU,KAAgB,CAAC,EAAS,KAAK,MAAM;QAC/C,WAAU;kBAHZ,CAKG,IAAe,kBAAC,GAAD;SAAS,MAAM;SAAI,WAAU;SAAiB,CAAA,GAAG,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAClF,EAAG,yBAAyB,iBAAiB,CACvC;UACL;;MACD;;IACH;;EACU,CAAA"}
1
+ {"version":3,"file":"CreateChannelPage.js","names":["CreateChannelPage","navigate","useNavigate","useChannel","useChannels","tr","useTr","PLATFORM_OPTIONS","useMemo","Mail","Smartphone","Bell","Inbox","Hash","Users","MessageCircle","Send","MessageSquare","Globe","isSubmitting","setIsSubmitting","useState","error","setError","formData","setFormData","availableAdapters","p","selectedPlatform","handlePlatformChange","prev","PermissionGuard","CHANNEL_PERMISSIONS","ArrowLeft","Radio","PagePurpose","AlertCircle","organizationId","parsedPlatformConfig","input","result","createChannel","err","EmphasisPanel","MarkdownEditor","next","GlassCard","a","Icon","platform","isSelected","CheckCircle2","OptionalFields","Loader2","Save"],"sources":["../../../../../src/channel/modules/channels/pages/CreateChannelPage.tsx"],"sourcesContent":["/**\n * Create Channel Page\n *\n * Form fields match the exact CreateChannelInput from global-channel-svc:\n * name (required), platform (required), organizationId (auto), description,\n * adapterType, status, priority, platformConfig\n *\n * organizationId is injected from shell context — not shown in form.\n * Credentials belong to createConnection mutation — not part of channel creation.\n */\n\nimport { useState, useMemo } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useTr } from '../../../hooks/useTr';\nimport { EmphasisPanel, GlassCard, OptionalFields, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport {\n Radio,\n ArrowLeft,\n Save,\n Loader2,\n AlertCircle,\n Mail,\n Smartphone,\n Bell,\n Inbox,\n Globe,\n Hash,\n Users,\n MessageCircle,\n Send,\n MessageSquare,\n CheckCircle2,\n} from 'lucide-react';\nimport { useChannel } from '../../../providers/ChannelProvider';\nimport {\n useChannels,\n type ChannelPlatform,\n type AdapterType,\n type ChannelStatus,\n type CreateChannelInput,\n} from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { MarkdownEditor } from '../../../components/MarkdownEditor';\n\n// ============================================================================\n// PLATFORM CONFIG\n// ============================================================================\n\n// NOTE: AdapterType values MUST match the backend Prisma schema exactly.\n// See: global-channel-svc/prisma/schema/schema.prisma (enum AdapterType)\n\ntype PlatformOption = {\n value: ChannelPlatform;\n labelKey: string;\n labelFallback: string;\n descriptionKey: string;\n descriptionFallback: string;\n icon: typeof Mail;\n color: string;\n adapters: { value: AdapterType; labelKey: string; labelFallback: string }[];\n};\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport function CreateChannelPage() {\n const navigate = useNavigate();\n const { organizationId } = useChannel();\n const { createChannel } = useChannels();\n const tr = useTr();\n\n const PLATFORM_OPTIONS: PlatformOption[] = useMemo(\n () => [\n {\n value: 'EMAIL',\n labelKey: 'channel.platformLabel.email',\n labelFallback: 'Email',\n descriptionKey: 'channel.platformDescription.emailDescription',\n descriptionFallback: 'Send emails via various providers',\n icon: Mail,\n color: 'text-accent-blue bg-accent-blue-subtle border-accent-blue-subtle',\n adapters: [\n { value: 'AWS_SES', labelKey: 'channel.adapter.awsSes', labelFallback: 'AWS SES' },\n {\n value: 'AZURE_ACS',\n labelKey: 'channel.adapter.azureAcs',\n labelFallback: 'Azure Communication Services',\n },\n ],\n },\n {\n value: 'SMS',\n labelKey: 'channel.platformLabel.sms',\n labelFallback: 'SMS',\n descriptionKey: 'channel.platformDescription.smsDescription',\n descriptionFallback: 'Send text messages',\n icon: Smartphone,\n color:\n 'text-status-success-text bg-status-success-bg-subtle border-status-success-bg-subtle',\n adapters: [\n { value: 'AWS_SNS', labelKey: 'channel.adapter.awsSns', labelFallback: 'AWS SNS' },\n {\n value: 'TWILIO_SMS',\n labelKey: 'channel.adapter.twilioSms',\n labelFallback: 'Twilio SMS',\n },\n ],\n },\n {\n value: 'PUSH',\n labelKey: 'channel.platformLabel.push',\n labelFallback: 'Push',\n descriptionKey: 'channel.platformDescription.pushDescription',\n descriptionFallback: 'Mobile & web push',\n icon: Bell,\n color: 'text-accent-purple bg-accent-purple-subtle border-accent-purple-subtle',\n adapters: [\n {\n value: 'FCM',\n labelKey: 'channel.adapter.fcm',\n labelFallback: 'Firebase Cloud Messaging',\n },\n ],\n },\n {\n value: 'INAPP',\n labelKey: 'channel.platformLabel.inApp',\n labelFallback: 'In-App',\n descriptionKey: 'channel.platformDescription.inAppDescription',\n descriptionFallback: 'In-app notifications',\n icon: Inbox,\n color: 'text-accent-indigo bg-accent-indigo-subtle border-accent-indigo-subtle',\n adapters: [\n {\n value: 'INAPP_NATIVE',\n labelKey: 'channel.adapter.inAppNative',\n labelFallback: 'Native In-App',\n },\n ],\n },\n {\n value: 'SLACK',\n labelKey: 'channel.platformLabel.slack',\n labelFallback: 'Slack',\n descriptionKey: 'channel.platformDescription.slackDescription',\n descriptionFallback: 'Workspace notifications',\n icon: Hash,\n color: 'text-accent-pink bg-accent-pink-subtle border-accent-pink-subtle',\n adapters: [\n { value: 'SLACK_BOT', labelKey: 'channel.adapter.slackBot', labelFallback: 'Slack Bot' },\n ],\n },\n {\n value: 'TEAMS',\n labelKey: 'channel.platformLabel.teams',\n labelFallback: 'Teams',\n descriptionKey: 'channel.platformDescription.teamsDescription',\n descriptionFallback: 'Teams notifications',\n icon: Users,\n color: 'text-accent-cyan bg-accent-cyan-subtle border-accent-cyan-subtle',\n adapters: [\n { value: 'TEAMS_BOT', labelKey: 'channel.adapter.teamsBot', labelFallback: 'Teams Bot' },\n ],\n },\n {\n value: 'DISCORD',\n labelKey: 'channel.platformLabel.discord',\n labelFallback: 'Discord',\n descriptionKey: 'channel.platformDescription.discordDescription',\n descriptionFallback: 'Server notifications',\n icon: MessageCircle,\n color: 'text-accent-violet bg-accent-violet-subtle border-accent-violet-subtle',\n adapters: [\n {\n value: 'DISCORD_BOT',\n labelKey: 'channel.adapter.discordBot',\n labelFallback: 'Discord Bot',\n },\n ],\n },\n {\n value: 'WHATSAPP',\n labelKey: 'channel.platformLabel.whatsapp',\n labelFallback: 'WhatsApp',\n descriptionKey: 'channel.platformDescription.whatsappDescription',\n descriptionFallback: 'Business messaging',\n icon: Send,\n color: 'text-accent-emerald bg-accent-emerald-subtle border-accent-emerald-subtle',\n adapters: [\n {\n value: 'WHATSAPP_CLOUD',\n labelKey: 'channel.adapter.whatsappCloud',\n labelFallback: 'WhatsApp Cloud API',\n },\n {\n value: 'WHATSAPP_TWILIO',\n labelKey: 'channel.adapter.whatsappTwilio',\n labelFallback: 'WhatsApp via Twilio',\n },\n ],\n },\n {\n value: 'WEBCHAT',\n labelKey: 'channel.platformLabel.webchat',\n labelFallback: 'Webchat',\n descriptionKey: 'channel.platformDescription.webchatDescription',\n descriptionFallback: 'Chat widgets',\n icon: MessageSquare,\n color: 'text-accent-teal bg-accent-teal-subtle border-accent-teal-subtle',\n adapters: [], // No WEBCHAT adapters in backend Prisma schema yet\n },\n {\n value: 'WEBHOOK',\n labelKey: 'channel.platformLabel.webhook',\n labelFallback: 'Webhook',\n descriptionKey: 'channel.platformDescription.webhookDescription',\n descriptionFallback: 'HTTP webhooks',\n icon: Globe,\n color: 'text-accent-orange bg-accent-orange-subtle border-accent-orange-subtle',\n adapters: [\n {\n value: 'WEBHOOK_HTTP',\n labelKey: 'channel.adapter.webhookHttp',\n labelFallback: 'HTTP Webhook',\n },\n ],\n },\n ],\n []\n );\n\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Form state — matches CreateChannelInput exactly\n const [formData, setFormData] = useState<{\n name: string; // required\n platform: ChannelPlatform; // required\n description: string; // optional\n adapterType: AdapterType | ''; // required (auto-selected based on platform)\n status: ChannelStatus; // optional — default ACTIVE on server\n priority: number; // optional — default 100 on server\n platformConfig: string; // optional — JSON string\n }>({\n name: '',\n platform: 'EMAIL',\n description: '',\n adapterType: 'AWS_SES', // Default to AWS SES for Email platform\n status: 'ACTIVE',\n priority: 100,\n platformConfig: '',\n });\n\n const availableAdapters =\n PLATFORM_OPTIONS.find((p) => p.value === formData.platform)?.adapters || [];\n\n const selectedPlatform = PLATFORM_OPTIONS.find((p) => p.value === formData.platform);\n\n const handlePlatformChange = (platform: ChannelPlatform) => {\n setFormData((prev) => ({ ...prev, platform, adapterType: '' }));\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!formData.name.trim()) {\n setError(tr('channel.validation.nameRequired', 'Channel name is required'));\n return;\n }\n\n if (!organizationId) {\n setError(\n tr(\n 'channel.validation.organizationRequired',\n 'Organization context is required. Please ensure an organization is selected.'\n )\n );\n return;\n }\n\n // Validate platformConfig JSON if provided\n let parsedPlatformConfig: Record<string, unknown> | undefined;\n if (formData.platformConfig.trim()) {\n try {\n parsedPlatformConfig = JSON.parse(formData.platformConfig);\n } catch {\n setError(tr('channel.validation.platformConfigJson', 'Platform Config must be valid JSON'));\n return;\n }\n }\n\n setIsSubmitting(true);\n try {\n const input: CreateChannelInput = {\n name: formData.name.trim(),\n platform: formData.platform,\n organizationId,\n description: formData.description.trim() || undefined,\n adapterType: (formData.adapterType as AdapterType) || undefined,\n status: formData.status,\n priority: formData.priority,\n platformConfig: parsedPlatformConfig,\n };\n\n const result = await createChannel(input);\n if (result) {\n navigate(`../${result.id}`);\n } else {\n setError(tr('channel.createFailed', 'Failed to create channel'));\n }\n } catch (err: unknown) {\n setError(\n err instanceof Error ? err.message : tr('channel.createFailed', 'Failed to create channel')\n );\n } finally {\n setIsSubmitting(false);\n }\n };\n\n // ── Render ────────────────────────────────────────────────────────────\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CREATE}>\n <div className=\"p-3 sm:p-6 space-y-6 max-w-5xl mx-auto\">\n {/* ── Header ───────────────────────────────────────────────────── */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-surface-hover rounded-lg transition-colors\"\n >\n <ArrowLeft size={20} />\n </button>\n <div>\n <h1 className=\"text-xl font-bold text-text-primary flex items-center gap-2\">\n <Radio size={22} className=\"text-accent-primary\" />\n {tr('channel.createChannel', 'Create Channel')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {tr('channel.createChannelSubtitle', 'Set up a new communication channel')}\n </p>\n </div>\n </div>\n </div>\n\n {/* ── Page purpose ─────────────────────────────────────────────── */}\n <PagePurpose icon={<Radio size={15} aria-hidden=\"true\" />}>\n {tr(\n 'channel.createChannelPurpose',\n 'Set up a new way to reach people — for example a transactional email sender, an SMS gateway, a Slack workspace or a push provider. Name it, pick the platform and provider, and you will get a channel you can then authenticate and send through. Create one channel per provider account you want messages to flow through.'\n )}\n </PagePurpose>\n\n {/* ── Error ────────────────────────────────────────────────────── */}\n {error && (\n <div className=\"flex items-center gap-3 p-3 bg-error-500/10 border border-status-error-border/30 rounded-lg\">\n <AlertCircle size={16} className=\"text-status-error-text shrink-0\" />\n <p className=\"text-sm text-error-400\">{error}</p>\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* ── Row 1: Name + Description | Status + Provider (2-col) ─── */}\n <div className=\"grid gap-5 lg:grid-cols-2\">\n {/* Left: Name & Description — the primary identity of the channel */}\n <EmphasisPanel className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary uppercase tracking-wider\">\n {tr('channel.basicInformation', 'Basic Information')}\n </h2>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {tr('channel.name', 'Name')} <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n aria-label={tr('channel.namePlaceholder', 'e.g., Primary Email, SMS Alerts')}\n type=\"text\"\n value={formData.name}\n onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 placeholder:text-text-secondary\"\n placeholder={tr('channel.namePlaceholder', 'e.g., Primary Email, SMS Alerts')}\n />\n </div>\n\n <div>\n <label\n htmlFor=\"channel-create-description\"\n className=\"block text-sm font-medium text-text-primary mb-1.5\"\n >\n {tr('channel.description', 'Description')}\n </label>\n <MarkdownEditor\n id=\"channel-create-description\"\n value={formData.description}\n onChange={(next) => setFormData((prev) => ({ ...prev, description: next }))}\n aria-label={tr(\n 'channel.descriptionPlaceholder',\n \"Optional description of this channel's purpose\"\n )}\n placeholder={tr(\n 'channel.descriptionPlaceholder',\n \"Optional description of this channel's purpose\"\n )}\n rows={3}\n />\n </div>\n </EmphasisPanel>\n\n {/* Right: Status + Provider / Adapter */}\n <GlassCard treatment=\"plain\" className=\"p-5 border-border-seam space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary uppercase tracking-wider\">\n {tr('channel.providerStatus', 'Provider & Status')}\n </h2>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {tr('channel.providerAdapter', 'Provider / Adapter')}\n </label>\n <select\n value={formData.adapterType}\n onChange={(e) =>\n setFormData((prev) => ({\n ...prev,\n adapterType: e.target.value as AdapterType | '',\n }))\n }\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">\n {tr('channel.selectProvider', 'Select a provider (optional)')}\n </option>\n {availableAdapters.map((a) => (\n <option key={a.value} value={a.value}>\n {tr(a.labelKey, a.labelFallback)}\n </option>\n ))}\n </select>\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n {selectedPlatform\n ? tr(\n 'channel.providersAvailableForPlatform',\n '{{count}} providers available for {{platform}}',\n {\n count: availableAdapters.length,\n platform: tr(selectedPlatform.labelKey, selectedPlatform.labelFallback),\n }\n )\n : tr('channel.selectPlatformFirst', 'Select a platform first')}\n </p>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {tr('channel.status', 'Status')}\n </label>\n <select\n value={formData.status}\n onChange={(e) =>\n setFormData((prev) => ({ ...prev, status: e.target.value as ChannelStatus }))\n }\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"ACTIVE\">{tr('channel.statusLabel.active', 'Active')}</option>\n <option value=\"INACTIVE\">\n {tr('channel.statusLabel.inactive', 'Inactive')}\n </option>\n </select>\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n {formData.status === 'ACTIVE'\n ? tr('channel.statusHint.active', 'Ready to send immediately')\n : tr('channel.statusHint.inactive', 'Configure before activating')}\n </p>\n </div>\n\n <div>\n <label\n htmlFor=\"create-channel-priority\"\n className=\"block text-sm font-medium text-text-primary mb-1.5\"\n >\n {tr('channel.priority', 'Priority')}\n </label>\n <input\n id=\"create-channel-priority\"\n aria-label={tr('channel.priority', 'Priority')}\n type=\"number\"\n value={formData.priority}\n onChange={(e) =>\n setFormData((prev) => ({\n ...prev,\n priority: parseInt(e.target.value) || 0,\n }))\n }\n min={0}\n max={100}\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50\"\n />\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n {tr('channel.priorityHint', 'Lower = higher priority (0-100)')}\n </p>\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* ── Row 2: Platform selection (full width) ─────────────────── */}\n <GlassCard treatment=\"plain\" className=\"p-5 border-border-seam space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary uppercase tracking-wider\">\n {tr('channel.platform', 'Platform')} <span className=\"text-status-error-text\">*</span>\n </h2>\n\n <div className=\"grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-5\">\n {PLATFORM_OPTIONS.map((platform) => {\n const Icon = platform.icon;\n const isSelected = formData.platform === platform.value;\n return (\n <button\n key={platform.value}\n type=\"button\"\n onClick={() => handlePlatformChange(platform.value)}\n className={`relative flex flex-col items-center gap-1.5 p-3 rounded-lg border transition-all text-center ${\n isSelected\n ? `${platform.color} border-current ring-1 ring-current`\n : 'border-border-subtle hover:bg-surface-hover text-text-secondary hover:text-text-primary'\n }`}\n >\n {isSelected && (\n <CheckCircle2 size={14} className=\"absolute top-1.5 right-1.5 text-current\" />\n )}\n <Icon size={20} />\n <span className=\"text-xs font-semibold\">\n {tr(platform.labelKey, platform.labelFallback)}\n </span>\n <span className=\"text-[10px] leading-tight opacity-70\">\n {tr(platform.descriptionKey, platform.descriptionFallback)}\n </span>\n </button>\n );\n })}\n </div>\n </GlassCard>\n\n {/* ── Row 3: Advanced (collapsible) — webhook config / delivery / retry policy (platformConfig) */}\n <OptionalFields title={tr('channel.advancedOptions', 'Advanced settings')}>\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n {tr('channel.platformConfig', 'Platform Config')}{' '}\n <span className=\"text-[10px] text-text-secondary\">\n {tr('channel.jsonMarker', '(JSON)')}\n </span>\n </label>\n <textarea\n aria-label={tr('channel.platformConfig', 'Platform Config')}\n value={formData.platformConfig}\n onChange={(e) =>\n setFormData((prev) => ({ ...prev, platformConfig: e.target.value }))\n }\n rows={3}\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 resize-none font-mono placeholder:text-text-secondary\"\n placeholder={tr(\n 'channel.platformConfigPlaceholder',\n '{\"fromEmail\": \"noreply@example.com\"}'\n )}\n />\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n {tr('channel.platformConfigHint', 'Platform-specific configuration as JSON')}\n </p>\n </div>\n </div>\n </OptionalFields>\n\n {/* ── Actions ────────────────────────────────────────────────── */}\n <div className=\"flex items-center justify-between pt-2\">\n <button\n type=\"button\"\n onClick={() => navigate('/channels/list')}\n className=\"px-4 py-2 text-sm text-text-secondary hover:text-text-primary hover:bg-surface-hover border border-border-subtle rounded-lg transition-colors\"\n >\n {tr('channel.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting || !formData.name.trim()}\n className=\"inline-flex items-center gap-2 px-5 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isSubmitting ? <Loader2 size={16} className=\"animate-spin\" /> : <Save size={16} />}\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n </div>\n </form>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -7,5 +7,4 @@
7
7
  * Form fields match CreateConnectionInput exactly (less channelId which is
8
8
  * resolved from the selector).
9
9
  */
10
- export declare function CreateConnectionPage(): import("react").JSX.Element;
11
- //# sourceMappingURL=CreateConnectionPage.d.ts.map
10
+ export declare function CreateConnectionPage(): import('react').JSX.Element;
@@ -8,7 +8,7 @@ import { AlertCircle as c, ArrowLeft as l, Loader2 as u, Save as d, Shield as f
8
8
  import { useNavigate as p, useSearchParams as m } from "react-router";
9
9
  import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
10
10
  import { EmphasisPanel as v, PagePurpose as y } from "@burdenoff/fe-libs/ui";
11
- //#region src/channel/modules/channels/pages/CreateConnectionPage.tsx
11
+ // src/channel/modules/channels/pages/CreateConnectionPage.tsx
12
12
  function b() {
13
13
  let b = p(), [x] = m(), { organizationId: S } = t(), { channels: C, isLoading: w } = r(), T = e(), E = o(() => [
14
14
  {
@@ -46,7 +46,7 @@ function b() {
46
46
  helpKey: "channel.authTypeLabel.smtpImapHelp",
47
47
  helpFallback: "Mail server credentials"
48
48
  }
49
- ], []), [D, O] = s(x.get("channelId") ?? ""), { createConnection: k } = n(D), [A, j] = s("OAUTH2"), [M, N] = s(""), [P, F] = s(""), [I, L] = s(""), [R, z] = s(""), [B, V] = s(!1), [H, U] = s(null), W = o(() => C.find((e) => e.id === D) ?? null, [C, D]);
49
+ ], []), D = x.get("channelId") ?? "", [O, k] = s(D), { createConnection: A } = n(O), [j, M] = s("OAUTH2"), [N, P] = s(""), [F, I] = s(""), [L, R] = s(""), [z, B] = s(""), [V, H] = s(!1), [U, W] = s(null), G = o(() => C.find((e) => e.id === O) ?? null, [C, O]);
50
50
  return /* @__PURE__ */ g(i, {
51
51
  permission: a.CHANNEL_CONNECTION_CREATE,
52
52
  children: /* @__PURE__ */ _("div", {
@@ -76,46 +76,46 @@ function b() {
76
76
  className: "mb-6",
77
77
  children: T("connection.create.purpose", "Authenticate a channel against its provider so it can actually send. Choose the channel, the auth method (OAuth, API key, bot token, webhook secret or mail credentials) and enter the secrets — they are stored encrypted. Do this once per channel; without a connection a channel can't deliver anything.")
78
78
  }),
79
- H && /* @__PURE__ */ _("div", {
79
+ U && /* @__PURE__ */ _("div", {
80
80
  className: "bg-status-error-bg-subtle border border-status-error-bg-subtle rounded-lg p-4 mb-6 flex gap-3",
81
81
  children: [/* @__PURE__ */ g(c, { className: "size-5 text-status-error-text shrink-0" }), /* @__PURE__ */ g("p", {
82
82
  className: "text-sm text-status-error-text",
83
- children: H
83
+ children: U
84
84
  })]
85
85
  }),
86
86
  /* @__PURE__ */ g(v, {
87
87
  className: "p-6",
88
88
  children: /* @__PURE__ */ _("form", {
89
89
  onSubmit: async (e) => {
90
- if (e.preventDefault(), U(null), !D) {
91
- U(T("connection.validation.channelRequired", "Pick a channel to connect"));
90
+ if (e.preventDefault(), W(null), !O) {
91
+ W(T("connection.validation.channelRequired", "Pick a channel to connect"));
92
92
  return;
93
93
  }
94
94
  if (!S) {
95
- U(T("connection.validation.organizationRequired", "Organization context missing"));
95
+ W(T("connection.validation.organizationRequired", "Organization context missing"));
96
96
  return;
97
97
  }
98
98
  let t;
99
- if (R.trim()) try {
100
- t = JSON.parse(R);
99
+ if (z.trim()) try {
100
+ t = JSON.parse(z);
101
101
  } catch {
102
- U(T("connection.validation.credentialsJson", "Credentials must be valid JSON"));
102
+ W(T("connection.validation.credentialsJson", "Credentials must be valid JSON"));
103
103
  return;
104
104
  }
105
- V(!0);
105
+ H(!0);
106
106
  try {
107
- let e = await k({
108
- authType: A,
109
- authCode: M.trim() || void 0,
110
- apiKey: P.trim() || void 0,
111
- redirectUri: I.trim() || void 0,
107
+ let e = await A({
108
+ authType: j,
109
+ authCode: N.trim() || void 0,
110
+ apiKey: F.trim() || void 0,
111
+ redirectUri: L.trim() || void 0,
112
112
  credentials: t
113
113
  });
114
- e ? b(`../${e.id}`) : U(T("connection.createFailed", "Failed to create connection"));
114
+ e ? b(`../${e.id}`) : W(T("connection.createFailed", "Failed to create connection"));
115
115
  } catch (e) {
116
- U(e instanceof Error ? e.message : T("connection.createFailed", "Failed to create connection"));
116
+ W(e instanceof Error ? e.message : T("connection.createFailed", "Failed to create connection"));
117
117
  } finally {
118
- V(!1);
118
+ H(!1);
119
119
  }
120
120
  },
121
121
  className: "space-y-6",
@@ -126,8 +126,8 @@ function b() {
126
126
  children: [T("connection.field.channel", "Channel"), " *"]
127
127
  }),
128
128
  /* @__PURE__ */ _("select", {
129
- value: D,
130
- onChange: (e) => O(e.target.value),
129
+ value: O,
130
+ onChange: (e) => k(e.target.value),
131
131
  className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
132
132
  required: !0,
133
133
  disabled: w,
@@ -144,17 +144,17 @@ function b() {
144
144
  ]
145
145
  }, e.id))]
146
146
  }),
147
- W && /* @__PURE__ */ g("p", {
147
+ G && /* @__PURE__ */ g("p", {
148
148
  className: "mt-1 text-xs text-fg-muted",
149
- children: T("connection.field.channelHint", "Adapter: {{adapterType}}", { adapterType: W.adapterType ?? "—" })
149
+ children: T("connection.field.channelHint", "Adapter: {{adapterType}}", { adapterType: G.adapterType ?? "—" })
150
150
  })
151
151
  ] }),
152
152
  /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ _("label", {
153
153
  className: "block text-sm font-medium text-fg mb-2",
154
154
  children: [T("connection.field.authType", "Authentication Type"), " *"]
155
155
  }), /* @__PURE__ */ g("select", {
156
- value: A,
157
- onChange: (e) => j(e.target.value),
156
+ value: j,
157
+ onChange: (e) => M(e.target.value),
158
158
  className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
159
159
  required: !0,
160
160
  children: E.map((e) => /* @__PURE__ */ _("option", {
@@ -166,7 +166,7 @@ function b() {
166
166
  ]
167
167
  }, e.value))
168
168
  })] }),
169
- A === "OAUTH2" && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
169
+ j === "OAUTH2" && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
170
170
  htmlFor: "create-conn-auth-code",
171
171
  className: "block text-sm font-medium text-fg mb-2",
172
172
  children: T("connection.field.authCode", "OAuth Authorization Code")
@@ -174,8 +174,8 @@ function b() {
174
174
  id: "create-conn-auth-code",
175
175
  "aria-label": T("connection.field.authCode", "OAuth Authorization Code"),
176
176
  type: "text",
177
- value: M,
178
- onChange: (e) => N(e.target.value),
177
+ value: N,
178
+ onChange: (e) => P(e.target.value),
179
179
  className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg",
180
180
  autoComplete: "off"
181
181
  })] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
@@ -186,11 +186,11 @@ function b() {
186
186
  id: "create-conn-redirect-uri",
187
187
  "aria-label": T("connection.field.redirectUri", "Redirect URI"),
188
188
  type: "url",
189
- value: I,
190
- onChange: (e) => L(e.target.value),
189
+ value: L,
190
+ onChange: (e) => R(e.target.value),
191
191
  className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg"
192
192
  })] })] }),
193
- (A === "API_KEY" || A === "BOT_TOKEN") && /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
193
+ (j === "API_KEY" || j === "BOT_TOKEN") && /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
194
194
  htmlFor: "create-conn-api-key",
195
195
  className: "block text-sm font-medium text-fg mb-2",
196
196
  children: T("connection.field.apiKey", "API Key / Token")
@@ -198,8 +198,8 @@ function b() {
198
198
  id: "create-conn-api-key",
199
199
  "aria-label": T("connection.field.apiKey", "API Key / Token"),
200
200
  type: "password",
201
- value: P,
202
- onChange: (e) => F(e.target.value),
201
+ value: F,
202
+ onChange: (e) => I(e.target.value),
203
203
  className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md text-fg",
204
204
  autoComplete: "new-password"
205
205
  })] }),
@@ -210,8 +210,8 @@ function b() {
210
210
  }),
211
211
  /* @__PURE__ */ g("textarea", {
212
212
  "aria-label": T("connection.field.credentialsExample", "{\"smtpHost\": \"smtp.example.com\"}"),
213
- value: R,
214
- onChange: (e) => z(e.target.value),
213
+ value: z,
214
+ onChange: (e) => B(e.target.value),
215
215
  rows: 6,
216
216
  className: "w-full px-3 py-2 bg-bg border border-border-subtle rounded-md font-mono text-sm text-fg",
217
217
  placeholder: T("connection.field.credentialsExample", "{\"smtpHost\": \"smtp.example.com\"}")
@@ -225,9 +225,9 @@ function b() {
225
225
  className: "flex gap-3 pt-2",
226
226
  children: [/* @__PURE__ */ _("button", {
227
227
  type: "submit",
228
- disabled: B || !D,
228
+ disabled: V || !O,
229
229
  className: "flex items-center gap-2 px-4 py-2 bg-accent-blue text-fg-on-accent rounded-md disabled:opacity-50",
230
- children: [B ? /* @__PURE__ */ g(u, { className: "size-4 animate-spin" }) : /* @__PURE__ */ g(d, { className: "size-4 " }), T("connection.create.submit", "Create Connection")]
230
+ children: [V ? /* @__PURE__ */ g(u, { className: "size-4 animate-spin" }) : /* @__PURE__ */ g(d, { className: "size-4 " }), T("connection.create.submit", "Create Connection")]
231
231
  }), /* @__PURE__ */ g("button", {
232
232
  type: "button",
233
233
  onClick: () => b(-1),
@@ -242,7 +242,6 @@ function b() {
242
242
  })
243
243
  });
244
244
  }
245
- //#endregion
246
245
  export { b as CreateConnectionPage };
247
246
 
248
247
  //# sourceMappingURL=CreateConnectionPage.js.map