From dbd793f14098ea61273a7c8adbc63d5fffbd1ce4 Mon Sep 17 00:00:00 2001 From: mlogclub Date: Wed, 24 Jun 2026 11:03:36 +0800 Subject: [PATCH] fix(workflow): improve run audit interactions --- web/app/dashboard/ai-workflow-runs/page.tsx | 137 ++++++++++++++++-- .../_components/detail.tsx | 90 ++++++------ web/messages/en-US.json | 1 + web/messages/zh-CN.json | 1 + 4 files changed, 179 insertions(+), 50 deletions(-) diff --git a/web/app/dashboard/ai-workflow-runs/page.tsx b/web/app/dashboard/ai-workflow-runs/page.tsx index d95ba56..f90a8dc 100644 --- a/web/app/dashboard/ai-workflow-runs/page.tsx +++ b/web/app/dashboard/ai-workflow-runs/page.tsx @@ -1,23 +1,34 @@ "use client" -import { useEffect, useMemo, useState } from "react" +import { useEffect, useMemo, useState, type ReactNode } from "react" import { AlertTriangleIcon, Clock3Icon, + MessageCircleMoreIcon, MessageSquareTextIcon, WorkflowIcon, } from "lucide-react" import { toast } from "sonner" +import { ConversationDetailDialog } from "@/app/dashboard/conversation-monitor/_components/detail" import { DashboardListPage } from "@/components/dashboard/list" import { JsonTreeViewer } from "@/components/json-tree-viewer" import { ProjectDialog } from "@/components/project-dialog" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" import { fetchAIAgentsAll, fetchAIWorkflowRun, fetchAIWorkflowRuns, + fetchConversationDetail, + fetchConversationMessages, + type AdminConversationDetail, + type AdminMessage, type AIAgent, type AIWorkflowNodeRun, type AIWorkflowRun, @@ -59,6 +70,10 @@ export default function DashboardAIWorkflowRunsPage() { const [detailOpen, setDetailOpen] = useState(false) const [detailLoading, setDetailLoading] = useState(false) const [activeRun, setActiveRun] = useState(null) + const [conversationOpen, setConversationOpen] = useState(false) + const [conversationLoading, setConversationLoading] = useState(false) + const [activeConversation, setActiveConversation] = useState(null) + const [conversationMessages, setConversationMessages] = useState([]) const statusOptions = useMemo(() => getStatusOptions(t), [t]) const agentOptions = useMemo( () => [ @@ -107,6 +122,27 @@ export default function DashboardAIWorkflowRunsPage() { } } + async function openConversation(conversationId: number) { + if (!conversationId) { + return + } + setConversationOpen(true) + setConversationLoading(true) + try { + const [detail, messagePage] = await Promise.all([ + fetchConversationDetail(conversationId), + fetchConversationMessages({ conversationId, limit: 20 }), + ]) + setActiveConversation(detail) + setConversationMessages(messagePage.results ?? []) + } catch (error) { + toast.error(error instanceof Error ? error.message : t("workflowRun.loadConversationFailed")) + setConversationOpen(false) + } finally { + setConversationLoading(false) + } + } + return ( <> @@ -198,7 +234,21 @@ export default function DashboardAIWorkflowRunsPage() { className: "w-48", render: (item) => (
-
{t("workflowRun.conversationShort", { id: item.conversationId || "-" })}
+
{t("workflowRun.messageShort", { id: item.messageId || "-" })}
@@ -224,13 +274,10 @@ export default function DashboardAIWorkflowRunsPage() { { key: "error", label: t("workflowRun.error"), - className: "min-w-48", + className: "w-72 max-w-72", render: (item) => item.errorMessage ? ( -
- - {item.errorMessage} -
+ ) : ( - ), @@ -255,23 +302,83 @@ export default function DashboardAIWorkflowRunsPage() { } }} t={t} + onOpenConversation={openConversation} + /> + { + setConversationOpen(open) + if (!open) { + setActiveConversation(null) + setConversationMessages([]) + } + }} + onOpenAssign={() => undefined} + onDispatch={async () => undefined} + onOpenTransfer={() => undefined} + onRead={async () => undefined} + onOpenClose={() => undefined} /> ) } +function ErrorMessagePreview({ message }: { message: string }) { + const [open, setOpen] = useState(false) + + return ( + + event.stopPropagation()} + onMouseEnter={() => setOpen(true)} + onMouseLeave={() => setOpen(false)} + onFocus={() => setOpen(true)} + onBlur={() => setOpen(false)} + /> + } + > + + {message} + + setOpen(true)} + onMouseLeave={() => setOpen(false)} + onClick={(event) => event.stopPropagation()} + > + {message} + + + ) +} + function WorkflowRunDetailDialog({ open, loading, run, onOpenChange, t, + onOpenConversation, }: { open: boolean loading: boolean run: AIWorkflowRun | null onOpenChange: (open: boolean) => void t: TFunction + onOpenConversation: (conversationId: number) => void | Promise }) { return ( - + void onOpenConversation(run.conversationId)} + > + #{run.conversationId} + + } + /> @@ -396,7 +515,7 @@ function PreviewBlock({ ) } -function DetailRow({ label, value }: { label: string; value: string }) { +function DetailRow({ label, value }: { label: string; value: ReactNode }) { return (
{label} diff --git a/web/app/dashboard/conversation-monitor/_components/detail.tsx b/web/app/dashboard/conversation-monitor/_components/detail.tsx index 059b203..a25e81c 100644 --- a/web/app/dashboard/conversation-monitor/_components/detail.tsx +++ b/web/app/dashboard/conversation-monitor/_components/detail.tsx @@ -32,6 +32,7 @@ type ConversationDetailDialogProps = { open: boolean; loading: boolean; saving: boolean; + readOnly?: boolean; item: AdminConversation | null; detail: AdminConversationDetail | null; messages?: AdminMessage[] | null; @@ -164,6 +165,7 @@ export function ConversationDetailDialog({ open, loading, saving, + readOnly = false, item, detail, messages: rawMessages, @@ -301,53 +303,59 @@ export function ConversationDetailDialog({ }) : t("conversationMonitor.noConversationInfo")}
-
- - - - - {!isClosedConversation ? ( + ) : ( +
+ + - ) : null} -
+ + {!isClosedConversation ? ( + + ) : null} +
+ )}
} > diff --git a/web/messages/en-US.json b/web/messages/en-US.json index f2aa589..19bcd47 100644 --- a/web/messages/en-US.json +++ b/web/messages/en-US.json @@ -2262,6 +2262,7 @@ "allAgents": "All agents", "loadAgentsFailed": "Could not load AI agents.", "loadDetailFailed": "Could not load workflow run details.", + "loadConversationFailed": "Could not load conversation details.", "conversationId": "Conversation ID", "messageId": "Message ID", "workflowVersionId": "Workflow Version ID", diff --git a/web/messages/zh-CN.json b/web/messages/zh-CN.json index 1c31c9f..7f52cdd 100644 --- a/web/messages/zh-CN.json +++ b/web/messages/zh-CN.json @@ -2262,6 +2262,7 @@ "allAgents": "全部 Agent", "loadAgentsFailed": "加载 AI Agent 列表失败", "loadDetailFailed": "加载流程执行详情失败", + "loadConversationFailed": "加载会话详情失败", "conversationId": "会话ID", "messageId": "消息ID", "workflowVersionId": "流程版本ID",