@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":"i18n.js","names":[],"sources":["../../../../src/channel/modules/channels/i18n.ts"],"sourcesContent":["import type {\n ChannelAuthType,\n ChannelPlatform,\n ChannelStatus,\n ConnectionHealth,\n} from '../../hooks/useChannels';\n\nexport type TranslateFn = (key: string, fallback: string) => string;\n\ntype I18nLabel = {\n key: string;\n fallback: string;\n};\n\nexport const PLATFORM_I18N: Record<ChannelPlatform, I18nLabel> = {\n EMAIL: { key: 'channel.platformLabel.email', fallback: 'Email' },\n SMS: { key: 'channel.platformLabel.sms', fallback: 'SMS' },\n PUSH: { key: 'channel.platformLabel.push', fallback: 'Push' },\n INAPP: { key: 'channel.platformLabel.inApp', fallback: 'In-App' },\n WEBHOOK: { key: 'channel.platformLabel.webhook', fallback: 'Webhook' },\n SLACK: { key: 'channel.platformLabel.slack', fallback: 'Slack' },\n TEAMS: { key: 'channel.platformLabel.teams', fallback: 'Teams' },\n DISCORD: { key: 'channel.platformLabel.discord', fallback: 'Discord' },\n WHATSAPP: { key: 'channel.platformLabel.whatsapp', fallback: 'WhatsApp' },\n WEBCHAT: { key: 'channel.platformLabel.webchat', fallback: 'Webchat' },\n};\n\nexport const STATUS_I18N: Record<ChannelStatus, I18nLabel> = {\n ACTIVE: { key: 'channel.statusLabel.active', fallback: 'Active' },\n INACTIVE: { key: 'channel.statusLabel.inactive', fallback: 'Inactive' },\n ERROR: { key: 'channel.statusLabel.error', fallback: 'Error' },\n SUSPENDED: { key: 'channel.statusLabel.suspended', fallback: 'Suspended' },\n ARCHIVED: { key: 'channel.statusLabel.archived', fallback: 'Archived' },\n};\n\nexport const HEALTH_I18N: Record<ConnectionHealth, I18nLabel> = {\n HEALTHY: { key: 'channel.healthLabel.healthy', fallback: 'Healthy' },\n DEGRADED: { key: 'channel.healthLabel.degraded', fallback: 'Degraded' },\n UNHEALTHY: { key: 'channel.healthLabel.unhealthy', fallback: 'Unhealthy' },\n UNKNOWN: { key: 'channel.healthLabel.unknown', fallback: 'Unknown' },\n};\n\nexport const AUTH_TYPE_I18N: Record<ChannelAuthType, I18nLabel> = {\n API_KEY: { key: 'channel.authTypeLabel.apiKey', fallback: 'API Key' },\n OAUTH2: { key: 'channel.authTypeLabel.oauth2', fallback: 'OAuth 2.0' },\n BOT_TOKEN: { key: 'channel.authTypeLabel.botToken', fallback: 'Bot Token' },\n WEBHOOK: { key: 'channel.authTypeLabel.webhook', fallback: 'Webhook' },\n SMTP_IMAP: { key: 'channel.authTypeLabel.smtpImap', fallback: 'SMTP/IMAP' },\n};\n\nexport function translateLabel<T extends string>(\n value: T,\n labels: Partial<Record<T, I18nLabel>>,\n tr: TranslateFn\n): string {\n const label = labels[value];\n return label ? tr(label.key, label.fallback) : value;\n}\n"],"mappings":";AAcA,IAAa,IAAoD;CAC/D,OAAO;EAAE,KAAK;EAA+B,UAAU;EAAS;CAChE,KAAK;EAAE,KAAK;EAA6B,UAAU;EAAO;CAC1D,MAAM;EAAE,KAAK;EAA8B,UAAU;EAAQ;CAC7D,OAAO;EAAE,KAAK;EAA+B,UAAU;EAAU;CACjE,SAAS;EAAE,KAAK;EAAiC,UAAU;EAAW;CACtE,OAAO;EAAE,KAAK;EAA+B,UAAU;EAAS;CAChE,OAAO;EAAE,KAAK;EAA+B,UAAU;EAAS;CAChE,SAAS;EAAE,KAAK;EAAiC,UAAU;EAAW;CACtE,UAAU;EAAE,KAAK;EAAkC,UAAU;EAAY;CACzE,SAAS;EAAE,KAAK;EAAiC,UAAU;EAAW;CACvE,EAEY,IAAgD;CAC3D,QAAQ;EAAE,KAAK;EAA8B,UAAU;EAAU;CACjE,UAAU;EAAE,KAAK;EAAgC,UAAU;EAAY;CACvE,OAAO;EAAE,KAAK;EAA6B,UAAU;EAAS;CAC9D,WAAW;EAAE,KAAK;EAAiC,UAAU;EAAa;CAC1E,UAAU;EAAE,KAAK;EAAgC,UAAU;EAAY;CACxE,EAEY,IAAmD;CAC9D,SAAS;EAAE,KAAK;EAA+B,UAAU;EAAW;CACpE,UAAU;EAAE,KAAK;EAAgC,UAAU;EAAY;CACvE,WAAW;EAAE,KAAK;EAAiC,UAAU;EAAa;CAC1E,SAAS;EAAE,KAAK;EAA+B,UAAU;EAAW;CACrE,EAEY,IAAqD;CAChE,SAAS;EAAE,KAAK;EAAgC,UAAU;EAAW;CACrE,QAAQ;EAAE,KAAK;EAAgC,UAAU;EAAa;CACtE,WAAW;EAAE,KAAK;EAAkC,UAAU;EAAa;CAC3E,SAAS;EAAE,KAAK;EAAiC,UAAU;EAAW;CACtE,WAAW;EAAE,KAAK;EAAkC,UAAU;EAAa;CAC5E;AAED,SAAgB,EACd,GACA,GACA,GACQ;CACR,IAAM,IAAQ,EAAO;AACrB,QAAO,IAAQ,EAAG,EAAM,KAAK,EAAM,SAAS,GAAG"}
1
+ {"version":3,"file":"i18n.js","names":["PLATFORM_I18N","STATUS_I18N","HEALTH_I18N","AUTH_TYPE_I18N","translateLabel","label","labels","value","tr"],"sources":["../../../../src/channel/modules/channels/i18n.ts"],"sourcesContent":["import type {\n ChannelAuthType,\n ChannelPlatform,\n ChannelStatus,\n ConnectionHealth,\n} from '../../hooks/useChannels';\n\nexport type TranslateFn = (key: string, fallback: string) => string;\n\ntype I18nLabel = {\n key: string;\n fallback: string;\n};\n\nexport const PLATFORM_I18N: Record<ChannelPlatform, I18nLabel> = {\n EMAIL: { key: 'channel.platformLabel.email', fallback: 'Email' },\n SMS: { key: 'channel.platformLabel.sms', fallback: 'SMS' },\n PUSH: { key: 'channel.platformLabel.push', fallback: 'Push' },\n INAPP: { key: 'channel.platformLabel.inApp', fallback: 'In-App' },\n WEBHOOK: { key: 'channel.platformLabel.webhook', fallback: 'Webhook' },\n SLACK: { key: 'channel.platformLabel.slack', fallback: 'Slack' },\n TEAMS: { key: 'channel.platformLabel.teams', fallback: 'Teams' },\n DISCORD: { key: 'channel.platformLabel.discord', fallback: 'Discord' },\n WHATSAPP: { key: 'channel.platformLabel.whatsapp', fallback: 'WhatsApp' },\n WEBCHAT: { key: 'channel.platformLabel.webchat', fallback: 'Webchat' },\n};\n\nexport const STATUS_I18N: Record<ChannelStatus, I18nLabel> = {\n ACTIVE: { key: 'channel.statusLabel.active', fallback: 'Active' },\n INACTIVE: { key: 'channel.statusLabel.inactive', fallback: 'Inactive' },\n ERROR: { key: 'channel.statusLabel.error', fallback: 'Error' },\n SUSPENDED: { key: 'channel.statusLabel.suspended', fallback: 'Suspended' },\n ARCHIVED: { key: 'channel.statusLabel.archived', fallback: 'Archived' },\n};\n\nexport const HEALTH_I18N: Record<ConnectionHealth, I18nLabel> = {\n HEALTHY: { key: 'channel.healthLabel.healthy', fallback: 'Healthy' },\n DEGRADED: { key: 'channel.healthLabel.degraded', fallback: 'Degraded' },\n UNHEALTHY: { key: 'channel.healthLabel.unhealthy', fallback: 'Unhealthy' },\n UNKNOWN: { key: 'channel.healthLabel.unknown', fallback: 'Unknown' },\n};\n\nexport const AUTH_TYPE_I18N: Record<ChannelAuthType, I18nLabel> = {\n API_KEY: { key: 'channel.authTypeLabel.apiKey', fallback: 'API Key' },\n OAUTH2: { key: 'channel.authTypeLabel.oauth2', fallback: 'OAuth 2.0' },\n BOT_TOKEN: { key: 'channel.authTypeLabel.botToken', fallback: 'Bot Token' },\n WEBHOOK: { key: 'channel.authTypeLabel.webhook', fallback: 'Webhook' },\n SMTP_IMAP: { key: 'channel.authTypeLabel.smtpImap', fallback: 'SMTP/IMAP' },\n};\n\nexport function translateLabel<T extends string>(\n value: T,\n labels: Partial<Record<T, I18nLabel>>,\n tr: TranslateFn\n): string {\n const label = labels[value];\n return label ? tr(label.key, label.fallback) : value;\n}\n"],"mappings":";AAcA,IAAaA,IAAoD;CAC/D,OAAO;EAAE,KAAK;EAA+B,UAAU;CAAQ;CAC/D,KAAK;EAAE,KAAK;EAA6B,UAAU;CAAM;CACzD,MAAM;EAAE,KAAK;EAA8B,UAAU;CAAO;CAC5D,OAAO;EAAE,KAAK;EAA+B,UAAU;CAAS;CAChE,SAAS;EAAE,KAAK;EAAiC,UAAU;CAAU;CACrE,OAAO;EAAE,KAAK;EAA+B,UAAU;CAAQ;CAC/D,OAAO;EAAE,KAAK;EAA+B,UAAU;CAAQ;CAC/D,SAAS;EAAE,KAAK;EAAiC,UAAU;CAAU;CACrE,UAAU;EAAE,KAAK;EAAkC,UAAU;CAAW;CACxE,SAAS;EAAE,KAAK;EAAiC,UAAU;CAAU;AACvE,GAEaC,IAAgD;CAC3D,QAAQ;EAAE,KAAK;EAA8B,UAAU;CAAS;CAChE,UAAU;EAAE,KAAK;EAAgC,UAAU;CAAW;CACtE,OAAO;EAAE,KAAK;EAA6B,UAAU;CAAQ;CAC7D,WAAW;EAAE,KAAK;EAAiC,UAAU;CAAY;CACzE,UAAU;EAAE,KAAK;EAAgC,UAAU;CAAW;AACxE,GAEaC,IAAmD;CAC9D,SAAS;EAAE,KAAK;EAA+B,UAAU;CAAU;CACnE,UAAU;EAAE,KAAK;EAAgC,UAAU;CAAW;CACtE,WAAW;EAAE,KAAK;EAAiC,UAAU;CAAY;CACzE,SAAS;EAAE,KAAK;EAA+B,UAAU;CAAU;AACrE,GAEaC,IAAqD;CAChE,SAAS;EAAE,KAAK;EAAgC,UAAU;CAAU;CACpE,QAAQ;EAAE,KAAK;EAAgC,UAAU;CAAY;CACrE,WAAW;EAAE,KAAK;EAAkC,UAAU;CAAY;CAC1E,SAAS;EAAE,KAAK;EAAiC,UAAU;CAAU;CACrE,WAAW;EAAE,KAAK;EAAkC,UAAU;CAAY;AAC5E;AAEA,SAAgBC,EACd,GACA,GACA,GACQ;CACR,IAAMC,IAAQC,EAAOC;CACrB,OAAOF,IAAQG,EAAGH,EAAM,KAAKA,EAAM,QAAQ,IAAIE;AACjD"}
@@ -10,5 +10,4 @@
10
10
  * 3. Search + platform/status filters
