// ======================================== // Platform Configuration Cards // ======================================== // Individual configuration cards for each notification platform import { useState } from 'react'; import { useIntl } from 'react-intl'; import { MessageCircle, Send, Link, Check, X, ChevronDown, ChevronUp, TestTube, Eye, EyeOff, MessageSquare, Bell, Users, Mail, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Badge } from '@/components/ui/Badge'; import { cn } from '@/lib/utils'; import type { RemoteNotificationConfig, NotificationPlatform, DiscordConfig, TelegramConfig, WebhookConfig, FeishuConfig, DingTalkConfig, WeComConfig, EmailConfig, } from '@/types/remote-notification'; import { PLATFORM_INFO } from '@/types/remote-notification'; interface PlatformConfigCardsProps { config: RemoteNotificationConfig; expandedPlatform: NotificationPlatform | null; testing: NotificationPlatform | null; onToggleExpand: (platform: NotificationPlatform | null) => void; onUpdateConfig: ( platform: NotificationPlatform, updates: Partial ) => void; onTest: ( platform: NotificationPlatform, config: DiscordConfig | TelegramConfig | WebhookConfig | FeishuConfig | DingTalkConfig | WeComConfig | EmailConfig ) => void; onSave: () => void; saving: boolean; } export function PlatformConfigCards({ config, expandedPlatform, testing, onToggleExpand, onUpdateConfig, onTest, onSave, saving, }: PlatformConfigCardsProps) { const { formatMessage } = useIntl(); const platforms: NotificationPlatform[] = ['discord', 'telegram', 'feishu', 'dingtalk', 'wecom', 'email', 'webhook']; const getPlatformIcon = (platform: NotificationPlatform) => { switch (platform) { case 'discord': return ; case 'telegram': return ; case 'feishu': return ; case 'dingtalk': return ; case 'wecom': return ; case 'email': return ; case 'webhook': return ; } }; const getPlatformConfig = ( platform: NotificationPlatform ): DiscordConfig | TelegramConfig | WebhookConfig | FeishuConfig | DingTalkConfig | WeComConfig | EmailConfig => { switch (platform) { case 'discord': return config.platforms.discord || { enabled: false, webhookUrl: '' }; case 'telegram': return config.platforms.telegram || { enabled: false, botToken: '', chatId: '' }; case 'feishu': return config.platforms.feishu || { enabled: false, webhookUrl: '' }; case 'dingtalk': return config.platforms.dingtalk || { enabled: false, webhookUrl: '' }; case 'wecom': return config.platforms.wecom || { enabled: false, webhookUrl: '' }; case 'email': return config.platforms.email || { enabled: false, host: '', port: 587, username: '', password: '', from: '', to: [] }; case 'webhook': return config.platforms.webhook || { enabled: false, url: '', method: 'POST' }; } }; const isConfigured = (platform: NotificationPlatform): boolean => { const platformConfig = getPlatformConfig(platform); switch (platform) { case 'discord': return !!(platformConfig as DiscordConfig).webhookUrl; case 'telegram': return !!(platformConfig as TelegramConfig).botToken && !!(platformConfig as TelegramConfig).chatId; case 'feishu': return !!(platformConfig as FeishuConfig).webhookUrl; case 'dingtalk': return !!(platformConfig as DingTalkConfig).webhookUrl; case 'wecom': return !!(platformConfig as WeComConfig).webhookUrl; case 'email': const emailConfig = platformConfig as EmailConfig; return !!(emailConfig.host && emailConfig.username && emailConfig.password && emailConfig.from && emailConfig.to?.length > 0); case 'webhook': return !!(platformConfig as WebhookConfig).url; } }; return (
{platforms.map((platform) => { const info = PLATFORM_INFO[platform]; const platformConfig = getPlatformConfig(platform); const configured = isConfigured(platform); const expanded = expandedPlatform === platform; return ( {/* Header */}
onToggleExpand(expanded ? null : platform)} >
{getPlatformIcon(platform)}
{info.name} {configured && ( {formatMessage({ id: 'settings.remoteNotifications.configured' })} )}

{info.description}

{expanded ? ( ) : ( )}
{/* Expanded Content */} {expanded && (
{platform === 'discord' && ( onUpdateConfig('discord', updates)} /> )} {platform === 'telegram' && ( onUpdateConfig('telegram', updates)} /> )} {platform === 'feishu' && ( onUpdateConfig('feishu', updates)} /> )} {platform === 'dingtalk' && ( onUpdateConfig('dingtalk', updates)} /> )} {platform === 'wecom' && ( onUpdateConfig('wecom', updates)} /> )} {platform === 'email' && ( onUpdateConfig('email', updates)} /> )} {platform === 'webhook' && ( onUpdateConfig('webhook', updates)} /> )} {/* Action Buttons */}
)}
); })}
); } // ========== Discord Config Form ========== function DiscordConfigForm({ config, onUpdate, }: { config: DiscordConfig; onUpdate: (updates: Partial) => void; }) { const { formatMessage } = useIntl(); const [showUrl, setShowUrl] = useState(false); return (
onUpdate({ webhookUrl: e.target.value })} placeholder="https://discord.com/api/webhooks/..." className="flex-1" />

