"use client"; import { Badge } from "@kandev/ui/badge"; import { IconAlertTriangle, IconFileText, IconUsersGroup } from "@tabler/icons-react"; import type { TaskContextDTO, TaskRefDTO } from "@/lib/api/domains/office-task-context-api"; type Props = { /** * The task context envelope from GET /api/v1/tasks/:id/context. * Pass null when the backend's HandoffService is not configured or the * fetch failed; the panel renders nothing in that case so the page * falls back to its pre-handoffs layout. */ context: TaskContextDTO | null; }; /** * Office task-handoffs phase 8.2 — task detail panel. * * Surfaces the cross-task relations + document references + workspace * group state introduced by the handoffs feature. Document bodies are * never rendered here; the panel only shows references that the user * can click through to fetch full content. */ export function TaskDetailContextPanel({ context }: Props) { if (!context) return null; const showRelations = !!context.parent || context.children.length > 0 || context.siblings.length > 0 || context.blockers.length > 0 || context.blocked_by.length > 0; const showDocs = context.available_documents.length > 0; const showWorkspace = !!context.workspace_group; if (!showRelations && !showDocs && !showWorkspace) return null; return (
{showWorkspace && context.workspace_group && ( )} {showRelations && } {showDocs && }
); } function WorkspaceStatusBanner({ context }: { context: TaskContextDTO }) { if (context.workspace_status !== "requires_configuration") return null; return (
Workspace requires configuration
Restoring this task's materialized workspace failed. Reconfigure the workspace before launching.
); } function WorkspaceSection({ group, mode, }: { group: NonNullable; mode?: TaskContextDTO["workspace_mode"]; }) { const memberCount = group.members.length; return (
Shared workspace · {memberCount} {memberCount === 1 ? "member" : "members"} {mode && ( mode: {mode} )}
{group.materialized_path && (
{group.materialized_path}
)} {memberCount > 0 && (
{group.members.map((m) => ( {refLabel(m)} ))}
)}
); } function RelationsSection({ context }: { context: TaskContextDTO }) { return (
Related tasks
{context.parent && } {context.siblings.map((t) => ( ))} {context.blockers.map((t) => ( ))} {context.blocked_by.map((t) => ( ))}
); } function Relation({ label, task, variant, }: { label: string; task: TaskRefDTO; variant?: "warning"; }) { return (
{label} {refLabel(task)}
); } function DocumentsSection({ docs }: { docs: TaskContextDTO["available_documents"] }) { return (
Documents available
); } function refLabel(t: TaskRefDTO): string { return t.identifier ? `${t.identifier} ${t.title}` : t.title; }