diff options
Diffstat (limited to 'makima/frontend/src/components/SupervisorQuestionNotification.tsx')
| -rw-r--r-- | makima/frontend/src/components/SupervisorQuestionNotification.tsx | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/makima/frontend/src/components/SupervisorQuestionNotification.tsx b/makima/frontend/src/components/SupervisorQuestionNotification.tsx new file mode 100644 index 0000000..6a71de2 --- /dev/null +++ b/makima/frontend/src/components/SupervisorQuestionNotification.tsx @@ -0,0 +1,135 @@ +import { useState } from "react"; +import { useNavigate } from "react-router"; +import { useSupervisorQuestions } from "../contexts/SupervisorQuestionsContext"; +import type { PendingQuestion } from "../lib/api"; + +export function SupervisorQuestionNotification() { + const navigate = useNavigate(); + const { pendingQuestions, submitAnswer } = useSupervisorQuestions(); + const [expandedQuestion, setExpandedQuestion] = useState<string | null>(null); + const [response, setResponse] = useState(""); + const [submitting, setSubmitting] = useState(false); + + if (pendingQuestions.length === 0) { + return null; + } + + const handleGoToTask = (taskId: string) => { + navigate(`/mesh/${taskId}`); + }; + + const handleExpand = (questionId: string) => { + setExpandedQuestion(expandedQuestion === questionId ? null : questionId); + setResponse(""); + }; + + const handleSubmit = async (question: PendingQuestion) => { + if (!response.trim()) return; + + setSubmitting(true); + const success = await submitAnswer(question.questionId, response.trim()); + setSubmitting(false); + + if (success) { + setExpandedQuestion(null); + setResponse(""); + } + }; + + const handleChoiceSelect = async (question: PendingQuestion, choice: string) => { + setSubmitting(true); + await submitAnswer(question.questionId, choice); + setSubmitting(false); + }; + + return ( + <div className="fixed bottom-4 right-4 z-50 max-w-md space-y-2"> + {pendingQuestions.map((question) => ( + <div + key={question.questionId} + className="bg-[#0d1b2d] border border-amber-500/50 rounded-lg shadow-lg overflow-hidden" + > + {/* Header */} + <div className="flex items-center justify-between px-4 py-3 bg-amber-900/30"> + <div className="flex items-center gap-2"> + <span className="text-amber-400 text-lg">?</span> + <span className="font-mono text-sm text-amber-300 uppercase"> + Supervisor Question + </span> + </div> + <div className="flex items-center gap-2"> + <button + onClick={() => handleGoToTask(question.taskId)} + className="px-2 py-1 font-mono text-xs text-amber-400 hover:text-amber-300 transition-colors" + title="Go to task" + > + View Task + </button> + <button + onClick={() => handleExpand(question.questionId)} + className="px-2 py-1 font-mono text-xs text-amber-400 border border-amber-500/30 hover:border-amber-400/50 transition-colors uppercase" + > + {expandedQuestion === question.questionId ? "Collapse" : "Answer"} + </button> + </div> + </div> + + {/* Question preview */} + <div className="px-4 py-3"> + {question.context && ( + <div className="text-xs text-[#8b949e] font-mono mb-1 uppercase"> + {question.context} + </div> + )} + <p className="text-sm text-[#dbe7ff] font-mono"> + {question.question} + </p> + </div> + + {/* Expanded answer section */} + {expandedQuestion === question.questionId && ( + <div className="px-4 pb-4 border-t border-amber-500/20 pt-3"> + {question.choices.length > 0 ? ( + // Choice buttons + <div className="space-y-2"> + <p className="text-xs text-[#8b949e] font-mono uppercase mb-2"> + Select an option: + </p> + {question.choices.map((choice, idx) => ( + <button + key={idx} + onClick={() => handleChoiceSelect(question, choice)} + disabled={submitting} + className="w-full px-3 py-2 text-left font-mono text-sm text-[#dbe7ff] bg-[#0a1628] border border-[#3f6fb3] hover:border-amber-400/50 hover:bg-amber-900/20 disabled:opacity-50 transition-colors" + > + {choice} + </button> + ))} + </div> + ) : ( + // Free-form text input + <div className="space-y-2"> + <textarea + value={response} + onChange={(e) => setResponse(e.target.value)} + placeholder="Type your response..." + rows={3} + className="w-full px-3 py-2 bg-[#0a1628] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-amber-400 resize-none" + disabled={submitting} + /> + <button + onClick={() => handleSubmit(question)} + disabled={submitting || !response.trim()} + className="w-full px-4 py-2 font-mono text-xs text-[#0a1628] bg-amber-500 hover:bg-amber-400 disabled:bg-amber-700 disabled:cursor-not-allowed transition-colors uppercase" + > + {submitting ? "Submitting..." : "Submit Response"} + </button> + </div> + )} + </div> + )} + </div> + ))} + </div> + ); +} |
