@burdenoff/microfe-channel 2026.526.1 → 2026.527.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/channel/ChannelRoutes.js +0 -1
  2. package/dist/channel/ChannelRoutes.js.map +1 -1
  3. package/dist/channel/components/AdapterCredentialsForm.js +14 -2
  4. package/dist/channel/components/AdapterCredentialsForm.js.map +1 -1
  5. package/dist/channel/components/FeatureNotEnabled.js +1 -1
  6. package/dist/channel/components/FeatureNotEnabled.js.map +1 -1
  7. package/dist/channel/components/PermissionGuard.js +1 -1
  8. package/dist/channel/components/PermissionGuard.js.map +1 -1
  9. package/dist/channel/config/adapterCredentials.js +19 -11
  10. package/dist/channel/config/adapterCredentials.js.map +1 -1
  11. package/dist/channel/hooks/useChannelSubscription.js.map +1 -1
  12. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +18 -0
  13. package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
  14. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +411 -386
  15. package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
  16. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +30 -15
  17. package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
  18. package/dist/channel/modules/channels/pages/ChannelUsersPage.js +7 -1
  19. package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
  20. package/dist/channel/modules/channels/pages/ChannelsListPage.js +26 -2
  21. package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
  22. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +19 -11
  23. package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
  24. package/dist/channel/modules/channels/pages/ConnectionsListPage.js +39 -22
  25. package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
  26. package/dist/channel/modules/channels/pages/CreateChannelPage.js +7 -0
  27. package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
  28. package/dist/channel/modules/channels/pages/CreateConnectionPage.js +16 -5
  29. package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
  30. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +10 -3
  31. package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
  32. package/dist/channel/modules/channels/pages/EditChannelPage.js +17 -8
  33. package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
  34. package/dist/channel/modules/channels/pages/EditConnectionPage.js +11 -6
  35. package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
  36. package/dist/channel/modules/channels/pages/SendMessagePage.js +21 -9
  37. package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
  38. package/dist/channel/modules/channels/pages/WebhookEventsPage.js +7 -0
  39. package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
  40. package/dist/channel/pages/ChannelLandingPage.js +0 -1
  41. package/dist/channel/pages/ChannelLandingPage.js.map +1 -1
  42. package/dist/channel/providers/ChannelProvider.js +1 -1
  43. package/dist/channel/providers/ChannelProvider.js.map +1 -1
  44. package/package.json +1 -1
  45. package/dist/channel/modules/channels/pages/index.js +0 -14
@@ -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-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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"}
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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 type=\"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 aria-label={getRecipientPlaceholder(channel.platform)}\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\n htmlFor=\"channel-details-test-subject\"\n className=\"block text-sm font-medium text-foreground mb-1.5\"\n >\n Subject\n </label>\n <input\n id=\"channel-details-test-subject\"\n aria-label=\"Email subject\"\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\n htmlFor=\"channel-details-test-message\"\n className=\"block text-sm font-medium text-foreground mb-1.5\"\n >\n Message\n </label>\n <textarea\n id=\"channel-details-test-message\"\n aria-label=\"Test message content\"\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 type=\"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 type=\"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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"}
@@ -21,8 +21,11 @@ function x() {
21
21
  let n = S(e);
22
22
  return n === e ? t : n;
23
23
  }, { settings: w, isLoading: T, error: E, saveSettings: D } = n(), [O, k] = a(b), [A, j] = a(!1), [M, N] = a(!1), [P, F] = a(null), I = i(null);
