summaryrefslogtreecommitdiff
path: root/makima/frontend/src/contexts/SupervisorQuestionsContext.tsx
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-15 03:26:28 +0000
committersoryu <soryu@soryu.co>2026-01-15 03:26:28 +0000
commiteeafe072bc6bb81459f7d087b48fc921afe9cc11 (patch)
tree7f835993edd732f8ff66d756391dedffe3d44e90 /makima/frontend/src/contexts/SupervisorQuestionsContext.tsx
parentc61a2b9b9c988f5460f85980d4ddf285f1a730b5 (diff)
downloadsoryu-eeafe072bc6bb81459f7d087b48fc921afe9cc11.tar.gz
soryu-eeafe072bc6bb81459f7d087b48fc921afe9cc11.zip
Automatically derive repo URL and add notifications for input
Diffstat (limited to 'makima/frontend/src/contexts/SupervisorQuestionsContext.tsx')
-rw-r--r--makima/frontend/src/contexts/SupervisorQuestionsContext.tsx94
1 files changed, 94 insertions, 0 deletions
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<void>;
+ submitAnswer: (questionId: string, response: string) => Promise<boolean>;
+}
+
+const SupervisorQuestionsContext = createContext<SupervisorQuestionsContextValue | null>(null);
+
+export function SupervisorQuestionsProvider({ children }: { children: ReactNode }) {
+ const { isAuthenticated } = useAuth();
+ const [pendingQuestions, setPendingQuestions] = useState<PendingQuestion[]>([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState<string | null>(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<boolean> => {
+ 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 (
+ <SupervisorQuestionsContext.Provider
+ value={{
+ pendingQuestions,
+ loading,
+ error,
+ refreshQuestions,
+ submitAnswer,
+ }}
+ >
+ {children}
+ </SupervisorQuestionsContext.Provider>
+ );
+}
+
+export function useSupervisorQuestions() {
+ const context = useContext(SupervisorQuestionsContext);
+ if (!context) {
+ throw new Error("useSupervisorQuestions must be used within SupervisorQuestionsProvider");
+ }
+ return context;
+}