'use client';
import Link from 'next/link';
import { useParams } from 'next/navigation';
import React from 'react';
import {
ArrowLeft,
ArrowRight,
Boxes,
FolderOutput,
ListTree,
MessageSquare,
PanelRightOpen,
Siren,
Users,
} from 'lucide-react';
import {
OfficeStatusBadge,
formatOfficeDuration,
formatOfficeTime,
progressPercent,
} from '@/components/office/OfficeShared';
import { OfficePhaserCanvas } from '@/components/office/OfficePhaserCanvas';
import { TaskManagementTabs } from '@/components/task-management/TaskManagementTabs';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet';
import { ScrollArea } from '@/components/ui/scroll-area';
import { buildOfficeView, isOfficeTaskTerminal } from '@/lib/office';
import { useChatStore } from '@/lib/store';
function PixelPanel({
title,
subtitle,
children,
icon: Icon,
}: {
title: string;
subtitle?: string;
children: React.ReactNode;
icon?: React.ComponentType<{ className?: string }>;
}) {
return (
{Icon ? : null}
{title}
{subtitle ? (
{subtitle}
) : null}
{children}
);
}
function BoardPanel({
icon: Icon,
title,
description,
children,
}: {
icon: React.ComponentType<{ className?: string }>;
title: string;
description?: string;
children: React.ReactNode;
}) {
return (
{title}
{description ? {description} : null}
{children}
);
}
export default function OfficeDetailPage() {
const params = useParams<{ taskId: string }>();
const taskId = decodeURIComponent(Array.isArray(params?.taskId) ? params.taskId[0] : params?.taskId ?? '');
const sessions = useChatStore((state) => state.sessions);
const processRuns = useChatStore((state) => state.processRuns);
const processEvents = useChatStore((state) => state.processEvents);
const processArtifacts = useChatStore((state) => state.processArtifacts);
const office = React.useMemo(
() => buildOfficeView(taskId, { sessions, processRuns, processEvents, processArtifacts }),
[processArtifacts, processEvents, processRuns, sessions, taskId]
);
const [selectedRunId, setSelectedRunId] = React.useState(null);
const [detailOpen, setDetailOpen] = React.useState(false);
React.useEffect(() => {
setSelectedRunId(office?.rootRunId ?? null);
setDetailOpen(false);
}, [office?.rootRunId]);
const selectedTask = React.useMemo(
() => office?.tasks.find((task) => task.runId === selectedRunId) ?? office?.tasks[0] ?? null,
[office?.tasks, selectedRunId]
);
const selectedEvents = React.useMemo(
() => processEvents
.filter((event) => event.run_id === selectedTask?.runId)
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
.slice(0, 8),
[processEvents, selectedTask?.runId]
);
const selectedArtifacts = React.useMemo(
() => processArtifacts
.filter((artifact) => artifact.run_id === selectedTask?.runId)
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()),
[processArtifacts, selectedTask?.runId]
);
const openRunDetail = React.useCallback((runId: string) => {
setSelectedRunId(runId);
setDetailOpen(true);
}, []);
if (!office) {
return (
任务不存在
当前 store 中没有这个 task 的运行数据。先从对话页发起任务,或者回到 Office 列表查看当前可用任务。
);
}
const progressValue = progressPercent(office.progress.value, office.progress.max);
return (
{office.title}
Lead: {office.rootActorName}
Session: {office.sourceSessionLabel}
Started: {formatOfficeTime(office.createdAt)}
Duration: {formatOfficeDuration(office.durationMs)}
{selectedTask?.summary || '当前选中任务没有摘要,先从右侧任务看板切一个具体 run 看现场。'}
{office.alerts.slice(0, 2).map((alert) => (
))}
{office.progress.label}
{progressValue}%
{selectedTask ? (
当前聚焦
{selectedTask.title}
{selectedTask.actorName} · {selectedTask.stageLabel ?? '无阶段标签'}
) : null}
{isOfficeTaskTerminal(office.status) ? (
任务已结束,办公室已解散,但现场记录仍可回看。
) : null}
{office.members.map((member) => (
))}
{office.tasks.map((task) => (
))}
{office.assignments.length === 0 ? (
当前没有可见的子任务分工。
) : (
office.assignments.map((assignment) => (
))
)}
{office.alerts.length === 0 ? (
当前没有高优先级告警。
) : (
office.alerts.map((alert) => (
))
)}
{selectedTask?.title ?? '任务详情'}
{selectedTask
? `${selectedTask.actorName} · ${selectedTask.stageLabel ?? '无阶段标签'}`
: '当前没有选中的任务实例。'}
{!selectedTask ? (
当前没有可展示的任务详情。
) : (
{selectedTask.title}
{selectedTask.actorName}
开始时间
{formatOfficeTime(selectedTask.startedAt)}
最近更新
{formatOfficeTime(selectedTask.updatedAt)}
阶段
{selectedTask.stageLabel ?? '-'}
{selectedTask.summary ? (
{selectedTask.summary}
) : null}
产物
{selectedArtifacts.length === 0 ? (
当前没有产物。
) : (
selectedArtifacts.map((artifact) => (
{artifact.title}
{artifact.artifact_type} · {formatOfficeTime(artifact.created_at)}
))
)}
最近事件
{selectedEvents.length === 0 ? (
当前没有事件。
) : (
selectedEvents.map((event) => (
{event.kind}
{formatOfficeTime(event.created_at)}
{event.text || '结构化更新'}
))
)}
)}
);
}
function MetricTile({ label, value }: { label: string; value: string }) {
return (
);
}
function MiniMetric({ label, value }: { label: string; value: string }) {
return (
);
}