'use client'; import { X } from 'lucide-react'; import { CommentPanel } from '@/components/comment/CommentPanel'; import type { Requirement, DictItem, SourceTarget } from '@/lib/requirement'; import { REQ_STATUS_LABEL, REQ_STATUS_COLOR, SOURCE_TYPE_LABEL } from '@/lib/requirement'; interface RequirementDetailProps { requirement: Requirement; projects: { id: string; name: string }[]; types: DictItem[]; platforms: DictItem[]; sourceTargets: SourceTarget[]; requirements: Requirement[]; resolveVersionName: (id?: string) => string; onClose: () => void; onEdit?: () => void; onAdopt?: () => void; onReject?: () => void; onCloseReq?: () => void; onDelete?: () => void; } const PRIORITY_COLORS: Record = { P0: 'bg-red-50 text-red-600 border-red-200', P1: 'bg-orange-50 text-orange-600 border-orange-200', P2: 'bg-blue-50 text-blue-600 border-blue-200', P3: 'bg-zinc-50 text-zinc-600 border-zinc-200', P4: 'bg-zinc-50 text-zinc-500 border-zinc-200', }; export function RequirementDetail({ requirement: req, projects, types, platforms, sourceTargets, requirements, resolveVersionName, onClose, onEdit, onAdopt, onReject, onCloseReq, onDelete, }: RequirementDetailProps) { const canEdit = Boolean(onEdit) && ['pending_review', 'adopted', 'planned'].includes(req.status); const projectName = projects.find((p) => p.id === req.projectId)?.name ?? '-'; const typeName = types.find((t) => t.id === req.typeId)?.name ?? '-'; const platformNames = req.platforms.map((pid) => platforms.find((p) => p.id === pid)?.name ?? pid).join('、') || '-'; const sourceName = `${SOURCE_TYPE_LABEL[req.sourceType]}${req.sourceTarget ? ` / ${req.sourceTarget}` : ''}`; const sourceTargetName = sourceTargets.find((item) => item.name === req.sourceTarget)?.name ?? req.sourceTarget ?? '-'; const versionName = resolveVersionName(req.versionId); const parentRequirement = req.parentId ? requirements.find((item) => item.id === req.parentId) : undefined; const parentRequirementLabel = parentRequirement ? `${parentRequirement.code} · ${parentRequirement.title}` : req.parentId || '-'; const contextItems = [projectName, versionName, typeName].filter((item) => item && item !== '-'); return (
e.stopPropagation()} >
{req.code} {REQ_STATUS_LABEL[req.status]} {req.priority}
{canEdit && 编辑} {onAdopt && req.status === 'pending_review' && 采纳} {onReject && req.status === 'pending_review' && 拒绝} {onCloseReq && (req.status === 'adopted' || req.status === 'planned') && 关闭} {onDelete && ['pending_review', 'adopted', 'rejected', 'closed'].includes(req.status) && 删除}

{req.title}

{contextItems.map((item) => {item})}
{req.description ? (

{req.description}

) : (

暂无描述

)}
); } function DetailSection({ title, children }: { title: string; children: React.ReactNode }) { return (

{title}

{children}
); } function InfoItem({ label, value }: { label: string; value: string }) { return (
{label}
{value}
); } function StatusBadge({ className, children }: { className: string; children: React.ReactNode }) { return ( {children} ); } function ContextPill({ children }: { children: React.ReactNode }) { return ( {children} ); } function ActionButton({ children, onClick, tone = 'neutral', }: { children: React.ReactNode; onClick?: () => void; tone?: 'neutral' | 'success' | 'warning' | 'danger'; }) { const toneClass = { neutral: 'border-[var(--line)] text-[var(--ink-soft)] hover:bg-[var(--bg-card)]', success: 'border-emerald-200 text-emerald-700 hover:bg-emerald-50', warning: 'border-orange-200 text-orange-700 hover:bg-orange-50', danger: 'border-red-200 text-red-600 hover:bg-red-50', }[tone]; return ( ); }