"use client"; import { useEffect, useState } from "react"; import { Brain, RefreshCw, Sparkles } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog } from "@/components/ui/dialog"; import type { ElderAiAnalysisHistoryItem } from "@/modules/ai/types"; import type { ApiResult } from "@/modules/core/server/api"; import type { Elder } from "@/modules/elders/types"; type ElderAiAnalysisDialogProps = { elder: Elder | null; open: boolean; onClose: () => void; }; const riskLabels: Record = { low: "低", medium: "中", high: "高", critical: "紧急", unknown: "未知", }; function formatDateTime(value: string): string { return new Date(value).toLocaleString("zh-CN"); } export function ElderAiAnalysisDialog({ elder, onClose, open }: ElderAiAnalysisDialogProps): React.ReactElement { const [history, setHistory] = useState([]); const [message, setMessage] = useState(""); const [isLoading, setIsLoading] = useState(false); const [isGenerating, setIsGenerating] = useState(false); async function loadHistory(target: Elder): Promise { setIsLoading(true); setMessage(""); const response = await fetch(`/api/ai/elders/${target.id}/analyses`); const result = (await response.json()) as ApiResult<{ history: ElderAiAnalysisHistoryItem[] }>; setIsLoading(false); if (!result.success) { setMessage(result.reason); return; } setHistory(result.history); } useEffect(() => { if (open && elder) { void loadHistory(elder); } }, [elder, open]); async function generate(): Promise { if (!elder) { return; } setIsGenerating(true); setMessage(""); const response = await fetch(`/api/ai/elders/${elder.id}/analyses`, { method: "POST" }); const result = (await response.json()) as ApiResult<{ analysis: ElderAiAnalysisHistoryItem }>; setIsGenerating(false); if (!result.success) { setMessage(result.reason); await loadHistory(elder); return; } setHistory((current) => [result.analysis, ...current]); setMessage(result.reason); } const latest = history[0]; const latestResult = latest && !latest.restricted ? latest.result : undefined; return ( {elder ? (

{elder.name}

{elder.age} 岁 · {elder.careLevel} · {elder.status} · {elder.room && elder.bed ? `${elder.room}-${elder.bed}` : "未绑定床位"}

{message ? (

{message}

) : null} {latestResult ? (
最新分析 {latest ? formatDateTime(latest.createdAt) : ""}
风险:{riskLabels[latestResult.overallRiskLevel] ?? latestResult.overallRiskLevel}

{latestResult.summary}

关键发现

{latestResult.keyFindings.map((finding, index) => (
{finding.category} {finding.severity}

{finding.evidence}

))}

建议动作

{latestResult.recommendations.map((recommendation, index) => (
{recommendation.title} {recommendation.priority}

{recommendation.rationale}

{recommendation.suggestedNextStep}

))}
{latestResult.dataGaps.length > 0 ? (

数据缺口

    {latestResult.dataGaps.map((gap) => (
  • {gap}
  • ))}
) : null}

引用来源

{latestResult.citations.map((citation) => (

[{citation.id}] {citation.title}

{citation.excerpt}

))}
) : latest ? (
最新记录 {formatDateTime(latest.createdAt)}
{latest.status === "failed" ? "生成失败" : "受限"}

{latest.restricted ? "当前角色缺少该分析所使用数据范围的读取权限。" : latest.errorReason ?? "暂无可展示分析。"}

{latest.status === "failed" ?

失败记录仅保存脱敏原因,不包含提示词、住民上下文或供应商原始错误。

: null}
) : ( {isLoading ? "正在加载分析历史" : "暂无 AI 分析历史"} )}

历史记录

{history.map((item) => (

{formatDateTime(item.createdAt)}

范围:{item.dataScopes.join(", ") || "-"}

{item.restricted ? 受限 : null} {item.status}
))}
) : (
)}
); }