@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.
- package/dist/channel/ChannelRoutes.js +0 -1
- package/dist/channel/ChannelRoutes.js.map +1 -1
- package/dist/channel/components/AdapterCredentialsForm.js +14 -2
- package/dist/channel/components/AdapterCredentialsForm.js.map +1 -1
- package/dist/channel/components/FeatureNotEnabled.js +1 -1
- package/dist/channel/components/FeatureNotEnabled.js.map +1 -1
- package/dist/channel/components/PermissionGuard.js +1 -1
- package/dist/channel/components/PermissionGuard.js.map +1 -1
- package/dist/channel/config/adapterCredentials.js +19 -11
- package/dist/channel/config/adapterCredentials.js.map +1 -1
- package/dist/channel/hooks/useChannelSubscription.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +18 -0
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +411 -386
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js +30 -15
- package/dist/channel/modules/channels/pages/ChannelSettingsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +7 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelsListPage.js +26 -2
- package/dist/channel/modules/channels/pages/ChannelsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js +19 -11
- package/dist/channel/modules/channels/pages/ConnectionDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js +39 -22
- package/dist/channel/modules/channels/pages/ConnectionsListPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/CreateChannelPage.js +7 -0
- package/dist/channel/modules/channels/pages/CreateChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/CreateConnectionPage.js +16 -5
- package/dist/channel/modules/channels/pages/CreateConnectionPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +10 -3
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditChannelPage.js +17 -8
- package/dist/channel/modules/channels/pages/EditChannelPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/EditConnectionPage.js +11 -6
- package/dist/channel/modules/channels/pages/EditConnectionPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/SendMessagePage.js +21 -9
- package/dist/channel/modules/channels/pages/SendMessagePage.js.map +1 -1
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js +7 -0
- package/dist/channel/modules/channels/pages/WebhookEventsPage.js.map +1 -1
- package/dist/channel/pages/ChannelLandingPage.js +0 -1
- package/dist/channel/pages/ChannelLandingPage.js.map +1 -1
- package/dist/channel/providers/ChannelProvider.js +1 -1
- package/dist/channel/providers/ChannelProvider.js.map +1 -1
- package/package.json +1 -1
- 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":";;;;;;;;;;;;;AAmEA,IAAM,IAA6E;CACjF,OAAO;EAAE,OAAO;EAAS,OAAO;EAA0C;CAC1E,KAAK;EAAE,OAAO;EAAO,OAAO;EAAwD;CACpF,MAAM;EAAE,OAAO;EAAQ,OAAO;EAA8C;CAC5E,OAAO;EAAE,OAAO;EAAU,OAAO;EAA8C;CAC/E,SAAS;EAAE,OAAO;EAAW,OAAO;EAA8C;CAClF,OAAO;EAAE,OAAO;EAAS,OAAO;EAA0C;CAC1E,OAAO;EAAE,OAAO;EAAS,OAAO;EAA0C;CAC1E,SAAS;EAAE,OAAO;EAAW,OAAO;EAA8C;CAClF,UAAU;EAAE,OAAO;EAAY,OAAO;EAAgD;CACtF,SAAS;EAAE,OAAO;EAAW,OAAO;EAA0C;CAC/E,EAGK,IAGF;CACF,QAAQ;EACN,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,UAAU;EAAE,OAAO;EAAY,OAAO;EAAkC,MAAM;EAAU;CACxF,OAAO;EACL,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,WAAW;EACT,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,UAAU;EAAE,OAAO;EAAY,OAAO;EAAkC,MAAM;EAAU;CACzF,EAGK,IAGF;CACF,SAAS;EAAE,OAAO;EAAW,OAAO;EAA4B,MAAM;EAAc;CACpF,UAAU;EAAE,OAAO;EAAY,OAAO;EAA4B,MAAM;EAAe;CACvF,WAAW;EAAE,OAAO;EAAa,OAAO;EAA0B,MAAM;EAAS;CACjF,SAAS;EAAE,OAAO;EAAW,OAAO;EAAyB,MAAM;EAAY;CAChF;AAOD,SAAgB,IAAqB;CACnC,IAAM,EAAE,iBAAc,GAAkC,EAClD,IAAW,IAAa,EACxB,IAAW,IAAa,EACxB,EAAE,SAAM,IAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAE9B,IAAgB,EAAS,OAEzB,CAAC,GAAe,KAAoB,EAAwB,KAAK,EACjE,CAAC,GAAoB,KAAyB,EAClD,GAAe,uBAAuB,GACvC,EACK,CAAC,GAAa,KAAkB,EACpC,GAAe,sBAAsB,EAAE,CACxC,EACK,CAAC,IAAkB,KAAuB,EAAS,GAAM,EACzD,CAAC,GAAY,KAAiB,EAAuD,KAAK,EAG1F,CAAC,IAAe,KAAoB,EAAS,GAAM,EACnD,CAAC,GAAe,KAAoB,EAAS,GAAG,EAChD,CAAC,GAAa,MAAkB,QACpC,EAAG,mCAAmC,oBAAoB,CAC3D,EACK,CAAC,GAAa,MAAkB,QACpC,EACE,gCACA,8DACD,CACF,EACK,CAAC,GAAoB,KAAyB,EAClD,KACD;AAGD,UAAgB;AACd,GAAI,GAAe,sBAAsB,GAAe,wBAEtD,EAAS,EAAS,UAAU;GAAE,SAAS;GAAM,OAAO;GAAM,CAAC;IAE5D,EAAE,CAAC;CAEN,IAAM,EACJ,YACA,eACA,UACA,mBACA,oBACA,sBACA,sBACA,sBACA,oBACA,6BACE,EAAW,EAAU,EAEnB,KAAoB,YAAY;AACpC,IAAiB,SAAS;AAC1B,MAAI;AACF,SAAM,IAAgB;WACf,GAAK;AACZ,WAAQ,MAAM,wCAAwC,EAAI;YAClD;AACR,KAAiB,KAAK;;IAIpB,KAAqB,YAAY;AAChC,SACL;KAAiB,SAAS;AAC1B,OAAI;AACF,UAAM,GAAc,EAClB,QAAQ,EAAQ,WAAW,WAAW,aAAa,UACpD,CAAC;YACK,GAAK;AACZ,YAAQ,MAAM,6CAA6C,EAAI;aACvD;AACR,MAAiB,KAAK;;;IAIpB,KAAyB,YAAY;AACpC,SACL;KAAiB,aAAa;AAC9B,OAAI;IAIF,IAAI,IAA4B,WAC1B,IAAiB,EAAQ;AAe/B,IAdI,GAAgB,SAAS,MAAM,GACjC,IAAW,cACF,GAAgB,SAAS,UAAU,GAC5C,IAAW,YACF,MAAmB,WAC5B,IAAW,cAGb,MAAM,GAAiB;KACrB;KACA;KACD,CAAC,EACF,EAAsB,GAAM,EAC5B,EAAe,EAAE,CAAC,EAClB,EAAoB,GAAM;YACnB,GAAK;AACZ,YAAQ,MAAM,yCAAyC,EAAI;aACnD;AACR,MAAiB,KAAK;;;IAIpB,KAAyB,OAAO,MAAyB;AAC7D,IAAiB,aAAa;AAC9B,MAAI;AAMF,GALA,MAAM,GAAiB,GAAc,EACnC,gBACD,CAAC,EACF,EAAsB,GAAM,EAC5B,EAAe,EAAE,CAAC,EAClB,EAAoB,GAAM;WACnB,GAAK;AACZ,WAAQ,MAAM,yCAAyC,EAAI;YACnD;AACR,KAAiB,KAAK;;IAIpB,KAAyB,OAAO,MAAyB;AAE7D,EADK,EAAa,SAAS,EAC3B,EAAiB,oBAAoB;AACrC,MAAI;AAEG,GADL,MAAM,GAAiB,EAAa,EAC/B,EAAa,UAAU;WACrB,GAAK;AAEZ,GADK,EAAa,QAAQ,EAC1B,QAAQ,MAAM,yCAAyC,EAAI;YACnD;AACR,KAAiB,KAAK;;IAIpB,KAAuB,OAAO,MAAyB;AAE3D,EADA,EAAiB,OAAO,EACxB,EAAc,KAAK;AACnB,MAAI;GACF,IAAM,IAAS,MAAM,GAAe,EAAa;AACjD,GAAI,KACF,EAAc;IACZ,SAAS,EAAO;IAChB,SACE,EAAO,YACN,EAAO,UACJ,EAAG,iCAAiC,yBAAyB,GAC7D,EAAG,gCAAgC,oBAAoB;IAC9D,CAAC;WAEG,GAAK;AAEZ,GADA,QAAQ,MAAM,uCAAuC,EAAI,EACzD,EAAc;IACZ,SAAS;IACT,SAAS,EAAG,uCAAuC,4BAA4B;IAChF,CAAC;YACM;AACR,KAAiB,KAAK;;IAIpB,KAA6B,YAAY;AACxC,QAAc,MAAM,EAGzB;GADA,EAAiB,YAAY,EAC7B,EAAsB,KAAK;AAE3B,OAAI;IACF,IAAM,IACJ,GAAS,aAAa,UAClB;KAAE,SAAS;KAAa,MAAM,MAAM,EAAY;KAAO,GACvD,KAAA,GAEA,IAAS,MAAM,GAAqB,EAAc,MAAM,EAAE,GAAa,EAAQ;AACrF,IAAI,KACF,EAAsB;KACpB,SAAS,EAAO;KAChB,WAAW,EAAO;KAClB,YAAY,EAAO;KACnB,QAAQ,EAAO;KACf,QAAQ,EAAO;KACf,OAAO,EAAO,QACV;MACE,MAAM,EAAO,MAAM;MACnB,SAAS,EAAO,MAAM;MACtB,WAAW,EAAO,MAAM;MACzB,GACD;KACL,CAAC;YAEG,GAAc;IACrB,IAAM,IACJ,aAAe,QACX,EAAI,UACJ,EAAG,sCAAsC,mCAAmC;AAElF,IADA,QAAQ,MAAM,8CAA8C,EAAI,EAChE,EAAsB;KACpB,SAAS;KACT,OAAO;MACL,MAAM;MACN,SAAS;MACT,WAAW;MACZ;KACF,CAAC;aACM;AACR,MAAiB,KAAK;;;IAIpB,MAA2B,MAA8B;AAC7D,UAAQ,GAAR;GACE,KAAK,QACH,QAAO,EAAG,sCAAsC,oBAAoB;GACtE,KAAK,MACH,QAAO,EAAG,sCAAsC,cAAc;GAChE,KAAK,OACH,QAAO,EAAG,4CAA4C,eAAe;GACvE,KAAK,QACH,QAAO,EAAG,sCAAsC,wBAAwB;GAC1E,KAAK,QACH,QAAO,EAAG,0CAA0C,aAAa;GACnE,KAAK,UACH,QAAO,EAAG,0CAA0C,aAAa;GACnE,KAAK,WACH,QAAO,EAAG,sCAAsC,cAAc;GAChE,QACE,QAAO,EAAG,wCAAwC,YAAY;;IAI9D,MAAqB,MAA8B;AACvD,UAAQ,GAAR;GACE,KAAK,QACH,QAAO,EAAG,gCAAgC,gBAAgB;GAC5D,KAAK;GACL,KAAK,WACH,QAAO,EAAG,gCAAgC,eAAe;GAC3D,KAAK,OACH,QAAO,EAAG,sCAAsC,eAAe;GACjE,KAAK;GACL,KAAK;GACL,KAAK,UACH,QAAO,EAAG,0CAA0C,kBAAkB;GACxE,QACE,QAAO,EAAG,kCAAkC,YAAY;;IAIxD,MAAqB,OAKlB;EACL,GALa,EAAgB,MAAa;GAC1C,OAAO;GACP,OAAO;GACR;EAGC,OAAO,EAAe,GAAU,IAAe,EAAG;EACnD,GAGG,MAAmB,OAEhB;EACL,GAFa,EAAc,MAAW,EAAc;EAGpD,OAAO,EAAe,GAAQ,GAAa,EAAG;EAC/C,GAGG,MAAmB,OAEhB;EACL,GAFa,EAAc,MAAW,EAAc;EAGpD,OAAO,EAAe,GAAQ,IAAa,EAAG;EAC/C;AAIH,KAAI,GACF,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAAqC,CAAA;EAC9D,CAAA;AAKV,KAAI,KAAS,CAAC,EACZ,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KAAa,MAAM;KAAI,WAAU;KAA2B,CAAA;IAC5D,kBAAC,OAAD,EAAA,UACE,kBAAC,KAAD;KAAG,WAAU;eACV,KAAS,EAAG,2BAA2B,oBAAoB;KAC1D,CAAA,EACA,CAAA;IACN,kBAAC,UAAD;KACE,eAAe,EAAS,GAAG;KAC3B,WAAU;eAET,EAAG,kBAAkB,UAAU;KACzB,CAAA;IACL;;EACF,CAAA;CAIV,IAAM,IAAiB,GAAkB,EAAQ,SAAS,EACpD,IAAe,GAAgB,EAAQ,OAAO,EAC9C,IAAe,GAAgB,EAAQ,iBAAiB,EACxD,KAAa,EAAa,MAC1B,KAAa,EAAa,MAC1B,IAAa,EAAQ;AAE3B,QACE,kBAAC,GAAD;EAAiB,YAAY,EAAoB;YAC/C,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,eAAe,EAAS,GAAG;OAC3B,WAAU;iBAEV,kBAAC,IAAD,EAAW,MAAM,IAAM,CAAA;OAChB,CAAA,EACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,IAAD;QAAO,MAAM;QAAI,WAAU;QAAwB,CAAA,EACnD,kBAAC,MAAD;QAAI,WAAU;kBAAiD,EAAQ;QAAU,CAAA,CAC7E;UACL,EAAQ,eACP,kBAAC,KAAD;OAAG,WAAU;iBAAsC,EAAQ;OAAgB,CAAA,CAEzE,EAAA,CAAA,CACF;SAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAiB,YAAY,EAAoB;kBAC/C,kBAAC,UAAD;SACE,SAAS;SACT,UAAU,MAAkB;SAC5B,WAAU;mBAHZ,CAKG,MAAkB,WACjB,kBAAC,GAAD;UAAS,MAAM;UAAI,WAAU;UAAiB,CAAA,GAE9C,kBAAC,IAAD,EAAW,MAAM,IAAM,CAAA,EAExB,EAAG,uBAAuB,eAAe,CACnC;;QACO,CAAA;OAClB,kBAAC,GAAD;QAAiB,YAAY,EAAoB;kBAC/C,kBAAC,UAAD;SACE,SAAS;SACT,UAAU,MAAkB;SAC5B,WAAW,8FACT,EAAQ,WAAW,WACf,+DACA;mBANR,CASG,MAAkB,WACjB,kBAAC,GAAD;UAAS,MAAM;UAAI,WAAU;UAAiB,CAAA,GAC5C,EAAQ,WAAW,WACrB,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,GAEtB,kBAAC,IAAD,EAAO,MAAM,IAAM,CAAA,EAEpB,EAAQ,WAAW,WAChB,EAAG,sBAAsB,aAAa,GACtC,EAAG,oBAAoB,WAAW,CAC/B;;QACO,CAAA;OAClB,kBAAC,UAAD;QACE,eAAe;AAGb,SAFA,EAAiB,GAAK,EACtB,EAAsB,KAAK,EAC3B,EAAiB,GAAG;;QAEtB,UAAU,CAAC,EAAQ;QACnB,WAAU;QACV,OACG,EAAQ,aAEL,EAAG,gCAAgC,yBAAyB,GAD5D,EAAG,qCAAqC,8BAA8B;kBAV9E,CAcE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,eAAe,CACnC;;OACT,kBAAC,UAAD;QACE,eAAe,EAAS,SAAS;QACjC,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA,EACnB,EAAG,gBAAgB,OAAO,CACpB;;OACL;QACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAG,oBAAoB,WAAW;QACjC,CAAA,EACJ,kBAAC,QAAD;QACE,WAAW,4DAA4D,EAAe;kBAErF,EAAe;QACX,CAAA,CACH;;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAsC,EAAG,kBAAkB,SAAS;QAAK,CAAA,EACtF,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,IAAD;SAAY,MAAM;SAAI,WAAW,EAAa,MAAM,MAAM,IAAI,CAAC;SAAM,CAAA,EACrE,kBAAC,QAAD;SAAM,WAAW,8CAA8C,EAAa;mBACzE,EAAa;SACT,CAAA,CACH;UACF;;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,4BAA4B,oBAAoB;SAClD,CAAA;QACJ,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,IAAD;UAAY,MAAM;UAAI,WAAW,EAAa;UAAS,CAAA,EACvD,kBAAC,QAAD;UAAM,WAAW,uBAAuB,EAAa;oBAClD,EAAa;UACT,CAAA,CACH;;QACL,EAAQ,qBACP,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EAAG,qBAAqB,aAAa;UAAC;UAAE;UACxC,IAAI,KAAK,EAAQ,kBAAkB,CAAC,gBAAgB;UACnD;;QAEF;;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAG,sBAAsB,aAAa;QACrC,CAAA,EACJ,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SACE,MAAM;SACN,WACE,EAAQ,cAAc,6BAA6B;SAErD,CAAA,EACF,kBAAC,QAAD;SACE,WAAW,uBAAuB,EAAQ,cAAc,6BAA6B;mBAEpF,EAAQ,cACL,EAAG,qBAAqB,YAAY,GACpC,EAAG,wBAAwB,gBAAgB;SAC1C,CAAA,CACH;UACF;;MACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,0BAA0B,kBAAkB;OAC7C,CAAA;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,uBAAuB,eAAe;SACxC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAQ,aAAa,QAAQ,MAAM,IAAI,IACtC,EAAG,yBAAyB,iBAAiB;SAC7C,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAiC,EAAG,oBAAoB,WAAW;SAAK,CAAA,EACrF,kBAAC,KAAD;SAAG,WAAU;mBAAuC,EAAQ;SAAa,CAAA,CACrE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,+BAA+B,uBAAuB;SACxD,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAuC,EAAQ;SAAwB,CAAA,CAChF,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,uBAAuB,iBAAiB;SAC1C,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAuC,EAAQ;SAAoB,CAAA,CAC5E,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,qBAAqB,aAAa;SACpC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,IAAI,KAAK,EAAQ,UAAU,CAAC,gBAAgB;SAC3C,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,qBAAqB,aAAa;SACpC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,IAAI,KAAK,EAAQ,UAAU,CAAC,gBAAgB;SAC3C,CAAA,CACA,EAAA,CAAA;QACF;;MAGL,EAAQ,kBAAkB,EAAQ,eAAe,SAAS,KACzD,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAwC,EAAG,gBAAgB,OAAO;QAAK,CAAA,EACpF,kBAAC,OAAD;QAAK,WAAU;QAAyB,cAAW;kBAChD,EAAQ,eAAe,KAAK,MAC3B,kBAAC,IAAD;SAAqB,IAAI,EAAE;SAAO,OAAO,EAAE;SAAO,MAAK;SAAO,EAA/C,EAAE,GAA6C,CAC9D;QACE,CAAA,CACF;;MAIP,EAAQ,oBACP,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAG,gCAAgC,0BAA0B;QAC5D,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAkC,EAAQ;QAAqB,CAAA,CACxE;;MAEJ;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,eAAe,EAAS,eAAe;OACvC,WAAU;iBAFZ;QAIE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,IAAD;UAAO,MAAM;UAAI,WAAU;UAAuB,CAAA;SAC9C,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAG,2BAA2B,mBAAmB;UAChD,CAAA,EACJ,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAG,iCAAiC,0BAA0B;UAC7D,CAAA,CACA;;QACN,kBAAC,GAAD;SAAY,MAAM;SAAI,WAAU;SAAmC,CAAA;QAC5D;;MACT,kBAAC,UAAD;OACE,eAAe,EAAS,aAAa;OACrC,WAAU;iBAFZ;QAIE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,IAAD;UAAO,MAAM;UAAI,WAAU;UAAuB,CAAA;SAC9C,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAG,yBAAyB,iBAAiB;UAC5C,CAAA,EACJ,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAG,gCAAgC,yBAAyB;UAC3D,CAAA,CACA;;QACN,kBAAC,GAAD;SAAY,MAAM;SAAI,WAAU;SAAmC,CAAA;QAC5D;;MACT,kBAAC,UAAD;OACE,eAAe,EAAS,UAAU;OAClC,WAAU;iBAFZ;QAIE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,IAAD;UAAO,MAAM;UAAI,WAAU;UAAqB,CAAA;SAC5C,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAG,wBAAwB,gBAAgB;UAC1C,CAAA,EACJ,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAG,8BAA8B,uBAAuB;UACvD,CAAA,CACA;;QACN,kBAAC,GAAD;SAAY,MAAM;SAAI,WAAU;SAAmC,CAAA;QAC5D;;MACL;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,IAAD;SAAK,MAAM;SAAI,WAAU;SAAwB,CAAA,EACjD,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,oCAAoC,2BAA2B;SAChE,CAAA,CACD;WACL,CAAC,KAAc,CAAC,KACf,kBAAC,UAAD;QACE,eAAe,EAAsB,GAAK;QAC1C,WAAU;kBAFZ,CAIE,kBAAC,IAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,0BAA0B,kBAAkB,CACzC;UAEP;;MAGL,KAAc,CAAC,KACd,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAG,oBAAoB,YAAY;WAClC,CAAA,EACJ,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAe,EAAW,UAAU,GAAgB,EAAG;WACtD,CAAA,CACA,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAiC,EAAG,kBAAkB,SAAS;WAAK,CAAA,EACjF,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACG,EAAW,YACV,kBAAC,GAAD;YAAc,MAAM;YAAI,WAAU;YAA6B,CAAA,GAE/D,kBAAC,GAAD;YAAS,MAAM;YAAI,WAAU;YAA2B,CAAA,EAE1D,kBAAC,QAAD;YACE,WAAW,uBAAuB,EAAW,YAAY,6BAA6B;sBAErF,EAAW,YACR,EAAG,mBAAmB,UAAU,GAChC,EAAG,qBAAqB,YAAY;YACnC,CAAA,CACH;aACF,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAG,sBAAsB,cAAc;WACtC,CAAA,EACJ,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAW,eACR,IAAI,KAAK,EAAW,aAAa,CAAC,gBAAgB,GAClD,EAAG,iBAAiB,QAAQ;WAC9B,CAAA,CACA,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAG,sBAAsB,cAAc;WACtC,CAAA,EACJ,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAW,cAAc,EAAG,qBAAqB,aAAa;WAC7D,CAAA,CACA,EAAA,CAAA;UACL,EAAW,kBACV,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAG,wBAAwB,gBAAgB;WAC1C,CAAA,EACJ,kBAAC,KAAD;WACE,WAAW,uBAAuB,EAAW,oBAAoB,6BAA6B;qBADhG,CAGG,IAAI,KAAK,EAAW,eAAe,CAAC,gBAAgB,EACpD,EAAW,oBAAoB,KAAA,KAC9B,kBAAC,QAAD;YAAM,WAAU;sBAAhB;aAAqD;aACjD,EAAW;aAAgB;aAAE,EAAG,gBAAgB,OAAO;aAAC;aACrD;cAEP;aACA,EAAA,CAAA;UAEJ;;QAGL,KACC,kBAAC,OAAD;SACE,WAAW,kBAAkB,EAAW,UAAU,oEAAoE;mBAEtH,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACG,EAAW,UACV,kBAAC,GAAD;WAAc,MAAM;WAAI,WAAU;WAA6B,CAAA,GAE/D,kBAAC,GAAD;WAAS,MAAM;WAAI,WAAU;WAA2B,CAAA,EAE1D,kBAAC,KAAD;WACE,WAAW,uBAAuB,EAAW,UAAU,6BAA6B;qBAEnF,EAAW;WACV,CAAA,CACA;;SACF,CAAA;QAIR,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,GAAD;WAAiB,YAAY,EAAoB;qBAC/C,kBAAC,UAAD;YACE,eAAe,GAAqB,EAAW,GAAG;YAClD,UAAU,MAAkB;YAC5B,WAAU;sBAHZ,CAKG,MAAkB,SACjB,kBAAC,GAAD;aAAS,MAAM;aAAI,WAAU;aAAiB,CAAA,GAE9C,kBAAC,IAAD,EAAM,MAAM,IAAM,CAAA,EAEnB,EAAG,0BAA0B,kBAAkB,CACzC;;WACO,CAAA;UAClB,kBAAC,UAAD;WACE,eAAe;AAQb,YAPA,EAAsB,GAAK,EAO3B,EAAgB,GAAY,oBAAgD,EAAE,CAAC;;WAEjF,WAAU;qBAXZ,CAaE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA,EACnB,EAAG,6BAA6B,qBAAqB,CAC/C;;UACT,kBAAC,GAAD;WAAiB,YAAY,EAAoB;qBAC/C,kBAAC,UAAD;YACE,eAAe,GAAuB,EAAW,GAAG;YACpD,UAAU,MAAkB;YAC5B,WAAU;sBAHZ,CAKG,MAAkB,sBACjB,kBAAC,GAAD;aAAS,MAAM;aAAI,WAAU;aAAiB,CAAA,GAE9C,kBAAC,IAAD,EAAQ,MAAM,IAAM,CAAA,EAErB,EAAG,kBAAkB,SAAS,CACxB;;WACO,CAAA;UACd;;QACF;;MAIP,CAAC,KAAc,CAAC,KACf,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SAAO,MAAM;SAAI,WAAU;SAAuC,CAAA;QAClE,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,mCACA,6CACD;SACC,CAAA;QACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,8BACA,kEACD;SACC,CAAA;QACA;;MAIP,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEG,CAAC,EAAQ,eACR,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WAAa,MAAM;WAAI,WAAU;WAA8B,CAAA,EAC/D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAG,mCAAmC,6BAA6B;WAClE,CAAA,EACJ,kBAAC,KAAD;WAAG,WAAU;qBACV,EACC,uCACA,qIACD;WACC,CAAA,CACA,EAAA,CAAA,CACF;;SACF,CAAA;QAIR,kBAAC,IAAD;SACE,aAAa,EAAQ,eAAe,KAAA;SACvB;SACb,UAAU;SACV,qBAAqB,MAAU,EAAoB,EAAM;SACzD,UAAU,MAAkB;SAC5B,CAAA;QAGD,EAAQ,eACP,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,IAAD;UACE,aAAa,EAAQ;UACR;UACb,CAAA;SACE,CAAA;QAIR,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAiB,YAAY,EAAoB;oBAC/C,kBAAC,UAAD;WACE,eAAe;AACb,YAAI,IACF,GAAuB,EAAW,GAAG,GAErC,IAAwB;;WAG5B,UACE,MAAkB,gBAAiB,CAAC,MAAoB,CAAC,CAAC,EAAQ;WAEpE,WAAU;qBAXZ,CAaG,MAAkB,eACjB,kBAAC,GAAD;YAAS,MAAM;YAAI,WAAU;YAAiB,CAAA,GAE9C,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAEnB,IACG,EAAG,6BAA6B,qBAAqB,GACrD,EAAG,2BAA2B,mBAAmB,CAC9C;;UACO,CAAA,EAClB,kBAAC,UAAD;UACE,eAAe;AAGb,WAFA,EAAsB,GAAM,EAC5B,EAAe,EAAE,CAAC,EAClB,EAAoB,GAAM;;UAE5B,WAAU;oBANZ,CAQE,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA,EACd,EAAG,kBAAkB,SAAS,CACxB;YACL;;QACF;;MAEJ;;IAGL,MACC,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAM,MAAM;SAAI,WAAU;SAAwB,CAAA,EAClD,kBAAC,MAAD;SAAI,WAAU;mBAAd;UAAsD;UAC9C,EAAgB,EAAQ,WAAW,SAAS,EAAQ;UAAS;UAChE;WACD;WACN,kBAAC,UAAD;QACE,eAAe,EAAiB,GAAM;QACtC,WAAU;kBAEV,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA;QACR,CAAA,CACL;UAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;mBAAjB;UACG,GAAkB,EAAQ,SAAS;UAAE;UACtC,kBAAC,QAAD;WAAM,WAAU;qBAAyB;WAAQ,CAAA;UAC3C;YACR,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,SAAD;UACE,MAAK;UACL,OAAO;UACP,WAAW,MAAM,EAAiB,EAAE,OAAO,MAAM;UACjD,aAAa,GAAwB,EAAQ,SAAS;UACtD,WAAU;UACV,CAAA,EACF,kBAAC,OAAD;UAAK,WAAU;oBACZ,EAAQ,aAAa,UACpB,kBAAC,IAAD,EAAM,MAAM,IAAM,CAAA,GAChB,EAAQ,aAAa,SAAS,EAAQ,aAAa,aACrD,kBAAC,IAAD,EAAO,MAAM,IAAM,CAAA,GAEnB,kBAAC,IAAD,EAAe,MAAM,IAAM,CAAA;UAEzB,CAAA,CACF;WACF,EAAA,CAAA;QAGL,EAAQ,aAAa,WACpB,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;mBAAmD;SAE5D,CAAA,EACR,kBAAC,SAAD;SACE,MAAK;SACL,OAAO;SACP,WAAW,MAAM,GAAe,EAAE,OAAO,MAAM;SAC/C,aAAY;SACZ,WAAU;SACV,CAAA,CACE,EAAA,CAAA;QAIR,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;mBAAmD;SAE5D,CAAA,EACR,kBAAC,YAAD;SACE,OAAO;SACP,WAAW,MAAM,GAAe,EAAE,OAAO,MAAM;SAC/C,aAAY;SACZ,MAAM;SACN,WAAU;SACV,CAAA,CACE,EAAA,CAAA;QAGL,KACC,kBAAC,OAAD;SACE,WAAW,kBACT,EAAmB,UACf,oEACA;mBAGN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACG,EAAmB,UAClB,kBAAC,GAAD;WAAc,MAAM;WAAI,WAAU;WAAoC,CAAA,GAEtE,kBAAC,GAAD;WAAS,MAAM;WAAI,WAAU;WAAkC,CAAA,EAEjE,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACE,kBAAC,KAAD;aACE,WAAW,uBACT,EAAmB,UACf,6BACA;uBAGL,EAAmB,UAChB,EAAG,8BAA8B,6BAA6B,GAC9D,EAAG,6BAA6B,yBAAyB;aAC3D,CAAA;YACH,EAAmB,aAClB,kBAAC,KAAD;aAAG,WAAU;uBAAb,CAA6C,gBAC9B,EAAmB,UAC9B;;YAEL,EAAmB,cAClB,kBAAC,KAAD;aAAG,WAAU;uBAAb,CAAkD,iBAClC,EAAmB,WAC/B;;YAEL,EAAmB,UAClB,kBAAC,KAAD;aAAG,WAAU;uBAAb,CAAkD,YACvC,EAAmB,OAC1B;;YAEL,EAAmB,SAClB,kBAAC,KAAD;aAAG,WAAU;uBAAb,CACG,EAAmB,MAAM,SACzB,EAAmB,MAAM,aACxB,kBAAC,QAAD;cAAM,WAAU;wBAAwB;cAAmB,CAAA,CAE3D;;YAEL,EAAmB,UAClB,kBAAC,KAAD;aAAG,WAAU;uBAAb,CAAkD,aACtC,IAAI,KAAK,EAAmB,OAAO,CAAC,gBAAgB,CAC5D;;YAEF;aACF;;SACF,CAAA;QAIR,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAiB,YAAY,EAAoB;oBAC/C,kBAAC,UAAD;WACE,SAAS;WACT,UAAU,MAAkB,eAAe,CAAC,EAAc,MAAM;WAChE,WAAU;qBAHZ,CAKG,MAAkB,cACjB,kBAAC,GAAD;YAAS,MAAM;YAAI,WAAU;YAAiB,CAAA,GAE9C,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAEnB,EAAG,oBAAoB,YAAY,CAC7B;;UACO,CAAA,EAClB,kBAAC,UAAD;UACE,eAAe,EAAiB,GAAM;UACtC,WAAU;oBAET,EAAG,iBAAiB,QAAQ;UACtB,CAAA,CACL;;QACF;SACF;;KACF,CAAA;IAEJ;;EACU,CAAA"}
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
]
|