@burdenoff/microfe-channel 2026.520.1 → 2026.520.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/channel/modules/channels/pages/ChannelDashboardPage.d.ts.map +1 -1
  2. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +226 -224
  3. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
  4. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +4 -4
  5. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
  6. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +2 -2
  7. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
  8. package/dist/channel/modules/channels/pages/ChannelUsersPage.d.ts.map +1 -1
  9. package/dist/channel/modules/channels/pages/ChannelUsersPage.js +65 -57
  10. package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
  11. package/dist/channel/modules/channels/pages/ChannelsListPage.d.ts.map +1 -1
  12. package/dist/channel/modules/channels/pages/ChannelsListPage.js +43 -41
  13. package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
  14. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +1 -1
  15. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
  16. package/dist/channel/modules/channels/pages/ConnectionsListPage.js +1 -1
  17. package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
  18. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.d.ts.map +1 -1
  19. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +183 -179
  20. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
  21. package/dist/channel/modules/channels/pages/EditChannelPage.js +2 -2
  22. package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
  23. package/dist/channel/modules/channels/pages/WebhookEventsPage.d.ts.map +1 -1
  24. package/dist/channel/modules/channels/pages/WebhookEventsPage.js +165 -159
  25. package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
  26. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelDetailsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelDetailsPage.tsx"],"sourcesContent":["/**\n * Channel Details Page\n *\n * Displays channel details and provides connection/credentials management\n */\n\nimport { useState, useEffect } from 'react';\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport { nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\nimport {\n Radio,\n ArrowLeft,\n Pencil,\n Loader2,\n AlertCircle,\n RefreshCw,\n Power,\n PowerOff,\n CheckCircle2,\n XCircle,\n AlertTriangle,\n HelpCircle,\n Cable,\n Key,\n Trash2,\n Play,\n Plus,\n Save,\n X,\n Send,\n Mail,\n Phone,\n MessageSquare,\n Clock,\n Truck,\n Users,\n ArrowRight,\n} from 'lucide-react';\nimport {\n useChannel,\n type ChannelPlatform,\n type ChannelStatus,\n type ConnectionHealth,\n type ChannelAuthType,\n type ChannelDeliveryError,\n} from '../../../hooks/useChannels';\nimport {\n AdapterCredentialsForm,\n CredentialValidationStatus,\n} from '../../../components/AdapterCredentialsForm';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { AUTH_TYPE_I18N, HEALTH_I18N, PLATFORM_I18N, STATUS_I18N, translateLabel } from '../i18n';\n\n/** Lightweight notification result for UI display (not the full GraphQL type) */\ninterface NotificationDisplayResult {\n success: boolean;\n messageId?: string | null;\n externalId?: string | null;\n status?: string | null;\n sentAt?: string | null;\n error?: Pick<ChannelDeliveryError, 'code' | 'message' | 'retryable'> | null;\n}\n\n// Platform display configuration\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\n// Status display configuration\nconst STATUS_CONFIG: Record<\n ChannelStatus,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n ACTIVE: {\n label: 'Active',\n color: 'text-status-success-text bg-status-success-bg-subtle',\n icon: CheckCircle2,\n },\n INACTIVE: { label: 'Inactive', color: 'text-muted-foreground bg-muted', icon: PowerOff },\n ERROR: {\n label: 'Error',\n color: 'text-status-error-text bg-status-error-bg-subtle',\n icon: XCircle,\n },\n SUSPENDED: {\n label: 'Suspended',\n color: 'text-status-warning-text bg-status-warning-bg-subtle',\n icon: AlertTriangle,\n },\n ARCHIVED: { label: 'Archived', color: 'text-muted-foreground bg-muted', icon: PowerOff },\n};\n\n// Health display configuration\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-muted-foreground', icon: HelpCircle },\n};\n\ninterface LocationState {\n pendingCredentials?: Record<string, unknown>;\n showCredentialsForm?: boolean;\n}\n\nexport function ChannelDetailsPage() {\n const { channelId } = useParams<{ channelId: string }>();\n const navigate = useNavigate();\n const location = useLocation();\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 locationState = location.state as LocationState | null;\n\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n const [showConnectionForm, setShowConnectionForm] = useState(\n locationState?.showCredentialsForm || false\n );\n const [credentials, setCredentials] = useState<Record<string, unknown>>(\n locationState?.pendingCredentials || {}\n );\n const [credentialsValid, setCredentialsValid] = useState(false);\n const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null);\n\n // Test notification modal state\n const [showTestModal, setShowTestModal] = useState(false);\n const [testRecipient, setTestRecipient] = useState('');\n const [testSubject, setTestSubject] = useState(() =>\n tr('channel.testNotificationSubject', 'Test Notification')\n );\n const [testMessage, setTestMessage] = useState(() =>\n tr(\n 'channel.testNotificationBody',\n 'This is a test notification from Burdenoff Channel Service.'\n )\n );\n const [notificationResult, setNotificationResult] = useState<NotificationDisplayResult | null>(\n null\n );\n\n // Clear location state after using it to prevent re-triggering on refresh\n useEffect(() => {\n if (locationState?.pendingCredentials || locationState?.showCredentialsForm) {\n // Replace state without the pending data\n navigate(location.pathname, { replace: true, state: null });\n }\n }, []);\n\n const {\n channel,\n isLoading,\n error,\n updateChannel,\n runHealthCheck,\n createConnection,\n updateConnection,\n deleteConnection,\n testConnection,\n sendTestNotification,\n } = useChannel(channelId);\n\n const handleHealthCheck = async () => {\n setActionLoading('health');\n try {\n await runHealthCheck();\n } catch (err) {\n console.error('[channel] Failed to run health check', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleToggleStatus = async () => {\n if (!channel) return;\n setActionLoading('status');\n try {\n await updateChannel({\n status: channel.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE',\n });\n } catch (err) {\n console.error('[channel] Failed to toggle channel status', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleCreateConnection = async () => {\n if (!channel) return;\n setActionLoading('connection');\n try {\n // Determine auth type based on adapter type\n // Most adapters use API_KEY, some use BOT_TOKEN or WEBHOOK\n // Narrow generated enum may lag UI's widened AdapterType (e.g. 'SMTP'); compare as string.\n let authType: ChannelAuthType = 'API_KEY';\n const adapterTypeStr = channel.adapterType as string | null | undefined;\n if (adapterTypeStr?.includes('BOT')) {\n authType = 'BOT_TOKEN';\n } else if (adapterTypeStr?.includes('WEBHOOK')) {\n authType = 'WEBHOOK';\n } else if (adapterTypeStr === 'SMTP') {\n authType = 'SMTP_IMAP';\n }\n\n await createConnection({\n authType,\n credentials,\n });\n setShowConnectionForm(false);\n setCredentials({});\n setCredentialsValid(false);\n } catch (err) {\n console.error('[channel] Failed to create connection', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleUpdateConnection = async (connectionId: string) => {\n setActionLoading('connection');\n try {\n await updateConnection(connectionId, {\n credentials,\n });\n setShowConnectionForm(false);\n setCredentials({});\n setCredentialsValid(false);\n } catch (err) {\n console.error('[channel] Failed to update connection', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleDeleteConnection = async (connectionId: string) => {\n void nativeImpact('medium');\n setActionLoading('delete-connection');\n try {\n await deleteConnection(connectionId);\n void nativeNotify('success');\n } catch (err) {\n void nativeNotify('error');\n console.error('[channel] Failed to delete connection', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleTestConnection = async (connectionId: string) => {\n setActionLoading('test');\n setTestResult(null);\n try {\n const result = await testConnection(connectionId);\n if (result) {\n setTestResult({\n success: result.success,\n message:\n result.message ||\n (result.success\n ? tr('channel.connectionTestSuccess', 'Connection successful!')\n : tr('channel.connectionTestFailed', 'Connection failed')),\n });\n }\n } catch (err) {\n console.error('[channel] Failed to test connection', err);\n setTestResult({\n success: false,\n message: tr('channel.connectionTestRequestFailed', 'Failed to test connection'),\n });\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleSendTestNotification = async () => {\n if (!testRecipient.trim()) return;\n\n setActionLoading('send-test');\n setNotificationResult(null);\n\n try {\n const options =\n channel?.platform === 'EMAIL'\n ? { subject: testSubject, html: `<p>${testMessage}</p>` }\n : undefined;\n\n const result = await sendTestNotification(testRecipient.trim(), testMessage, options);\n if (result) {\n setNotificationResult({\n success: result.success,\n messageId: result.messageId,\n externalId: result.externalId,\n status: result.status,\n sentAt: result.sentAt,\n error: result.error\n ? {\n code: result.error.code,\n message: result.error.message,\n retryable: result.error.retryable,\n }\n : null,\n });\n }\n } catch (err: unknown) {\n const errMsg =\n err instanceof Error\n ? err.message\n : tr('channel.sendTestNotificationFailed', 'Failed to send test notification');\n console.error('[channel] Failed to send test notification', err);\n setNotificationResult({\n success: false,\n error: {\n code: 'SEND_FAILED',\n message: errMsg,\n retryable: false,\n },\n });\n } finally {\n setActionLoading(null);\n }\n };\n\n const getRecipientPlaceholder = (platform: ChannelPlatform) => {\n switch (platform) {\n case 'EMAIL':\n return tr('channel.recipientPlaceholder.email', 'email@example.com');\n case 'SMS':\n return tr('channel.recipientPlaceholder.phone', '+1234567890');\n case 'PUSH':\n return tr('channel.recipientPlaceholder.deviceToken', 'Device token');\n case 'SLACK':\n return tr('channel.recipientPlaceholder.slack', 'Channel ID or User ID');\n case 'TEAMS':\n return tr('channel.recipientPlaceholder.channelId', 'Channel ID');\n case 'DISCORD':\n return tr('channel.recipientPlaceholder.channelId', 'Channel ID');\n case 'WHATSAPP':\n return tr('channel.recipientPlaceholder.phone', '+1234567890');\n default:\n return tr('channel.recipientPlaceholder.default', 'Recipient');\n }\n };\n\n const getRecipientLabel = (platform: ChannelPlatform) => {\n switch (platform) {\n case 'EMAIL':\n return tr('channel.recipientLabel.email', 'Email Address');\n case 'SMS':\n case 'WHATSAPP':\n return tr('channel.recipientLabel.phone', 'Phone Number');\n case 'PUSH':\n return tr('channel.recipientLabel.deviceToken', 'Device Token');\n case 'SLACK':\n case 'TEAMS':\n case 'DISCORD':\n return tr('channel.recipientLabel.channelOrUserId', 'Channel/User ID');\n default:\n return tr('channel.recipientLabel.default', 'Recipient');\n }\n };\n\n const getPlatformConfig = (platform: ChannelPlatform) => {\n const config = PLATFORM_CONFIG[platform] || {\n label: platform,\n color: 'bg-bg-sunken text-text-secondary',\n };\n return {\n ...config,\n label: translateLabel(platform, PLATFORM_I18N, tr),\n };\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\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 // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 size={32} className=\"animate-spin text-accent-primary\" />\n </div>\n );\n }\n\n // Error state\n if (error || !channel) {\n return (\n <div className=\"p-6\">\n <div className=\"flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <div>\n <p className=\"text-sm font-medium text-status-error-text\">\n {error || tr('channel.channelNotFound', 'Channel not found')}\n </p>\n </div>\n <button\n onClick={() => navigate(-1)}\n className=\"ml-auto px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n {tr('channel.goBack', 'Go Back')}\n </button>\n </div>\n </div>\n );\n }\n\n const platformConfig = getPlatformConfig(channel.platform);\n const statusConfig = getStatusConfig(channel.status);\n const healthConfig = getHealthConfig(channel.connectionHealth);\n const StatusIcon = statusConfig.icon;\n const HealthIcon = healthConfig.icon;\n const connection = channel.connection;\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_READ}>\n <div className=\"space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n onClick={() => navigate(-1)}\n className=\"p-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <ArrowLeft size={20} />\n </button>\n <div>\n <div className=\"flex items-center gap-2\">\n <Radio size={24} className=\"text-accent-primary\" />\n <h1 className=\"text-xl sm:text-2xl font-bold text-foreground\">{channel.name}</h1>\n </div>\n {channel.description && (\n <p className=\"text-sm text-muted-foreground mt-1\">{channel.description}</p>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXECUTE}>\n <button\n onClick={handleHealthCheck}\n disabled={actionLoading === 'health'}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-info-500 hover:bg-info-100 rounded-lg transition-colors disabled:opacity-50\"\n >\n {actionLoading === 'health' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <RefreshCw size={16} />\n )}\n {tr('channel.healthCheck', 'Health Check')}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n onClick={handleToggleStatus}\n disabled={actionLoading === 'status'}\n className={`flex items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors disabled:opacity-50 ${\n channel.status === 'ACTIVE'\n ? 'text-status-warning-text hover:bg-status-warning-bg-subtle'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {actionLoading === 'status' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : channel.status === 'ACTIVE' ? (\n <PowerOff size={16} />\n ) : (\n <Power size={16} />\n )}\n {channel.status === 'ACTIVE'\n ? tr('channel.deactivate', 'Deactivate')\n : tr('channel.activate', 'Activate')}\n </button>\n </PermissionGuard>\n <button\n onClick={() => {\n setShowTestModal(true);\n setNotificationResult(null);\n setTestRecipient('');\n }}\n disabled={!channel.connection}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-accent-primary hover:bg-accent-primary/10 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n title={\n !channel.connection\n ? tr('channel.configureCredentialsFirst', 'Configure credentials first')\n : tr('channel.sendTestNotification', 'Send test notification')\n }\n >\n <Send size={16} />\n {tr('channel.testChannel', 'Test Channel')}\n </button>\n <button\n onClick={() => navigate('./edit')}\n className=\"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 shadow-sm\"\n >\n <Pencil size={16} />\n {tr('channel.edit', 'Edit')}\n </button>\n </div>\n </div>\n\n {/* Status Cards */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {/* Platform */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-xs text-muted-foreground mb-1\">\n {tr('channel.platform', 'Platform')}\n </p>\n <span\n className={`inline-flex px-2 py-1 text-sm font-semibold rounded-full ${platformConfig.color}`}\n >\n {platformConfig.label}\n </span>\n </div>\n\n {/* Status */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-xs text-muted-foreground mb-1\">{tr('channel.status', 'Status')}</p>\n <div className=\"flex items-center gap-2\">\n <StatusIcon size={16} className={statusConfig.color.split(' ')[0]} />\n <span className={`px-2 py-1 text-sm font-medium rounded-full ${statusConfig.color}`}>\n {statusConfig.label}\n </span>\n </div>\n </div>\n\n {/* Health */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-xs text-muted-foreground mb-1\">\n {tr('channel.connectionHealth', 'Connection Health')}\n </p>\n <div className=\"flex items-center gap-2\">\n <HealthIcon size={16} className={healthConfig.color} />\n <span className={`text-sm font-medium ${healthConfig.color}`}>\n {healthConfig.label}\n </span>\n </div>\n {channel.lastHealthCheckAt && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n {tr('channel.lastCheck', 'Last check')}:{' '}\n {new Date(channel.lastHealthCheckAt).toLocaleString()}\n </p>\n )}\n </div>\n\n {/* Connection Status */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-xs text-muted-foreground mb-1\">\n {tr('channel.connection', 'Connection')}\n </p>\n <div className=\"flex items-center gap-2\">\n <Cable\n size={16}\n className={\n channel.isConnected ? 'text-status-success-text' : 'text-muted-foreground'\n }\n />\n <span\n className={`text-sm font-medium ${channel.isConnected ? 'text-status-success-text' : 'text-muted-foreground'}`}\n >\n {channel.isConnected\n ? tr('channel.connected', 'Connected')\n : tr('channel.notConnected', 'Not Connected')}\n </span>\n </div>\n </div>\n </div>\n\n {/* Channel Details */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <h2 className=\"text-lg font-semibold text-foreground mb-4\">\n {tr('channel.channelDetails', 'Channel Details')}\n </h2>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.adapterType', 'Adapter Type')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {channel.adapterType?.replace(/_/g, ' ') ||\n tr('channel.notConfigured', 'Not configured')}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">{tr('channel.priority', 'Priority')}</p>\n <p className=\"text-sm font-medium text-foreground\">{channel.priority}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.consecutiveFailures', 'Consecutive Failures')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">{channel.consecutiveFailures}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.messages24h', 'Messages (24h)')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">{channel.messageCount24h}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.createdAt', 'Created At')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {new Date(channel.createdAt).toLocaleString()}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.updatedAt', 'Updated At')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {new Date(channel.updatedAt).toLocaleString()}\n </p>\n </div>\n </div>\n\n {/* Tags */}\n {channel.tagAssignments && channel.tagAssignments.length > 0 && (\n <div className=\"mt-4\">\n <p className=\"text-xs text-muted-foreground mb-1.5\">{tr('channel.tags', 'Tags')}</p>\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"Channel tags\">\n {channel.tagAssignments.map((a) => (\n <TagBadge key={a.id} id={a.tagId} label={a.tagId} size=\"md\" />\n ))}\n </div>\n </div>\n )}\n\n {/* Health Check Error */}\n {channel.healthCheckError && (\n <div className=\"mt-4 p-3 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <p className=\"text-xs text-status-error-text font-medium\">\n {tr('channel.lastHealthCheckError', 'Last Health Check Error')}\n </p>\n <p className=\"text-sm text-status-error-text\">{channel.healthCheckError}</p>\n </div>\n )}\n </div>\n\n {/* Quick Links to Channel Sub-Pages */}\n <div className=\"grid gap-3 sm:grid-cols-3\">\n <button\n onClick={() => navigate(`./deliveries`)}\n className=\"flex items-center gap-3 p-3 bg-surface border border-border rounded-lg hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <div className=\"p-2 bg-accent-orange/10 rounded-lg\">\n <Truck size={18} className=\"text-accent-orange\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground\">\n {tr('channel.deliveryHistory', 'Delivery History')}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">\n {tr('channel.viewMessageDeliveries', 'View message deliveries')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-muted-foreground shrink-0\" />\n </button>\n <button\n onClick={() => navigate(`./webhooks`)}\n className=\"flex items-center gap-3 p-3 bg-surface border border-border rounded-lg hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <div className=\"p-2 bg-accent-purple/10 rounded-lg\">\n <Clock size={18} className=\"text-accent-purple\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground\">\n {tr('channel.webhookEvents', 'Webhook Events')}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">\n {tr('channel.viewIncomingWebhooks', 'View incoming webhooks')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-muted-foreground shrink-0\" />\n </button>\n <button\n onClick={() => navigate(`./users`)}\n className=\"flex items-center gap-3 p-3 bg-surface border border-border rounded-lg hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <div className=\"p-2 bg-accent-blue/10 rounded-lg\">\n <Users size={18} className=\"text-accent-blue\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground\">\n {tr('channel.channelUsers', 'Channel Users')}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">\n {tr('channel.viewConnectedUsers', 'View connected users')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-muted-foreground shrink-0\" />\n </button>\n </div>\n\n {/* Connection / Credentials Management */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <div className=\"flex items-center justify-between mb-4\">\n <div className=\"flex items-center gap-2\">\n <Key size={20} className=\"text-accent-primary\" />\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('channel.connectionAndCredentials', 'Connection & Credentials')}\n </h2>\n </div>\n {!connection && !showConnectionForm && (\n <button\n onClick={() => setShowConnectionForm(true)}\n className=\"flex items-center gap-2 px-3 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={16} />\n {tr('channel.addCredentials', 'Add Credentials')}\n </button>\n )}\n </div>\n\n {/* Existing Connection */}\n {connection && !showConnectionForm && (\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.authType', 'Auth Type')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {translateLabel(connection.authType, AUTH_TYPE_I18N, tr)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">{tr('channel.status', 'Status')}</p>\n <div className=\"flex items-center gap-2\">\n {connection.isHealthy ? (\n <CheckCircle2 size={14} className=\"text-status-success-text\" />\n ) : (\n <XCircle size={14} className=\"text-status-error-text\" />\n )}\n <span\n className={`text-sm font-medium ${connection.isHealthy ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {connection.isHealthy\n ? tr('channel.healthy', 'Healthy')\n : tr('channel.unhealthy', 'Unhealthy')}\n </span>\n </div>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.lastTested', 'Last Tested')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {connection.lastTestedAt\n ? new Date(connection.lastTestedAt).toLocaleString()\n : tr('channel.never', 'Never')}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.testStatus', 'Test Status')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {connection.testStatus || tr('channel.notTested', 'Not tested')}\n </p>\n </div>\n {connection.tokenExpiresAt && (\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.tokenExpires', 'Token Expires')}\n </p>\n <p\n className={`text-sm font-medium ${connection.needsTokenRefresh ? 'text-status-warning-text' : 'text-foreground'}`}\n >\n {new Date(connection.tokenExpiresAt).toLocaleString()}\n {connection.daysUntilExpiry !== undefined && (\n <span className=\"text-xs text-muted-foreground ml-1\">\n ({connection.daysUntilExpiry} {tr('channel.days', 'days')})\n </span>\n )}\n </p>\n </div>\n )}\n </div>\n\n {/* Test Result */}\n {testResult && (\n <div\n className={`p-3 rounded-lg ${testResult.success ? 'bg-status-success-bg-subtle border border-success-300' : 'bg-status-error-bg-subtle border border-status-error-border'}`}\n >\n <div className=\"flex items-center gap-2\">\n {testResult.success ? (\n <CheckCircle2 size={16} className=\"text-status-success-text\" />\n ) : (\n <XCircle size={16} className=\"text-status-error-text\" />\n )}\n <p\n className={`text-sm font-medium ${testResult.success ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {testResult.message}\n </p>\n </div>\n </div>\n )}\n\n {/* Connection Actions */}\n <div className=\"flex items-center gap-2 pt-2\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_UPDATE}>\n <button\n onClick={() => handleTestConnection(connection.id)}\n disabled={actionLoading === 'test'}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-info-500 hover:bg-info-100 rounded-lg transition-colors disabled:opacity-50\"\n >\n {actionLoading === 'test' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Play size={16} />\n )}\n {tr('channel.testConnection', 'Test Connection')}\n </button>\n </PermissionGuard>\n <button\n onClick={() => {\n setShowConnectionForm(true);\n // platformMetadata is the non-sensitive metadata the backend returns for\n // an existing connection (e.g. region, endpoint, accountId). The backend\n // intentionally omits or masks sensitive credential fields (access keys,\n // secrets, tokens) before returning this object, so pre-populating the\n // form is safe. The user will be prompted to re-enter any masked secrets\n // before submission because AdapterCredentialsForm marks them as required.\n setCredentials((connection?.platformMetadata as Record<string, unknown>) ?? {});\n }}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-accent-primary hover:bg-accent-primary/10 rounded-lg transition-colors\"\n >\n <Pencil size={16} />\n {tr('channel.updateCredentials', 'Update Credentials')}\n </button>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_DELETE}>\n <button\n onClick={() => handleDeleteConnection(connection.id)}\n disabled={actionLoading === 'delete-connection'}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors disabled:opacity-50\"\n >\n {actionLoading === 'delete-connection' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Trash2 size={16} />\n )}\n {tr('channel.remove', 'Remove')}\n </button>\n </PermissionGuard>\n </div>\n </div>\n )}\n\n {/* No Connection */}\n {!connection && !showConnectionForm && (\n <div className=\"text-center py-8\">\n <Cable size={32} className=\"mx-auto text-muted-foreground mb-3\" />\n <p className=\"text-sm text-muted-foreground mb-2\">\n {tr(\n 'channel.noCredentialsConfigured',\n 'No credentials configured for this channel'\n )}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {tr(\n 'channel.addCredentialsHint',\n 'Add credentials to enable message delivery through this channel'\n )}\n </p>\n </div>\n )}\n\n {/* Connection Form */}\n {showConnectionForm && (\n <div className=\"space-y-4\">\n {/* Show info if no adapter type selected */}\n {!channel.adapterType && (\n <div className=\"p-3 bg-accent-orange/10 border border-accent-orange/30 rounded-lg\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle size={16} className=\"text-accent-orange mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-accent-orange\">\n {tr('channel.noAdapterTypeConfigured', 'No adapter type configured')}\n </p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {tr(\n 'channel.noAdapterTypeConfiguredHint',\n 'You can still add credentials below. The system will attempt to validate them when the channel is configured with an adapter type.'\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Adapter-specific credential form */}\n <AdapterCredentialsForm\n adapterType={channel.adapterType ?? undefined}\n credentials={credentials}\n onChange={setCredentials}\n onValidationChange={(valid) => setCredentialsValid(valid)}\n disabled={actionLoading === 'connection'}\n />\n\n {/* Validation status */}\n {channel.adapterType && (\n <div className=\"pt-2\">\n <CredentialValidationStatus\n adapterType={channel.adapterType}\n credentials={credentials}\n />\n </div>\n )}\n\n {/* Form Actions */}\n <div className=\"flex items-center gap-2 pt-2 border-t border-border\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_UPDATE}>\n <button\n onClick={() => {\n if (connection) {\n handleUpdateConnection(connection.id);\n } else {\n handleCreateConnection();\n }\n }}\n disabled={\n actionLoading === 'connection' || (!credentialsValid && !!channel.adapterType)\n }\n className=\"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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {actionLoading === 'connection' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Save size={16} />\n )}\n {connection\n ? tr('channel.updateCredentials', 'Update Credentials')\n : tr('channel.saveCredentials', 'Save Credentials')}\n </button>\n </PermissionGuard>\n <button\n onClick={() => {\n setShowConnectionForm(false);\n setCredentials({});\n setCredentialsValid(false);\n }}\n className=\"flex items-center gap-2 px-4 py-2 text-sm text-muted-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <X size={16} />\n {tr('channel.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n )}\n </div>\n\n {/* Test Notification Modal */}\n {showTestModal && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\">\n <div className=\"w-full max-w-md mx-4 p-6 bg-surface border border-border rounded-lg shadow-xl\">\n <div className=\"flex items-center justify-between mb-4\">\n <div className=\"flex items-center gap-2\">\n <Send size={20} className=\"text-accent-primary\" />\n <h3 className=\"text-lg font-semibold text-foreground\">\n Test {PLATFORM_CONFIG[channel.platform]?.label || channel.platform} Channel\n </h3>\n </div>\n <button\n onClick={() => setShowTestModal(false)}\n className=\"p-1 text-muted-foreground hover:text-foreground rounded\"\n >\n <X size={20} />\n </button>\n </div>\n\n <div className=\"space-y-4\">\n {/* Recipient */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1.5\">\n {getRecipientLabel(channel.platform)}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <input\n type=\"text\"\n value={testRecipient}\n onChange={(e) => setTestRecipient(e.target.value)}\n placeholder={getRecipientPlaceholder(channel.platform)}\n className=\"w-full pl-9 pr-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n <div className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\">\n {channel.platform === 'EMAIL' ? (\n <Mail size={16} />\n ) : channel.platform === 'SMS' || channel.platform === 'WHATSAPP' ? (\n <Phone size={16} />\n ) : (\n <MessageSquare size={16} />\n )}\n </div>\n </div>\n </div>\n\n {/* Subject (for email only) */}\n {channel.platform === 'EMAIL' && (\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1.5\">\n Subject\n </label>\n <input\n type=\"text\"\n value={testSubject}\n onChange={(e) => setTestSubject(e.target.value)}\n placeholder=\"Email subject\"\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n )}\n\n {/* Message */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1.5\">\n Message\n </label>\n <textarea\n value={testMessage}\n onChange={(e) => setTestMessage(e.target.value)}\n placeholder=\"Test message content\"\n rows={3}\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg resize-none\"\n />\n </div>\n\n {/* Result */}\n {notificationResult && (\n <div\n className={`p-3 rounded-lg ${\n notificationResult.success\n ? 'bg-status-success-bg-subtle border border-success-300'\n : 'bg-status-error-bg-subtle border border-status-error-border'\n }`}\n >\n <div className=\"flex items-start gap-2\">\n {notificationResult.success ? (\n <CheckCircle2 size={16} className=\"text-status-success-text mt-0.5\" />\n ) : (\n <XCircle size={16} className=\"text-status-error-text mt-0.5\" />\n )}\n <div className=\"flex-1\">\n <p\n className={`text-sm font-medium ${\n notificationResult.success\n ? 'text-status-success-text'\n : 'text-status-error-text'\n }`}\n >\n {notificationResult.success\n ? tr('channel.messageSentSuccess', 'Message sent successfully!')\n : tr('channel.messageSendFailed', 'Failed to send message')}\n </p>\n {notificationResult.messageId && (\n <p className=\"text-xs text-success-600 mt-1\">\n Message ID: {notificationResult.messageId}\n </p>\n )}\n {notificationResult.externalId && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n External ID: {notificationResult.externalId}\n </p>\n )}\n {notificationResult.status && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n Status: {notificationResult.status}\n </p>\n )}\n {notificationResult.error && (\n <p className=\"text-xs text-status-error-text mt-1\">\n {notificationResult.error.message}\n {notificationResult.error.retryable && (\n <span className=\"text-warning-600 ml-1\">(will retry)</span>\n )}\n </p>\n )}\n {notificationResult.sentAt && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n Sent at: {new Date(notificationResult.sentAt).toLocaleString()}\n </p>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center gap-2 pt-2\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_DELIVERY_SEND}>\n <button\n onClick={handleSendTestNotification}\n disabled={actionLoading === 'send-test' || !testRecipient.trim()}\n className=\"flex-1 flex items-center justify-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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {actionLoading === 'send-test' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Send size={16} />\n )}\n {tr('channel.sendTest', 'Send Test')}\n </button>\n </PermissionGuard>\n <button\n onClick={() => setShowTestModal(false)}\n className=\"px-4 py-2 text-sm text-muted-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n {tr('channel.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChannelDetailsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelDetailsPage.tsx"],"sourcesContent":["/**\n * Channel Details Page\n *\n * Displays channel details and provides connection/credentials management\n */\n\nimport { useState, useEffect } from 'react';\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport { nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\nimport {\n Radio,\n ArrowLeft,\n Pencil,\n Loader2,\n AlertCircle,\n RefreshCw,\n Power,\n PowerOff,\n CheckCircle2,\n XCircle,\n AlertTriangle,\n HelpCircle,\n Cable,\n Key,\n Trash2,\n Play,\n Plus,\n Save,\n X,\n Send,\n Mail,\n Phone,\n MessageSquare,\n Clock,\n Truck,\n Users,\n ArrowRight,\n} from 'lucide-react';\nimport {\n useChannel,\n type ChannelPlatform,\n type ChannelStatus,\n type ConnectionHealth,\n type ChannelAuthType,\n type ChannelDeliveryError,\n} from '../../../hooks/useChannels';\nimport {\n AdapterCredentialsForm,\n CredentialValidationStatus,\n} from '../../../components/AdapterCredentialsForm';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { AUTH_TYPE_I18N, HEALTH_I18N, PLATFORM_I18N, STATUS_I18N, translateLabel } from '../i18n';\n\n/** Lightweight notification result for UI display (not the full GraphQL type) */\ninterface NotificationDisplayResult {\n success: boolean;\n messageId?: string | null;\n externalId?: string | null;\n status?: string | null;\n sentAt?: string | null;\n error?: Pick<ChannelDeliveryError, 'code' | 'message' | 'retryable'> | null;\n}\n\n// Platform display configuration\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\n// Status display configuration\nconst STATUS_CONFIG: Record<\n ChannelStatus,\n { label: string; color: string; icon: typeof CheckCircle2 }\n> = {\n ACTIVE: {\n label: 'Active',\n color: 'text-status-success-text bg-status-success-bg-subtle',\n icon: CheckCircle2,\n },\n INACTIVE: { label: 'Inactive', color: 'text-muted-foreground bg-muted', icon: PowerOff },\n ERROR: {\n label: 'Error',\n color: 'text-status-error-text bg-status-error-bg-subtle',\n icon: XCircle,\n },\n SUSPENDED: {\n label: 'Suspended',\n color: 'text-status-warning-text bg-status-warning-bg-subtle',\n icon: AlertTriangle,\n },\n ARCHIVED: { label: 'Archived', color: 'text-muted-foreground bg-muted', icon: PowerOff },\n};\n\n// Health display configuration\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-muted-foreground', icon: HelpCircle },\n};\n\ninterface LocationState {\n pendingCredentials?: Record<string, unknown>;\n showCredentialsForm?: boolean;\n}\n\nexport function ChannelDetailsPage() {\n const { channelId } = useParams<{ channelId: string }>();\n const navigate = useNavigate();\n const location = useLocation();\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 locationState = location.state as LocationState | null;\n\n const [actionLoading, setActionLoading] = useState<string | null>(null);\n const [showConnectionForm, setShowConnectionForm] = useState(\n locationState?.showCredentialsForm || false\n );\n const [credentials, setCredentials] = useState<Record<string, unknown>>(\n locationState?.pendingCredentials || {}\n );\n const [credentialsValid, setCredentialsValid] = useState(false);\n const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null);\n\n // Test notification modal state\n const [showTestModal, setShowTestModal] = useState(false);\n const [testRecipient, setTestRecipient] = useState('');\n const [testSubject, setTestSubject] = useState(() =>\n tr('channel.testNotificationSubject', 'Test Notification')\n );\n const [testMessage, setTestMessage] = useState(() =>\n tr(\n 'channel.testNotificationBody',\n 'This is a test notification from Burdenoff Channel Service.'\n )\n );\n const [notificationResult, setNotificationResult] = useState<NotificationDisplayResult | null>(\n null\n );\n\n // Clear location state after using it to prevent re-triggering on refresh\n useEffect(() => {\n if (locationState?.pendingCredentials || locationState?.showCredentialsForm) {\n // Replace state without the pending data\n navigate(location.pathname, { replace: true, state: null });\n }\n }, []);\n\n const {\n channel,\n isLoading,\n error,\n updateChannel,\n runHealthCheck,\n createConnection,\n updateConnection,\n deleteConnection,\n testConnection,\n sendTestNotification,\n } = useChannel(channelId);\n\n const handleHealthCheck = async () => {\n setActionLoading('health');\n try {\n await runHealthCheck();\n } catch (err) {\n console.error('[channel] Failed to run health check', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleToggleStatus = async () => {\n if (!channel) return;\n setActionLoading('status');\n try {\n await updateChannel({\n status: channel.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE',\n });\n } catch (err) {\n console.error('[channel] Failed to toggle channel status', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleCreateConnection = async () => {\n if (!channel) return;\n setActionLoading('connection');\n try {\n // Determine auth type based on adapter type\n // Most adapters use API_KEY, some use BOT_TOKEN or WEBHOOK\n // Narrow generated enum may lag UI's widened AdapterType (e.g. 'SMTP'); compare as string.\n let authType: ChannelAuthType = 'API_KEY';\n const adapterTypeStr = channel.adapterType as string | null | undefined;\n if (adapterTypeStr?.includes('BOT')) {\n authType = 'BOT_TOKEN';\n } else if (adapterTypeStr?.includes('WEBHOOK')) {\n authType = 'WEBHOOK';\n } else if (adapterTypeStr === 'SMTP') {\n authType = 'SMTP_IMAP';\n }\n\n await createConnection({\n authType,\n credentials,\n });\n setShowConnectionForm(false);\n setCredentials({});\n setCredentialsValid(false);\n } catch (err) {\n console.error('[channel] Failed to create connection', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleUpdateConnection = async (connectionId: string) => {\n setActionLoading('connection');\n try {\n await updateConnection(connectionId, {\n credentials,\n });\n setShowConnectionForm(false);\n setCredentials({});\n setCredentialsValid(false);\n } catch (err) {\n console.error('[channel] Failed to update connection', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleDeleteConnection = async (connectionId: string) => {\n void nativeImpact('medium');\n setActionLoading('delete-connection');\n try {\n await deleteConnection(connectionId);\n void nativeNotify('success');\n } catch (err) {\n void nativeNotify('error');\n console.error('[channel] Failed to delete connection', err);\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleTestConnection = async (connectionId: string) => {\n setActionLoading('test');\n setTestResult(null);\n try {\n const result = await testConnection(connectionId);\n if (result) {\n setTestResult({\n success: result.success,\n message:\n result.message ||\n (result.success\n ? tr('channel.connectionTestSuccess', 'Connection successful!')\n : tr('channel.connectionTestFailed', 'Connection failed')),\n });\n }\n } catch (err) {\n console.error('[channel] Failed to test connection', err);\n setTestResult({\n success: false,\n message: tr('channel.connectionTestRequestFailed', 'Failed to test connection'),\n });\n } finally {\n setActionLoading(null);\n }\n };\n\n const handleSendTestNotification = async () => {\n if (!testRecipient.trim()) return;\n\n setActionLoading('send-test');\n setNotificationResult(null);\n\n try {\n const options =\n channel?.platform === 'EMAIL'\n ? { subject: testSubject, html: `<p>${testMessage}</p>` }\n : undefined;\n\n const result = await sendTestNotification(testRecipient.trim(), testMessage, options);\n if (result) {\n setNotificationResult({\n success: result.success,\n messageId: result.messageId,\n externalId: result.externalId,\n status: result.status,\n sentAt: result.sentAt,\n error: result.error\n ? {\n code: result.error.code,\n message: result.error.message,\n retryable: result.error.retryable,\n }\n : null,\n });\n }\n } catch (err: unknown) {\n const errMsg =\n err instanceof Error\n ? err.message\n : tr('channel.sendTestNotificationFailed', 'Failed to send test notification');\n console.error('[channel] Failed to send test notification', err);\n setNotificationResult({\n success: false,\n error: {\n code: 'SEND_FAILED',\n message: errMsg,\n retryable: false,\n },\n });\n } finally {\n setActionLoading(null);\n }\n };\n\n const getRecipientPlaceholder = (platform: ChannelPlatform) => {\n switch (platform) {\n case 'EMAIL':\n return tr('channel.recipientPlaceholder.email', 'email@example.com');\n case 'SMS':\n return tr('channel.recipientPlaceholder.phone', '+1234567890');\n case 'PUSH':\n return tr('channel.recipientPlaceholder.deviceToken', 'Device token');\n case 'SLACK':\n return tr('channel.recipientPlaceholder.slack', 'Channel ID or User ID');\n case 'TEAMS':\n return tr('channel.recipientPlaceholder.channelId', 'Channel ID');\n case 'DISCORD':\n return tr('channel.recipientPlaceholder.channelId', 'Channel ID');\n case 'WHATSAPP':\n return tr('channel.recipientPlaceholder.phone', '+1234567890');\n default:\n return tr('channel.recipientPlaceholder.default', 'Recipient');\n }\n };\n\n const getRecipientLabel = (platform: ChannelPlatform) => {\n switch (platform) {\n case 'EMAIL':\n return tr('channel.recipientLabel.email', 'Email Address');\n case 'SMS':\n case 'WHATSAPP':\n return tr('channel.recipientLabel.phone', 'Phone Number');\n case 'PUSH':\n return tr('channel.recipientLabel.deviceToken', 'Device Token');\n case 'SLACK':\n case 'TEAMS':\n case 'DISCORD':\n return tr('channel.recipientLabel.channelOrUserId', 'Channel/User ID');\n default:\n return tr('channel.recipientLabel.default', 'Recipient');\n }\n };\n\n const getPlatformConfig = (platform: ChannelPlatform) => {\n const config = PLATFORM_CONFIG[platform] || {\n label: platform,\n color: 'bg-bg-sunken text-text-secondary',\n };\n return {\n ...config,\n label: translateLabel(platform, PLATFORM_I18N, tr),\n };\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\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 // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <Loader2 size={32} className=\"animate-spin text-accent-primary\" />\n </div>\n );\n }\n\n // Error state\n if (error || !channel) {\n return (\n <div className=\"p-6\">\n <div className=\"flex items-center gap-3 p-4 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <AlertCircle size={20} className=\"text-status-error-text\" />\n <div>\n <p className=\"text-sm font-medium text-status-error-text\">\n {error || tr('channel.channelNotFound', 'Channel not found')}\n </p>\n </div>\n <button\n onClick={() => navigate(-1)}\n className=\"ml-auto px-3 py-1 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n >\n {tr('channel.goBack', 'Go Back')}\n </button>\n </div>\n </div>\n );\n }\n\n const platformConfig = getPlatformConfig(channel.platform);\n const statusConfig = getStatusConfig(channel.status);\n const healthConfig = getHealthConfig(channel.connectionHealth);\n const StatusIcon = statusConfig.icon;\n const HealthIcon = healthConfig.icon;\n const connection = channel.connection;\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_READ}>\n <div className=\"space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n onClick={() => navigate(-1)}\n className=\"p-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <ArrowLeft size={20} />\n </button>\n <div>\n <div className=\"flex items-center gap-2\">\n <Radio size={24} className=\"text-accent-primary\" />\n <h1 className=\"text-xl sm:text-2xl font-bold text-foreground\">{channel.name}</h1>\n </div>\n {channel.description && (\n <p className=\"text-sm text-muted-foreground mt-1\">{channel.description}</p>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_EXECUTE}>\n <button\n onClick={handleHealthCheck}\n disabled={actionLoading === 'health'}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-status-info-text hover:bg-status-info-bg-subtle rounded-lg transition-colors disabled:opacity-50\"\n >\n {actionLoading === 'health' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <RefreshCw size={16} />\n )}\n {tr('channel.healthCheck', 'Health Check')}\n </button>\n </PermissionGuard>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <button\n onClick={handleToggleStatus}\n disabled={actionLoading === 'status'}\n className={`flex items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors disabled:opacity-50 ${\n channel.status === 'ACTIVE'\n ? 'text-status-warning-text hover:bg-status-warning-bg-subtle'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {actionLoading === 'status' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : channel.status === 'ACTIVE' ? (\n <PowerOff size={16} />\n ) : (\n <Power size={16} />\n )}\n {channel.status === 'ACTIVE'\n ? tr('channel.deactivate', 'Deactivate')\n : tr('channel.activate', 'Activate')}\n </button>\n </PermissionGuard>\n <button\n onClick={() => {\n setShowTestModal(true);\n setNotificationResult(null);\n setTestRecipient('');\n }}\n disabled={!channel.connection}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-accent-primary hover:bg-accent-primary/10 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n title={\n !channel.connection\n ? tr('channel.configureCredentialsFirst', 'Configure credentials first')\n : tr('channel.sendTestNotification', 'Send test notification')\n }\n >\n <Send size={16} />\n {tr('channel.testChannel', 'Test Channel')}\n </button>\n <button\n onClick={() => navigate('./edit')}\n className=\"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 shadow-sm\"\n >\n <Pencil size={16} />\n {tr('channel.edit', 'Edit')}\n </button>\n </div>\n </div>\n\n {/* Status Cards */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {/* Platform */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-xs text-muted-foreground mb-1\">\n {tr('channel.platform', 'Platform')}\n </p>\n <span\n className={`inline-flex px-2 py-1 text-sm font-semibold rounded-full ${platformConfig.color}`}\n >\n {platformConfig.label}\n </span>\n </div>\n\n {/* Status */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-xs text-muted-foreground mb-1\">{tr('channel.status', 'Status')}</p>\n <div className=\"flex items-center gap-2\">\n <StatusIcon size={16} className={statusConfig.color.split(' ')[0]} />\n <span className={`px-2 py-1 text-sm font-medium rounded-full ${statusConfig.color}`}>\n {statusConfig.label}\n </span>\n </div>\n </div>\n\n {/* Health */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-xs text-muted-foreground mb-1\">\n {tr('channel.connectionHealth', 'Connection Health')}\n </p>\n <div className=\"flex items-center gap-2\">\n <HealthIcon size={16} className={healthConfig.color} />\n <span className={`text-sm font-medium ${healthConfig.color}`}>\n {healthConfig.label}\n </span>\n </div>\n {channel.lastHealthCheckAt && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n {tr('channel.lastCheck', 'Last check')}:{' '}\n {new Date(channel.lastHealthCheckAt).toLocaleString()}\n </p>\n )}\n </div>\n\n {/* Connection Status */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <p className=\"text-xs text-muted-foreground mb-1\">\n {tr('channel.connection', 'Connection')}\n </p>\n <div className=\"flex items-center gap-2\">\n <Cable\n size={16}\n className={\n channel.isConnected ? 'text-status-success-text' : 'text-muted-foreground'\n }\n />\n <span\n className={`text-sm font-medium ${channel.isConnected ? 'text-status-success-text' : 'text-muted-foreground'}`}\n >\n {channel.isConnected\n ? tr('channel.connected', 'Connected')\n : tr('channel.notConnected', 'Not Connected')}\n </span>\n </div>\n </div>\n </div>\n\n {/* Channel Details */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <h2 className=\"text-lg font-semibold text-foreground mb-4\">\n {tr('channel.channelDetails', 'Channel Details')}\n </h2>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.adapterType', 'Adapter Type')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {channel.adapterType?.replace(/_/g, ' ') ||\n tr('channel.notConfigured', 'Not configured')}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">{tr('channel.priority', 'Priority')}</p>\n <p className=\"text-sm font-medium text-foreground\">{channel.priority}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.consecutiveFailures', 'Consecutive Failures')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">{channel.consecutiveFailures}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.messages24h', 'Messages (24h)')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">{channel.messageCount24h}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.createdAt', 'Created At')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {new Date(channel.createdAt).toLocaleString()}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.updatedAt', 'Updated At')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {new Date(channel.updatedAt).toLocaleString()}\n </p>\n </div>\n </div>\n\n {/* Tags */}\n {channel.tagAssignments && channel.tagAssignments.length > 0 && (\n <div className=\"mt-4\">\n <p className=\"text-xs text-muted-foreground mb-1.5\">{tr('channel.tags', 'Tags')}</p>\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"Channel tags\">\n {channel.tagAssignments.map((a) => (\n <TagBadge key={a.id} id={a.tagId} label={a.tagId} size=\"md\" />\n ))}\n </div>\n </div>\n )}\n\n {/* Health Check Error */}\n {channel.healthCheckError && (\n <div className=\"mt-4 p-3 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <p className=\"text-xs text-status-error-text font-medium\">\n {tr('channel.lastHealthCheckError', 'Last Health Check Error')}\n </p>\n <p className=\"text-sm text-status-error-text\">{channel.healthCheckError}</p>\n </div>\n )}\n </div>\n\n {/* Quick Links to Channel Sub-Pages */}\n <div className=\"grid gap-3 sm:grid-cols-3\">\n <button\n onClick={() => navigate(`./deliveries`)}\n className=\"flex items-center gap-3 p-3 bg-surface border border-border rounded-lg hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <div className=\"p-2 bg-accent-orange/10 rounded-lg\">\n <Truck size={18} className=\"text-accent-orange\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground\">\n {tr('channel.deliveryHistory', 'Delivery History')}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">\n {tr('channel.viewMessageDeliveries', 'View message deliveries')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-muted-foreground shrink-0\" />\n </button>\n <button\n onClick={() => navigate(`./webhooks`)}\n className=\"flex items-center gap-3 p-3 bg-surface border border-border rounded-lg hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <div className=\"p-2 bg-accent-purple/10 rounded-lg\">\n <Clock size={18} className=\"text-accent-purple\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground\">\n {tr('channel.webhookEvents', 'Webhook Events')}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">\n {tr('channel.viewIncomingWebhooks', 'View incoming webhooks')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-muted-foreground shrink-0\" />\n </button>\n <button\n onClick={() => navigate(`./users`)}\n className=\"flex items-center gap-3 p-3 bg-surface border border-border rounded-lg hover:bg-action-primary-bg/10 transition-colors text-left\"\n >\n <div className=\"p-2 bg-accent-blue/10 rounded-lg\">\n <Users size={18} className=\"text-accent-blue\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground\">\n {tr('channel.channelUsers', 'Channel Users')}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">\n {tr('channel.viewConnectedUsers', 'View connected users')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-muted-foreground shrink-0\" />\n </button>\n </div>\n\n {/* Connection / Credentials Management */}\n <div className=\"p-4 bg-surface border border-border rounded-lg\">\n <div className=\"flex items-center justify-between mb-4\">\n <div className=\"flex items-center gap-2\">\n <Key size={20} className=\"text-accent-primary\" />\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('channel.connectionAndCredentials', 'Connection & Credentials')}\n </h2>\n </div>\n {!connection && !showConnectionForm && (\n <button\n onClick={() => setShowConnectionForm(true)}\n className=\"flex items-center gap-2 px-3 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={16} />\n {tr('channel.addCredentials', 'Add Credentials')}\n </button>\n )}\n </div>\n\n {/* Existing Connection */}\n {connection && !showConnectionForm && (\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.authType', 'Auth Type')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {translateLabel(connection.authType, AUTH_TYPE_I18N, tr)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">{tr('channel.status', 'Status')}</p>\n <div className=\"flex items-center gap-2\">\n {connection.isHealthy ? (\n <CheckCircle2 size={14} className=\"text-status-success-text\" />\n ) : (\n <XCircle size={14} className=\"text-status-error-text\" />\n )}\n <span\n className={`text-sm font-medium ${connection.isHealthy ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {connection.isHealthy\n ? tr('channel.healthy', 'Healthy')\n : tr('channel.unhealthy', 'Unhealthy')}\n </span>\n </div>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.lastTested', 'Last Tested')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {connection.lastTestedAt\n ? new Date(connection.lastTestedAt).toLocaleString()\n : tr('channel.never', 'Never')}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.testStatus', 'Test Status')}\n </p>\n <p className=\"text-sm font-medium text-foreground\">\n {connection.testStatus || tr('channel.notTested', 'Not tested')}\n </p>\n </div>\n {connection.tokenExpiresAt && (\n <div>\n <p className=\"text-xs text-muted-foreground\">\n {tr('channel.tokenExpires', 'Token Expires')}\n </p>\n <p\n className={`text-sm font-medium ${connection.needsTokenRefresh ? 'text-status-warning-text' : 'text-foreground'}`}\n >\n {new Date(connection.tokenExpiresAt).toLocaleString()}\n {connection.daysUntilExpiry !== undefined && (\n <span className=\"text-xs text-muted-foreground ml-1\">\n ({connection.daysUntilExpiry} {tr('channel.days', 'days')})\n </span>\n )}\n </p>\n </div>\n )}\n </div>\n\n {/* Test Result */}\n {testResult && (\n <div\n className={`p-3 rounded-lg ${testResult.success ? 'bg-status-success-bg-subtle border border-status-success-border' : 'bg-status-error-bg-subtle border border-status-error-border'}`}\n >\n <div className=\"flex items-center gap-2\">\n {testResult.success ? (\n <CheckCircle2 size={16} className=\"text-status-success-text\" />\n ) : (\n <XCircle size={16} className=\"text-status-error-text\" />\n )}\n <p\n className={`text-sm font-medium ${testResult.success ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {testResult.message}\n </p>\n </div>\n </div>\n )}\n\n {/* Connection Actions */}\n <div className=\"flex items-center gap-2 pt-2\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_UPDATE}>\n <button\n onClick={() => handleTestConnection(connection.id)}\n disabled={actionLoading === 'test'}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-status-info-text hover:bg-status-info-bg-subtle rounded-lg transition-colors disabled:opacity-50\"\n >\n {actionLoading === 'test' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Play size={16} />\n )}\n {tr('channel.testConnection', 'Test Connection')}\n </button>\n </PermissionGuard>\n <button\n onClick={() => {\n setShowConnectionForm(true);\n // platformMetadata is the non-sensitive metadata the backend returns for\n // an existing connection (e.g. region, endpoint, accountId). The backend\n // intentionally omits or masks sensitive credential fields (access keys,\n // secrets, tokens) before returning this object, so pre-populating the\n // form is safe. The user will be prompted to re-enter any masked secrets\n // before submission because AdapterCredentialsForm marks them as required.\n setCredentials((connection?.platformMetadata as Record<string, unknown>) ?? {});\n }}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-accent-primary hover:bg-accent-primary/10 rounded-lg transition-colors\"\n >\n <Pencil size={16} />\n {tr('channel.updateCredentials', 'Update Credentials')}\n </button>\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_DELETE}>\n <button\n onClick={() => handleDeleteConnection(connection.id)}\n disabled={actionLoading === 'delete-connection'}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors disabled:opacity-50\"\n >\n {actionLoading === 'delete-connection' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Trash2 size={16} />\n )}\n {tr('channel.remove', 'Remove')}\n </button>\n </PermissionGuard>\n </div>\n </div>\n )}\n\n {/* No Connection */}\n {!connection && !showConnectionForm && (\n <div className=\"text-center py-8\">\n <Cable size={32} className=\"mx-auto text-muted-foreground mb-3\" />\n <p className=\"text-sm text-muted-foreground mb-2\">\n {tr(\n 'channel.noCredentialsConfigured',\n 'No credentials configured for this channel'\n )}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {tr(\n 'channel.addCredentialsHint',\n 'Add credentials to enable message delivery through this channel'\n )}\n </p>\n </div>\n )}\n\n {/* Connection Form */}\n {showConnectionForm && (\n <div className=\"space-y-4\">\n {/* Show info if no adapter type selected */}\n {!channel.adapterType && (\n <div className=\"p-3 bg-accent-orange/10 border border-accent-orange/30 rounded-lg\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle size={16} className=\"text-accent-orange mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-accent-orange\">\n {tr('channel.noAdapterTypeConfigured', 'No adapter type configured')}\n </p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {tr(\n 'channel.noAdapterTypeConfiguredHint',\n 'You can still add credentials below. The system will attempt to validate them when the channel is configured with an adapter type.'\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Adapter-specific credential form */}\n <AdapterCredentialsForm\n adapterType={channel.adapterType ?? undefined}\n credentials={credentials}\n onChange={setCredentials}\n onValidationChange={(valid) => setCredentialsValid(valid)}\n disabled={actionLoading === 'connection'}\n />\n\n {/* Validation status */}\n {channel.adapterType && (\n <div className=\"pt-2\">\n <CredentialValidationStatus\n adapterType={channel.adapterType}\n credentials={credentials}\n />\n </div>\n )}\n\n {/* Form Actions */}\n <div className=\"flex items-center gap-2 pt-2 border-t border-border\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_UPDATE}>\n <button\n onClick={() => {\n if (connection) {\n handleUpdateConnection(connection.id);\n } else {\n handleCreateConnection();\n }\n }}\n disabled={\n actionLoading === 'connection' || (!credentialsValid && !!channel.adapterType)\n }\n className=\"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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {actionLoading === 'connection' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Save size={16} />\n )}\n {connection\n ? tr('channel.updateCredentials', 'Update Credentials')\n : tr('channel.saveCredentials', 'Save Credentials')}\n </button>\n </PermissionGuard>\n <button\n onClick={() => {\n setShowConnectionForm(false);\n setCredentials({});\n setCredentialsValid(false);\n }}\n className=\"flex items-center gap-2 px-4 py-2 text-sm text-muted-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <X size={16} />\n {tr('channel.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n )}\n </div>\n\n {/* Test Notification Modal */}\n {showTestModal && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\">\n <div className=\"w-full max-w-md mx-4 p-6 bg-surface border border-border rounded-lg shadow-xl\">\n <div className=\"flex items-center justify-between mb-4\">\n <div className=\"flex items-center gap-2\">\n <Send size={20} className=\"text-accent-primary\" />\n <h3 className=\"text-lg font-semibold text-foreground\">\n Test {PLATFORM_CONFIG[channel.platform]?.label || channel.platform} Channel\n </h3>\n </div>\n <button\n onClick={() => setShowTestModal(false)}\n className=\"p-1 text-muted-foreground hover:text-foreground rounded\"\n >\n <X size={20} />\n </button>\n </div>\n\n <div className=\"space-y-4\">\n {/* Recipient */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1.5\">\n {getRecipientLabel(channel.platform)}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <input\n type=\"text\"\n value={testRecipient}\n onChange={(e) => setTestRecipient(e.target.value)}\n placeholder={getRecipientPlaceholder(channel.platform)}\n className=\"w-full pl-9 pr-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n <div className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\">\n {channel.platform === 'EMAIL' ? (\n <Mail size={16} />\n ) : channel.platform === 'SMS' || channel.platform === 'WHATSAPP' ? (\n <Phone size={16} />\n ) : (\n <MessageSquare size={16} />\n )}\n </div>\n </div>\n </div>\n\n {/* Subject (for email only) */}\n {channel.platform === 'EMAIL' && (\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1.5\">\n Subject\n </label>\n <input\n type=\"text\"\n value={testSubject}\n onChange={(e) => setTestSubject(e.target.value)}\n placeholder=\"Email subject\"\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n )}\n\n {/* Message */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1.5\">\n Message\n </label>\n <textarea\n value={testMessage}\n onChange={(e) => setTestMessage(e.target.value)}\n placeholder=\"Test message content\"\n rows={3}\n className=\"w-full px-3 py-2 text-sm bg-surface border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-bg resize-none\"\n />\n </div>\n\n {/* Result */}\n {notificationResult && (\n <div\n className={`p-3 rounded-lg ${\n notificationResult.success\n ? 'bg-status-success-bg-subtle border border-status-success-border'\n : 'bg-status-error-bg-subtle border border-status-error-border'\n }`}\n >\n <div className=\"flex items-start gap-2\">\n {notificationResult.success ? (\n <CheckCircle2 size={16} className=\"text-status-success-text mt-0.5\" />\n ) : (\n <XCircle size={16} className=\"text-status-error-text mt-0.5\" />\n )}\n <div className=\"flex-1\">\n <p\n className={`text-sm font-medium ${\n notificationResult.success\n ? 'text-status-success-text'\n : 'text-status-error-text'\n }`}\n >\n {notificationResult.success\n ? tr('channel.messageSentSuccess', 'Message sent successfully!')\n : tr('channel.messageSendFailed', 'Failed to send message')}\n </p>\n {notificationResult.messageId && (\n <p className=\"text-xs text-success-600 mt-1\">\n Message ID: {notificationResult.messageId}\n </p>\n )}\n {notificationResult.externalId && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n External ID: {notificationResult.externalId}\n </p>\n )}\n {notificationResult.status && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n Status: {notificationResult.status}\n </p>\n )}\n {notificationResult.error && (\n <p className=\"text-xs text-status-error-text mt-1\">\n {notificationResult.error.message}\n {notificationResult.error.retryable && (\n <span className=\"text-warning-600 ml-1\">(will retry)</span>\n )}\n </p>\n )}\n {notificationResult.sentAt && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n Sent at: {new Date(notificationResult.sentAt).toLocaleString()}\n </p>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center gap-2 pt-2\">\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_DELIVERY_SEND}>\n <button\n onClick={handleSendTestNotification}\n disabled={actionLoading === 'send-test' || !testRecipient.trim()}\n className=\"flex-1 flex items-center justify-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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {actionLoading === 'send-test' ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Send size={16} />\n )}\n {tr('channel.sendTest', 'Send Test')}\n </button>\n </PermissionGuard>\n <button\n onClick={() => setShowTestModal(false)}\n className=\"px-4 py-2 text-sm text-muted-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n {tr('channel.close', 'Close')}\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -113,7 +113,7 @@ function x() {
113
113
  })]
