'use client'; import { AlertTriangle, Bot, Download, ExternalLink, FileText, Users } from 'lucide-react'; import { TaskRuntimeStatusBadge, formatTaskRuntimeTime } from '@/components/task-runtime/TaskRuntimeShared'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { getFileUrl } from '@/lib/api'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; import type { TaskRuntimeStatus } from '@/lib/task-runtime'; import type { BackendTask, ProcessArtifact, ProcessRun, TaskTimelineCard } from '@/types'; type Props = { task: BackendTask; runs: ProcessRun[]; artifacts: ProcessArtifact[]; cards: TaskTimelineCard[]; }; const ACTIVE_RUN_STATUSES = new Set(['queued', 'running', 'waiting']); const RUNTIME_STATUSES = new Set(['queued', 'running', 'waiting', 'blocked', 'done', 'error', 'cancelled']); function isRuntimeStatus(status: string): status is TaskRuntimeStatus { return RUNTIME_STATUSES.has(status); } function humanTaskStatus(status: string, locale: 'zh-CN' | 'en-US') { const map: Record = { open: ['已创建', 'Open'], running: ['执行中', 'Running'], awaiting_acceptance: ['等待验收', 'Awaiting acceptance'], needs_revision: ['需要修改', 'Needs revision'], closed: ['已完成', 'Closed'], abandoned: ['已放弃', 'Abandoned'], }; const item = map[status]; return item ? pickAppText(locale, item[0], item[1]) : status; } function toTime(value: string): number { const parsed = new Date(value).getTime(); return Number.isFinite(parsed) ? parsed : 0; } function isWarningOrError(card: TaskTimelineCard): boolean { const severity = String(card.details?.severity || card.details?.level || '').toLowerCase(); return card.type === 'error' || card.status === 'error' || severity === 'warning' || severity === 'error'; } function artifactHref(artifact: ProcessArtifact): string | null { if (artifact.url) return artifact.url; if (artifact.file_id) return getFileUrl(artifact.file_id); return null; } function RunRow({ run }: { run: ProcessRun }) { const { locale } = useAppI18n(); return (
{run.title || run.actor_name}
{run.actor_name}
{formatTaskRuntimeTime(run.started_at, locale)}
{run.summary ?

{run.summary}

: null}
); } export function TaskSideRail({ task, runs, artifacts, cards }: Props) { const { locale } = useAppI18n(); const activeRuns = runs.filter((run) => ACTIVE_RUN_STATUSES.has(run.status)); const childRuns = runs.filter((run) => Boolean(run.parent_run_id)); const latestAlert = cards.filter(isWarningOrError).sort((a, b) => toTime(b.createdAt) - toTime(a.createdAt))[0] ?? null; return ( ); }