summaryrefslogblamecommitdiff
path: root/makima/frontend/src/components/mesh/ContractCompleteQuestion.tsx
blob: d4ef618bfb9e53bd4f9407e42b8fbb2a38cb84ce (plain) (tree)




































































































































































                                                                                                                                                                            
import { useState } from "react";
import type { PendingQuestion } from "../../lib/api";

interface ContractCompleteQuestionProps {
  question: PendingQuestion;
  onAnswer: (questionId: string, response: string) => Promise<void>;
}

/**
 * Component for displaying contract_complete type questions prominently on the task page.
 * These questions persist until answered and are not shown as floating notifications.
 */
export function ContractCompleteQuestion({
  question,
  onAnswer,
}: ContractCompleteQuestionProps) {
  const [submitting, setSubmitting] = useState(false);
  const [minimized, setMinimized] = useState(false);
  const [customInput, setCustomInput] = useState("");
  const [showCustom, setShowCustom] = useState(false);

  const handleAnswer = async (response: string) => {
    if (submitting) return;
    setSubmitting(true);
    try {
      await onAnswer(question.questionId, response);
    } finally {
      setSubmitting(false);
    }
  };

  const handleCustomSubmit = async () => {
    if (!customInput.trim() || submitting) return;
    await handleAnswer(customInput.trim());
    setCustomInput("");
    setShowCustom(false);
  };

  // Default choices for contract completion questions
  const defaultChoices =
    question.choices.length > 0
      ? question.choices
      : ["Yes, contract is complete", "No, more work needed"];

  if (minimized) {
    return (
      <div className="fixed bottom-4 left-4 z-40">
        <button
          onClick={() => setMinimized(false)}
          className="flex items-center gap-2 px-4 py-2 bg-green-600 hover:bg-green-500 text-white font-mono text-sm rounded-lg shadow-lg transition-colors"
        >
          <span className="w-2 h-2 bg-white rounded-full animate-pulse" />
          Contract Review Pending
        </button>
      </div>
    );
  }

  return (
    <div className="bg-gradient-to-r from-green-900/40 to-emerald-900/40 border-2 border-green-500/60 rounded-lg shadow-xl my-4 overflow-hidden">
      {/* Header */}
      <div className="flex items-center justify-between px-4 py-3 bg-green-900/50 border-b border-green-500/30">
        <div className="flex items-center gap-3">
          <div className="w-8 h-8 flex items-center justify-center bg-green-500/20 rounded-full">
            <span className="text-green-400 text-xl">?</span>
          </div>
          <div>
            <h3 className="font-mono text-sm text-green-300 uppercase tracking-wide">
              Contract Completion Review
            </h3>
            <p className="text-xs text-green-400/60">
              Please review and respond
            </p>
          </div>
        </div>
        <button
          onClick={() => setMinimized(true)}
          className="px-2 py-1 text-xs font-mono text-green-400/70 hover:text-green-300 border border-green-500/30 hover:border-green-400/50 rounded transition-colors"
          title="Minimize (question will remain pending)"
        >
          Minimize
        </button>
      </div>

      {/* Content */}
      <div className="p-4 space-y-4">
        {/* Context */}
        {question.context && (
          <div className="text-xs text-green-300/70 font-mono uppercase tracking-wide">
            {question.context}
          </div>
        )}

        {/* Question */}
        <div className="text-green-100 font-mono text-base leading-relaxed">
          {question.question}
        </div>

        {/* Choices */}
        <div className="flex flex-wrap gap-3 pt-2">
          {defaultChoices.map((choice, idx) => (
            <button
              key={idx}
              onClick={() => handleAnswer(choice)}
              disabled={submitting}
              className={`px-4 py-2.5 font-mono text-sm border rounded-md transition-all disabled:opacity-50 disabled:cursor-not-allowed ${
                idx === 0
                  ? "bg-green-500/20 border-green-400/60 hover:bg-green-500/30 text-green-100 hover:border-green-400"
                  : "bg-amber-500/20 border-amber-400/60 hover:bg-amber-500/30 text-amber-100 hover:border-amber-400"
              }`}
            >
              {submitting ? "..." : choice}
            </button>
          ))}
        </div>

        {/* Custom input option */}
        {!showCustom ? (
          <button
            onClick={() => setShowCustom(true)}
            className="text-xs text-green-400/70 hover:text-green-300 font-mono transition-colors"
          >
            + Provide custom response
          </button>
        ) : (
          <div className="flex gap-2 pt-2">
            <input
              type="text"
              value={customInput}
              onChange={(e) => setCustomInput(e.target.value)}
              placeholder="Type your response..."
              disabled={submitting}
              className="flex-1 px-3 py-2 bg-[#0a1525] border border-green-500/30 text-green-100 text-sm font-mono rounded focus:outline-none focus:border-green-400"
              onKeyDown={(e) => {
                if (e.key === "Enter" && customInput.trim()) {
                  handleCustomSubmit();
                }
                if (e.key === "Escape") {
                  setShowCustom(false);
                  setCustomInput("");
                }
              }}
            />
            <button
              onClick={handleCustomSubmit}
              disabled={submitting || !customInput.trim()}
              className="px-4 py-2 bg-green-500 text-black text-sm font-medium rounded disabled:opacity-50 disabled:cursor-not-allowed transition-colors hover:bg-green-400"
            >
              {submitting ? "..." : "Submit"}
            </button>
            <button
              onClick={() => {
                setShowCustom(false);
                setCustomInput("");
              }}
              className="px-2 py-2 text-green-400/70 hover:text-green-300 text-sm font-mono"
            >
              Cancel
            </button>
          </div>
        )}
      </div>
    </div>
  );
}