"use client"; /** * TaskHeader — shared title / identifier / state pill / assignee row. * * Pure read; no domain branching. Both the kanban shell (/t/:id) and * the office shell (/office/tasks/:id) render this above the body. * * Designed to be source-agnostic: callers pass plain primitives, so the * component works for both the kanban Task DTO (lib/types/http) and the * office Task DTO (app/office/tasks/[id]/types). */ import { Badge } from "@kandev/ui/badge"; import type { ForegroundActivity } from "@/lib/types/http"; export type TaskHeaderProps = { identifier?: string | null; title: string; state?: string | null; /** * Pre-resolved assignee display name. Resolution (initials, agent * profile name) happens at the call site against the appropriate * store; this component is a pure renderer. */ assigneeName?: string | null; /** Optional pill colour for the state badge. Falls back to outline. */ stateBadgeVariant?: "default" | "secondary" | "outline" | "destructive"; /** * Task-level MOST-ACTIVE-WINS activity aggregate. * When set it takes precedence over the coarse workflow state in the badge, so a * task still doing background work never reads as a done coarse state and stays * distinct from a generating task. */ foregroundActivity?: ForegroundActivity | null; /** * Message-derived "needs me" flags. When set * the badge reads the waiting variant distinctly ("Permission requested" / * "Waiting for input") instead of the raw coarse state, matching the sidebar. */ hasPendingClarification?: boolean; hasPendingPermission?: boolean; }; // Pending input is the most actionable state and sits above live activity, with // pending permission taking precedence over clarification. Without pending input, // the task-level activity aggregate sits above the coarse workflow state. function resolveBadgeLabel( state: string | null | undefined, foregroundActivity: ForegroundActivity | null | undefined, hasPendingClarification: boolean, hasPendingPermission: boolean, ): string | null { if (hasPendingPermission) return "Permission requested"; if (hasPendingClarification) return "Waiting for input"; if (foregroundActivity === "generating") return "Generating"; if (foregroundActivity === "background") return "Background running"; if (state === "WAITING_FOR_INPUT") return "Waiting for input"; return state ?? null; } export function TaskHeader({ identifier, title, state, assigneeName, stateBadgeVariant = "outline", foregroundActivity, hasPendingClarification = false, hasPendingPermission = false, }: TaskHeaderProps) { const badgeLabel = resolveBadgeLabel( state, foregroundActivity, hasPendingClarification, hasPendingPermission, ); return (
{identifier && ( {identifier} )} {title} {badgeLabel && ( {badgeLabel} )} {assigneeName && ( {assigneeName} )}
); }