From 6b07707a4cc99c7e127a2bf6a0ca790fa033b5f5 Mon Sep 17 00:00:00 2001 From: soryu Date: Sat, 17 Jan 2026 06:44:22 +0000 Subject: feat(frontend): Add UI for phase transition confirmation requests When phase_guard is enabled and a supervisor tries to advance the contract phase, users now see a confirmation modal with: - Current and proposed next phase visualization - Phase deliverables checklist (if available) - Summary of the phase work - Options to "Approve & Advance" or "Request Changes" with feedback Components added: - PhaseConfirmationModal: Full modal dialog for phase confirmations - PhaseConfirmationInline: Inline variant for task output view - PhaseConfirmationNotification: Global notification wrapper - PhaseConfirmationToast: Alternative toast-style notification Integration: - Added phase_confirmation message type to TaskOutput renderer - Extended PendingQuestion API type with phase confirmation data - Integrated notification into main app layout The UI uses the existing supervisor question infrastructure (polling via /api/v1/mesh/questions) and responds with APPROVE or CHANGES_REQUESTED prefixed feedback. Co-Authored-By: Claude Opus 4.5 --- makima/frontend/src/components/mesh/TaskOutput.tsx | 66 ++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'makima/frontend/src/components/mesh/TaskOutput.tsx') diff --git a/makima/frontend/src/components/mesh/TaskOutput.tsx b/makima/frontend/src/components/mesh/TaskOutput.tsx index d53429d..7140c8a 100644 --- a/makima/frontend/src/components/mesh/TaskOutput.tsx +++ b/makima/frontend/src/components/mesh/TaskOutput.tsx @@ -2,6 +2,11 @@ import { useRef, useEffect, useState, useCallback } from "react"; import { SimpleMarkdown } from "../SimpleMarkdown"; import type { TaskOutputEvent } from "../../hooks/useTaskSubscription"; import { sendTaskMessage } from "../../lib/api"; +import { + PhaseConfirmationInline, + type PhaseConfirmationData, +} from "../contracts/PhaseConfirmationModal"; +import type { ContractPhase } from "../../lib/api"; interface TaskOutputProps { /** Array of parsed output events from the backend */ @@ -312,6 +317,15 @@ function OutputEntryRenderer({ entry, pendingQuestionIds, onAnswerQuestion }: Ou /> ); + case "phase_confirmation": + return ( + + ); + default: return null; } @@ -520,3 +534,55 @@ function AuthRequiredEntry({ entry }: { entry: TaskOutputEvent }) { ); } + +/** Entry for phase transition confirmations */ +function PhaseConfirmationEntry({ + entry, + pendingQuestionIds, + onAnswerQuestion, +}: { + entry: TaskOutputEvent; + pendingQuestionIds?: Set; + onAnswerQuestion?: (questionId: string, response: string) => Promise; +}) { + const questionId = entry.toolInput?.question_id as string; + const currentPhase = entry.toolInput?.current_phase as ContractPhase; + const nextPhase = entry.toolInput?.next_phase as ContractPhase; + const contractId = entry.toolInput?.contract_id as string; + const contractName = entry.toolInput?.contract_name as string | undefined; + const summary = entry.toolInput?.summary as string | undefined; + const deliverables = entry.toolInput?.deliverables as + | Array<{ name: string; completed: boolean }> + | undefined; + + const isPending = pendingQuestionIds?.has(questionId) ?? false; + + const data: PhaseConfirmationData = { + questionId, + contractId, + contractName, + currentPhase, + nextPhase, + summary, + deliverables, + }; + + const handleApprove = async (qId: string) => { + if (!onAnswerQuestion) return; + await onAnswerQuestion(qId, "APPROVE"); + }; + + const handleRequestChanges = async (qId: string, feedback: string) => { + if (!onAnswerQuestion) return; + await onAnswerQuestion(qId, `CHANGES_REQUESTED: ${feedback}`); + }; + + return ( + + ); +} -- cgit v1.2.3