24
- r(() => () => {
25
- I.current && clearTimeout(I.current);
24
+ r(() => {
25
+ let e = I;
26
+ return () => {
27
+ e.current && clearTimeout(e.current);
28
+ };
26
29
  }, []);
27
30
  let L = i(null);
28
31
  r(() => {
@@ -76,14 +79,15 @@ function x() {
76
79
  children: /* @__PURE__ */ y("div", {
77
80
  className: "flex items-center gap-4",
78
81
  children: [/* @__PURE__ */ v("button", {
82
+ type: "button",
79
83
  onClick: () => x("./list"),
80
84
  className: "p-2 hover:bg-muted rounded-lg transition-colors",
81
- children: /* @__PURE__ */ v(c, { className: "w-5 h-5" })
85
+ children: /* @__PURE__ */ v(c, { className: "size-5 " })
82
86
  }), /* @__PURE__ */ y("div", {
83
87
  className: "flex items-center gap-3",
84
88
  children: [/* @__PURE__ */ v("div", {
85
89
  className: "p-2 bg-primary/10 rounded-lg",
86
- children: /* @__PURE__ */ v(h, { className: "w-5 h-5 text-primary" })
90
+ children: /* @__PURE__ */ v(h, { className: "size-5 text-primary" })
87
91
  }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("h1", {
88
92
  className: "text-2xl font-bold",
89
93
  children: C("channel.channelSettings", "Channel Settings")
@@ -100,28 +104,28 @@ function x() {
100
104
  children: [
101
105
  T && /* @__PURE__ */ y("div", {
102
106
  className: "flex items-center gap-2 text-muted-foreground",
103
- children: [/* @__PURE__ */ v(p, { className: "w-4 h-4 animate-spin" }), /* @__PURE__ */ v("span", {
107
+ children: [/* @__PURE__ */ v(p, { className: "size-4 animate-spin" }), /* @__PURE__ */ v("span", {
104
108
  className: "text-sm",
105
109
  children: "Loading settings…"
106
110
  })]
107
111
  }),
108
112
  E && /* @__PURE__ */ y("div", {
109
113
  className: "p-4 rounded-lg bg-status-error-bg-subtle border border-status-error-border flex items-center gap-3",
110
- children: [/* @__PURE__ */ v(s, { className: "w-5 h-5 text-status-error-text shrink-0" }), /* @__PURE__ */ v("p", {
114
+ children: [/* @__PURE__ */ v(s, { className: "size-5 text-status-error-text shrink-0" }), /* @__PURE__ */ v("p", {
111
115
  className: "text-sm text-status-error-text",
112
116
  children: C("channel.settingsLoadError", "Failed to load settings — saving is disabled to prevent overwriting existing configuration.")
113
117
  })]
114
118
  }),
115
119
  M && /* @__PURE__ */ y("div", {
116
120
  className: "p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border flex items-center gap-3",
117
- children: [/* @__PURE__ */ v(u, { className: "w-5 h-5 text-status-success-text shrink-0" }), /* @__PURE__ */ v("p", {
121
+ children: [/* @__PURE__ */ v(u, { className: "size-5 text-status-success-text shrink-0" }), /* @__PURE__ */ v("p", {
118
122
  className: "text-sm text-status-success-text",
119
123
  children: C("channel.settingsSaved", "Settings saved successfully")
120
124
  })]
121
125
  }),
122
126
  P && /* @__PURE__ */ y("div", {
123
127
  className: "p-4 rounded-lg bg-destructive/10 border border-destructive/20 flex items-center gap-3",
124
- children: [/* @__PURE__ */ v(s, { className: "w-5 h-5 text-destructive shrink-0" }), /* @__PURE__ */ v("p", {
128
+ children: [/* @__PURE__ */ v(s, { className: "size-5 text-destructive shrink-0" }), /* @__PURE__ */ v("p", {
125
129
  className: "text-sm text-destructive",
126
130
  children: P
127
131
  })]
@@ -130,7 +134,7 @@ function x() {
130
134
  className: "border rounded-lg p-6 space-y-4",
131
135
  children: [/* @__PURE__ */ y("div", {
132
136
  className: "flex items-center gap-2",
133
- children: [/* @__PURE__ */ v(l, { className: "w-5 h-5 text-primary" }), /* @__PURE__ */ v("h2", {
137
+ children: [/* @__PURE__ */ v(l, { className: "size-5 text-primary" }), /* @__PURE__ */ v("h2", {
134
138
  className: "text-lg font-semibold",
135
139
  children: C("channel.deliverySettings", "Delivery Settings")
136
140
  })]
@@ -175,9 +179,12 @@ function x() {
175
179
  ] }), /* @__PURE__ */ y("div", {
176
180
  className: "grid grid-cols-2 gap-4",
177
181
  children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
182
+ htmlFor: "channel-settings-rate-limit-min",
178
183
  className: "text-sm font-medium block mb-1",
179
184
  children: C("channel.rateLimitPerMin", "Rate Limit (per minute)")
180
185
  }), /* @__PURE__ */ v("input", {
186
+ id: "channel-settings-rate-limit-min",
187
+ "aria-label": C("channel.rateLimitPerMin", "Rate Limit (per minute)"),
181
188
  type: "number",
182
189
  value: O.rateLimitPerMinute,
183
190
  onChange: (e) => z("rateLimitPerMinute", Number(e.target.value)),
@@ -185,9 +192,12 @@ function x() {
185
192
  max: 1e3,
186
193
  className: "w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
187
194
  })] }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
195
+ htmlFor: "channel-settings-rate-limit-hour",
188
196
  className: "text-sm font-medium block mb-1",
189
197
  children: C("channel.rateLimitPerHour", "Rate Limit (per hour)")
190
198
  }), /* @__PURE__ */ v("input", {
199
+ id: "channel-settings-rate-limit-hour",
200
+ "aria-label": C("channel.rateLimitPerHour", "Rate Limit (per hour)"),
191
201
  type: "number",
192
202
  value: O.rateLimitPerHour,
193
203
  onChange: (e) => z("rateLimitPerHour", Number(e.target.value)),
@@ -202,16 +212,18 @@ function x() {
202
212
  className: "border rounded-lg p-6 space-y-4",
203
213
  children: [/* @__PURE__ */ y("div", {
204
214
  className: "flex items-center gap-2",
205
- children: [/* @__PURE__ */ v(d, { className: "w-5 h-5 text-primary" }), /* @__PURE__ */ v("h2", {
215
+ children: [/* @__PURE__ */ v(d, { className: "size-5 text-primary" }), /* @__PURE__ */ v("h2", {
206
216
  className: "text-lg font-semibold",
207
217
  children: C("channel.healthMonitoring", "Health Monitoring")
208
218
  })]
209
219
  }), /* @__PURE__ */ y("div", { children: [
210
220
  /* @__PURE__ */ v("label", {
221
+ htmlFor: "channel-settings-health-interval",
211
222
  className: "text-sm font-medium block mb-1",
212
223
  children: "Health Check Interval (minutes)"
213
224
  }),
214
225
  /* @__PURE__ */ y("select", {
226
+ id: "channel-settings-health-interval",
215
227
  value: O.healthCheckIntervalMinutes,
216
228
  onChange: (e) => z("healthCheckIntervalMinutes", Number(e.target.value)),
217
229
  className: "w-full px-4 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary",
@@ -252,7 +264,7 @@ function x() {
252
264
  className: "border rounded-lg p-6 space-y-4",
253
265
  children: [/* @__PURE__ */ y("div", {
254
266
  className: "flex items-center gap-2",
255
- children: [/* @__PURE__ */ v(g, { className: "w-5 h-5 text-primary" }), /* @__PURE__ */ v("h2", {
267
+ children: [/* @__PURE__ */ v(g, { className: "size-5 text-primary" }), /* @__PURE__ */ v("h2", {
256
268
  className: "text-lg font-semibold",
257
269
  children: C("channel.security", "Security")
258
270
  })]
@@ -269,9 +281,10 @@ function x() {
269
281
  children: "Verify HMAC signatures on incoming webhooks"
270
282
  })] }), /* @__PURE__ */ v("input", {
271
283
  type: "checkbox",
284
+ "aria-label": C("channel.webhookSignatureVerification", "Webhook Signature Verification"),
272
285
  checked: O.enableWebhookVerification,
273
286
  onChange: (e) => z("enableWebhookVerification", e.target.checked),
274
- className: "w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary"
287
+ className: "size-5 rounded border-gray-300 text-primary focus:ring-primary"
275
288
  })]
276
289
  }),
277
290
  /* @__PURE__ */ y("label", {
@@ -284,9 +297,10 @@ function x() {
284
297
  children: "Log all channel operations for audit trail"
285
298
  })] }), /* @__PURE__ */ v("input", {
286
299
  type: "checkbox",
300
+ "aria-label": C("channel.auditLogging", "Audit Logging"),
287
301
  checked: O.enableAuditLogging,
288
302
  onChange: (e) => z("enableAuditLogging", e.target.checked),
289
- className: "w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary"
303
+ className: "size-5 rounded border-gray-300 text-primary focus:ring-primary"
290
304
  })]
291
305
  }),
292
306
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
@@ -326,7 +340,7 @@ function x() {
326
340
  className: "border rounded-lg p-6 space-y-4",
327
341
  children: [/* @__PURE__ */ y("div", {
328
342
  className: "flex items-center gap-2",
329
- children: [/* @__PURE__ */ v(f, { className: "w-5 h-5 text-primary" }), /* @__PURE__ */ v("h2", {
343
+ children: [/* @__PURE__ */ v(f, { className: "size-5 text-primary" }), /* @__PURE__ */ v("h2", {
330
344
  className: "text-lg font-semibold",
331
345
  children: C("channel.dataManagement", "Data Management")
332
346
  })]
@@ -387,10 +401,11 @@ function x() {
387
401
  /* @__PURE__ */ v("div", {
388
402
  className: "flex justify-end pt-4",
389
403
  children: /* @__PURE__ */ y("button", {
404
+ type: "button",
390
405
  onClick: R,
391
406
  disabled: A || T || !!E,
392
407
  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",
393
- children: [A ? /* @__PURE__ */ v(p, { className: "w-5 h-5 animate-spin" }) : /* @__PURE__ */ v(m, { className: "w-5 h-5" }), C("channel.saveSettings", "Save Settings")]
408
+ children: [A ? /* @__PURE__ */ v(p, { className: "size-5 animate-spin" }) : /* @__PURE__ */ v(m, { className: "size-5 " }), C("channel.saveSettings", "Save Settings")]
394
409
  })
395
410
  })
396
411
  ]