@burdenoff/microfe-channel 2026.624.1 → 2026.624.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js +8 -8
- package/dist/channel/modules/channels/pages/ChannelDashboardPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js +383 -389
- package/dist/channel/modules/channels/pages/ChannelDetailsPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js +212 -238
- package/dist/channel/modules/channels/pages/ChannelUsersPage.js.map +1 -1
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js +196 -221
- package/dist/channel/modules/channels/pages/DeliveryHistoryPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelDetailsPage.js","names":[],"sources":["../../../../../src/channel/modules/channels/pages/ChannelDetailsPage.tsx"],"sourcesContent":["/**\n * Channel Details Page\n *\n * Displays channel details and provides connection/credentials management\n */\n\nimport { useState, useEffect } from 'react';\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\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-text-secondary bg-bg-sunken', 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-text-secondary bg-bg-sunken', 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-text-secondary', 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 { hasPermission } = usePermissions();\n // Admin-only affordances — hide when the user lacks the permission.\n const canUpdate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_UPDATE);\n const canConnectionCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_CREATE);\n const canConnectionUpdate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_UPDATE);\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-text-secondary hover:text-text-primary hover:bg-bg-sunken 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-text-primary\">{channel.name}</h1>\n </div>\n {channel.description && (\n <p className=\"text-sm text-text-secondary 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-text-secondary 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 {canUpdate && (\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 )}\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-subtle rounded-lg\">\n <p className=\"text-xs text-text-secondary 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-subtle rounded-lg\">\n <p className=\"text-xs text-text-secondary 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-subtle rounded-lg\">\n <p className=\"text-xs text-text-secondary 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-text-secondary 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-subtle rounded-lg\">\n <p className=\"text-xs text-text-secondary 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-text-secondary'\n }\n />\n <span\n className={`text-sm font-medium ${channel.isConnected ? 'text-status-success-text' : 'text-text-secondary'}`}\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-subtle rounded-lg\">\n <h2 className=\"text-lg font-semibold text-text-primary 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-text-secondary\">\n {tr('channel.adapterType', 'Adapter Type')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\n {channel.adapterType?.replace(/_/g, ' ') ||\n tr('channel.notConfigured', 'Not configured')}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">{tr('channel.priority', 'Priority')}</p>\n <p className=\"text-sm font-medium text-text-primary\">{channel.priority}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.consecutiveFailures', 'Consecutive Failures')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">{channel.consecutiveFailures}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.messages24h', 'Messages (24h)')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">{channel.messageCount24h}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.createdAt', 'Created At')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\n {new Date(channel.createdAt).toLocaleString()}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.updatedAt', 'Updated At')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\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-text-secondary 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-subtle 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-text-primary\">\n {tr('channel.deliveryHistory', 'Delivery History')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.viewMessageDeliveries', 'View message deliveries')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-text-secondary 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-subtle 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-text-primary\">\n {tr('channel.webhookEvents', 'Webhook Events')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.viewIncomingWebhooks', 'View incoming webhooks')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-text-secondary 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-subtle 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-text-primary\">\n {tr('channel.channelUsers', 'Channel Users')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.viewConnectedUsers', 'View connected users')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-text-secondary shrink-0\" />\n </button>\n </div>\n\n {/* Connection / Credentials Management */}\n <div className=\"p-4 bg-surface border border-border-subtle 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-text-primary\">\n {tr('channel.connectionAndCredentials', 'Connection & Credentials')}\n </h2>\n </div>\n {canConnectionCreate && !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-text-secondary\">\n {tr('channel.authType', 'Auth Type')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\n {translateLabel(connection.authType, AUTH_TYPE_I18N, tr)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">{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-text-secondary\">\n {tr('channel.lastTested', 'Last Tested')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\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-text-secondary\">\n {tr('channel.testStatus', 'Test Status')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\n {connection.testStatus || tr('channel.notTested', 'Not tested')}\n </p>\n </div>\n {connection.tokenExpiresAt && (\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.tokenExpires', 'Token Expires')}\n </p>\n <p\n className={`text-sm font-medium ${connection.needsTokenRefresh ? 'text-status-warning-text' : 'text-text-primary'}`}\n >\n {new Date(connection.tokenExpiresAt).toLocaleString()}\n {connection.daysUntilExpiry !== undefined && (\n <span className=\"text-xs text-text-secondary 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 {canConnectionUpdate && (\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(\n (connection?.platformMetadata as Record<string, unknown>) ?? {}\n );\n }}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-text-secondary 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 )}\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-text-secondary mb-3\" />\n <p className=\"text-sm text-text-secondary mb-2\">\n {tr(\n 'channel.noCredentialsConfigured',\n 'No credentials configured for this channel'\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\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-text-secondary 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-subtle\">\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-text-secondary hover:bg-bg-sunken 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-subtle 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-text-primary\">\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-text-secondary hover:text-text-primary 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-text-primary 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-subtle 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-text-secondary\">\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-text-primary 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-subtle 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-text-primary 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-subtle 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-text-secondary mt-1\">\n External ID: {notificationResult.externalId}\n </p>\n )}\n {notificationResult.status && (\n <p className=\"text-xs text-text-secondary 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-text-secondary 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-text-secondary hover:bg-bg-sunken 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":";;;;;;;;;;;;;;AAoEA,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;EAAoC,MAAM;EAAU;CAC1F,OAAO;EACL,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,WAAW;EACT,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,UAAU;EAAE,OAAO;EAAY,OAAO;EAAoC,MAAM;EAAU;CAC3F,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;EAAuB,MAAM;EAAY;CAC9E;AAOD,SAAgB,IAAqB;CACnC,IAAM,EAAE,iBAAc,IAAkC,EAClD,IAAW,IAAa,EACxB,IAAW,IAAa,EACxB,EAAE,qBAAkB,IAAgB,EAEpC,IAAY,EAAc,EAAoB,eAAe,EAC7D,KAAsB,EAAc,EAAoB,0BAA0B,EAClF,KAAsB,EAAc,EAAoB,0BAA0B,EAClF,EAAE,UAAM,IAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,GAAE,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,KAA2B,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,KAAqB,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,IAAa,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,MAAK;KACL,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,MAAK;OACL,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;kBAAmD,EAAQ;QAAU,CAAA,CAC/E;UACL,EAAQ,eACP,kBAAC,KAAD;OAAG,WAAU;iBAAoC,EAAQ;OAAgB,CAAA,CAEvE,EAAA,CAAA,CACF;SAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAiB,YAAY,EAAoB;kBAC/C,kBAAC,UAAD;SACE,MAAK;SACL,SAAS;SACT,UAAU,MAAkB;SAC5B,WAAU;mBAJZ,CAMG,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,MAAK;SACL,SAAS;SACT,UAAU,MAAkB;SAC5B,WAAW,8FACT,EAAQ,WAAW,WACf,+DACA;mBAPR,CAUG,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,MAAK;QACL,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;kBAX9E,CAeE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,eAAe,CACnC;;OACR,KACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,SAAS;QACjC,WAAU;kBAHZ,CAKE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA,EACnB,EAAG,gBAAgB,OAAO,CACpB;;OAEP;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;kBAAoC,EAAG,kBAAkB,SAAS;QAAK,CAAA,EACpF,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;mBAA+B,EAAG,oBAAoB,WAAW;SAAK,CAAA,EACnF,kBAAC,KAAD;SAAG,WAAU;mBAAyC,EAAQ;SAAa,CAAA,CACvE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,+BAA+B,uBAAuB;SACxD,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAyC,EAAQ;SAAwB,CAAA,CAClF,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,uBAAuB,iBAAiB;SAC1C,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAyC,EAAQ;SAAoB,CAAA,CAC9E,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;kBAAsC,EAAG,gBAAgB,OAAO;QAAK,CAAA,EAClF,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,MAAK;OACL,eAAe,EAAS,eAAe;OACvC,WAAU;iBAHZ;QAKE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD;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;SAAiC,CAAA;QAC1D;;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,aAAa;OACrC,WAAU;iBAHZ;QAKE,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;SAAiC,CAAA;QAC1D;;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,UAAU;OAClC,WAAU;iBAHZ;QAKE,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;SAAiC,CAAA;QAC1D;;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,MAAuB,CAAC,KAAc,CAAC,KACtC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAsB,GAAK;QAC1C,WAAU;kBAHZ,CAKE,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,IAAgB,EAAG;WACtD,CAAA,CACA,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAA+B,EAAG,kBAAkB,SAAS;WAAK,CAAA,EAC/E,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;aAAmD;aAC/C,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,MAAK;YACL,eAAe,GAAqB,EAAW,GAAG;YAClD,UAAU,MAAkB;YAC5B,WAAU;sBAJZ,CAMG,MAAkB,SACjB,kBAAC,GAAD;aAAS,MAAM;aAAI,WAAU;aAAiB,CAAA,GAE9C,kBAAC,IAAD,EAAM,MAAM,IAAM,CAAA,EAEnB,EAAG,0BAA0B,kBAAkB,CACzC;;WACO,CAAA;UACjB,MACC,kBAAC,UAAD;WACE,MAAK;WACL,eAAe;AAQb,YAPA,EAAsB,GAAK,EAO3B,EACG,GAAY,oBAAgD,EAAE,CAChE;;WAEH,WAAU;qBAdZ,CAgBE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA,EACnB,EAAG,6BAA6B,qBAAqB,CAC/C;;UAEX,kBAAC,GAAD;WAAiB,YAAY,EAAoB;qBAC/C,kBAAC,UAAD;YACE,MAAK;YACL,eAAe,GAAuB,EAAW,GAAG;YACpD,UAAU,MAAkB;YAC5B,WAAU;sBAJZ,CAMG,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;SAAqC,CAAA;QAChE,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,MAAK;WACL,eAAe;AACb,YAAI,IACF,GAAuB,EAAW,GAAG,GAErC,IAAwB;;WAG5B,UACE,MAAkB,gBAAiB,CAAC,MAAoB,CAAC,CAAC,EAAQ;WAEpE,WAAU;qBAZZ,CAcG,MAAkB,eACjB,kBAAC,GAAD;YAAS,MAAM;YAAI,WAAU;YAAiB,CAAA,GAE9C,kBAAC,IAAD,EAAM,MAAM,IAAM,CAAA,EAEnB,IACG,EAAG,6BAA6B,qBAAqB,GACrD,EAAG,2BAA2B,mBAAmB,CAC9C;;UACO,CAAA,EAClB,kBAAC,UAAD;UACE,MAAK;UACL,eAAe;AAGb,WAFA,EAAsB,GAAM,EAC5B,EAAe,EAAE,CAAC,EAClB,EAAoB,GAAM;;UAE5B,WAAU;oBAPZ,CASE,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;UAAwD;UAChD,EAAgB,EAAQ,WAAW,SAAS,EAAQ;UAAS;UAChE;WACD;WACN,kBAAC,UAAD;QACE,MAAK;QACL,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,EAAkB,EAAQ,SAAS;UAAE;UACtC,kBAAC,QAAD;WAAM,WAAU;qBAAyB;WAAQ,CAAA;UAC3C;YACR,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,SAAD;UACE,cAAY,EAAwB,EAAQ,SAAS;UACrD,MAAK;UACL,OAAO;UACP,WAAW,MAAM,EAAiB,EAAE,OAAO,MAAM;UACjD,aAAa,EAAwB,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;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA,EACR,kBAAC,SAAD;SACE,IAAG;SACH,cAAW;SACX,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;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA,EACR,kBAAC,YAAD;SACE,IAAG;SACH,cAAW;SACX,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,CAAgD,iBAChC,EAAmB,WAC/B;;YAEL,EAAmB,UAClB,kBAAC,KAAD;aAAG,WAAU;uBAAb,CAAgD,YACrC,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,CAAgD,aACpC,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,MAAK;WACL,SAAS;WACT,UAAU,MAAkB,eAAe,CAAC,EAAc,MAAM;WAChE,WAAU;qBAJZ,CAMG,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,MAAK;UACL,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 { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\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-text-secondary bg-bg-sunken', 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-text-secondary bg-bg-sunken', 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-text-secondary', 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 { hasPermission } = usePermissions();\n // Admin-only affordances — hide when the user lacks the permission.\n const canUpdate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_UPDATE);\n const canConnectionCreate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_CREATE);\n const canConnectionUpdate = hasPermission(CHANNEL_PERMISSIONS.CHANNEL_CONNECTION_UPDATE);\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-text-secondary hover:text-text-primary hover:bg-bg-sunken 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-text-primary\">{channel.name}</h1>\n </div>\n {channel.description && (\n <p className=\"text-sm text-text-secondary 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-text-secondary 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 {canUpdate && (\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 )}\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-subtle rounded-lg\">\n <p className=\"text-xs text-text-secondary 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-subtle rounded-lg\">\n <p className=\"text-xs text-text-secondary 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-subtle rounded-lg\">\n <p className=\"text-xs text-text-secondary 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-text-secondary 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-subtle rounded-lg\">\n <p className=\"text-xs text-text-secondary 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-text-secondary'\n }\n />\n <span\n className={`text-sm font-medium ${channel.isConnected ? 'text-status-success-text' : 'text-text-secondary'}`}\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-subtle rounded-lg\">\n <h2 className=\"text-lg font-semibold text-text-primary 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-text-secondary\">\n {tr('channel.adapterType', 'Adapter Type')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\n {channel.adapterType?.replace(/_/g, ' ') ||\n tr('channel.notConfigured', 'Not configured')}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">{tr('channel.priority', 'Priority')}</p>\n <p className=\"text-sm font-medium text-text-primary\">{channel.priority}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.consecutiveFailures', 'Consecutive Failures')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">{channel.consecutiveFailures}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.messages24h', 'Messages (24h)')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">{channel.messageCount24h}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.createdAt', 'Created At')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\n {new Date(channel.createdAt).toLocaleString()}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.updatedAt', 'Updated At')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\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-text-secondary 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-subtle 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-text-primary\">\n {tr('channel.deliveryHistory', 'Delivery History')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.viewMessageDeliveries', 'View message deliveries')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-text-secondary 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-subtle 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-text-primary\">\n {tr('channel.webhookEvents', 'Webhook Events')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.viewIncomingWebhooks', 'View incoming webhooks')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-text-secondary 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-subtle 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-text-primary\">\n {tr('channel.channelUsers', 'Channel Users')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {tr('channel.viewConnectedUsers', 'View connected users')}\n </p>\n </div>\n <ArrowRight size={16} className=\"text-text-secondary shrink-0\" />\n </button>\n </div>\n\n {/* Connection / Credentials Management */}\n <div className=\"p-4 bg-surface border border-border-subtle 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-text-primary\">\n {tr('channel.connectionAndCredentials', 'Connection & Credentials')}\n </h2>\n </div>\n {canConnectionCreate && !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-text-secondary\">\n {tr('channel.authType', 'Auth Type')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\n {translateLabel(connection.authType, AUTH_TYPE_I18N, tr)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">{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-text-secondary\">\n {tr('channel.lastTested', 'Last Tested')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\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-text-secondary\">\n {tr('channel.testStatus', 'Test Status')}\n </p>\n <p className=\"text-sm font-medium text-text-primary\">\n {connection.testStatus || tr('channel.notTested', 'Not tested')}\n </p>\n </div>\n {connection.tokenExpiresAt && (\n <div>\n <p className=\"text-xs text-text-secondary\">\n {tr('channel.tokenExpires', 'Token Expires')}\n </p>\n <p\n className={`text-sm font-medium ${connection.needsTokenRefresh ? 'text-status-warning-text' : 'text-text-primary'}`}\n >\n {new Date(connection.tokenExpiresAt).toLocaleString()}\n {connection.daysUntilExpiry !== undefined && (\n <span className=\"text-xs text-text-secondary 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 {canConnectionUpdate && (\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(\n (connection?.platformMetadata as Record<string, unknown>) ?? {}\n );\n }}\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-text-secondary 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 )}\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-text-secondary mb-3\" />\n <p className=\"text-sm text-text-secondary mb-2\">\n {tr(\n 'channel.noCredentialsConfigured',\n 'No credentials configured for this channel'\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\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-text-secondary 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-subtle\">\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-text-secondary hover:bg-bg-sunken 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 <ResponsiveDialog\n open={showTestModal}\n onOpenChange={(open) => {\n if (!open) setShowTestModal(false);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center gap-2\">\n <Send size={20} className=\"text-accent-primary\" />\n Test {PLATFORM_CONFIG[channel.platform]?.label || channel.platform} Channel\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4\">\n {/* Recipient */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary 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-subtle 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-text-secondary\">\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-text-primary 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-subtle 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-text-primary 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-subtle 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-text-secondary mt-1\">\n External ID: {notificationResult.externalId}\n </p>\n )}\n {notificationResult.status && (\n <p className=\"text-xs text-text-secondary 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-text-secondary 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-text-secondary hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n {tr('channel.close', 'Close')}\n </button>\n </div>\n </div>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA0EA,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;EAAoC,MAAM;EAAU;CAC1F,OAAO;EACL,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,WAAW;EACT,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,UAAU;EAAE,OAAO;EAAY,OAAO;EAAoC,MAAM;EAAU;CAC3F,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;EAAuB,MAAM;EAAY;CAC9E;AAOD,SAAgB,IAAqB;CACnC,IAAM,EAAE,iBAAc,IAAkC,EAClD,IAAW,IAAa,EACxB,IAAW,IAAa,EACxB,EAAE,qBAAkB,IAAgB,EAEpC,KAAY,EAAc,EAAoB,eAAe,EAC7D,KAAsB,EAAc,EAAoB,0BAA0B,EAClF,KAAsB,EAAc,EAAoB,0BAA0B,EAClF,EAAE,UAAM,IAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,GAAE,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,KAA2B,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,IAAa,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,MAAK;KACL,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,MAAK;OACL,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;kBAAmD,EAAQ;QAAU,CAAA,CAC/E;UACL,EAAQ,eACP,kBAAC,KAAD;OAAG,WAAU;iBAAoC,EAAQ;OAAgB,CAAA,CAEvE,EAAA,CAAA,CACF;SAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAiB,YAAY,EAAoB;kBAC/C,kBAAC,UAAD;SACE,MAAK;SACL,SAAS;SACT,UAAU,MAAkB;SAC5B,WAAU;mBAJZ,CAMG,MAAkB,WACjB,kBAAC,GAAD;UAAS,MAAM;UAAI,WAAU;UAAiB,CAAA,GAE9C,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA,EAExB,EAAG,uBAAuB,eAAe,CACnC;;QACO,CAAA;OAClB,kBAAC,GAAD;QAAiB,YAAY,EAAoB;kBAC/C,kBAAC,UAAD;SACE,MAAK;SACL,SAAS;SACT,UAAU,MAAkB;SAC5B,WAAW,8FACT,EAAQ,WAAW,WACf,+DACA;mBAPR,CAUG,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,MAAK;QACL,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;kBAX9E,CAeE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,eAAe,CACnC;;OACR,MACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,SAAS;QACjC,WAAU;kBAHZ,CAKE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA,EACnB,EAAG,gBAAgB,OAAO,CACpB;;OAEP;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;kBAAoC,EAAG,kBAAkB,SAAS;QAAK,CAAA,EACpF,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;mBAA+B,EAAG,oBAAoB,WAAW;SAAK,CAAA,EACnF,kBAAC,KAAD;SAAG,WAAU;mBAAyC,EAAQ;SAAa,CAAA,CACvE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,+BAA+B,uBAAuB;SACxD,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAyC,EAAQ;SAAwB,CAAA,CAClF,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,uBAAuB,iBAAiB;SAC1C,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAyC,EAAQ;SAAoB,CAAA,CAC9E,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;kBAAsC,EAAG,gBAAgB,OAAO;QAAK,CAAA,EAClF,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,MAAK;OACL,eAAe,EAAS,eAAe;OACvC,WAAU;iBAHZ;QAKE,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;SAAiC,CAAA;QAC1D;;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,aAAa;OACrC,WAAU;iBAHZ;QAKE,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;SAAiC,CAAA;QAC1D;;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,UAAU;OAClC,WAAU;iBAHZ;QAKE,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;SAAiC,CAAA;QAC1D;;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,MAAuB,CAAC,KAAc,CAAC,KACtC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAsB,GAAK;QAC1C,WAAU;kBAHZ,CAKE,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,IAAgB,EAAG;WACtD,CAAA,CACA,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAA+B,EAAG,kBAAkB,SAAS;WAAK,CAAA,EAC/E,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;aAAmD;aAC/C,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,MAAK;YACL,eAAe,GAAqB,EAAW,GAAG;YAClD,UAAU,MAAkB;YAC5B,WAAU;sBAJZ,CAMG,MAAkB,SACjB,kBAAC,GAAD;aAAS,MAAM;aAAI,WAAU;aAAiB,CAAA,GAE9C,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAEnB,EAAG,0BAA0B,kBAAkB,CACzC;;WACO,CAAA;UACjB,MACC,kBAAC,UAAD;WACE,MAAK;WACL,eAAe;AAQb,YAPA,EAAsB,GAAK,EAO3B,EACG,GAAY,oBAAgD,EAAE,CAChE;;WAEH,WAAU;qBAdZ,CAgBE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA,EACnB,EAAG,6BAA6B,qBAAqB,CAC/C;;UAEX,kBAAC,GAAD;WAAiB,YAAY,EAAoB;qBAC/C,kBAAC,UAAD;YACE,MAAK;YACL,eAAe,GAAuB,EAAW,GAAG;YACpD,UAAU,MAAkB;YAC5B,WAAU;sBAJZ,CAMG,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;SAAqC,CAAA;QAChE,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,MAAK;WACL,eAAe;AACb,YAAI,IACF,GAAuB,EAAW,GAAG,GAErC,IAAwB;;WAG5B,UACE,MAAkB,gBAAiB,CAAC,MAAoB,CAAC,CAAC,EAAQ;WAEpE,WAAU;qBAZZ,CAcG,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,MAAK;UACL,eAAe;AAGb,WAFA,EAAsB,GAAM,EAC5B,EAAe,EAAE,CAAC,EAClB,EAAoB,GAAM;;UAE5B,WAAU;oBAPZ,CASE,kBAAC,IAAD,EAAG,MAAM,IAAM,CAAA,EACd,EAAG,kBAAkB,SAAS,CACxB;YACL;;QACF;;MAEJ;;IAGN,kBAAC,IAAD;KACE,MAAM;KACN,eAAe,MAAS;AACtB,MAAK,KAAM,EAAiB,GAAM;;eAGpC,kBAAC,IAAD;MAAyB,WAAU;gBAAnC,CACE,kBAAC,IAAD,EAAA,UACE,kBAAC,GAAD;OAAuB,WAAU;iBAAjC;QACE,kBAAC,GAAD;SAAM,MAAM;SAAI,WAAU;SAAwB,CAAA;;QAC5C,EAAgB,EAAQ,WAAW,SAAS,EAAQ;QAAS;QAC7C;UACD,CAAA,EAEvB,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,cAAY,EAAwB,EAAQ,SAAS;UACrD,MAAK;UACL,OAAO;UACP,WAAW,MAAM,EAAiB,EAAE,OAAO,MAAM;UACjD,aAAa,EAAwB,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;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA,EACR,kBAAC,SAAD;SACE,IAAG;SACH,cAAW;SACX,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;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA,EACR,kBAAC,YAAD;SACE,IAAG;SACH,cAAW;SACX,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,CAAgD,iBAChC,EAAmB,WAC/B;;YAEL,EAAmB,UAClB,kBAAC,KAAD;aAAG,WAAU;uBAAb,CAAgD,YACrC,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,CAAgD,aACpC,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,MAAK;WACL,SAAS;WACT,UAAU,MAAkB,eAAe,CAAC,EAAc,MAAM;WAChE,WAAU;qBAJZ,CAMG,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,MAAK;UACL,eAAe,EAAiB,GAAM;UACtC,WAAU;oBAET,EAAG,iBAAiB,QAAQ;UACtB,CAAA,CACL;;QACF;SACgB;;KACT,CAAA;IACf;;EACU,CAAA"}
|