"use client"; import { useMemo, useState } from "react"; import { ConversationDetail, ConversationSummary } from "@/features/agent/types"; import { formatConversationTime, isVisitorOnline, } from "@/utils/format"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Separator } from "@/components/ui/separator"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; type ContactField = "email" | "phone" | "notes"; type ContactUpdatePayload = Partial>; interface VisitorDetailPanelProps { conversation: ConversationSummary | null; detail: ConversationDetail | null; onRefresh: () => void; onUpdateContact: (payload: ContactUpdatePayload) => Promise; } const displayValue = (value?: string | null, placeholder = "暂未填写") => { if (!value) { return placeholder; } const trimmed = value.trim(); return trimmed || placeholder; }; export function VisitorDetailPanel({ conversation, detail, onRefresh, onUpdateContact, }: VisitorDetailPanelProps) { const [editingField, setEditingField] = useState(null); const [editingValue, setEditingValue] = useState(""); const [saving, setSaving] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const fieldLabels = useMemo>( () => ({ email: "邮箱", phone: "电话", notes: "备注", }), [] ); if (!conversation) { return (
选择一个对话查看详情
); } const avatarColor = `hsl(${(conversation.visitor_id * 137.5) % 360}, 70%, 50%)`; // 根据 last_seen_at 判断是否在线(优先使用 detail,因为它是最新的) // 如果 detail 不存在,使用 conversation.last_seen_at const isOnline = isVisitorOnline( detail?.last_seen_at ?? conversation.last_seen_at ?? null ); const getFieldValue = (field: ContactField) => { if (!detail) { return ""; } switch (field) { case "email": return detail.email ?? ""; case "phone": return detail.phone ?? ""; case "notes": return detail.notes ?? ""; default: return ""; } }; const handleOpenEditor = (field: ContactField) => { setEditingField(field); setEditingValue(getFieldValue(field)); setErrorMessage(""); }; const handleCloseEditor = () => { if (saving) { return; } setEditingField(null); setEditingValue(""); setErrorMessage(""); }; const handleSubmit = async () => { if (!editingField) { return; } setSaving(true); try { const payload: ContactUpdatePayload = { [editingField]: editingValue, }; await onUpdateContact(payload); setEditingField(null); setEditingValue(""); setErrorMessage(""); } catch (error) { setErrorMessage((error as Error).message || "保存失败,请稍后重试"); } finally { setSaving(false); } }; const actionLabel = (field: ContactField) => { const current = getFieldValue(field).trim(); return current ? "编辑" : "+ Add"; }; return (
{conversation.visitor_id.toString().slice(-2)}
访客 #{conversation.visitor_id}
{isOnline ? ( ● 在线 ) : ( ● 离线 )}
{/* 联系信息区域 */} 联系信息
邮箱
{displayValue(detail?.email, "暂未填写")}
电话
{displayValue(detail?.phone, "暂未填写")}
备注
{displayValue(detail?.notes, "暂无备注")}
{/* 技术信息区域 */} 技术信息
网站
{detail?.website ? ( {detail.website} ) : (
暂未收集
)}
来源
{detail?.referrer ? ( {detail.referrer} ) : (
暂无来源信息
)}
语言
{displayValue(detail?.language, "暂未收集")}
浏览器
{displayValue(detail?.browser, "暂未收集")}
操作系统
{displayValue(detail?.os, "暂未收集")}
IP 地址
{displayValue(detail?.ip_address, "暂未收集")}
位置
{displayValue(detail?.location, "暂未收集")}
最后活跃
{detail?.last_seen_at ? formatConversationTime(detail.last_seen_at) : "未知"}
!saving && handleCloseEditor()}> 编辑{editingField ? fieldLabels[editingField] : ""} {editingField === "notes" ? (