11
11
  * 4. Channel table with inline actions
12
12
  */
13
- export declare function ChannelDashboardPage(): import("react").JSX.Element;
14
- //# sourceMappingURL=ChannelDashboardPage.d.ts.map
13
+ export declare function ChannelDashboardPage(): import('react').JSX.Element;
@@ -1,6 +1,6 @@
1
- import { useChannel as e } from "../../../providers/ChannelProvider.js";
2
- import { nativeImpact as t, nativeNotify as n } from "../../../../utils/nativeBridge.js";
3
- import { subscribeShellEvent as r } from "../../../utils/shellBridge.js";
1
+ import { nativeImpact as e, nativeNotify as t } from "../../../../utils/nativeBridge.js";
2
+ import { subscribeShellEvent as n } from "../../../utils/shellBridge.js";
3
+ import { useChannel as r } from "../../../providers/ChannelProvider.js";
4
4
  import { CHANNEL_HEALTH_CHANGED_SUBSCRIPTION as i, useChannelSubscription as a } from "../../../hooks/useChannelSubscription.js";
5
5
  import { useChannels as o } from "../../../hooks/useChannels.js";
6
6
  import { PermissionGuard as s } from "../../../components/PermissionGuard.js";
@@ -13,7 +13,7 @@ import { useNavigate as me } from "react-router";
13
13
  import { usePermissions as he } from "@burdenoff/fe-libs/shared/providers/shell";
14
14
  import { jsx as w, jsxs as T } from "react/jsx-runtime";
15
15
  import { AuroraBackground as ge, CTAOverflowMenu as _e, GlassCard as E, GradientText as ve, IllustratedEmptyState as ye, PagePurpose as be } from "@burdenoff/fe-libs/ui";
16
- //#region src/channel/modules/channels/pages/ChannelDashboardPage.tsx
16
+ // src/channel/modules/channels/pages/ChannelDashboardPage.tsx
17
17
  var D = {
18
18
  EMAIL: {
19
19
  label: "Email",
@@ -177,7 +177,7 @@ function j() {
177
177
  U.platform,
178
178
  U.status
179
179
  ]);
180
- let { organizationId: Q } = e();
180
+ let { organizationId: Q } = r();
181
181
  a({
182
182
  query: i,
183
183
  variables: { organizationId: Q },
@@ -196,7 +196,7 @@ function j() {
196
196
  "connection.deleted",
197
197
  "delivery.completed",
198
198
  "webhook.received"
199
- ].map((e) => r(e, () => void Y()));
199
+ ].map((e) => n(e, () => void Y()));
200
200
  return () => e.forEach((e) => e());
201
201
  }, [Y]);
202
202
  let $ = re(() => {
@@ -210,12 +210,12 @@ function j() {
210
210
  errored: i,
211
211
  unhealthy: a
212
212
  };
213
- }, [z]), we = u(async (e) => {
214
- t("medium"), N(e);
213
+ }, [z]), we = u(async (n) => {
214
+ e("medium"), N(n);
215
215
  try {
216
- await G(e), n("success"), M(null);
216
+ await G(n), t("success"), M(null);
217
217
  } catch (e) {
218
- throw n("error"), e;
218
+ throw t("error"), e;
219
219
  } finally {
220
220
  N(null);
221
221
  }
@@ -740,7 +740,6 @@ function j() {
740
740
  })
741
741
  });
742
742
  }
743
- //#endregion
744
743
  export { j as ChannelDashboardPage };
745
744
 
