summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/PhaseConfirmationNotification.tsx
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-17 06:44:22 +0000
committersoryu <soryu@soryu.co>2026-01-17 16:39:28 +0000
commit6b07707a4cc99c7e127a2bf6a0ca790fa033b5f5 (patch)
tree23ae5355bf4fb16dd7c1cd5dbcd50f4b72c715dc /makima/frontend/src/components/PhaseConfirmationNotification.tsx
parentbfc5d837c6212a8253accfdf95ae1a2fd692df4e (diff)
downloadsoryu-6b07707a4cc99c7e127a2bf6a0ca790fa033b5f5.tar.gz
soryu-6b07707a4cc99c7e127a2bf6a0ca790fa033b5f5.zip
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 <noreply@anthropic.com>
Diffstat (limited to 'makima/frontend/src/components/PhaseConfirmationNotification.tsx')
-rw-r--r--makima/frontend/src/components/PhaseConfirmationNotification.tsx141
1 files changed, 141 insertions, 0 deletions
diff --git a/makima/frontend/src/components/PhaseConfirmationNotification.tsx b/makima/frontend/src/components/PhaseConfirmationNotification.tsx
new file mode 100644
index 0000000..516211f
--- /dev/null
+++ b/makima/frontend/src/components/PhaseConfirmationNotification.tsx
@@ -0,0 +1,141 @@
+import { useNavigate } from "react-router";
+import { useSupervisorQuestions } from "../contexts/SupervisorQuestionsContext";
+import { PhaseConfirmationModal, type PhaseConfirmationData } from "./contracts/PhaseConfirmationModal";
+import type { PendingQuestion } from "../lib/api";
+
+/**
+ * Notification component for phase confirmation requests.
+ * Shows a modal when there are pending phase_confirmation type questions.
+ * Uses the same question infrastructure as supervisor questions.
+ */
+export function PhaseConfirmationNotification() {
+ const { notificationQuestions, submitAnswer, dismissNotification } =
+ useSupervisorQuestions();
+
+ // Filter for phase_confirmation type questions
+ const phaseConfirmationQuestions = notificationQuestions.filter(
+ (q) => q.questionType === "phase_confirmation"
+ );
+
+ if (phaseConfirmationQuestions.length === 0) {
+ return null;
+ }
+
+ // Show the first phase confirmation question as a modal
+ const question = phaseConfirmationQuestions[0];
+
+ // Build phase confirmation data from the question
+ const data: PhaseConfirmationData = {
+ questionId: question.questionId,
+ contractId: question.contractId,
+ contractName: question.phaseConfirmation?.contractName,
+ currentPhase: question.phaseConfirmation?.currentPhase || "research",
+ nextPhase: question.phaseConfirmation?.nextPhase || "specify",
+ summary: question.phaseConfirmation?.summary,
+ deliverables: question.phaseConfirmation?.deliverables,
+ };
+
+ const handleApprove = async (questionId: string) => {
+ const success = await submitAnswer(questionId, "APPROVE");
+ if (success) {
+ dismissNotification(questionId);
+ }
+ };
+
+ const handleRequestChanges = async (questionId: string, feedback: string) => {
+ const success = await submitAnswer(
+ questionId,
+ `CHANGES_REQUESTED: ${feedback}`
+ );
+ if (success) {
+ dismissNotification(questionId);
+ }
+ };
+
+ const handleDismiss = () => {
+ // Dismiss to notification (user can still respond via task output)
+ dismissNotification(question.questionId);
+ };
+
+ return (
+ <PhaseConfirmationModal
+ data={data}
+ onApprove={handleApprove}
+ onRequestChanges={handleRequestChanges}
+ onDismiss={handleDismiss}
+ />
+ );
+}
+
+/**
+ * Alternative: Notification toast-style for phase confirmations
+ * Shows as a small notification in the corner (like regular supervisor questions)
+ */
+export function PhaseConfirmationToast() {
+ const navigate = useNavigate();
+ const { notificationQuestions, dismissNotification } = useSupervisorQuestions();
+
+ // Filter for phase_confirmation type questions
+ const phaseConfirmationQuestions = notificationQuestions.filter(
+ (q) => q.questionType === "phase_confirmation"
+ );
+
+ if (phaseConfirmationQuestions.length === 0) {
+ return null;
+ }
+
+ const handleGoToTask = (question: PendingQuestion) => {
+ dismissNotification(question.questionId);
+ navigate(`/mesh/${question.taskId}`);
+ };
+
+ return (
+ <div className="fixed bottom-4 right-4 z-50 max-w-md space-y-2">
+ {phaseConfirmationQuestions.map((question) => (
+ <div
+ key={question.questionId}
+ className="bg-[#0d1b2d] border border-[rgba(117,170,252,0.5)] rounded-lg shadow-lg overflow-hidden"
+ >
+ {/* Header */}
+ <div className="flex items-center justify-between px-4 py-3 bg-[rgba(117,170,252,0.1)]">
+ <div className="flex items-center gap-2">
+ <span className="text-[#75aafc] text-lg">?</span>
+ <span className="font-mono text-sm text-[#9bc3ff] uppercase">
+ Phase Transition
+ </span>
+ </div>
+ <button
+ onClick={() => handleGoToTask(question)}
+ className="px-3 py-1 font-mono text-xs text-[#75aafc] border border-[rgba(117,170,252,0.3)] hover:border-[rgba(117,170,252,0.5)] hover:bg-[rgba(117,170,252,0.1)] transition-colors uppercase"
+ >
+ Review
+ </button>
+ </div>
+
+ {/* Content preview */}
+ <div className="px-4 py-3">
+ {question.phaseConfirmation && (
+ <div className="flex items-center gap-2 mb-2">
+ <span className="font-mono text-xs text-purple-400">
+ {question.phaseConfirmation.currentPhase}
+ </span>
+ <span className="text-[#555] font-mono text-xs">&rarr;</span>
+ <span className="font-mono text-xs text-green-400">
+ {question.phaseConfirmation.nextPhase}
+ </span>
+ </div>
+ )}
+ <p className="text-sm text-[#dbe7ff] font-mono line-clamp-2">
+ {question.question}
+ </p>
+ {question.phaseConfirmation?.contractName && (
+ <p className="text-xs text-[#555] font-mono mt-1">
+ Contract: {question.phaseConfirmation.contractName}
+ </p>
+ )}
+ </div>
+ </div>
+ ))}
+ </div>
+ );
+}