summaryrefslogblamecommitdiff
path: root/makima/frontend/src/components/PhaseConfirmationNotification.tsx
blob: 2681fdc880f5372c15dc031e48f7a626cec9ba63 (plain) (tree)























































































                                                                                                        
                                         



















































                                                                                                                                                                                                            
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(`/exec/${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>
  );
}