"use client"; import { useCallback } from "react"; import { useRouter } from "@/lib/routing/client-router"; import { IconChecklist } from "@tabler/icons-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@kandev/ui/dropdown-menu"; import { Badge } from "@kandev/ui/badge"; import { Tooltip, TooltipContent, TooltipTrigger } from "@kandev/ui/tooltip"; import { useAppStore } from "@/components/state-provider"; import { cn } from "@/lib/utils"; import { linkToTask } from "@/lib/links"; import { useTaskById } from "@/hooks/domains/kanban/use-task-by-id"; import type { KanbanState } from "@/lib/state/slices"; import type { TaskPR } from "@/lib/types/github"; type PRRowTaskIndicatorProps = { tasks: TaskPR[] | undefined; }; function useTaskStepTitle(workflowStepId: string | undefined): string | null { return useAppStore((state) => { if (!workflowStepId) return null; const findIn = (steps: KanbanState["steps"]) => steps.find((s) => s.id === workflowStepId)?.title ?? null; const fromActive = findIn(state.kanban.steps); if (fromActive) return fromActive; for (const snap of Object.values(state.kanbanMulti.snapshots)) { const t = findIn(snap.steps); if (t) return t; } return null; }); } function truncateTitle(title: string): string { return title.length > 40 ? title.slice(0, 40) + "…" : title; } function TaskTitle({ taskId, fallback }: { taskId: string; fallback: string }) { const taskData = useTaskById(taskId); const title = taskData?.title ?? fallback; return {truncateTitle(title)}; } function SingleTaskButton({ task, onNavigate, }: { task: TaskPR; onNavigate: (taskId: string) => void; }) { const taskData = useTaskById(task.task_id); const stepTitle = useTaskStepTitle(taskData?.workflowStepId); const title = taskData?.title ?? task.pr_title; return ( onNavigate(task.task_id)} className={buttonClass} > {truncateTitle(title)} Task: {title} {stepTitle ? Step: {stepTitle} : null} ); } const buttonClass = cn( "inline-flex items-center gap-1.5 rounded px-1.5 py-0.5 text-xs", "hover:bg-muted/70 transition-colors cursor-pointer w-fit max-w-full", ); const iconClass = "h-3 w-3 shrink-0 text-muted-foreground"; export function PRRowTaskIndicator({ tasks }: PRRowTaskIndicatorProps) { const setActiveTask = useAppStore((state) => state.setActiveTask); const router = useRouter(); const navigate = useCallback( (taskId: string) => { setActiveTask(taskId); router.push(linkToTask(taskId)); }, [setActiveTask, router], ); if (!tasks || tasks.length === 0) { return ( No task created yet ); } if (tasks.length === 1) { return ; } return ( Tasks {tasks.length} {tasks.map((t) => ( // Use the TaskPR row id rather than task_id — multi-repo tasks can // produce more than one TaskPR row for the same PR, which would // collide on task_id. navigate(t.task_id)} > ))} ); }