'use client'; import { AlertTriangle } from 'lucide-react'; import { StatusBadge } from './StatusBadge'; import { CategoryChip } from './CategoryChip'; import { getEstimateHours, getActualHours } from '@/lib/dev-task'; import { formatShortTime, formatWorkHours } from '@/lib/work-hours'; import type { DevTask } from '@/lib/dev-task'; import type { TaskCategory } from '@/lib/task-category'; interface Props { task: DevTask; category?: TaskCategory; onClick?: () => void; } const PRIORITY_DOT: Record = { P0: 'bg-red-500', P1: 'bg-orange-400', P2: 'bg-blue-400', P3: 'bg-zinc-300', }; function timeRangeText(task: DevTask): { text: string; tone: string } { if (task.status === 'submitted' && task.actualStartAt && task.actualEndAt) { return { text: `实际 ${formatShortTime(task.actualStartAt)} → ${formatShortTime(task.actualEndAt)}`, tone: 'text-[var(--ink-muted)]' }; } if (task.actualStartAt) { return { text: `实际 ${formatShortTime(task.actualStartAt)} → 进行中`, tone: 'text-emerald-600' }; } if (task.expectedStartAt && task.expectedEndAt) { return { text: `预计 ${formatShortTime(task.expectedStartAt)} → ${formatShortTime(task.expectedEndAt)}`, tone: 'text-blue-600' }; } return { text: '', tone: 'text-[var(--ink-muted)]' }; } function hoursText(task: DevTask, estimate: number, actual: number): { text: string; tone: string } { const hasExecutorEstimate = typeof task.estimateHours === 'number' && task.estimateHours > 0; const hasAiEstimate = typeof task.aiEstimateHours === 'number' && task.aiEstimateHours > 0; const prefix = hasExecutorEstimate ? '执行预' : hasAiEstimate ? 'AI预' : '预'; if (actual <= 0) return { text: `${prefix} ${formatWorkHours(estimate)}`, tone: 'text-[var(--ink-muted)]' }; let tone = 'text-[var(--ink-soft)]'; if (actual > estimate) tone = 'text-red-600'; else if (actual < estimate) tone = 'text-emerald-600'; return { text: `${formatWorkHours(actual)} / ${formatWorkHours(estimate)}`, tone }; } export function DevTaskRow({ task, category, onClick }: Props) { const estimate = getEstimateHours(task); const actual = getActualHours(task); const range = timeRangeText(task); const hours = hoursText(task, estimate, actual); return (
{task.taskNo}
{task.title} {task.aiDraft && ( AI 草案 )} {task.isBlocked && ( 阻塞 )}
{range.text} {hours.text} {task.assigneeId}
); }