import type { CSSProperties, ReactNode } from "react"; import { ArrowRight, Bot, BriefcaseBusiness, CheckCircle2, ChevronDown, CirclePlus, Code2, FileText, FlaskConical, Maximize2, Paperclip, Send, Settings2, ShieldAlert, TrendingUp, X, } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { usePMFlowOsStore } from "@/features/pmflow-os/store"; import type { PMFlowReviewerRouting, PMFlowReviewerRoutingItem, PMFlowRiskLevel } from "@/features/pmflow-os/types"; import { cn } from "@/lib/utils"; const activityItems = [ ["新增提取对象", "AI 从对话中提取了 3 个新对象:会议纪要分享、自动任务创建、跨时区支持", "10:24 · 由 PMFlow AI 提取", CirclePlus, "brand"], ["PR 已回写", "PR #128 已合并并回写 2 个需求与 1 个风险", "10:12 · 由 开发智能体", Code2, "success"], ["测试失败", "用例「会议纪要分享 - 权限校验」失败,影响发布门禁", "09:48 · 由 测试智能体", X, "danger"], ["发布风险升级", "风险「第三方转写服务稳定性」升级为高风险", "09:31 · 由 PMFlow AI", ShieldAlert, "warning"], ["决策已记录", "已记录决策:采用混合模型方案(本地 + 云端)", "昨天 18:22 · 由 张子墨", FileText, "brand"], ["用户反馈回写", "新增 12 条用户反馈,主要集中在「长会议跟踪助手」", "昨天 17:05 · 由 运营智能体", TrendingUp, "success"], ] as const; const decisions = [ ["范围调整", "是否将「AI 生成行动项」从 MVP 范围移出", "发起人:张子墨 · 截止:今天 18:00", "高"], ["发布时间确认", "确认 v1.0 版本发布时间窗口(建议 6.15 - 6.18)", "发起人:李文博 · 截止:明天 12:00", "中"], ["风险接受", "接受风险「第三方转写服务稳定性」并制定应对方案", "发起人:PMFlow AI · 截止:后天 17:00", "高"], ["资源冲突", "开发资源与「数据分析看板」项目冲突", "发起人:系统 · 截止:06-14 12:00", "中"], ] as const; const inboxItems = [ ["需求澄清", "会议纪要分享的权限范围需要进一步明确", "来自:PMFlow AI · 10:24", "需多确认", "brand"], ["用户反馈聚类", "长会议识别准确率问题较多,建议评估优化方案", "来自:运营智能体 · 09:40", "需多评估", "warning"], ["范围影响评估", "若移除「AI 生成行动项」,预计节省 2 周开发时间", "来自:开发智能体 · 昨天", "异步决策", "brand"], ] as const; const agentCards = [ ["产品智能体", Bot, "运行中", "梳理会议纪要分享需求细节", 60, "", [["提取 3 个新对象", "10:24"], ["更新需求 PRD-012", "昨天"], ["发起 1 项待决策", "昨天"]]], ["开发智能体", Code2, "运行中", "实现会议纪要分享 API", 75, "", [["PR #128 已合并并回写", "10:12"], ["创建 2 个开发任务", "昨天"], ["更新技术设计文档", "06-10"]]], ["测试智能体", FlaskConical, "阻塞中", "执行回归测试(纪要分享模块)", 40, "用例 2 失败,等待开发修复", [["执行 32 个用例", "09:48"], ["发现 2 个缺陷", "昨天"]]], ["运营智能体", TrendingUp, "运行中", "分析用户反馈与套用数据", 65, "", [["新增 12 条用户反馈", "09:31"], ["生成运营周报", "昨天"], ["更新增长实验建议", "06-10"]]], ] as const; export default function TeamLayerPage() { const navigate = useNavigate(); const { state, openReview, projectionWarnings } = usePMFlowOsStore(); const reviewerRouting = state.reviewerRouting || emptyReviewerRouting(); const pendingReviewCount = state.changeItems.filter((item) => item.status === "pending_review").length; const reviewerWarnings = projectionWarnings.filter((warning) => warning.includes("reviewer_routing_warning")); const handleOpenReview = (item: PMFlowReviewerRoutingItem) => { openReview(item.changeSetId); navigate(item.targetUrl || `/change-review?projectId=${item.projectId}&changeSetId=${item.changeSetId}`); }; return (
); } function ReviewerRoutingPanel({ pendingReviewCount, routings, warnings, onOpenReview, }: { pendingReviewCount: number; routings: PMFlowReviewerRouting; warnings: string[]; onOpenReview: (item: PMFlowReviewerRoutingItem) => void; }) { const visibleRoutings = routings.items.slice(0, 3); return (
审查路由

待审 ChangeSet 审查路由

基于项目成员角色、待审变更风险和跨对象影响生成建议审查人;这里只展示建议,不自动分派审查人。

{routings.status === "warning" ? (
{reviewerRoutingWarningText(routings.warningCode, warnings)}
) : null} {visibleRoutings.length > 0 ? (
{visibleRoutings.map((routing) => (
{riskLabel(routing.riskLevel)} {sourceLabel(routing.sourceType)} {priorityLabel(routing.priority)}

{routing.changeSetTitle}

{routing.reviewReason}

{routing.changeTypes.map((type) => ( {type} ))} 待审 {routing.pendingItemCount} 证据 {routing.evidenceCount}
建议审查人
{routing.suggestedReviewer ? (
{routing.suggestedReviewer.displayName} {routing.suggestedReviewer.role}

{routing.suggestedReviewer.reason}

) : (
当前项目缺少 REVIEWER/OWNER,建议先补充项目审查人。兜底角色:{routing.fallbackReviewerRole || "OWNER"}
)}
))}
) : (
{routings.status === "empty" ? "当前没有待团队审查的变更。" : "团队审查路由暂时不可用,请稍后重试。"}
)}
); } function MetricPill({ label, value }: { label: string; value: string }) { return (
{label}
{value}
); } function TeamActivityPanel() { return ( } />
{activityItems.map(([title, detail, time, Icon, tone], index) => (

{title}

{detail}

{time}

))}
查看全部动态
); } function DecisionPanel() { return ( 待拍板 6
} />
{decisions.map(([title, detail, meta, risk]) => (

{title}

{risk}

{detail}

{meta}

))}
查看全部待拍板
); } function RoleInboxPanel() { return (
产品 3 开发 4 测试 2 运营 2
{inboxItems.map(([title, detail, meta, tag, tone]) => (

{title}

{detail}

{meta} {tag}
))}
查看全部消息
); } function AgentWorkstationPanel() { return (

智能体工位

{agentCards.map(([title, Icon, status, task, progress, blocked, outputs]) => (

{title}

{status}
当前任务

{task}

进度 {progress}%
{blocked ?

阻塞原因:{blocked}

: null}
近期产出
{outputs.map(([label, time]) => (
{label} {time}
))}
))}
); } function AICopilotPanel() { return ( ); } function Panel({ children, className, style }: { children: ReactNode; className?: string; style?: CSSProperties }) { return
{children}
; } function PanelHeader({ title, action }: { title: string; action?: ReactNode }) { return

{title}

{action}
; } function LinkButton({ children, className }: { children: ReactNode; className?: string }) { return ; } function CopilotBlock({ title, children }: { title: string; children: ReactNode }) { return

{title}

{children}
; } function iconTone(tone: string) { if (tone === "success") return "bg-emerald-600"; if (tone === "danger") return "bg-red-500"; if (tone === "warning") return "bg-orange-500"; return "bg-[#2463EB]"; } function riskLabel(level: PMFlowRiskLevel) { if (level === "high") return "高风险"; if (level === "medium") return "中风险"; return "低风险"; } function riskTone(level: PMFlowRiskLevel) { if (level === "high") return "bg-red-50 text-red-600"; if (level === "medium") return "bg-orange-50 text-orange-600"; return "bg-emerald-50 text-emerald-600"; } function sourceLabel(sourceType: PMFlowReviewerRoutingItem["sourceType"]) { if (sourceType === "iteration_recommendation") return "下一轮迭代"; if (sourceType === "ai_extraction") return "AI 提取"; if (sourceType === "manual") return "手动"; return "来源未知"; } function priorityLabel(priority: PMFlowReviewerRoutingItem["priority"]) { if (priority === "urgent") return "紧急"; if (priority === "elevated") return "需关注"; return "常规"; } function reviewerRoutingWarningText(code: string | null | undefined, warnings: string[]) { if (code === "reviewer_missing") return "当前项目缺少 REVIEWER/OWNER,建议先补充项目审查人。"; if (code === "evidence_missing") return "待审变更缺少足够证据,建议回到 Change Review 查看来源。"; if (warnings.length > 0) return "团队审查路由存在 warning,请优先检查 reviewer 和证据。"; return "团队审查路由暂时不可用,请稍后重试。"; } function emptyReviewerRouting(): PMFlowReviewerRouting { return { status: "empty", warningCode: null, items: [], summary: { totalPendingReview: 0, highRiskCount: 0, missingReviewerCount: 0, iterationDraftCount: 0, }, }; }