746
745
  //# sourceMappingURL=ChannelDashboardPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelDashboardPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelDashboardPage.tsx"],"sourcesContent":["/**\n * Channel Dashboard Page\n *\n * Main overview page for the Channel microfrontend.\n * Fetches real data from global-channel-svc via the useChannels hook.\n *\n * Sections:\n * 1. Header with Create Channel action\n * 2. KPI summary cards (total, active, healthy, connected)\n * 3. Search + platform/status filters\n * 4. Channel table with inline actions\n */\n\nimport { useState, useMemo, useCallback, useEffect, useRef } from 'react';\nimport { nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\nimport { subscribeShellEvent } from '../../../utils/shellBridge';\nimport {\n CTAOverflowMenu,\n GlassCard,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport {\n CHANNEL_HEALTH_CHANGED_SUBSCRIPTION,\n useChannelSubscription,\n} from '../../../hooks/useChannelSubscription';\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 {\n Radio,\n Plus,\n Search,\n Loader2,\n AlertCircle,\n RefreshCw,\n Power,\n PowerOff,\n Eye,\n Trash2,\n CheckCircle2,\n XCircle,\n AlertTriangle,\n HelpCircle,\n Cable,\n MessageSquare,\n ShieldCheck,\n Activity,\n LayoutDashboard,\n} from 'lucide-react';\nimport {\n useChannels,\n type Channel,\n type ChannelPlatform,\n type ChannelStatus,\n type ConnectionHealth,\n} from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { HEALTH_I18N, PLATFORM_I18N, STATUS_I18N, translateLabel } from '../i18n';\n\n// ============================================================================\n// DISPLAY CONFIG\n// ============================================================================\n\nconst PLATFORM_CONFIG: Record<ChannelPlatform, { label: string; color: string }> = {\n EMAIL: { label: 'Email', color: 'bg-accent-blue-subtle text-accent-blue' },\n SMS: { label: 'SMS', color: 'bg-status-success-bg-subtle text-status-success-text' },\n PUSH: { label: 'Push', color: 'bg-accent-purple-subtle text-accent-purple' },\n INAPP: { label: 'In-App', color: 'bg-accent-indigo-subtle text-accent-indigo' },\n WEBHOOK: { label: 'Webhook', color: 'bg-accent-orange-subtle text-accent-orange' },\n SLACK: { label: 'Slack', color: 'bg-accent-pink-subtle text-accent-pink' },\n TEAMS: { label: 'Teams', color: 'bg-accent-cyan-subtle text-accent-cyan' },\n DISCORD: { label: 'Discord', color: 'bg-accent-violet-subtle text-accent-violet' },\n WHATSAPP: { label: 'WhatsApp', color: 'bg-accent-emerald-subtle text-accent-emerald' },\n WEBCHAT: { label: 'Webchat', color: 'bg-accent-teal-subtle text-accent-teal' },\n};\n\nconst STATUS_CONFIG: Record<\n ChannelStatus,\n { label: string; dotColor: string; textColor: string; icon: typeof CheckCircle2 }\n> = {\n ACTIVE: {\n label: 'Active',\n dotColor: 'bg-status-success-bg',\n textColor: 'text-status-success-text',\n icon: CheckCircle2,\n },\n INACTIVE: {\n label: 'Inactive',\n dotColor: 'bg-text-tertiary',\n textColor: 'text-text-secondary',\n icon: PowerOff,\n },\n ERROR: {\n label: 'Error',\n dotColor: 'bg-status-error-bg-subtle',\n textColor: 'text-status-error-text',\n icon: XCircle,\n },\n SUSPENDED: {\n label: 'Suspended',\n dotColor: 'bg-status-warning-bg',\n textColor: 'text-status-warning-text',\n icon: AlertTriangle,\n },\n ARCHIVED: {\n label: 'Archived',\n dotColor: 'bg-text-tertiary',\n textColor: 'text-text-secondary',\n icon: PowerOff,\n },\n};\n\nconst HEALTH_CONFIG: Record<\n ConnectionHealth,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n HEALTHY: { label: 'Healthy', color: 'text-status-success-text', icon: CheckCircle2 },\n DEGRADED: { label: 'Degraded', color: 'text-status-warning-text', icon: AlertTriangle },\n UNHEALTHY: { label: 'Unhealthy', color: 'text-status-error-text', icon: XCircle },\n UNKNOWN: { label: 'Unknown', color: 'text-text-secondary', icon: HelpCircle },\n};\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction formatRelativeTime(dateStr: string): string {\n const diffMs = Date.now() - new Date(dateStr).getTime();\n if (Number.isNaN(diffMs) || diffMs < 0) return 'just now';\n const sec = Math.floor(diffMs / 1000);\n if (sec < 60) return 'just now';\n const min = Math.floor(sec / 60);\n if (min < 60) return `${min}m ago`;\n const hr = Math.floor(min / 60);\n if (hr < 24) return `${hr}h ago`;\n const d = Math.floor(hr / 24);\n if (d < 7) return `${d}d ago`;\n if (d < 30) return `${Math.floor(d / 7)}w ago`;\n return `${Math.floor(d / 30)}mo ago`;\n}\n\n// ============================================================================\n// SUB-COMPONENTS\n// ============================================================================\n\nfunction KpiCard({\n icon: Icon,\n iconClass,\n value,\n label,\n sub,\n featured,\n}: {\n icon: typeof Radio;\n iconClass: string;\n value: string | number;\n label: string;\n sub?: string;\n featured?: boolean;\n}) {\n return (\n <GlassCard\n treatment={featured ? 'glass' : 'metric'}\n glow={featured}\n className=\"p-4 border-border-seam\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-lg bg-accent-primary/10\">\n <Icon size={18} className={iconClass} />\n </div>\n <div>\n <p className=\"text-2xl font-bold text-text-primary leading-tight tabular-nums\">{value}</p>\n <p className=\"text-xs text-text-secondary\">{label}</p>\n </div>\n </div>\n {sub && <p className=\"text-[11px] text-text-muted mt-2 pl-11\">{sub}</p>}\n </GlassCard>\n );\n}\n\n// ============================================================================\n// MAIN COMPONENT\n// ============================================================================\n\nexport function ChannelDashboardPage() {\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n // Channel creation is admin-only (channel:create) — hide the affordance otherwise.\n const canCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CREATE);\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n const pageSize = 10;\n const [currentPage, setCurrentPage] = useState(0);\n const getPlatformConfig = (platform: ChannelPlatform) => {\n const config = PLATFORM_CONFIG[platform] ?? {\n label: platform,\n color: 'bg-bg-sunken text-text-tertiary',\n };\n return {\n ...config,\n label: translateLabel(platform, PLATFORM_I18N, tr),\n };\n };\n const getStatusConfig = (status: ChannelStatus) => {\n const config = STATUS_CONFIG[status] ?? STATUS_CONFIG.INACTIVE;\n return {\n ...config,\n label: translateLabel(status, STATUS_I18N, tr),\n };\n };\n const getHealthConfig = (health: ConnectionHealth) => {\n const config = HEALTH_CONFIG[health] ?? HEALTH_CONFIG.UNKNOWN;\n return {\n ...config,\n label: translateLabel(health, HEALTH_I18N, tr),\n };\n };\n\n const {\n channels,\n isLoading,\n error,\n total,\n filter,\n updateFilter,\n deleteChannel,\n activateChannel,\n deactivateChannel,\n runHealthCheck,\n refetch,\n } = useChannels();\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n\n const refetchRef = useRef(refetch);\n useEffect(() => {\n refetchRef.current = refetch;\n });\n\n useEffect(() => {\n void refetchRef.current({ offset: currentPage * pageSize, limit: pageSize });\n }, [currentPage, pageSize]);\n\n useEffect(() => {\n void Promise.resolve().then(() => setCurrentPage(0));\n }, [filter.search, filter.platform, filter.status]);\n\n const { organizationId } = useChannelContext();\n\n // Real-time channel health stream. Subscribes when the gateway exposes\n // `channelHealthChanged`; otherwise falls back to polling every 60s.\n // Conservative fallback interval keeps gateway/svc cost low when subs\n // are unavailable; live updates flow through the subscription path.\n useChannelSubscription({\n query: CHANNEL_HEALTH_CHANGED_SUBSCRIPTION,\n variables: { organizationId },\n operationName: 'ChannelHealthChanged',\n enabled: Boolean(organizationId),\n onData: () => void refetch(),\n pollFn: refetch,\n pollIntervalMs: 60_000,\n });\n\n // ── Cross-MFE refresh: subscribe to channel/connection bus events so the\n // dashboard auto-updates when another tab or another MFE mutates state. ─\n useEffect(() => {\n const events = [\n 'channel.created',\n 'channel.updated',\n 'channel.deleted',\n 'connection.created',\n 'connection.updated',\n 'connection.deleted',\n 'delivery.completed',\n 'webhook.received',\n ];\n const unsubs = events.map((name) => subscribeShellEvent(name, () => void refetch()));\n return () => unsubs.forEach((u) => u());\n }, [refetch]);\n\n // ── Derived KPIs ──────────────────────────────────────────────────────\n const kpis = useMemo(() => {\n let active = 0;\n let healthy = 0;\n let connected = 0;\n let messages24h = 0;\n let errored = 0;\n let unhealthy = 0;\n\n for (const ch of channels) {\n if (ch.status === 'ACTIVE') active++;\n if (ch.status === 'ERROR') errored++;\n if (ch.connectionHealth === 'HEALTHY') healthy++;\n if (ch.connectionHealth === 'UNHEALTHY') unhealthy++;\n if (ch.isConnected) connected++;\n messages24h += ch.messageCount24h ?? 0;\n }\n\n return { active, healthy, connected, messages24h, errored, unhealthy };\n }, [channels]);\n\n // ── Action handlers ───────────────────────────────────────────────────\n const handleDelete = useCallback(\n async (id: string) => {\n void nativeImpact('medium');\n setActionLoading(id);\n try {\n await deleteChannel(id);\n void nativeNotify('success');\n setDeleteConfirm(null);\n } catch (err) {\n void nativeNotify('error');\n throw err;\n } finally {\n setActionLoading(null);\n }\n },\n [deleteChannel]\n );\n\n const handleToggle = useCallback(\n async (ch: Channel) => {\n setActionLoading(ch.id);\n try {\n if (ch.status === 'ACTIVE') await deactivateChannel(ch.id);\n else await activateChannel(ch.id);\n } finally {\n setActionLoading(null);\n }\n },\n [activateChannel, deactivateChannel]\n );\n\n const handleHealthCheck = useCallback(\n async (id: string) => {\n setActionLoading(id);\n try {\n await runHealthCheck(id);\n } finally {\n setActionLoading(null);\n }\n },\n [runHealthCheck]\n );\n\n // ── Render ────────────────────────────────────────────────────────────\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_READ}>\n <div className=\"space-y-5 p-3 sm:p-6\">\n {/* ── Header ───────────────────────────────────────────────────── */}\n <div className=\"relative overflow-hidden rounded-xl\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex flex-col gap-3 p-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-lg bg-accent-primary/10\">\n <LayoutDashboard size={20} className=\"text-accent-primary\" />\n </div>\n <div>\n <h1 className=\"text-xl font-bold text-text-primary\">\n <GradientText>{tr('channel.channelDashboard', 'Channel Dashboard')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'channel.dashboardSubtitle',\n 'Manage and monitor your communication channels'\n )}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\" data-tour=\"channels-dashboard-create-button\">\n <CTAOverflowMenu\n primary={\n canCreate\n ? {\n label: tr('channel.createChannel', 'Create Channel'),\n icon: <Plus size={16} aria-hidden=\"true\" />,\n onSelect: () => navigate('/channels/create'),\n }\n : undefined\n }\n actions={[\n {\n label: tr('channel.refresh', 'Refresh'),\n icon: <RefreshCw size={16} aria-hidden=\"true\" />,\n onSelect: () => void refetch(),\n },\n ]}\n />\n </div>\n </div>\n </div>\n\n {/* ── Page purpose ─────────────────────────────────────────────── */}\n <PagePurpose icon={<Radio size={15} aria-hidden=\"true\" />}>\n {tr(\n 'channel.dashboardPurpose',\n 'Your command centre for every outbound communication channel — Email, SMS, Push, Slack, WhatsApp and more. Watch health and delivery volume at a glance, spot channels in error, and jump straight to fixing or testing them. Use it to confirm a channel is live before a campaign or to triage why a notification did not arrive.'\n )}\n </PagePurpose>\n\n {/* ── KPI Cards ────────────────────────────────────────────────── */}\n <div\n data-tour=\"channels-dashboard-kpi-section\"\n className=\"grid gap-3 grid-cols-2 md:grid-cols-3 lg:grid-cols-4\"\n >\n <KpiCard\n featured\n icon={Radio}\n iconClass=\"text-accent-primary\"\n value={total}\n label={tr('channel.totalChannels', 'Total Channels')}\n sub={\n kpis.active > 0\n ? `${kpis.active} ${tr('channel.statusLabel.active', 'Active').toLowerCase()}, ${total - kpis.active} ${tr('channel.statusLabel.inactive', 'Inactive').toLowerCase()}`\n : undefined\n }\n />\n <KpiCard\n icon={ShieldCheck}\n iconClass=\"text-status-success-text\"\n value={kpis.healthy}\n label={tr('channel.healthy', 'Healthy')}\n sub={\n kpis.unhealthy > 0\n ? `${kpis.unhealthy} ${tr('channel.healthLabel.unhealthy', 'Unhealthy').toLowerCase()}`\n : total > 0\n ? kpis.healthy === total\n ? tr('channel.allSystemsNominal', 'All systems nominal')\n : `${total - kpis.healthy} ${tr('channel.healthLabel.unchecked', 'unchecked')}`\n : undefined\n }\n />\n <KpiCard\n icon={Cable}\n iconClass=\"text-accent-blue\"\n value={kpis.connected}\n label={tr('channel.connected', 'Connected')}\n sub={\n total > 0\n ? `${total - kpis.connected} ${tr('channel.disconnected', 'Disconnected').toLowerCase()}`\n : undefined\n }\n />\n <KpiCard\n icon={MessageSquare}\n iconClass=\"text-accent-violet\"\n value={kpis.messages24h.toLocaleString()}\n label={tr('channel.messages24h', 'Messages (24h)')}\n />\n </div>\n\n {/* ── Alerts ───────────────────────────────────────────────────── */}\n {kpis.errored > 0 && (\n <div className=\"flex items-center gap-3 p-3 border border-status-error-border/30 bg-status-error-bg-subtle rounded-lg\">\n <AlertCircle size={16} className=\"text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">\n <span className=\"font-semibold\">{kpis.errored}</span>{' '}\n {tr(\n kpis.errored === 1\n ? 'channel.errorStateSummarySingle'\n : 'channel.errorStateSummaryMultiple',\n kpis.errored === 1 ? 'channel in error state' : 'channels in error state'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => updateFilter({ status: 'ERROR' as ChannelStatus })}\n className=\"ml-auto text-xs font-medium text-status-error-text hover:text-status-error-text/80 transition-colors\"\n >\n {tr('channel.showErrors', 'Show errors')}\n </button>\n </div>\n )}\n\n {/* ── Search & Filters ─────────────────────────────────────────── */}\n <div\n className=\"flex flex-col gap-3 sm:flex-row sm:items-center\"\n data-tour=\"channels-dashboard-search-filters\"\n >\n <div className=\"relative flex-1\">\n <Search\n size={14}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"text\"\n aria-label={tr('channel.searchChannels', 'Search channels...')}\n placeholder={tr('channel.searchChannels', 'Search channels...')}\n value={filter.search || ''}\n onChange={(e) => updateFilter({ search: e.target.value })}\n className=\"w-full pl-9 pr-4 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500/50 placeholder:text-text-secondary\"\n />\n </div>\n\n <select\n value={filter.platform || ''}\n onChange={(e) =>\n updateFilter({ platform: (e.target.value as ChannelPlatform) || undefined })\n }\n className=\"px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500/50 text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">{tr('channel.allPlatforms', 'All Platforms')}</option>\n {Object.entries(PLATFORM_CONFIG).map(([key, cfg]) => (\n <option key={key} value={key}>\n {cfg.label}\n </option>\n ))}\n </select>\n\n <select\n value={filter.status || ''}\n onChange={(e) =>\n updateFilter({ status: (e.target.value as ChannelStatus) || undefined })\n }\n className=\"px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500/50 text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">{tr('channel.allStatus', 'All Status')}</option>\n {Object.entries(STATUS_CONFIG).map(([key, cfg]) => (\n <option key={key} value={key}>\n {cfg.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* ── Error State ──────────────────────────────────────────────── */}\n {error && (\n <div className=\"flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border/30 rounded-lg\">\n <AlertCircle size={18} className=\"text-status-error-text shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-status-error-text\">\n {tr('channel.loadFailed', 'Failed to load channels')}\n </p>\n <p className=\"text-xs text-status-error-text/70 truncate\">{error}</p>\n </div>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"shrink-0 px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n {tr('channel.retry', 'Retry')}\n </button>\n </div>\n )}\n\n {/* ── Loading ──────────────────────────────────────────────────── */}\n {isLoading && channels.length === 0 && (\n <div className=\"flex items-center justify-center py-16\">\n <Loader2 size={28} className=\"animate-spin text-accent-primary\" />\n </div>\n )}\n\n {/* ── Empty State ──────────────────────────────────────────────── */}\n {!isLoading && !error && channels.length === 0 && (\n <IllustratedEmptyState\n illustration={\n filter.search || filter.platform || filter.status ? 'empty-search' : 'onboarding'\n }\n title={\n filter.search || filter.platform || filter.status\n ? tr('channel.noChannelsMatchFilter', 'No channels match your filters')\n : tr('channel.noChannels', 'No channels yet')\n }\n description={\n filter.search || filter.platform || filter.status\n ? tr('channel.adjustFilters', 'Try adjusting your search or filters.')\n : tr(\n 'channel.createFirstChannel',\n 'Create your first communication channel to start sending messages across Email, SMS, Push, Slack and more.'\n )\n }\n action={\n canCreate && !(filter.search || filter.platform || filter.status) ? (\n <button\n type=\"button\"\n onClick={() => navigate('/channels/create')}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors\"\n >\n <Plus size={14} />\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n ) : undefined\n }\n />\n )}\n\n {/* ── Channel Table ────────────────────────────────────────────── */}\n {channels.length > 0 && (\n <GlassCard\n treatment=\"plain\"\n className=\"border-border-seam rounded-lg overflow-hidden p-0\"\n data-tour=\"channels-dashboard-table\"\n >\n <div className=\"overflow-x-auto min-w-full\">\n <table className=\"w-full text-sm min-w-[800px]\">\n <thead>\n <tr className=\"hover:bg-action-primary-bg/10 border-b border-border-subtle\">\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.channel', 'Channel')}\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.platform', 'Platform')}\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.status', 'Status')}\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.health', 'Health')}\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.connection', 'Connection')}\n </th>\n <th className=\"text-right px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.msgs24h', 'Msgs 24h')}\n </th>\n <th className=\"text-right px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.updated', 'Updated')}\n </th>\n <th className=\"text-right px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n\n <tbody className=\"divide-y divide-border\">\n {channels.map((ch) => {\n const isAL = actionLoading === ch.id;\n\n return (\n <tr\n key={ch.id}\n className=\"bg-surface hover:bg-action-primary-bg/60 transition-colors group\"\n >\n {/* Name */}\n <td className=\"px-4 py-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${ch.id}`)}\n className=\"text-left group/name\"\n >\n <p className=\"font-medium text-text-primary group-hover/name:text-accent-primary transition-colors truncate max-w-[200px]\">\n {ch.name}\n </p>\n {ch.description && (\n <p className=\"text-xs text-text-secondary truncate max-w-[200px]\">\n {ch.description}\n </p>\n )}\n </button>\n </td>\n\n {/* Platform */}\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex items-center px-2 py-0.5 text-[11px] font-semibold rounded-full ${getPlatformConfig(ch.platform).color}`}\n >\n {getPlatformConfig(ch.platform).label}\n </span>\n {ch.adapterType && (\n <p className=\"text-[10px] text-text-secondary mt-0.5 font-mono\">\n {ch.adapterType.replace(/_/g, ' ')}\n </p>\n )}\n </td>\n\n {/* Status */}\n <td className=\"px-4 py-3\">\n <span className=\"inline-flex items-center gap-1.5\">\n <span\n className={`w-1.5 h-1.5 rounded-full ${getStatusConfig(ch.status).dotColor} ${\n ch.status === 'ACTIVE' ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n />\n <span className={`text-xs ${getStatusConfig(ch.status).textColor}`}>\n {getStatusConfig(ch.status).label}\n </span>\n </span>\n </td>\n\n {/* Health */}\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex items-center gap-1 text-xs ${getHealthConfig(ch.connectionHealth).color}`}\n >\n {(() => {\n const Icon = getHealthConfig(ch.connectionHealth).icon;\n return <Icon size={12} />;\n })()}\n {getHealthConfig(ch.connectionHealth).label}\n </span>\n </td>\n\n {/* Connection */}\n <td className=\"px-4 py-3\">\n <span\n className={`text-xs ${ch.isConnected ? 'text-status-success-text' : 'text-text-secondary'}`}\n >\n {ch.isConnected\n ? tr('channel.connected', 'Connected')\n : tr('channel.disconnected', 'Disconnected')}\n </span>\n </td>\n\n {/* Messages 24h */}\n <td className=\"px-4 py-3 text-right\">\n <span className=\"text-xs text-text-primary tabular-nums\">\n {(ch.messageCount24h ?? 0).toLocaleString()}\n </span>\n </td>\n\n {/* Updated */}\n <td className=\"px-4 py-3 text-right\">\n <span className=\"text-xs text-text-secondary whitespace-nowrap\">\n {formatRelativeTime(ch.updatedAt)}\n </span>\n </td>\n\n {/* Actions */}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center justify-end gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity\">\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${ch.id}`)}\n className=\"p-1.5 text-text-secondary hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors\"\n title={tr('channel.view', 'View')}\n >\n <Eye size={14} />\n </button>\n\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXECUTE}>\n <button\n type=\"button\"\n onClick={() => handleHealthCheck(ch.id)}\n disabled={isAL}\n className=\"p-1.5 text-text-secondary hover:text-accent-blue hover:bg-accent-blue-subtle rounded transition-colors disabled:opacity-40\"\n title={tr('channel.healthCheck', 'Health Check')}\n >\n {isAL ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : (\n <RefreshCw size={14} />\n )}\n </button>\n </PermissionGuard>\n\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n type=\"button\"\n onClick={() => handleToggle(ch)}\n disabled={isAL}\n className={`p-1.5 rounded transition-colors disabled:opacity-40 ${\n ch.status === 'ACTIVE'\n ? 'text-text-secondary hover:text-status-warning-text hover:bg-status-warning-bg-subtle'\n : 'text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n title={\n ch.status === 'ACTIVE'\n ? tr('channel.deactivate', 'Deactivate')\n : tr('channel.activate', 'Activate')\n }\n >\n {ch.status === 'ACTIVE' ? (\n <PowerOff size={14} />\n ) : (\n <Power size={14} />\n )}\n </button>\n </PermissionGuard>\n\n {deleteConfirm === ch.id ? (\n <span className=\"inline-flex items-center gap-1 ml-1\">\n <button\n type=\"button\"\n onClick={() => handleDelete(ch.id)}\n disabled={isAL}\n className=\"px-1.5 py-0.5 text-[11px] font-medium text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n {isAL ? '...' : tr('channel.yes', 'Yes')}\n </button>\n <button\n type=\"button\"\n onClick={() => setDeleteConfirm(null)}\n className=\"px-1.5 py-0.5 text-[11px] text-text-secondary hover:bg-action-primary-bg/10 rounded transition-colors\"\n >\n {tr('channel.no', 'No')}\n </button>\n </span>\n ) : (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_DELETE}>\n <button\n type=\"button\"\n onClick={() => setDeleteConfirm(ch.id)}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n title={tr('channel.delete', 'Delete')}\n >\n <Trash2 size={14} />\n </button>\n </PermissionGuard>\n )}\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {/* Table footer — pagination */}\n <div className=\"px-4 py-2.5 border-t border-border-subtle flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium text-text-primary\">\n {total === 0 ? 0 : currentPage * pageSize + 1}–\n {Math.min((currentPage + 1) * pageSize, total)}\n </span>{' '}\n {tr('channel.of', 'of')}{' '}\n <span className=\"font-medium text-text-primary\">{total}</span>{' '}\n {tr('channel.channelsLower', 'channels')}\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-2.5 py-1 text-xs border border-border-subtle rounded hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Prev')}\n </button>\n <span className=\"text-xs 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-2.5 py-1 text-xs border border-border-subtle rounded 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 </GlassCard>\n )}\n\n {/* ── Quick Links ──────────────────────────────────────────────── */}\n {channels.length > 0 && (\n <div className=\"grid gap-3 sm:grid-cols-3\" data-tour=\"channels-dashboard-quick-links\">\n <button\n type=\"button\"\n onClick={() => navigate('/channels/?tab=all')}\n className=\"flex items-center gap-3 p-3 bg-gloss-card border border-border-seam rounded-lg shadow-[var(--shadow-pop)] hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <Radio size={16} className=\"text-accent-primary shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('channel.allChannels', 'All Channels')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.browseChannels', 'Browse and manage channel cards')}\n </p>\n </div>\n </button>\n <button\n type=\"button\"\n onClick={() => navigate('/channels/?tab=connections')}\n className=\"flex items-center gap-3 p-3 bg-gloss-card border border-border-seam rounded-lg shadow-[var(--shadow-pop)] hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <Cable size={16} className=\"text-accent-blue shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('channel.connections', 'Connections')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.connectionsSubtitle', 'OAuth, API keys & credentials')}\n </p>\n </div>\n </button>\n <button\n type=\"button\"\n onClick={() => navigate('/channels/?tab=send')}\n className=\"flex items-center gap-3 p-3 bg-gloss-card border border-border-seam rounded-lg shadow-[var(--shadow-pop)] hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <Activity size={16} className=\"text-accent-violet shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('channel.sendMessage', 'Send Message')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.sendMessageSubtitle', 'Send a test or one-off notification')}\n </p>\n </div>\n </button>\n </div>\n )}\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChannelDashboardPage.js","names":["useChannelContext","PLATFORM_CONFIG","STATUS_CONFIG","CheckCircle2","PowerOff","XCircle","AlertTriangle","HEALTH_CONFIG","HelpCircle","formatRelativeTime","diffMs","dateStr","sec","min","hr","d","KpiCard","Icon","GlassCard","featured","iconClass","value","label","sub","ChannelDashboardPage","navigate","useNavigate","usePermissions","canCreate","hasPermission","CHANNEL_PERMISSIONS","useI18n","tr","t","key","fallback","deleteConfirm","setDeleteConfirm","useState","actionLoading","setActionLoading","currentPage","setCurrentPage","getPlatformConfig","platform","translateLabel","PLATFORM_I18N","getStatusConfig","status","STATUS_I18N","getHealthConfig","health","HEALTH_I18N","useChannels","totalPages","total","refetchRef","useRef","refetch","useEffect","filter","useChannel","useChannelSubscription","CHANNEL_HEALTH_CHANGED_SUBSCRIPTION","organizationId","unsubs","subscribeShellEvent","name","u","kpis","useMemo","active","healthy","connected","messages24h","errored","unhealthy","ch","channels","handleDelete","useCallback","nativeImpact","id","deleteChannel","nativeNotify","err","handleToggle","deactivateChannel","activateChannel","handleHealthCheck","runHealthCheck","PermissionGuard","AuroraBackground","LayoutDashboard","GradientText","CTAOverflowMenu","Plus","RefreshCw","PagePurpose","Radio","ShieldCheck","Cable","MessageSquare","AlertCircle","updateFilter","Search","cfg","error","isLoading","Loader2","IllustratedEmptyState","isAL","Eye","Power","Trash2","p","Activity"],"sources":["../../../../../src/channel/modules/channels/pages/ChannelDashboardPage.tsx"],"sourcesContent":["/**\n * Channel Dashboard Page\n *\n * Main overview page for the Channel microfrontend.\n * Fetches real data from global-channel-svc via the useChannels hook.\n *\n * Sections:\n * 1. Header with Create Channel action\n * 2. KPI summary cards (total, active, healthy, connected)\n * 3. Search + platform/status filters\n * 4. Channel table with inline actions\n */\n\nimport { useState, useMemo, useCallback, useEffect, useRef } from 'react';\nimport { nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\nimport { subscribeShellEvent } from '../../../utils/shellBridge';\nimport {\n CTAOverflowMenu,\n GlassCard,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useChannel as useChannelContext } from '../../../providers/ChannelProvider';\nimport {\n CHANNEL_HEALTH_CHANGED_SUBSCRIPTION,\n useChannelSubscription,\n} from '../../../hooks/useChannelSubscription';\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 {\n Radio,\n Plus,\n Search,\n Loader2,\n AlertCircle,\n RefreshCw,\n Power,\n PowerOff,\n Eye,\n Trash2,\n CheckCircle2,\n XCircle,\n AlertTriangle,\n HelpCircle,\n Cable,\n MessageSquare,\n ShieldCheck,\n Activity,\n LayoutDashboard,\n} from 'lucide-react';\nimport {\n useChannels,\n type Channel,\n type ChannelPlatform,\n type ChannelStatus,\n type ConnectionHealth,\n} from '../../../hooks/useChannels';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { HEALTH_I18N, PLATFORM_I18N, STATUS_I18N, translateLabel } from '../i18n';\n\n// ============================================================================\n// DISPLAY CONFIG\n// ============================================================================\n\nconst PLATFORM_CONFIG: Record<ChannelPlatform, { label: string; color: string }> = {\n EMAIL: { label: 'Email', color: 'bg-accent-blue-subtle text-accent-blue' },\n SMS: { label: 'SMS', color: 'bg-status-success-bg-subtle text-status-success-text' },\n PUSH: { label: 'Push', color: 'bg-accent-purple-subtle text-accent-purple' },\n INAPP: { label: 'In-App', color: 'bg-accent-indigo-subtle text-accent-indigo' },\n WEBHOOK: { label: 'Webhook', color: 'bg-accent-orange-subtle text-accent-orange' },\n SLACK: { label: 'Slack', color: 'bg-accent-pink-subtle text-accent-pink' },\n TEAMS: { label: 'Teams', color: 'bg-accent-cyan-subtle text-accent-cyan' },\n DISCORD: { label: 'Discord', color: 'bg-accent-violet-subtle text-accent-violet' },\n WHATSAPP: { label: 'WhatsApp', color: 'bg-accent-emerald-subtle text-accent-emerald' },\n WEBCHAT: { label: 'Webchat', color: 'bg-accent-teal-subtle text-accent-teal' },\n};\n\nconst STATUS_CONFIG: Record<\n ChannelStatus,\n { label: string; dotColor: string; textColor: string; icon: typeof CheckCircle2 }\n> = {\n ACTIVE: {\n label: 'Active',\n dotColor: 'bg-status-success-bg',\n textColor: 'text-status-success-text',\n icon: CheckCircle2,\n },\n INACTIVE: {\n label: 'Inactive',\n dotColor: 'bg-text-tertiary',\n textColor: 'text-text-secondary',\n icon: PowerOff,\n },\n ERROR: {\n label: 'Error',\n dotColor: 'bg-status-error-bg-subtle',\n textColor: 'text-status-error-text',\n icon: XCircle,\n },\n SUSPENDED: {\n label: 'Suspended',\n dotColor: 'bg-status-warning-bg',\n textColor: 'text-status-warning-text',\n icon: AlertTriangle,\n },\n ARCHIVED: {\n label: 'Archived',\n dotColor: 'bg-text-tertiary',\n textColor: 'text-text-secondary',\n icon: PowerOff,\n },\n};\n\nconst HEALTH_CONFIG: Record<\n ConnectionHealth,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n HEALTHY: { label: 'Healthy', color: 'text-status-success-text', icon: CheckCircle2 },\n DEGRADED: { label: 'Degraded', color: 'text-status-warning-text', icon: AlertTriangle },\n UNHEALTHY: { label: 'Unhealthy', color: 'text-status-error-text', icon: XCircle },\n UNKNOWN: { label: 'Unknown', color: 'text-text-secondary', icon: HelpCircle },\n};\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction formatRelativeTime(dateStr: string): string {\n const diffMs = Date.now() - new Date(dateStr).getTime();\n if (Number.isNaN(diffMs) || diffMs < 0) return 'just now';\n const sec = Math.floor(diffMs / 1000);\n if (sec < 60) return 'just now';\n const min = Math.floor(sec / 60);\n if (min < 60) return `${min}m ago`;\n const hr = Math.floor(min / 60);\n if (hr < 24) return `${hr}h ago`;\n const d = Math.floor(hr / 24);\n if (d < 7) return `${d}d ago`;\n if (d < 30) return `${Math.floor(d / 7)}w ago`;\n return `${Math.floor(d / 30)}mo ago`;\n}\n\n// ============================================================================\n// SUB-COMPONENTS\n// ============================================================================\n\nfunction KpiCard({\n icon: Icon,\n iconClass,\n value,\n label,\n sub,\n featured,\n}: {\n icon: typeof Radio;\n iconClass: string;\n value: string | number;\n label: string;\n sub?: string;\n featured?: boolean;\n}) {\n return (\n <GlassCard\n treatment={featured ? 'glass' : 'metric'}\n glow={featured}\n className=\"p-4 border-border-seam\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-lg bg-accent-primary/10\">\n <Icon size={18} className={iconClass} />\n </div>\n <div>\n <p className=\"text-2xl font-bold text-text-primary leading-tight tabular-nums\">{value}</p>\n <p className=\"text-xs text-text-secondary\">{label}</p>\n </div>\n </div>\n {sub && <p className=\"text-[11px] text-text-muted mt-2 pl-11\">{sub}</p>}\n </GlassCard>\n );\n}\n\n// ============================================================================\n// MAIN COMPONENT\n// ============================================================================\n\nexport function ChannelDashboardPage() {\n const navigate = useNavigate();\n const { hasPermission } = usePermissions();\n // Channel creation is admin-only (channel:create) — hide the affordance otherwise.\n const canCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CREATE);\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n const pageSize = 10;\n const [currentPage, setCurrentPage] = useState(0);\n const getPlatformConfig = (platform: ChannelPlatform) => {\n const config = PLATFORM_CONFIG[platform] ?? {\n label: platform,\n color: 'bg-bg-sunken text-text-tertiary',\n };\n return {\n ...config,\n label: translateLabel(platform, PLATFORM_I18N, tr),\n };\n };\n const getStatusConfig = (status: ChannelStatus) => {\n const config = STATUS_CONFIG[status] ?? STATUS_CONFIG.INACTIVE;\n return {\n ...config,\n label: translateLabel(status, STATUS_I18N, tr),\n };\n };\n const getHealthConfig = (health: ConnectionHealth) => {\n const config = HEALTH_CONFIG[health] ?? HEALTH_CONFIG.UNKNOWN;\n return {\n ...config,\n label: translateLabel(health, HEALTH_I18N, tr),\n };\n };\n\n const {\n channels,\n isLoading,\n error,\n total,\n filter,\n updateFilter,\n deleteChannel,\n activateChannel,\n deactivateChannel,\n runHealthCheck,\n refetch,\n } = useChannels();\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n\n const refetchRef = useRef(refetch);\n useEffect(() => {\n refetchRef.current = refetch;\n });\n\n useEffect(() => {\n void refetchRef.current({ offset: currentPage * pageSize, limit: pageSize });\n }, [currentPage, pageSize]);\n\n useEffect(() => {\n void Promise.resolve().then(() => setCurrentPage(0));\n }, [filter.search, filter.platform, filter.status]);\n\n const { organizationId } = useChannelContext();\n\n // Real-time channel health stream. Subscribes when the gateway exposes\n // `channelHealthChanged`; otherwise falls back to polling every 60s.\n // Conservative fallback interval keeps gateway/svc cost low when subs\n // are unavailable; live updates flow through the subscription path.\n useChannelSubscription({\n query: CHANNEL_HEALTH_CHANGED_SUBSCRIPTION,\n variables: { organizationId },\n operationName: 'ChannelHealthChanged',\n enabled: Boolean(organizationId),\n onData: () => void refetch(),\n pollFn: refetch,\n pollIntervalMs: 60_000,\n });\n\n // ── Cross-MFE refresh: subscribe to channel/connection bus events so the\n // dashboard auto-updates when another tab or another MFE mutates state. ─\n useEffect(() => {\n const events = [\n 'channel.created',\n 'channel.updated',\n 'channel.deleted',\n 'connection.created',\n 'connection.updated',\n 'connection.deleted',\n 'delivery.completed',\n 'webhook.received',\n ];\n const unsubs = events.map((name) => subscribeShellEvent(name, () => void refetch()));\n return () => unsubs.forEach((u) => u());\n }, [refetch]);\n\n // ── Derived KPIs ──────────────────────────────────────────────────────\n const kpis = useMemo(() => {\n let active = 0;\n let healthy = 0;\n let connected = 0;\n let messages24h = 0;\n let errored = 0;\n let unhealthy = 0;\n\n for (const ch of channels) {\n if (ch.status === 'ACTIVE') active++;\n if (ch.status === 'ERROR') errored++;\n if (ch.connectionHealth === 'HEALTHY') healthy++;\n if (ch.connectionHealth === 'UNHEALTHY') unhealthy++;\n if (ch.isConnected) connected++;\n messages24h += ch.messageCount24h ?? 0;\n }\n\n return { active, healthy, connected, messages24h, errored, unhealthy };\n }, [channels]);\n\n // ── Action handlers ───────────────────────────────────────────────────\n const handleDelete = useCallback(\n async (id: string) => {\n void nativeImpact('medium');\n setActionLoading(id);\n try {\n await deleteChannel(id);\n void nativeNotify('success');\n setDeleteConfirm(null);\n } catch (err) {\n void nativeNotify('error');\n throw err;\n } finally {\n setActionLoading(null);\n }\n },\n [deleteChannel]\n );\n\n const handleToggle = useCallback(\n async (ch: Channel) => {\n setActionLoading(ch.id);\n try {\n if (ch.status === 'ACTIVE') await deactivateChannel(ch.id);\n else await activateChannel(ch.id);\n } finally {\n setActionLoading(null);\n }\n },\n [activateChannel, deactivateChannel]\n );\n\n const handleHealthCheck = useCallback(\n async (id: string) => {\n setActionLoading(id);\n try {\n await runHealthCheck(id);\n } finally {\n setActionLoading(null);\n }\n },\n [runHealthCheck]\n );\n\n // ── Render ────────────────────────────────────────────────────────────\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_READ}>\n <div className=\"space-y-5 p-3 sm:p-6\">\n {/* ── Header ───────────────────────────────────────────────────── */}\n <div className=\"relative overflow-hidden rounded-xl\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex flex-col gap-3 p-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-lg bg-accent-primary/10\">\n <LayoutDashboard size={20} className=\"text-accent-primary\" />\n </div>\n <div>\n <h1 className=\"text-xl font-bold text-text-primary\">\n <GradientText>{tr('channel.channelDashboard', 'Channel Dashboard')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'channel.dashboardSubtitle',\n 'Manage and monitor your communication channels'\n )}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\" data-tour=\"channels-dashboard-create-button\">\n <CTAOverflowMenu\n primary={\n canCreate\n ? {\n label: tr('channel.createChannel', 'Create Channel'),\n icon: <Plus size={16} aria-hidden=\"true\" />,\n onSelect: () => navigate('/channels/create'),\n }\n : undefined\n }\n actions={[\n {\n label: tr('channel.refresh', 'Refresh'),\n icon: <RefreshCw size={16} aria-hidden=\"true\" />,\n onSelect: () => void refetch(),\n },\n ]}\n />\n </div>\n </div>\n </div>\n\n {/* ── Page purpose ─────────────────────────────────────────────── */}\n <PagePurpose icon={<Radio size={15} aria-hidden=\"true\" />}>\n {tr(\n 'channel.dashboardPurpose',\n 'Your command centre for every outbound communication channel — Email, SMS, Push, Slack, WhatsApp and more. Watch health and delivery volume at a glance, spot channels in error, and jump straight to fixing or testing them. Use it to confirm a channel is live before a campaign or to triage why a notification did not arrive.'\n )}\n </PagePurpose>\n\n {/* ── KPI Cards ────────────────────────────────────────────────── */}\n <div\n data-tour=\"channels-dashboard-kpi-section\"\n className=\"grid gap-3 grid-cols-2 md:grid-cols-3 lg:grid-cols-4\"\n >\n <KpiCard\n featured\n icon={Radio}\n iconClass=\"text-accent-primary\"\n value={total}\n label={tr('channel.totalChannels', 'Total Channels')}\n sub={\n kpis.active > 0\n ? `${kpis.active} ${tr('channel.statusLabel.active', 'Active').toLowerCase()}, ${total - kpis.active} ${tr('channel.statusLabel.inactive', 'Inactive').toLowerCase()}`\n : undefined\n }\n />\n <KpiCard\n icon={ShieldCheck}\n iconClass=\"text-status-success-text\"\n value={kpis.healthy}\n label={tr('channel.healthy', 'Healthy')}\n sub={\n kpis.unhealthy > 0\n ? `${kpis.unhealthy} ${tr('channel.healthLabel.unhealthy', 'Unhealthy').toLowerCase()}`\n : total > 0\n ? kpis.healthy === total\n ? tr('channel.allSystemsNominal', 'All systems nominal')\n : `${total - kpis.healthy} ${tr('channel.healthLabel.unchecked', 'unchecked')}`\n : undefined\n }\n />\n <KpiCard\n icon={Cable}\n iconClass=\"text-accent-blue\"\n value={kpis.connected}\n label={tr('channel.connected', 'Connected')}\n sub={\n total > 0\n ? `${total - kpis.connected} ${tr('channel.disconnected', 'Disconnected').toLowerCase()}`\n : undefined\n }\n />\n <KpiCard\n icon={MessageSquare}\n iconClass=\"text-accent-violet\"\n value={kpis.messages24h.toLocaleString()}\n label={tr('channel.messages24h', 'Messages (24h)')}\n />\n </div>\n\n {/* ── Alerts ───────────────────────────────────────────────────── */}\n {kpis.errored > 0 && (\n <div className=\"flex items-center gap-3 p-3 border border-status-error-border/30 bg-status-error-bg-subtle rounded-lg\">\n <AlertCircle size={16} className=\"text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">\n <span className=\"font-semibold\">{kpis.errored}</span>{' '}\n {tr(\n kpis.errored === 1\n ? 'channel.errorStateSummarySingle'\n : 'channel.errorStateSummaryMultiple',\n kpis.errored === 1 ? 'channel in error state' : 'channels in error state'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => updateFilter({ status: 'ERROR' as ChannelStatus })}\n className=\"ml-auto text-xs font-medium text-status-error-text hover:text-status-error-text/80 transition-colors\"\n >\n {tr('channel.showErrors', 'Show errors')}\n </button>\n </div>\n )}\n\n {/* ── Search & Filters ─────────────────────────────────────────── */}\n <div\n className=\"flex flex-col gap-3 sm:flex-row sm:items-center\"\n data-tour=\"channels-dashboard-search-filters\"\n >\n <div className=\"relative flex-1\">\n <Search\n size={14}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"text\"\n aria-label={tr('channel.searchChannels', 'Search channels...')}\n placeholder={tr('channel.searchChannels', 'Search channels...')}\n value={filter.search || ''}\n onChange={(e) => updateFilter({ search: e.target.value })}\n className=\"w-full pl-9 pr-4 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500/50 placeholder:text-text-secondary\"\n />\n </div>\n\n <select\n value={filter.platform || ''}\n onChange={(e) =>\n updateFilter({ platform: (e.target.value as ChannelPlatform) || undefined })\n }\n className=\"px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500/50 text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">{tr('channel.allPlatforms', 'All Platforms')}</option>\n {Object.entries(PLATFORM_CONFIG).map(([key, cfg]) => (\n <option key={key} value={key}>\n {cfg.label}\n </option>\n ))}\n </select>\n\n <select\n value={filter.status || ''}\n onChange={(e) =>\n updateFilter({ status: (e.target.value as ChannelStatus) || undefined })\n }\n className=\"px-3 py-2 text-sm bg-surface border border-border-subtle rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500/50 text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">{tr('channel.allStatus', 'All Status')}</option>\n {Object.entries(STATUS_CONFIG).map(([key, cfg]) => (\n <option key={key} value={key}>\n {cfg.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* ── Error State ──────────────────────────────────────────────── */}\n {error && (\n <div className=\"flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border/30 rounded-lg\">\n <AlertCircle size={18} className=\"text-status-error-text shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-status-error-text\">\n {tr('channel.loadFailed', 'Failed to load channels')}\n </p>\n <p className=\"text-xs text-status-error-text/70 truncate\">{error}</p>\n </div>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"shrink-0 px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n {tr('channel.retry', 'Retry')}\n </button>\n </div>\n )}\n\n {/* ── Loading ──────────────────────────────────────────────────── */}\n {isLoading && channels.length === 0 && (\n <div className=\"flex items-center justify-center py-16\">\n <Loader2 size={28} className=\"animate-spin text-accent-primary\" />\n </div>\n )}\n\n {/* ── Empty State ──────────────────────────────────────────────── */}\n {!isLoading && !error && channels.length === 0 && (\n <IllustratedEmptyState\n illustration={\n filter.search || filter.platform || filter.status ? 'empty-search' : 'onboarding'\n }\n title={\n filter.search || filter.platform || filter.status\n ? tr('channel.noChannelsMatchFilter', 'No channels match your filters')\n : tr('channel.noChannels', 'No channels yet')\n }\n description={\n filter.search || filter.platform || filter.status\n ? tr('channel.adjustFilters', 'Try adjusting your search or filters.')\n : tr(\n 'channel.createFirstChannel',\n 'Create your first communication channel to start sending messages across Email, SMS, Push, Slack and more.'\n )\n }\n action={\n canCreate && !(filter.search || filter.platform || filter.status) ? (\n <button\n type=\"button\"\n onClick={() => navigate('/channels/create')}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-lg transition-colors\"\n >\n <Plus size={14} />\n {tr('channel.createChannel', 'Create Channel')}\n </button>\n ) : undefined\n }\n />\n )}\n\n {/* ── Channel Table ────────────────────────────────────────────── */}\n {channels.length > 0 && (\n <GlassCard\n treatment=\"plain\"\n className=\"border-border-seam rounded-lg overflow-hidden p-0\"\n data-tour=\"channels-dashboard-table\"\n >\n <div className=\"overflow-x-auto min-w-full\">\n <table className=\"w-full text-sm min-w-[800px]\">\n <thead>\n <tr className=\"hover:bg-action-primary-bg/10 border-b border-border-subtle\">\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.channel', 'Channel')}\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.platform', 'Platform')}\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.status', 'Status')}\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.health', 'Health')}\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.connection', 'Connection')}\n </th>\n <th className=\"text-right px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.msgs24h', 'Msgs 24h')}\n </th>\n <th className=\"text-right px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.updated', 'Updated')}\n </th>\n <th className=\"text-right px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('channel.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n\n <tbody className=\"divide-y divide-border\">\n {channels.map((ch) => {\n const isAL = actionLoading === ch.id;\n\n return (\n <tr\n key={ch.id}\n className=\"bg-surface hover:bg-action-primary-bg/60 transition-colors group\"\n >\n {/* Name */}\n <td className=\"px-4 py-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${ch.id}`)}\n className=\"text-left group/name\"\n >\n <p className=\"font-medium text-text-primary group-hover/name:text-accent-primary transition-colors truncate max-w-[200px]\">\n {ch.name}\n </p>\n {ch.description && (\n <p className=\"text-xs text-text-secondary truncate max-w-[200px]\">\n {ch.description}\n </p>\n )}\n </button>\n </td>\n\n {/* Platform */}\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex items-center px-2 py-0.5 text-[11px] font-semibold rounded-full ${getPlatformConfig(ch.platform).color}`}\n >\n {getPlatformConfig(ch.platform).label}\n </span>\n {ch.adapterType && (\n <p className=\"text-[10px] text-text-secondary mt-0.5 font-mono\">\n {ch.adapterType.replace(/_/g, ' ')}\n </p>\n )}\n </td>\n\n {/* Status */}\n <td className=\"px-4 py-3\">\n <span className=\"inline-flex items-center gap-1.5\">\n <span\n className={`w-1.5 h-1.5 rounded-full ${getStatusConfig(ch.status).dotColor} ${\n ch.status === 'ACTIVE' ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n />\n <span className={`text-xs ${getStatusConfig(ch.status).textColor}`}>\n {getStatusConfig(ch.status).label}\n </span>\n </span>\n </td>\n\n {/* Health */}\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex items-center gap-1 text-xs ${getHealthConfig(ch.connectionHealth).color}`}\n >\n {(() => {\n const Icon = getHealthConfig(ch.connectionHealth).icon;\n return <Icon size={12} />;\n })()}\n {getHealthConfig(ch.connectionHealth).label}\n </span>\n </td>\n\n {/* Connection */}\n <td className=\"px-4 py-3\">\n <span\n className={`text-xs ${ch.isConnected ? 'text-status-success-text' : 'text-text-secondary'}`}\n >\n {ch.isConnected\n ? tr('channel.connected', 'Connected')\n : tr('channel.disconnected', 'Disconnected')}\n </span>\n </td>\n\n {/* Messages 24h */}\n <td className=\"px-4 py-3 text-right\">\n <span className=\"text-xs text-text-primary tabular-nums\">\n {(ch.messageCount24h ?? 0).toLocaleString()}\n </span>\n </td>\n\n {/* Updated */}\n <td className=\"px-4 py-3 text-right\">\n <span className=\"text-xs text-text-secondary whitespace-nowrap\">\n {formatRelativeTime(ch.updatedAt)}\n </span>\n </td>\n\n {/* Actions */}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center justify-end gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity\">\n <button\n type=\"button\"\n onClick={() => navigate(`/channels/${ch.id}`)}\n className=\"p-1.5 text-text-secondary hover:text-accent-primary hover:bg-accent-primary/10 rounded transition-colors\"\n title={tr('channel.view', 'View')}\n >\n <Eye size={14} />\n </button>\n\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXECUTE}>\n <button\n type=\"button\"\n onClick={() => handleHealthCheck(ch.id)}\n disabled={isAL}\n className=\"p-1.5 text-text-secondary hover:text-accent-blue hover:bg-accent-blue-subtle rounded transition-colors disabled:opacity-40\"\n title={tr('channel.healthCheck', 'Health Check')}\n >\n {isAL ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : (\n <RefreshCw size={14} />\n )}\n </button>\n </PermissionGuard>\n\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n type=\"button\"\n onClick={() => handleToggle(ch)}\n disabled={isAL}\n className={`p-1.5 rounded transition-colors disabled:opacity-40 ${\n ch.status === 'ACTIVE'\n ? 'text-text-secondary hover:text-status-warning-text hover:bg-status-warning-bg-subtle'\n : 'text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n title={\n ch.status === 'ACTIVE'\n ? tr('channel.deactivate', 'Deactivate')\n : tr('channel.activate', 'Activate')\n }\n >\n {ch.status === 'ACTIVE' ? (\n <PowerOff size={14} />\n ) : (\n <Power size={14} />\n )}\n </button>\n </PermissionGuard>\n\n {deleteConfirm === ch.id ? (\n <span className=\"inline-flex items-center gap-1 ml-1\">\n <button\n type=\"button\"\n onClick={() => handleDelete(ch.id)}\n disabled={isAL}\n className=\"px-1.5 py-0.5 text-[11px] font-medium text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n {isAL ? '...' : tr('channel.yes', 'Yes')}\n </button>\n <button\n type=\"button\"\n onClick={() => setDeleteConfirm(null)}\n className=\"px-1.5 py-0.5 text-[11px] text-text-secondary hover:bg-action-primary-bg/10 rounded transition-colors\"\n >\n {tr('channel.no', 'No')}\n </button>\n </span>\n ) : (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_DELETE}>\n <button\n type=\"button\"\n onClick={() => setDeleteConfirm(ch.id)}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n title={tr('channel.delete', 'Delete')}\n >\n <Trash2 size={14} />\n </button>\n </PermissionGuard>\n )}\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {/* Table footer — pagination */}\n <div className=\"px-4 py-2.5 border-t border-border-subtle flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.showing', 'Showing')}{' '}\n <span className=\"font-medium text-text-primary\">\n {total === 0 ? 0 : currentPage * pageSize + 1}–\n {Math.min((currentPage + 1) * pageSize, total)}\n </span>{' '}\n {tr('channel.of', 'of')}{' '}\n <span className=\"font-medium text-text-primary\">{total}</span>{' '}\n {tr('channel.channelsLower', 'channels')}\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-2.5 py-1 text-xs border border-border-subtle rounded hover:bg-bg-sunken transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('channel.previous', 'Prev')}\n </button>\n <span className=\"text-xs 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-2.5 py-1 text-xs border border-border-subtle rounded 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 </GlassCard>\n )}\n\n {/* ── Quick Links ──────────────────────────────────────────────── */}\n {channels.length > 0 && (\n <div className=\"grid gap-3 sm:grid-cols-3\" data-tour=\"channels-dashboard-quick-links\">\n <button\n type=\"button\"\n onClick={() => navigate('/channels/?tab=all')}\n className=\"flex items-center gap-3 p-3 bg-gloss-card border border-border-seam rounded-lg shadow-[var(--shadow-pop)] hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <Radio size={16} className=\"text-accent-primary shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('channel.allChannels', 'All Channels')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.browseChannels', 'Browse and manage channel cards')}\n </p>\n </div>\n </button>\n <button\n type=\"button\"\n onClick={() => navigate('/channels/?tab=connections')}\n className=\"flex items-center gap-3 p-3 bg-gloss-card border border-border-seam rounded-lg shadow-[var(--shadow-pop)] hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <Cable size={16} className=\"text-accent-blue shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('channel.connections', 'Connections')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.connectionsSubtitle', 'OAuth, API keys & credentials')}\n </p>\n </div>\n </button>\n <button\n type=\"button\"\n onClick={() => navigate('/channels/?tab=send')}\n className=\"flex items-center gap-3 p-3 bg-gloss-card border border-border-seam rounded-lg shadow-[var(--shadow-pop)] hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <Activity size={16} className=\"text-accent-violet shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('channel.sendMessage', 'Send Message')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.sendMessageSubtitle', 'Send a test or one-off notification')}\n </p>\n </div>\n </button>\n </div>\n )}\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -3,5 +3,4 @@
3
3
  *
4
4
  * Displays channel details and provides connection/credentials management
5
5
  */
6
- export declare function ChannelDetailsPage(): import("react").JSX.Element;
7
- //# sourceMappingURL=ChannelDetailsPage.d.ts.map
6
+ export declare function ChannelDetailsPage(): import('react').JSX.Element;