import dayjs from "dayjs"; import { Calendar, MessageSquare } from "lucide-react"; import { useMemo } from "react"; import { agentColor } from "../lib/agentIdentity"; import { AgentIdenticon } from "./AgentIdenticon"; import { LabelChip } from "./LabelChip"; import { Badge } from "./ui/badge"; interface TaskCardProps { task: any; labels?: { name: string; color: string; description: string }[]; onClick: () => void; onAgentClick?: (task: any) => void; isNew?: boolean; } function getPriorityBadge(priority?: string) { switch (priority) { case "urgent": case "high": case "High": return { label: "High", color: "bg-red-50 text-red-600 border-red-200/60 dark:bg-red-950/40 dark:text-red-400 dark:border-red-900/60", dot: "bg-red-500", }; case "medium": case "med": case "Med": return { label: "Med", color: "bg-amber-50 text-amber-600 border-amber-200/60 dark:bg-amber-950/40 dark:text-amber-400 dark:border-amber-900/60", dot: "bg-amber-500", }; case "low": case "Low": return { label: "Low", color: "bg-blue-50 text-blue-600 border-blue-200/60 dark:bg-blue-950/40 dark:text-blue-400 dark:border-blue-900/60", dot: "bg-blue-500", }; default: return { label: priority || "Low", color: "bg-slate-50 text-slate-600 border-slate-200/60 dark:bg-zinc-800 dark:text-slate-300 dark:border-zinc-700/60", dot: "bg-slate-400", }; } } export function TaskCard({ task, labels = [], onClick, onAgentClick, isNew }: TaskCardProps) { const isAssigned = !!task.assigned_to; const isWorking = task.status === "in_progress" && !task.glow_suppressed; const labelByName = new Map(labels.map((label) => [label.name, label])); const meta = useMemo(() => { if (!task.metadata) return {}; if (typeof task.metadata === "string") { try { return JSON.parse(task.metadata); } catch { return {}; } } return task.metadata; }, [task.metadata]); const priorityVal = task.priority || meta.priority || "low"; const priorityInfo = getPriorityBadge(priorityVal); const steps = Array.isArray(meta.steps) ? meta.steps : []; // Calculate DoD from description checkboxes const calculateDodProgress = (desc?: string | null) => { if (!desc) return null; const regex = /^\s*-\s*\[([ xX])\]\s+/gm; let total = 0; let checked = 0; let match; while ((match = regex.exec(desc)) !== null) { total++; if (match[1].toLowerCase() === "x") checked++; } return total > 0 ? Math.round((checked / total) * 100) : null; }; const desc = task.description || meta.businessContext || meta.technicalNotes || ""; const dodPct = calculateDodProgress(desc); let computedPct = 0; if (dodPct !== null) { computedPct = dodPct; } else if (steps.length > 0) { const completedSteps = steps.filter((s: any) => s.completed || s.status === "done" || s.verified).length; computedPct = Math.round((completedSteps / steps.length) * 100); } let progressPct = 0; if (task.status === "done") { progressPct = 100; } else if (task.status === "in_review") { progressPct = dodPct !== null ? dodPct : 100; } else if (task.status === "in_progress") { progressPct = dodPct !== null ? dodPct : meta.progress !== undefined ? meta.progress : computedPct; } return (