"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { deleteLogMinLevelPolicy, fetchLogMinLevelPolicy, fetchSystemLogs, putLogMinLevelPolicy, type LogMinLevelPolicy, type QuerySystemLogsResult, } from "@/features/agent/services/systemLogApi"; import { toast } from "@/hooks/useToast"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Copy } from "lucide-react"; import type { I18nKey } from "@/lib/i18n/dict"; import { useI18n } from "@/lib/i18n/provider"; function tryFormatJSON(raw?: string | null): string { if (!raw) return ""; try { const parsed = JSON.parse(raw); return JSON.stringify(parsed, null, 2); } catch { return raw; } } function levelColor(level: string): string { if (level === "error") return "text-red-600"; if (level === "warn") return "text-amber-600"; return "text-emerald-600"; } export default function LogsPage({ embedded = false }: { embedded?: boolean }) { const { t, lang } = useI18n(); const tr = (key: I18nKey, vars?: Record) => { let s = t(key); if (!vars) return s; for (const k of Object.keys(vars)) { s = s.replaceAll(`{{${k}}}`, vars[k] ?? ""); } return s; }; const locale = lang === "en" ? "en-US" : "zh-CN"; const [from, setFrom] = useState(() => { const d = new Date(); d.setDate(d.getDate() - 6); return d.toISOString().slice(0, 10); }); const [to, setTo] = useState(() => new Date().toISOString().slice(0, 10)); const [level, setLevel] = useState(""); const [category, setCategory] = useState(""); const [source, setSource] = useState(""); const [event, setEvent] = useState(""); const [keyword, setKeyword] = useState(""); const [conversationId, setConversationId] = useState(""); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [page, setPage] = useState(1); const pageSize = 50; const [selected, setSelected] = useState<(QuerySystemLogsResult["items"][number]) | null>(null); const [policy, setPolicy] = useState(null); const [policyDraft, setPolicyDraft] = useState("info"); const [policyLoading, setPolicyLoading] = useState(false); const selectedMeta = useMemo(() => tryFormatJSON(selected?.meta_json), [selected]); const loadPolicy = useCallback(async () => { setPolicyLoading(true); try { const p = await fetchLogMinLevelPolicy(); setPolicy(p); setPolicyDraft(p.effective_min_level); } catch (e) { toast.error((e as Error).message || t("agent.logs.toast.loadPolicyFailed")); setPolicy(null); } finally { setPolicyLoading(false); } }, [t]); useEffect(() => { void loadPolicy(); }, [loadPolicy]); const load = useCallback(async () => { setLoading(true); try { const conv = conversationId.trim() ? Number(conversationId) : undefined; const res = await fetchSystemLogs({ from, to, level: level || undefined, category: category || undefined, source: source || undefined, event: event || undefined, keyword: keyword || undefined, conversationId: conv, page, pageSize, }); setData(res); } catch (e) { toast.error((e as Error).message || t("agent.logs.toast.loadLogsFailed")); setData(null); } finally { setLoading(false); } }, [from, to, level, category, source, event, keyword, conversationId, page, t]); useEffect(() => { void load(); }, [load]); const totalPages = useMemo(() => { if (!data) return 1; return Math.max(1, Math.ceil(data.total / data.page_size)); }, [data]); return (

{t("agent.logs.title")}

{t("agent.logs.subtitle")}

{t("agent.logs.policy.title")}

{t("agent.logs.policy.desc")}

{policy ? (

{t("agent.logs.policy.current")}{policy.effective_min_level} {" ยท "} {t("agent.logs.policy.env")}{policy.env_min_level} {policy.persisted_in_database ? ( {t("agent.logs.policy.overridden")} ) : null}

) : null}
setFrom(e.target.value)} className="rounded-md border px-2 py-1 text-sm" /> {t("common.to")} setTo(e.target.value)} className="rounded-md border px-2 py-1 text-sm" /> setEvent(e.target.value)} className="rounded-md border px-2 py-1 text-sm min-w-[180px]" /> setConversationId(e.target.value)} className="rounded-md border px-2 py-1 text-sm w-24" /> setKeyword(e.target.value)} className="rounded-md border px-2 py-1 text-sm min-w-[220px]" />
{tr("agent.logs.paginationSummary", { total: String(data?.total ?? 0), page: String(data?.page ?? page), pages: String(totalPages), })}
{(data?.items ?? []).map((item) => ( setSelected(item)} > ))} {(data?.items ?? []).length === 0 && !loading && ( )}
{t("agent.logs.table.time")} {t("agent.logs.table.level")} {t("agent.logs.table.category")} {t("agent.logs.table.event")} {t("agent.logs.table.conversation")} {t("agent.logs.table.source")} {t("agent.logs.table.message")}
{new Date(item.timestamp).toLocaleString(locale)} {item.level} {item.category} {item.event} {item.conversation_id ?? "-"} {item.source} {item.message}
{t("agent.logs.empty")}
{ if (!open) setSelected(null); }} > {t("agent.logs.detail.title")} {selected ? ( {selected.level} ) : null} {selected ? (
{t("agent.logs.detail.time")}
{new Date(selected.timestamp).toLocaleString(locale)}
{t("agent.logs.detail.sourceEvent")}
{selected.source} / {selected.event}
{t("agent.logs.detail.category")}
{selected.category}
{t("agent.logs.detail.traceId")}
{selected.trace_id || "-"}
{t("agent.logs.detail.conversationId")}
{selected.conversation_id ?? "-"}
{t("agent.logs.detail.userVisitor")}
{selected.user_id ?? "-"} / {selected.visitor_id ?? "-"}
{t("agent.logs.detail.message")}
{selected.message}
{t("agent.logs.detail.metaJson")}
                  {selectedMeta || t("agent.logs.detail.noMeta")}
                
) : null}
); }