114
114
  }),
115
115
  M && /* @__PURE__ */ y("div", {
116
- className: "p-4 rounded-lg bg-success-500/10 border border-success-500/20 flex items-center gap-3",
116
+ className: "p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border flex items-center gap-3",
117
117
  children: [/* @__PURE__ */ v(u, { className: "w-5 h-5 text-status-success-text shrink-0" }), /* @__PURE__ */ v("p", {
118
118
  className: "text-sm text-status-success-text",
119
119
  children: C("channel.settingsSaved", "Settings saved successfully")
@@ -366,7 +366,7 @@ function x() {
366
366
  })]
367
367
  }),
368
368
  /* @__PURE__ */ y("div", {
369
- className: "p-4 rounded-lg border border-warning-500/20 bg-warning-500/5",
369
+ className: "p-4 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle",
370
370
  children: [/* @__PURE__ */ y("div", {
371
371
  className: "flex items-center justify-between mb-2",
372
372
  children: [/* @__PURE__ */ v("p", {
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelSettingsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelSettingsPage.tsx"],"sourcesContent":["/**\n * Channel Settings Page\n *\n * Global settings for the channels module.\n */\n\nimport { useState, useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ArrowLeft,\n Settings,\n Bell,\n Shield,\n Clock,\n Database,\n Save,\n Loader2,\n AlertCircle,\n CheckCircle2,\n} from 'lucide-react';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { useChannelOrgSettings } from '../../../hooks/useChannelOrgSettings';\nimport type { ChannelOrgSettingsInput } from '../../../hooks/useChannelOrgSettings';\n\ninterface ChannelSettings {\n defaultPriority: number;\n rateLimitPerMinute: number;\n rateLimitPerHour: number;\n healthCheckIntervalMinutes: number;\n enableAuditLogging: boolean;\n enableWebhookVerification: boolean;\n sessionTimeoutMinutes: number;\n}\n\nconst DEFAULT_SETTINGS: ChannelSettings = {\n defaultPriority: 2,\n rateLimitPerMinute: 100,\n rateLimitPerHour: 5000,\n healthCheckIntervalMinutes: 5,\n enableAuditLogging: true,\n enableWebhookVerification: true,\n sessionTimeoutMinutes: 30,\n};\n\nexport function ChannelSettingsPage() {\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const {\n settings: remoteSettings,\n isLoading: isLoadingSettings,\n error: settingsLoadError,\n saveSettings,\n } = useChannelOrgSettings();\n\n const [settings, setSettings] = useState<ChannelSettings>(DEFAULT_SETTINGS);\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const saveSuccessTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Populate / re-sync form from remote settings whenever they change.\n // This handles both the initial load and server-side normalisations returned\n // by saveSettings (e.g. clamped values), so the form always reflects the\n // true persisted state rather than potentially stale local values.\n // Defer setState to a microtask to avoid react-hooks/exhaustive-deps warnings.\n useEffect(() => {\n return () => {\n if (saveSuccessTimerRef.current) clearTimeout(saveSuccessTimerRef.current);\n };\n }, []);\n\n const loadedOrgIdRef = useRef<string | null>(null);\n useEffect(() => {\n if (!remoteSettings) return;\n // Re-hydrate whenever the org changes *or* the updatedAt timestamp changes,\n // so server-side coercion after a save is reflected immediately.\n const changeKey = `${remoteSettings.organizationId}:${remoteSettings.updatedAt}`;\n if (changeKey === loadedOrgIdRef.current) return;\n loadedOrgIdRef.current = changeKey;\n void Promise.resolve().then(() => {\n setSettings({\n defaultPriority: remoteSettings.defaultPriority,\n rateLimitPerMinute: remoteSettings.rateLimitPerMinute,\n rateLimitPerHour: remoteSettings.rateLimitPerHour,\n healthCheckIntervalMinutes: remoteSettings.healthCheckIntervalMinutes,\n enableAuditLogging: remoteSettings.enableAuditLogging,\n enableWebhookVerification: remoteSettings.enableWebhookVerification,\n sessionTimeoutMinutes: remoteSettings.sessionTimeoutMinutes,\n });\n });\n }, [remoteSettings]);\n\n const handleSave = async () => {\n setIsSaving(true);\n setSaveError(null);\n setSaveSuccess(false);\n\n try {\n const input: ChannelOrgSettingsInput = {\n defaultPriority: settings.defaultPriority,\n rateLimitPerMinute: settings.rateLimitPerMinute,\n rateLimitPerHour: settings.rateLimitPerHour,\n healthCheckIntervalMinutes: settings.healthCheckIntervalMinutes,\n enableAuditLogging: settings.enableAuditLogging,\n enableWebhookVerification: settings.enableWebhookVerification,\n sessionTimeoutMinutes: settings.sessionTimeoutMinutes,\n };\n // saveSettings returns null when organizationId is absent (context not\n // ready); only show success when something was actually persisted (P2).\n const saved = await saveSettings(input);\n if (saved) {\n setSaveSuccess(true);\n if (saveSuccessTimerRef.current) clearTimeout(saveSuccessTimerRef.current);\n saveSuccessTimerRef.current = setTimeout(() => setSaveSuccess(false), 3000);\n } else {\n setSaveError(\n tr(\n 'channel.settingsSaveNotPersisted',\n 'Settings could not be saved — organization context is unavailable.'\n )\n );\n }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : 'Failed to save settings';\n setSaveError(message);\n } finally {\n setIsSaving(false);\n }\n };\n\n const updateSetting = <K extends keyof ChannelSettings>(key: K, value: ChannelSettings[K]) => {\n setSettings((prev) => ({ ...prev, [key]: value }));\n };\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <div className=\"min-h-screen bg-background\">\n {/* Header */}\n <div className=\"border-b bg-card\">\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 onClick={() => navigate('./list')}\n className=\"p-2 hover:bg-muted rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"w-5 h-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-primary/10 rounded-lg\">\n <Settings className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold\">\n {tr('channel.channelSettings', 'Channel Settings')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'channel.channelSettingsSubtitle',\n 'Configure global channel preferences'\n )}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"container mx-auto px-6 py-8 max-w-2xl space-y-8\">\n {/* Loading state */}\n {isLoadingSettings && (\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n <span className=\"text-sm\">Loading settings…</span>\n </div>\n )}\n\n {/* Settings load error */}\n {settingsLoadError && (\n <div className=\"p-4 rounded-lg bg-status-error-bg-subtle border border-status-error-border flex items-center gap-3\">\n <AlertCircle className=\"w-5 h-5 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">\n {tr(\n 'channel.settingsLoadError',\n 'Failed to load settings — saving is disabled to prevent overwriting existing configuration.'\n )}\n </p>\n </div>\n )}\n\n {/* Alerts */}\n {saveSuccess && (\n <div className=\"p-4 rounded-lg bg-success-500/10 border border-success-500/20 flex items-center gap-3\">\n <CheckCircle2 className=\"w-5 h-5 text-status-success-text shrink-0\" />\n <p className=\"text-sm text-status-success-text\">\n {tr('channel.settingsSaved', 'Settings saved successfully')}\n </p>\n </div>\n )}\n\n {saveError && (\n <div className=\"p-4 rounded-lg bg-destructive/10 border border-destructive/20 flex items-center gap-3\">\n <AlertCircle className=\"w-5 h-5 text-destructive shrink-0\" />\n <p className=\"text-sm text-destructive\">{saveError}</p>\n </div>\n )}\n\n {/* Delivery Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Bell className=\"w-5 h-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.deliverySettings', 'Delivery Settings')}\n </h2>\n </div>\n\n <div className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.defaultPriority', 'Default Priority')}\n </label>\n <select\n value={settings.defaultPriority}\n onChange={(e) => updateSetting('defaultPriority', Number(e.target.value))}\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={0}>0 - Lowest</option>\n <option value={1}>1 - Low</option>\n <option value={2}>2 - Normal</option>\n <option value={3}>3 - High</option>\n <option value={4}>4 - Highest</option>\n </select>\n <p className=\"text-xs text-muted-foreground mt-1\">\n Priority used when creating new channels without explicit priority\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.rateLimitPerMin', 'Rate Limit (per minute)')}\n </label>\n <input\n type=\"number\"\n value={settings.rateLimitPerMinute}\n onChange={(e) => updateSetting('rateLimitPerMinute', Number(e.target.value))}\n min={1}\n max={1000}\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.rateLimitPerHour', 'Rate Limit (per hour)')}\n </label>\n <input\n type=\"number\"\n value={settings.rateLimitPerHour}\n onChange={(e) => updateSetting('rateLimitPerHour', Number(e.target.value))}\n min={1}\n max={50000}\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n </div>\n </div>\n </div>\n\n {/* Health Check Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Clock className=\"w-5 h-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.healthMonitoring', 'Health Monitoring')}\n </h2>\n </div>\n\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n Health Check Interval (minutes)\n </label>\n <select\n value={settings.healthCheckIntervalMinutes}\n onChange={(e) =>\n updateSetting('healthCheckIntervalMinutes', Number(e.target.value))\n }\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={1}>1 minute</option>\n <option value={5}>5 minutes</option>\n <option value={10}>10 minutes</option>\n <option value={15}>15 minutes</option>\n <option value={30}>30 minutes</option>\n <option value={60}>1 hour</option>\n </select>\n <p className=\"text-xs text-muted-foreground mt-1\">\n How often to check connection health status\n </p>\n </div>\n </div>\n\n {/* Security Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Shield className=\"w-5 h-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">{tr('channel.security', 'Security')}</h2>\n </div>\n\n <div className=\"space-y-4\">\n <label className=\"flex items-center justify-between p-4 rounded-lg border hover:bg-muted/50 cursor-pointer\">\n <div>\n <p className=\"font-medium\">\n {tr('channel.webhookSignatureVerification', 'Webhook Signature Verification')}\n </p>\n <p className=\"text-sm text-muted-foreground\">\n Verify HMAC signatures on incoming webhooks\n </p>\n </div>\n <input\n type=\"checkbox\"\n checked={settings.enableWebhookVerification}\n onChange={(e) => updateSetting('enableWebhookVerification', e.target.checked)}\n className=\"w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary\"\n />\n </label>\n\n <label className=\"flex items-center justify-between p-4 rounded-lg border hover:bg-muted/50 cursor-pointer\">\n <div>\n <p className=\"font-medium\">{tr('channel.auditLogging', 'Audit Logging')}</p>\n <p className=\"text-sm text-muted-foreground\">\n Log all channel operations for audit trail\n </p>\n </div>\n <input\n type=\"checkbox\"\n checked={settings.enableAuditLogging}\n onChange={(e) => updateSetting('enableAuditLogging', e.target.checked)}\n className=\"w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary\"\n />\n </label>\n\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.sessionTimeout', 'Session Timeout (minutes)')}\n </label>\n <select\n value={settings.sessionTimeoutMinutes}\n onChange={(e) => updateSetting('sessionTimeoutMinutes', Number(e.target.value))}\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={15}>15 minutes</option>\n <option value={30}>30 minutes</option>\n <option value={60}>1 hour</option>\n <option value={120}>2 hours</option>\n <option value={240}>4 hours</option>\n </select>\n </div>\n </div>\n </div>\n\n {/* Data Management */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Database className=\"w-5 h-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.dataManagement', 'Data Management')}\n </h2>\n </div>\n\n <div className=\"space-y-4\">\n <div className=\"p-4 rounded-lg border\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.webhookEventRetention', 'Webhook Event Retention')}\n </p>\n <span className=\"text-sm text-muted-foreground\">7 days</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n Incoming webhook events are automatically deleted after 7 days\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.deliveryAttemptRetention', 'Delivery Attempt Retention')}\n </p>\n <span className=\"text-sm text-muted-foreground\">7 days</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n Successful delivery attempts are automatically deleted after 7 days\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border border-warning-500/20 bg-warning-500/5\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.failedDeliveryRetention', 'Failed Delivery Retention')}\n </p>\n <span className=\"text-sm text-status-warning-text\">30 days</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n Failed delivery attempts are kept for 30 days for debugging\n </p>\n </div>\n </div>\n </div>\n\n {/* Save Button */}\n <div className=\"flex justify-end pt-4\">\n <button\n onClick={handleSave}\n disabled={isSaving || isLoadingSettings || !!settingsLoadError}\n className=\"px-6 py-3 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50 flex items-center gap-2\"\n >\n {isSaving ? (\n <Loader2 className=\"w-5 h-5 animate-spin\" />\n ) : (\n <Save className=\"w-5 h-5\" />\n )}\n {tr('channel.saveSettings', 'Save Settings')}\n </button>\n </div>\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChannelSettingsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelSettingsPage.tsx"],"sourcesContent":["/**\n * Channel Settings Page\n *\n * Global settings for the channels module.\n */\n\nimport { useState, useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ArrowLeft,\n Settings,\n Bell,\n Shield,\n Clock,\n Database,\n Save,\n Loader2,\n AlertCircle,\n CheckCircle2,\n} from 'lucide-react';\nimport { PermissionGuard } from '../../../components/PermissionGuard';\nimport { CHANNEL_PERMISSIONS } from '../../../constants/permissions';\nimport { useChannelOrgSettings } from '../../../hooks/useChannelOrgSettings';\nimport type { ChannelOrgSettingsInput } from '../../../hooks/useChannelOrgSettings';\n\ninterface ChannelSettings {\n defaultPriority: number;\n rateLimitPerMinute: number;\n rateLimitPerHour: number;\n healthCheckIntervalMinutes: number;\n enableAuditLogging: boolean;\n enableWebhookVerification: boolean;\n sessionTimeoutMinutes: number;\n}\n\nconst DEFAULT_SETTINGS: ChannelSettings = {\n defaultPriority: 2,\n rateLimitPerMinute: 100,\n rateLimitPerHour: 5000,\n healthCheckIntervalMinutes: 5,\n enableAuditLogging: true,\n enableWebhookVerification: true,\n sessionTimeoutMinutes: 30,\n};\n\nexport function ChannelSettingsPage() {\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const {\n settings: remoteSettings,\n isLoading: isLoadingSettings,\n error: settingsLoadError,\n saveSettings,\n } = useChannelOrgSettings();\n\n const [settings, setSettings] = useState<ChannelSettings>(DEFAULT_SETTINGS);\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const saveSuccessTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Populate / re-sync form from remote settings whenever they change.\n // This handles both the initial load and server-side normalisations returned\n // by saveSettings (e.g. clamped values), so the form always reflects the\n // true persisted state rather than potentially stale local values.\n // Defer setState to a microtask to avoid react-hooks/exhaustive-deps warnings.\n useEffect(() => {\n return () => {\n if (saveSuccessTimerRef.current) clearTimeout(saveSuccessTimerRef.current);\n };\n }, []);\n\n const loadedOrgIdRef = useRef<string | null>(null);\n useEffect(() => {\n if (!remoteSettings) return;\n // Re-hydrate whenever the org changes *or* the updatedAt timestamp changes,\n // so server-side coercion after a save is reflected immediately.\n const changeKey = `${remoteSettings.organizationId}:${remoteSettings.updatedAt}`;\n if (changeKey === loadedOrgIdRef.current) return;\n loadedOrgIdRef.current = changeKey;\n void Promise.resolve().then(() => {\n setSettings({\n defaultPriority: remoteSettings.defaultPriority,\n rateLimitPerMinute: remoteSettings.rateLimitPerMinute,\n rateLimitPerHour: remoteSettings.rateLimitPerHour,\n healthCheckIntervalMinutes: remoteSettings.healthCheckIntervalMinutes,\n enableAuditLogging: remoteSettings.enableAuditLogging,\n enableWebhookVerification: remoteSettings.enableWebhookVerification,\n sessionTimeoutMinutes: remoteSettings.sessionTimeoutMinutes,\n });\n });\n }, [remoteSettings]);\n\n const handleSave = async () => {\n setIsSaving(true);\n setSaveError(null);\n setSaveSuccess(false);\n\n try {\n const input: ChannelOrgSettingsInput = {\n defaultPriority: settings.defaultPriority,\n rateLimitPerMinute: settings.rateLimitPerMinute,\n rateLimitPerHour: settings.rateLimitPerHour,\n healthCheckIntervalMinutes: settings.healthCheckIntervalMinutes,\n enableAuditLogging: settings.enableAuditLogging,\n enableWebhookVerification: settings.enableWebhookVerification,\n sessionTimeoutMinutes: settings.sessionTimeoutMinutes,\n };\n // saveSettings returns null when organizationId is absent (context not\n // ready); only show success when something was actually persisted (P2).\n const saved = await saveSettings(input);\n if (saved) {\n setSaveSuccess(true);\n if (saveSuccessTimerRef.current) clearTimeout(saveSuccessTimerRef.current);\n saveSuccessTimerRef.current = setTimeout(() => setSaveSuccess(false), 3000);\n } else {\n setSaveError(\n tr(\n 'channel.settingsSaveNotPersisted',\n 'Settings could not be saved — organization context is unavailable.'\n )\n );\n }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : 'Failed to save settings';\n setSaveError(message);\n } finally {\n setIsSaving(false);\n }\n };\n\n const updateSetting = <K extends keyof ChannelSettings>(key: K, value: ChannelSettings[K]) => {\n setSettings((prev) => ({ ...prev, [key]: value }));\n };\n\n return (\n <PermissionGuard permission={CHANNEL_PERMISSIONS.CHANNEL_UPDATE}>\n <div className=\"min-h-screen bg-background\">\n {/* Header */}\n <div className=\"border-b bg-card\">\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 onClick={() => navigate('./list')}\n className=\"p-2 hover:bg-muted rounded-lg transition-colors\"\n >\n <ArrowLeft className=\"w-5 h-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-primary/10 rounded-lg\">\n <Settings className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold\">\n {tr('channel.channelSettings', 'Channel Settings')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'channel.channelSettingsSubtitle',\n 'Configure global channel preferences'\n )}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"container mx-auto px-6 py-8 max-w-2xl space-y-8\">\n {/* Loading state */}\n {isLoadingSettings && (\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n <span className=\"text-sm\">Loading settings…</span>\n </div>\n )}\n\n {/* Settings load error */}\n {settingsLoadError && (\n <div className=\"p-4 rounded-lg bg-status-error-bg-subtle border border-status-error-border flex items-center gap-3\">\n <AlertCircle className=\"w-5 h-5 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">\n {tr(\n 'channel.settingsLoadError',\n 'Failed to load settings — saving is disabled to prevent overwriting existing configuration.'\n )}\n </p>\n </div>\n )}\n\n {/* Alerts */}\n {saveSuccess && (\n <div className=\"p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border flex items-center gap-3\">\n <CheckCircle2 className=\"w-5 h-5 text-status-success-text shrink-0\" />\n <p className=\"text-sm text-status-success-text\">\n {tr('channel.settingsSaved', 'Settings saved successfully')}\n </p>\n </div>\n )}\n\n {saveError && (\n <div className=\"p-4 rounded-lg bg-destructive/10 border border-destructive/20 flex items-center gap-3\">\n <AlertCircle className=\"w-5 h-5 text-destructive shrink-0\" />\n <p className=\"text-sm text-destructive\">{saveError}</p>\n </div>\n )}\n\n {/* Delivery Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Bell className=\"w-5 h-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.deliverySettings', 'Delivery Settings')}\n </h2>\n </div>\n\n <div className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.defaultPriority', 'Default Priority')}\n </label>\n <select\n value={settings.defaultPriority}\n onChange={(e) => updateSetting('defaultPriority', Number(e.target.value))}\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={0}>0 - Lowest</option>\n <option value={1}>1 - Low</option>\n <option value={2}>2 - Normal</option>\n <option value={3}>3 - High</option>\n <option value={4}>4 - Highest</option>\n </select>\n <p className=\"text-xs text-muted-foreground mt-1\">\n Priority used when creating new channels without explicit priority\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.rateLimitPerMin', 'Rate Limit (per minute)')}\n </label>\n <input\n type=\"number\"\n value={settings.rateLimitPerMinute}\n onChange={(e) => updateSetting('rateLimitPerMinute', Number(e.target.value))}\n min={1}\n max={1000}\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.rateLimitPerHour', 'Rate Limit (per hour)')}\n </label>\n <input\n type=\"number\"\n value={settings.rateLimitPerHour}\n onChange={(e) => updateSetting('rateLimitPerHour', Number(e.target.value))}\n min={1}\n max={50000}\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n </div>\n </div>\n </div>\n\n {/* Health Check Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Clock className=\"w-5 h-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.healthMonitoring', 'Health Monitoring')}\n </h2>\n </div>\n\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n Health Check Interval (minutes)\n </label>\n <select\n value={settings.healthCheckIntervalMinutes}\n onChange={(e) =>\n updateSetting('healthCheckIntervalMinutes', Number(e.target.value))\n }\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={1}>1 minute</option>\n <option value={5}>5 minutes</option>\n <option value={10}>10 minutes</option>\n <option value={15}>15 minutes</option>\n <option value={30}>30 minutes</option>\n <option value={60}>1 hour</option>\n </select>\n <p className=\"text-xs text-muted-foreground mt-1\">\n How often to check connection health status\n </p>\n </div>\n </div>\n\n {/* Security Settings */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Shield className=\"w-5 h-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">{tr('channel.security', 'Security')}</h2>\n </div>\n\n <div className=\"space-y-4\">\n <label className=\"flex items-center justify-between p-4 rounded-lg border hover:bg-muted/50 cursor-pointer\">\n <div>\n <p className=\"font-medium\">\n {tr('channel.webhookSignatureVerification', 'Webhook Signature Verification')}\n </p>\n <p className=\"text-sm text-muted-foreground\">\n Verify HMAC signatures on incoming webhooks\n </p>\n </div>\n <input\n type=\"checkbox\"\n checked={settings.enableWebhookVerification}\n onChange={(e) => updateSetting('enableWebhookVerification', e.target.checked)}\n className=\"w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary\"\n />\n </label>\n\n <label className=\"flex items-center justify-between p-4 rounded-lg border hover:bg-muted/50 cursor-pointer\">\n <div>\n <p className=\"font-medium\">{tr('channel.auditLogging', 'Audit Logging')}</p>\n <p className=\"text-sm text-muted-foreground\">\n Log all channel operations for audit trail\n </p>\n </div>\n <input\n type=\"checkbox\"\n checked={settings.enableAuditLogging}\n onChange={(e) => updateSetting('enableAuditLogging', e.target.checked)}\n className=\"w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary\"\n />\n </label>\n\n <div>\n <label className=\"text-sm font-medium block mb-1\">\n {tr('channel.sessionTimeout', 'Session Timeout (minutes)')}\n </label>\n <select\n value={settings.sessionTimeoutMinutes}\n onChange={(e) => updateSetting('sessionTimeoutMinutes', Number(e.target.value))}\n className=\"w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={15}>15 minutes</option>\n <option value={30}>30 minutes</option>\n <option value={60}>1 hour</option>\n <option value={120}>2 hours</option>\n <option value={240}>4 hours</option>\n </select>\n </div>\n </div>\n </div>\n\n {/* Data Management */}\n <div className=\"border rounded-lg p-6 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Database className=\"w-5 h-5 text-primary\" />\n <h2 className=\"text-lg font-semibold\">\n {tr('channel.dataManagement', 'Data Management')}\n </h2>\n </div>\n\n <div className=\"space-y-4\">\n <div className=\"p-4 rounded-lg border\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.webhookEventRetention', 'Webhook Event Retention')}\n </p>\n <span className=\"text-sm text-muted-foreground\">7 days</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n Incoming webhook events are automatically deleted after 7 days\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.deliveryAttemptRetention', 'Delivery Attempt Retention')}\n </p>\n <span className=\"text-sm text-muted-foreground\">7 days</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n Successful delivery attempts are automatically deleted after 7 days\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle\">\n <div className=\"flex items-center justify-between mb-2\">\n <p className=\"font-medium\">\n {tr('channel.failedDeliveryRetention', 'Failed Delivery Retention')}\n </p>\n <span className=\"text-sm text-status-warning-text\">30 days</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n Failed delivery attempts are kept for 30 days for debugging\n </p>\n </div>\n </div>\n </div>\n\n {/* Save Button */}\n <div className=\"flex justify-end pt-4\">\n <button\n onClick={handleSave}\n disabled={isSaving || isLoadingSettings || !!settingsLoadError}\n className=\"px-6 py-3 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50 flex items-center gap-2\"\n >\n {isSaving ? (\n <Loader2 className=\"w-5 h-5 animate-spin\" />\n ) : (\n <Save className=\"w-5 h-5\" />\n )}\n {tr('channel.saveSettings', 'Save Settings')}\n </button>\n </div>\n </div>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChannelUsersPage.d.ts","sourceRoot":"","sources":["../../../../../src/channel/modules/channels/pages/ChannelUsersPage.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAwDH,wBAAgB,gBAAgB,4CAyV/B"}
1
+ {"version":3,"file":"ChannelUsersPage.d.ts","sourceRoot":"","sources":["../../../../../src/channel/modules/channels/pages/ChannelUsersPage.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAwDH,wBAAgB,gBAAgB,4CAwW/B"}