'use client'; import { useMemo, useState } from 'react'; import Link from 'next/link'; import { AlertTriangle, Check, Clipboard, ExternalLink, Flame, Lightbulb, Link2, Radar, Target, UserRoundCheck, } from 'lucide-react'; import type { ReactNode } from 'react'; export interface DashboardSignalItem { chatroom_id: string; chat_name: string; local_id: number; sender: string; time: string; title: string; snippet: string; score: number; reasons: string[]; } export interface DashboardOpportunityItem extends DashboardSignalItem { action: string; } export interface DashboardActionItem extends DashboardOpportunityItem { why: string; urgency: 'high' | 'medium' | 'low'; } export interface DashboardSignalSource { sender: string; signal_count: number; group_count: number; top_group: string; last_seen: string; strengths: string[]; } export interface DashboardTopicLifecycle { title: string; status: 'rising' | 'spreading' | 'hot' | 'cooling'; today_count: number; previous_avg: number; group_count: number; reason: string; keywords: string[]; } export interface DashboardLinkHighlight { kind: 'article' | 'tool'; title: string; url: string; domain: string; score: number; verdict: string; count: number; group_count: number; last_seen: string; } export interface DashboardPeopleRadar { sender: string; role: '分享者' | '需求提出者' | '连接者' | '观点源'; score: number; group_count: number; signal_count: number; top_group: string; reason: string; } export interface DashboardContentIdea { title: string; angle: string; suggested_channel: '公众号' | 'X' | '小红书' | '博客'; evidence: string; source_count: number; } export interface DashboardAnomalySignal { kind: 'spike' | 'cross_group' | 'dense_links' | 'quiet_day'; title: string; description: string; severity: 'high' | 'medium' | 'low'; href?: string; } export interface DashboardIntelligence { date: string; must_read: DashboardSignalItem[]; opportunities: DashboardOpportunityItem[]; signal_sources: DashboardSignalSource[]; action_items: DashboardActionItem[]; topic_lifecycle: DashboardTopicLifecycle[]; link_highlights: DashboardLinkHighlight[]; people_radar: DashboardPeopleRadar[]; content_ideas: DashboardContentIdea[]; anomalies: DashboardAnomalySignal[]; } export default function IntelligenceBrief({ intelligence }: { intelligence?: DashboardIntelligence }) { const [copied, setCopied] = useState(false); const data = intelligence ?? ({ date: '', must_read: [], opportunities: [], signal_sources: [], action_items: [], topic_lifecycle: [], link_highlights: [], people_radar: [], content_ideas: [], anomalies: [], } satisfies DashboardIntelligence); const summary = useMemo(() => buildDailySummary(data), [data]); async function copySummary() { await navigator.clipboard.writeText(summary); setCopied(true); window.setTimeout(() => setCopied(false), 1200); } return (
Briefing Note
今日情报简报
{summary.split('\n').slice(1, 4).join(' · ')}
); } function ActionPanel({ date, items, fallback, }: { date: string; items: DashboardActionItem[]; fallback: DashboardOpportunityItem[]; }) { const displayItems = items.length > 0 ? items : fallback.map((item) => ({ ...item, why: '包含明确需求或行动线索,适合进入上下文判断', urgency: 'medium' as const, })); return (
} title="今日值得出手" meta={`${displayItems.length} 条`} /> {displayItems.length === 0 ? ( ) : (
{displayItems.slice(0, 6).map((item) => (
{item.action} {urgencyText(item.urgency)}
{item.title}
{item.why}
))}
)}
); } function TopicLifecyclePanel({ topics }: { topics: DashboardTopicLifecycle[] }) { return (
} title="趋势升温" meta={`${topics.length} 个话题`} /> {topics.length === 0 ? ( ) : (
{topics.slice(0, 6).map((topic) => (
{topic.title}
{topic.reason}
{topicStatusText(topic.status)}
{topic.today_count} 条 {topic.group_count} 群 均值 {topic.previous_avg}
))}
)}
); } function AnomalyPanel({ anomalies }: { anomalies: DashboardAnomalySignal[] }) { return (
} title="异常信号" meta={`${anomalies.length} 条`} /> {anomalies.length === 0 ? ( ) : (
{anomalies.slice(0, 6).map((item) => { const body = ( <>
{item.title}
{severityText(item.severity)}
{item.description}
); return item.href ? ( {body} ) : (
{body}
); })}
)}
); } function LinkHighlightPanel({ items }: { items: DashboardLinkHighlight[] }) { return (
} title="链接精选" meta={`${items.length} 条`} /> {items.length === 0 ? ( ) : (
{items.slice(0, 6).map((item) => (
{item.title}
{item.verdict}
{item.kind === 'tool' ? '工具' : '文章'}
{item.domain} {item.group_count} 群 {item.count} 次
))}
)}
); } function PeopleRadarPanel({ people, fallback, }: { people: DashboardPeopleRadar[]; fallback: DashboardSignalSource[]; }) { const displayPeople = people.length > 0 ? people : fallback.map((source) => ({ sender: source.sender, role: '分享者' as const, score: source.signal_count, group_count: source.group_count, signal_count: source.signal_count, top_group: source.top_group, reason: `${source.signal_count} 条高信号`, })); return (
} title="人物雷达" meta={`${displayPeople.length} 人`} /> {displayPeople.length === 0 ? ( ) : (
{displayPeople.slice(0, 7).map((person, index) => (
{index + 1}
{person.sender}
{person.role} · {person.reason}
{person.score}
{person.group_count} 群
))}
)}
); } function ContentIdeaPanel({ ideas }: { ideas: DashboardContentIdea[] }) { return (
} title="内容选题" meta={`${ideas.length} 个`} /> {ideas.length === 0 ? ( ) : (
{ideas.slice(0, 6).map((idea) => (
{idea.title}
{idea.suggested_channel}
{idea.angle}
证据:{idea.evidence}
))}
)}
); } function MustReadPanel({ date, items }: { date: string; items: DashboardSignalItem[] }) { return (
} title="最值得关注" meta={`${items.length} 条高信号`} /> {items.length === 0 ? ( ) : (
{items.slice(0, 5).map((item, index) => ( ))}
)}
); } function SignalRow({ item, date, rank, }: { item: DashboardSignalItem; date: string; rank: number; }) { return ( {rank} {item.title} {item.score} {item.chat_name} · {item.sender} {item.time.slice(11)} {item.reasons.slice(0, 3).map((reason) => ( {reason} ))} ); } function PanelTitle({ icon, title, meta }: { icon: ReactNode; title: string; meta: string }) { return (
{icon} {title}
{meta}
); } function EmptyState({ text }: { text: string }) { return (
{text}
); } function buildDailySummary(data: DashboardIntelligence): string { const lines = [`${data.date || '今日'} 情报摘要`]; lines.push(`出手:${data.action_items.slice(0, 3).map((item) => `${item.action}|${item.title}`).join(';') || '暂无'}`); lines.push(`趋势:${data.topic_lifecycle.slice(0, 3).map((topic) => `${topic.title}(${topicStatusText(topic.status)})`).join(';') || '暂无'}`); lines.push( `链接:${data.link_highlights.slice(0, 3).map((item) => item.title).join(';') || '暂无'}`, ); lines.push( `异常:${data.anomalies.slice(0, 2).map((item) => item.title).join(';') || '暂无'}`, ); return lines.join('\n'); } function urgencyText(urgency: DashboardActionItem['urgency']): string { if (urgency === 'high') return '高'; if (urgency === 'medium') return '中'; return '低'; } function urgencyClass(urgency: DashboardActionItem['urgency']): string { const base = 'shrink-0 rounded px-1.5 py-0.5 text-[10px]'; if (urgency === 'high') return `${base} bg-[var(--warn-soft)] text-[var(--warn)]`; if (urgency === 'medium') return `${base} bg-[var(--accent-soft)] text-[var(--accent)]`; return `${base} bg-[var(--surface-2)] text-[var(--text-3)]`; } function topicStatusText(status: DashboardTopicLifecycle['status']): string { if (status === 'spreading') return '扩散'; if (status === 'rising') return '升温'; if (status === 'cooling') return '退潮'; return '高热'; } function topicStatusClass(status: DashboardTopicLifecycle['status']): string { const base = 'shrink-0 rounded px-1.5 py-0.5 text-[10px]'; if (status === 'spreading') return `${base} bg-[var(--accent-soft)] text-[var(--accent)]`; if (status === 'rising') return `${base} bg-[var(--warn-soft)] text-[var(--warn)]`; if (status === 'cooling') return `${base} bg-[var(--surface-2)] text-[var(--text-3)]`; return `${base} bg-[var(--surface-2)] text-[var(--text-2)]`; } function severityText(severity: DashboardAnomalySignal['severity']): string { if (severity === 'high') return '高'; if (severity === 'medium') return '中'; return '低'; } function severityClass(severity: DashboardAnomalySignal['severity']): string { const base = 'shrink-0 rounded px-1.5 py-0.5 text-[10px]'; if (severity === 'high') return `${base} bg-[var(--warn-soft)] text-[var(--warn)]`; if (severity === 'medium') return `${base} bg-[var(--accent-soft)] text-[var(--accent)]`; return `${base} bg-[var(--surface-2)] text-[var(--text-3)]`; } function reasonExplanation(reason: string): string { const map: Record = { '机会/需求': '包含合作、采购、报名、求推荐或找资源等可行动信号', '工具/产品': '提到了工具、产品、模型、插件、项目或技术栈', 链接信号: '包含可跳转链接,适合进入原文或资源查看', 可跟进: '含有联系、报名、评估、试用、帮忙等行动线索', 长观点: '消息长度较高,可能包含完整观点或经验复盘', 问题: '包含明确问题,可能适合回复或继续追踪', }; return map[reason] ?? reason; }