{formatMessage({ id: 'settings.remoteNotifications.discord.webhookUrlHint' })}

onUpdate({ username: e.target.value })} placeholder="CCW Notification" className="mt-1" />
); } // ========== Telegram Config Form ========== function TelegramConfigForm({ config, onUpdate, }: { config: TelegramConfig; onUpdate: (updates: Partial) => void; }) { const { formatMessage } = useIntl(); const [showToken, setShowToken] = useState(false); return (
onUpdate({ botToken: e.target.value })} placeholder="1234567890:ABCdefGHIjklMNOpqrsTUVwxyz" className="flex-1" />

{formatMessage({ id: 'settings.remoteNotifications.telegram.botTokenHint' })}

onUpdate({ chatId: e.target.value })} placeholder="-1001234567890" className="mt-1" />

{formatMessage({ id: 'settings.remoteNotifications.telegram.chatIdHint' })}

); } // ========== Webhook Config Form ========== function WebhookConfigForm({ config, onUpdate, }: { config: WebhookConfig; onUpdate: (updates: Partial) => void; }) { const { formatMessage } = useIntl(); return (
onUpdate({ url: e.target.value })} placeholder="https://your-server.com/webhook" className="mt-1" />
{ try { const headers = e.target.value ? JSON.parse(e.target.value) : undefined; onUpdate({ headers }); } catch { // Invalid JSON, ignore } }} placeholder='{"Authorization": "Bearer token"}' className="mt-1 font-mono text-xs" />

{formatMessage({ id: 'settings.remoteNotifications.webhook.headersHint' })}

); } // ========== Feishu Config Form ========== function FeishuConfigForm({ config, onUpdate, }: { config: FeishuConfig; onUpdate: (updates: Partial) => void; }) { const { formatMessage } = useIntl(); const [showUrl, setShowUrl] = useState(false); return (
onUpdate({ webhookUrl: e.target.value })} placeholder="https://open.feishu.cn/open-apis/bot/v2/hook/..." className="flex-1" />

{formatMessage({ id: 'settings.remoteNotifications.feishu.webhookUrlHint' })}

onUpdate({ useCard: e.target.checked })} className="rounded border-border" />

{formatMessage({ id: 'settings.remoteNotifications.feishu.useCardHint' })}

onUpdate({ title: e.target.value })} placeholder="CCW Notification" className="mt-1" />
); } // ========== DingTalk Config Form ========== function DingTalkConfigForm({ config, onUpdate, }: { config: DingTalkConfig; onUpdate: (updates: Partial) => void; }) { const { formatMessage } = useIntl(); const [showUrl, setShowUrl] = useState(false); return (
onUpdate({ webhookUrl: e.target.value })} placeholder="https://oapi.dingtalk.com/robot/send?access_token=..." className="flex-1" />

{formatMessage({ id: 'settings.remoteNotifications.dingtalk.webhookUrlHint' })}

onUpdate({ keywords: e.target.value.split(',').map(k => k.trim()).filter(Boolean) })} placeholder="keyword1, keyword2" className="mt-1" />

{formatMessage({ id: 'settings.remoteNotifications.dingtalk.keywordsHint' })}

); } // ========== WeCom Config Form ========== function WeComConfigForm({ config, onUpdate, }: { config: WeComConfig; onUpdate: (updates: Partial) => void; }) { const { formatMessage } = useIntl(); const [showUrl, setShowUrl] = useState(false); return (
onUpdate({ webhookUrl: e.target.value })} placeholder="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=..." className="flex-1" />

{formatMessage({ id: 'settings.remoteNotifications.wecom.webhookUrlHint' })}

onUpdate({ mentionedList: e.target.value.split(',').map(m => m.trim()).filter(Boolean) })} placeholder="userid1, userid2, @all" className="mt-1" />

{formatMessage({ id: 'settings.remoteNotifications.wecom.mentionedListHint' })}

); } // ========== Email Config Form ========== function EmailConfigForm({ config, onUpdate, }: { config: EmailConfig; onUpdate: (updates: Partial) => void; }) { const { formatMessage } = useIntl(); const [showPassword, setShowPassword] = useState(false); return (
onUpdate({ host: e.target.value })} placeholder="smtp.gmail.com" className="mt-1" />

{formatMessage({ id: 'settings.remoteNotifications.email.hostHint' })}

onUpdate({ port: parseInt(e.target.value, 10) || 587 })} placeholder="587" className="mt-1" />
onUpdate({ secure: e.target.checked })} className="rounded border-border" />
onUpdate({ username: e.target.value })} placeholder="your-email@gmail.com" className="mt-1" />
onUpdate({ password: e.target.value })} placeholder="********" className="flex-1" />
onUpdate({ from: e.target.value })} placeholder="noreply@example.com" className="mt-1" />
onUpdate({ to: e.target.value.split(',').map(t => t.trim()).filter(Boolean) })} placeholder="user1@example.com, user2@example.com" className="mt-1" />

{formatMessage({ id: 'settings.remoteNotifications.email.toHint' })}

); } export default PlatformConfigCards;