import { Download, Settings, Sparkles, Users } from "lucide-react"; import { Pie, PieChart, ResponsiveContainer, Cell } from "recharts"; import type { SSTCOverview } from "@/api/sstc"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { compactTitle } from "@/lib/title"; import { formatProjectNameWithId, toZhRole, toZhStatus } from "@/lib/display"; import { formatBeijingDateTime } from "@/lib/datetime"; interface SSTCGlobalHeaderProps { overview: SSTCOverview; onOpenAi: () => void; onOpenSettings: () => void; onExport: () => void; onOpenWorkloads: () => void; } function statusVariant(status: string): "default" | "secondary" | "destructive" | "outline" { const v = (status || "").toLowerCase(); if (v.includes("active") || v.includes("进行")) return "default"; if (v.includes("paused")) return "secondary"; if (v.includes("completed") || v.includes("done")) return "outline"; return "secondary"; } function scoreColor(score: number): string { if (score < 60) return "#dc2626"; if (score < 80) return "#ca8a04"; return "#16a34a"; } function ratioColor(ratio: number): string { if (ratio > 1.0) return "#dc2626"; if (ratio >= 0.7) return "#ca8a04"; return "#16a34a"; } function ratioClass(ratio: number): string { if (ratio > 1.0) return "text-rose-600"; if (ratio < 0.3) return "text-sky-600"; return "text-emerald-600"; } function memberStatusLabel(status: "idle" | "normal" | "overloaded"): string { if (status === "overloaded") return "过载"; if (status === "idle") return "空闲"; return "正常"; } function memberStatusClass(status: "idle" | "normal" | "overloaded"): string { if (status === "overloaded") return "bg-rose-100 text-rose-700"; if (status === "idle") return "bg-sky-100 text-sky-700"; return "bg-emerald-100 text-emerald-700"; } function normalizeRoleLabel(rawRole: string): string { const primaryRole = String(rawRole || "") .split(",") .map((item) => item.trim()) .find(Boolean); return toZhRole(primaryRole || rawRole); } export function SSTCGlobalHeader({ overview, onOpenAi, onOpenSettings, onExport, onOpenWorkloads, }: SSTCGlobalHeaderProps) { const alignmentColor = scoreColor(overview.alignment_score || 0); const progress = overview.task_progress.progress || 0; const ratio = overview.workload_health.avg_ratio || 0; const workloadMembers = overview.workload_health.members || []; const projectLabel = formatProjectNameWithId({ id: overview.project.id, title: overview.project.title, }); const compactProjectTitle = compactTitle(projectLabel, 24); return (
项目描述:{overview.project.description || "暂无"}
创建时间(北京时间):{formatBeijingDateTime(overview.project.created_at)}
任务总数:{overview.project.task_count}
{Math.round(overview.alignment_score || 0)}%
未确认节点 {overview.pending_alignment_nodes} | 冲突 {overview.unresolved_conflicts}
{Math.round(progress)}%
已完成 {overview.task_progress.done}/{overview.task_progress.total}
延期 {overview.task_progress.overdue} | 阻塞 {overview.task_progress.blocked} | 今日完成 {overview.task_progress.today_done}
{(ratio * 100).toFixed(0)}%
过载 {overview.workload_health.overloaded_count} 人 | 空闲 {overview.workload_health.idle_count} 人
暂无成员负载明细,请先分配项目成员
) : ( workloadMembers.slice(0, 4).map((member) => ({member.name}
{normalizeRoleLabel(member.role)}
其余 {workloadMembers.length - 4} 人请点击查看完整负载
) : null}