From eeafe072bc6bb81459f7d087b48fc921afe9cc11 Mon Sep 17 00:00:00 2001 From: soryu Date: Thu, 15 Jan 2026 03:26:28 +0000 Subject: Automatically derive repo URL and add notifications for input --- .../src/contexts/SupervisorQuestionsContext.tsx | 94 ++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 makima/frontend/src/contexts/SupervisorQuestionsContext.tsx (limited to 'makima/frontend/src/contexts/SupervisorQuestionsContext.tsx') diff --git a/makima/frontend/src/contexts/SupervisorQuestionsContext.tsx b/makima/frontend/src/contexts/SupervisorQuestionsContext.tsx new file mode 100644 index 0000000..aa1bb12 --- /dev/null +++ b/makima/frontend/src/contexts/SupervisorQuestionsContext.tsx @@ -0,0 +1,94 @@ +import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from "react"; +import { listPendingQuestions, answerQuestion, type PendingQuestion } from "../lib/api"; +import { useAuth } from "./AuthContext"; + +interface SupervisorQuestionsContextValue { + pendingQuestions: PendingQuestion[]; + loading: boolean; + error: string | null; + refreshQuestions: () => Promise; + submitAnswer: (questionId: string, response: string) => Promise; +} + +const SupervisorQuestionsContext = createContext(null); + +export function SupervisorQuestionsProvider({ children }: { children: ReactNode }) { + const { isAuthenticated } = useAuth(); + const [pendingQuestions, setPendingQuestions] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const refreshQuestions = useCallback(async () => { + if (!isAuthenticated) return; + + try { + setLoading(true); + const questions = await listPendingQuestions(); + if (questions.length > 0) { + console.log("[SupervisorQuestions] Received questions:", questions); + } + setPendingQuestions(questions); + setError(null); + } catch (err) { + // Log but don't spam + console.warn("[SupervisorQuestions] Failed to fetch:", err); + setError(err instanceof Error ? err.message : "Failed to load questions"); + } finally { + setLoading(false); + } + }, [isAuthenticated]); + + const submitAnswer = useCallback(async (questionId: string, response: string): Promise => { + try { + const result = await answerQuestion(questionId, response); + if (result.success) { + // Remove the question from local state + setPendingQuestions(prev => prev.filter(q => q.questionId !== questionId)); + } + return result.success; + } catch (err) { + console.error("Failed to submit answer:", err); + return false; + } + }, []); + + // Poll for questions every 5 seconds when authenticated + useEffect(() => { + if (!isAuthenticated) { + setPendingQuestions([]); + return; + } + + // Initial fetch (delayed slightly to ensure auth is ready) + const initialTimeout = setTimeout(refreshQuestions, 500); + + // Poll periodically (every 5 seconds for responsiveness) + const interval = setInterval(refreshQuestions, 5000); + return () => { + clearTimeout(initialTimeout); + clearInterval(interval); + }; + }, [isAuthenticated, refreshQuestions]); + + return ( + + {children} + + ); +} + +export function useSupervisorQuestions() { + const context = useContext(SupervisorQuestionsContext); + if (!context) { + throw new Error("useSupervisorQuestions must be used within SupervisorQuestionsProvider"); + } + return context; +} -- cgit v1.2.3