refactor(dashboard): enhance layout and improve log display in Agent Run Logs page

This commit is contained in:
mlogclub
2026-04-19 10:02:38 +08:00
parent 42077c1aab
commit 77a5c68b01
@@ -9,14 +9,6 @@ import { OptionCombobox } from "@/components/option-combobox"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { AgentRunLogDetailDialog } from "./_components/detail" import { AgentRunLogDetailDialog } from "./_components/detail"
import { import {
fetchAgentRunLogs, fetchAgentRunLogs,
@@ -183,8 +175,8 @@ export default function DashboardAgentRunLogsPage() {
return ( return (
<> <>
<div className="flex flex-1 flex-col gap-6 p-4 lg:p-6"> <div className="flex flex-1 flex-col gap-6 p-4 lg:p-6">
<div className="flex flex-col gap-2 xl:flex-row xl:items-center xl:justify-end"> <div className="grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-[minmax(0,1.6fr)_repeat(5,minmax(0,0.7fr))_auto_auto] xl:items-center">
<div className="relative min-w-72"> <div className="relative min-w-0">
<SearchIcon className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" /> <SearchIcon className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
<Input <Input
value={keywordInput} value={keywordInput}
@@ -194,7 +186,7 @@ export default function DashboardAgentRunLogsPage() {
className="pl-9" className="pl-9"
/> />
</div> </div>
<div className="w-full xl:w-40"> <div className="min-w-0">
<OptionCombobox <OptionCombobox
value={plannedActionInput} value={plannedActionInput}
options={actionOptions} options={actionOptions}
@@ -204,7 +196,7 @@ export default function DashboardAgentRunLogsPage() {
onChange={(value) => setPlannedActionInput(value || "all")} onChange={(value) => setPlannedActionInput(value || "all")}
/> />
</div> </div>
<div className="w-full xl:w-40"> <div className="min-w-0">
<OptionCombobox <OptionCombobox
value={finalActionInput} value={finalActionInput}
options={actionOptions} options={actionOptions}
@@ -214,7 +206,7 @@ export default function DashboardAgentRunLogsPage() {
onChange={(value) => setFinalActionInput(value || "all")} onChange={(value) => setFinalActionInput(value || "all")}
/> />
</div> </div>
<div className="w-full xl:w-40"> <div className="min-w-0">
<OptionCombobox <OptionCombobox
value={finalStatusInput} value={finalStatusInput}
options={finalStatusOptions} options={finalStatusOptions}
@@ -224,7 +216,7 @@ export default function DashboardAgentRunLogsPage() {
onChange={(value) => setFinalStatusInput(value || "all")} onChange={(value) => setFinalStatusInput(value || "all")}
/> />
</div> </div>
<div className="w-full xl:w-40"> <div className="min-w-0">
<OptionCombobox <OptionCombobox
value={hitlStatusInput} value={hitlStatusInput}
options={hitlStatusOptions} options={hitlStatusOptions}
@@ -234,7 +226,7 @@ export default function DashboardAgentRunLogsPage() {
onChange={(value) => setHitlStatusInput(value || "all")} onChange={(value) => setHitlStatusInput(value || "all")}
/> />
</div> </div>
<div className="w-full xl:w-52"> <div className="min-w-0">
<OptionCombobox <OptionCombobox
value={aiAgentIdInput} value={aiAgentIdInput}
options={aiAgentOptions} options={aiAgentOptions}
@@ -244,73 +236,77 @@ export default function DashboardAgentRunLogsPage() {
onChange={(value) => setAiAgentIdInput(value || "all")} onChange={(value) => setAiAgentIdInput(value || "all")}
/> />
</div> </div>
<Button variant="outline" onClick={applyFilters} disabled={loading}> <Button variant="outline" onClick={applyFilters} disabled={loading} className="w-full xl:w-auto">
<SearchIcon /> <SearchIcon />
</Button> </Button>
<Button variant="outline" onClick={() => void loadData()} disabled={loading}> <Button
variant="outline"
onClick={() => void loadData()}
disabled={loading}
className="w-full xl:w-auto"
>
<RefreshCwIcon className={loading ? "animate-spin" : ""} /> <RefreshCwIcon className={loading ? "animate-spin" : ""} />
</Button> </Button>
</div> </div>
<div className="overflow-hidden rounded-lg border bg-background"> <div className="overflow-hidden rounded-lg border bg-background">
<Table>
<TableHeader className="bg-muted/40">
<TableRow>
<TableHead className="w-[180px]"></TableHead>
<TableHead></TableHead>
<TableHead className="w-[120px]"></TableHead>
<TableHead className="w-[220px]">Skill / Tool</TableHead>
<TableHead className="w-[140px]"></TableHead>
<TableHead className="w-[110px] text-right"></TableHead>
<TableHead className="w-[96px] text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{!loading && result.results.length === 0 ? ( {!loading && result.results.length === 0 ? (
<TableRow> <div className="py-14 text-center text-sm text-muted-foreground">
<TableCell colSpan={7} className="py-14 text-center text-muted-foreground">
Agent Agent
</TableCell> </div>
</TableRow> ) : (
) : null} <div>
<div className="hidden grid-cols-[160px_minmax(0,1.8fr)_110px_minmax(0,1.2fr)_130px_90px_76px] gap-3 border-b bg-muted/40 px-4 py-3 text-sm text-muted-foreground lg:grid">
<div></div>
<div></div>
<div></div>
<div>Skill / Tool</div>
<div></div>
<div className="text-right"></div>
<div className="text-right"></div>
</div>
<div className="divide-y">
{result.results.map((item) => ( {result.results.map((item) => (
<TableRow key={item.id}> <article
<TableCell className="text-sm text-muted-foreground"> key={item.id}
className="grid grid-cols-1 gap-2 px-4 py-3 lg:grid-cols-[160px_minmax(0,1.8fr)_110px_minmax(0,1.2fr)_130px_90px_76px] lg:items-center lg:gap-3"
>
<div className="min-w-0 text-sm text-muted-foreground">
{formatDateTime(item.createdAt)} {formatDateTime(item.createdAt)}
</TableCell> </div>
<TableCell>
<div className="min-w-0">
<UserMessagePreview value={item.userMessage} /> <UserMessagePreview value={item.userMessage} />
{item.errorMessage ? ( {item.errorMessage ? (
<div className="mt-1 line-clamp-1 text-xs text-destructive"> <div className="truncate text-xs text-destructive">{item.errorMessage}</div>
{item.errorMessage}
</div>
) : null} ) : null}
</TableCell> </div>
<TableCell>
<div className="min-w-0">
<Badge variant={actionBadgeVariant(item.plannedAction)}> <Badge variant={actionBadgeVariant(item.plannedAction)}>
{item.plannedAction || "-"} {item.plannedAction || "-"}
</Badge> </Badge>
</TableCell> </div>
<TableCell className="text-sm">
{item.plannedSkillCode || item.plannedToolCode ? ( <div className="min-w-0 text-sm">
<div className="space-y-1"> {item.plannedSkillCode || item.graphToolCode || item.plannedToolCode ? (
<Badge variant="outline"> <div className="min-w-0 space-y-1">
<div className="truncate font-medium">
{item.plannedSkillCode || item.graphToolCode || item.plannedToolCode} {item.plannedSkillCode || item.graphToolCode || item.plannedToolCode}
</Badge> </div>
{item.plannedSkillName ? ( {item.plannedSkillName ? (
<div className="line-clamp-1 text-xs text-muted-foreground"> <div className="truncate text-xs text-muted-foreground">
{item.plannedSkillName} {item.plannedSkillName}
</div> </div>
) : null} ) : item.handoffReason ? (
{item.handoffReason ? ( <div className="truncate text-xs text-muted-foreground">
<div className="line-clamp-1 text-xs text-muted-foreground">
{item.handoffReason} {item.handoffReason}
</div> </div>
) : null} ) : item.recommendedAction ? (
{item.recommendedAction ? ( <div className="truncate text-xs text-muted-foreground">
<div className="line-clamp-1 text-xs text-muted-foreground">
{item.recommendedAction} {item.recommendedAction}
{item.riskLevel ? ` / ${item.riskLevel} risk` : ""} {item.riskLevel ? ` / ${item.riskLevel} risk` : ""}
{item.ticketDraftReady ? " / 草稿已就绪" : ""} {item.ticketDraftReady ? " / 草稿已就绪" : ""}
@@ -318,30 +314,28 @@ export default function DashboardAgentRunLogsPage() {
) : null} ) : null}
</div> </div>
) : ( ) : (
"-" <span className="text-muted-foreground">-</span>
)} )}
</TableCell> </div>
<TableCell>
<div className="min-w-0">
<div className="space-y-1"> <div className="space-y-1">
<Badge variant={actionBadgeVariant(item.finalAction)}> <Badge variant={actionBadgeVariant(item.finalAction)}>
{item.finalAction || "-"} {item.finalAction || "-"}
</Badge> </Badge>
{item.hitlStatusName ? ( <div className="truncate text-xs text-muted-foreground">
<div> {item.hitlStatusName
<Badge variant={hitlBadgeVariant(item.hitlStatus)}> ? `${item.hitlStatusName} / ${item.finalStatus || "-"}`
{item.hitlStatusName} : item.finalStatus || "-"}
</Badge>
</div>
) : null}
<div className="text-xs text-muted-foreground">
{item.finalStatus || "-"}
</div> </div>
</div> </div>
</TableCell> </div>
<TableCell className="text-right text-sm text-muted-foreground">
<div className="text-sm text-muted-foreground lg:text-right">
{item.latencyMs} ms {item.latencyMs} ms
</TableCell> </div>
<TableCell className="text-right">
<div className="lg:text-right">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
@@ -352,11 +346,12 @@ export default function DashboardAgentRunLogsPage() {
> >
</Button> </Button>
</TableCell> </div>
</TableRow> </article>
))} ))}
</TableBody> </div>
</Table> </div>
)}
<div className="border-t px-4 py-3"> <div className="border-t px-4 py-3">
<ListPagination <ListPagination
page={result.page.page} page={result.page.page}
@@ -390,7 +385,7 @@ function UserMessagePreview({ value }: { value?: string }) {
const preview = useMemo(() => summarizeUserMessage(value), [value]) const preview = useMemo(() => summarizeUserMessage(value), [value])
return ( return (
<div className="line-clamp-2 max-w-[620px] text-sm text-muted-foreground"> <div className="truncate text-sm text-foreground">
{preview} {preview}
</div> </div>
) )