'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(estimate: number, actual: number): { text: string; tone: string } { if (actual <= 0) return { text: `预 ${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(estimate, actual); return (
{task.taskNo}
{task.title} {task.isBlocked && ( 阻塞 )}
{range.text} {hours.text} {task.assigneeId}
); }