import { useCallback, useEffect, useRef, useState, type ReactNode, } from "react"; import { Banner, Button, Card, SideSheet, Spin, Tag, } from "@douyinfe/semi-ui-19"; import { CalendarClock, ExternalLink, Megaphone, RefreshCw, Sparkles, Trophy, TrendingUp, } from "lucide-react"; import { api, formatTime } from "../api"; import "./home-information.css"; type Item = { id: string; title: string; description: string; date?: string; type?: string; start?: string; end?: string; timezone?: string; original_start?: string; original_end?: string; date_status?: string; status?: string; rank?: number | null; alphas?: number | null; robustness_score?: number | null; progress?: string | null; url: string; links: string[]; }; type Content = { items?: Item[]; total?: number; next_offset?: number | null; rank?: number | null; change?: number | null; label?: string; url?: string; detail?: Item; sources?: { id: string; text: string }[]; agreement_updated?: string; agreement_title?: string; agreement_url?: string; }; type Analysis = { items: { title: string; text: string; evidence: { source_id: string; quote: string }[]; }[]; suggestions: string[]; model: string; generated_at: string; outdated: boolean; }; type Snapshot = { ephemeral?: boolean; content: Content | null; fetched_at: string | null; error: string | null; stale: boolean; source_version: string; analysis: Analysis | null; can_generate: boolean; }; function useInformation( module: string, refresh: number, query = "", dependency = "", ) { const [data, setData] = useState(); const [busy, setBusy] = useState(""); const [error, setError] = useState(""); const sequence = useRef(0); const controller = useRef(null); const previousRefresh = useRef(refresh); const generating = useRef(false); const deferredRead = useRef(null); const load = useCallback( async function loadInformation(action = "") { if (generating.current) { if (action !== "generate") deferredRead.current = deferredRead.current === "refresh" ? "refresh" : action; return; } generating.current = action === "generate"; const id = ++sequence.current; controller.current?.abort(); const abort = new AbortController(); controller.current = abort; setBusy(action || "read"); setError(""); try { const result = await api( `/dashboard/information/${module}${action ? `/${action}` : ""}${query}`, { method: action ? "POST" : "GET", signal: abort.signal, }, ); if (id === sequence.current) setData(result); } catch (e) { if (!abort.signal.aborted && id === sequence.current) setError((e as Error).message); } finally { if (id === sequence.current) { generating.current = false; setBusy(""); const deferred = deferredRead.current; deferredRead.current = null; if (deferred !== null) void loadInformation(deferred); } } }, [module, query], ); useEffect(() => { const action = previousRefresh.current === refresh ? "" : "refresh"; previousRefresh.current = refresh; void load(action); }, [load, refresh, dependency]); useEffect( () => () => { controller.current?.abort(); sequence.current++; }, [], ); useEffect(() => { const update = () => { void load(); }; window.addEventListener("ai-settings-changed", update); return () => window.removeEventListener("ai-settings-changed", update); }, [load]); return { data, busy, error, load }; } type Resource = ReturnType; function SourceLink({ url, children = "查看来源", }: { url?: string; children?: ReactNode; }) { if (!url || !/^https?:\/\//i.test(url)) return null; return ( {children} ); } function DateLabel({ value, zone }: { value?: string | null; zone: string }) { // Date-only/naive timestamps have no proven timezone; preserve instead of guessing. if (!value) return 日期不明; if (!/(Z|[+-]\d{2}:\d{2})$/i.test(value)) return {value}(时区未知); return ( ); } function Interpretation({ state, zone }: { state: Resource; zone: string }) { const analysis = state.data?.analysis; return (
{!state.data?.can_generate && (

配置基础信息处理模型并读取平台信息后,可手动生成解读。

)} {analysis ? ( <>
{analysis.model} · {formatTime(analysis.generated_at, zone)} {analysis.outdated && 解读待更新}
{analysis.outdated && (

来源或模型配置已变化,以下为上次解读。

)} {analysis.items.map((item, index) => (
{item.title}

{item.text}

{item.evidence.map((e, i) => (
原文依据 · {e.source_id}
{e.quote}
))}
))} {!!analysis.suggestions.length && ( <> 建议安排 · 仅供参考
    {analysis.suggestions.map((s, i) => (
  • {s}
  • ))}
)} ) : (

{state.data?.ephemeral ? "解读仅暂存在内存,不写入数据库;重启或缓存到期后清除。" : "手动生成并保存,重新打开首页不会调用模型。"}

)}
); } function InformationCard({ title, icon, state, zone, children, analysis = true, }: { title: string; icon: ReactNode; state: Resource; zone: string; children: ReactNode; analysis?: boolean; }) { return ( {icon} {title} } headerExtraContent={ } >
更新:{formatTime(state.data?.fetched_at, zone)} {(state.data?.stale || (state.error && state.data?.content)) && ( 缓存已过期 )}
{(state.error || state.data?.error) && ( )} {!state.data && state.busy && (
正在读取{title}…
)} {state.data?.content && children} {analysis && }
); } function Empty({ children }: { children: ReactNode }) { return

{children}

; } function MessageCard({ refresh, zone }: { refresh: number; zone: string }) { const [offsets, setOffsets] = useState([0]); const offset = offsets[offsets.length - 1]; return ( 1 ? () => setOffsets(offsets.slice(0, -1)) : undefined } next={(value) => setOffsets([...offsets, value])} /> ); } function MessagePage({ offset, refresh, zone, back, next, }: { offset: number; refresh: number; zone: string; back?: () => void; next: (offset: number) => void; }) { const state = useInformation("messages", refresh, `?offset=${offset}`); const content = state.data?.content; return ( } state={state} zone={zone} >

仅展示最近一个月的公告、更新及社区资讯(按美东时间筛选),不写入数据库。

{content?.items?.length ? (
{content.items.map((item, index) => (
{item.title} {item.type && {item.type}}

查看消息正文

{item.description || "平台未提供正文"}

{item.links.map((link) => (
{link}
))}
))}
) : ( 最近一个月本页暂无平台消息 )}
从第 {offset + 1} 条开始 {content?.total != null ? ` · 最近一个月共 ${content.total} 条` : ""}{" "} · 解读仅针对本页
); } function LeaderboardCard({ refresh, zone }: { refresh: number; zone: string }) { const state = useInformation("leaderboard", refresh); const data = state.data?.content; return ( } state={state} zone={zone} analysis={false} >

{data?.label}

{data?.rank?.toLocaleString() ?? "—"}

{data?.rank == null ? "平台未提供有效名次" : data?.change == null ? "暂无可比较的历史快照" : data.change === 0 ? "较上次有效快照持平" : `较上次有效快照${data.change > 0 ? "上升" : "下降"} ${Math.abs(data.change)} 名`}

仅比较同一账户、美东同日的顾问日度榜快照。比赛排名在比赛卡片中单独展示。

); } function CompetitionDetail({ id, zone, refresh, }: { id: string; zone: string; refresh: number; }) { const state = useInformation( "competition", refresh, `?competition_id=${encodeURIComponent(id)}`, ); const data = state.data?.content; return ( } state={state} zone={zone} > {data?.detail && ( <>

{data.detail.title}

{data.detail.description}

截止:

比赛排名:{data.detail.rank ?? "平台未提供"} · Alpha 数: {data.detail.alphas ?? "平台未提供"}

进度:{data.detail.progress ?? "平台未提供"} · 稳健性分数: {data.detail.robustness_score ?? "平台未提供"}

平台比赛页面 )}

{data?.agreement_title || "比赛协议"}

协议更新:

协议原文入口
展开协议原文 {data?.sources?.length ? ( data.sources.map((s) => (
{s.id}

{s.text}

)) ) : ( 平台未提供可解读的协议正文 )}
); } function EventsCard({ refresh, zone, dependency, }: { refresh: number; zone: string; dependency: string; }) { const state = useInformation("events", refresh, "", dependency); return ( } state={state} zone={zone} > {state.data?.content?.items?.length ? (
{state.data.content.items.map((item, i) => (
{item.title} {item.date_status}

开始:
结束:

平台时区:{item.timezone || "未知"} · 原始时间: {item.original_start || item.start || "未知"} 至{" "} {item.original_end || item.end || "未知"}

{item.description}

))}
) : ( 暂无尚未结束的活动 )}
); } export function HomeInformation({ timezone: zone }: { timezone: string }) { const [refresh, setRefresh] = useState(0); const [selected, setSelected] = useState(""); const competitions = useInformation("competitions", refresh); return (

平台动向

} state={competitions} zone={zone} >

优先展示未结束比赛,按截止时间排序;点击查看当前协议及规则解读。

{competitions.data?.content?.items?.length ? (
{competitions.data.content.items.map((item) => (
{item.title} {item.date_status}

截止:

平台状态:{item.status || "平台未提供"}
比赛排名:{item.rank ?? "平台未提供"} · Alpha 数: {item.alphas ?? "平台未提供"}

))}
) : ( 暂无已参加的比赛 )}
setSelected("")} width="min(760px, 100vw)" closeOnEsc > {selected && ( )}
); }