'use client';
import {
createContext,
useContext,
useState,
useEffect,
ReactNode
} from 'react';
import { createClient } from '@/utils/supabase/client';
import { QuizAnswer } from '@/types/types';
import { getRound, groupByRound } from '@/utils/quizHelpers';
type QuizContextType = {
answers: QuizAnswer[];
groupedAnswers: {
'1-10': QuizAnswer[];
'11-20': QuizAnswer[];
'21-30': QuizAnswer[];
'31-35': QuizAnswer[];
};
allTeams: string[];
teamTotals: Record<string, { round1: number; round2: number; round3: number; round4: number }>;
};
const QuizContext = createContext<QuizContextType | null>(null);
export function QuizProvider({ children, quizId }: { children: ReactNode; quizId: number }) {
const supabase = createClient();
const [answers, setAnswers] = useState<QuizAnswer[]>([]);
// Fetch initial data
useEffect(() => {
const fetchAnswers = async () => {
console.log('Fetching quiz answers for quiz_id:', quizId);
// First, let's check if there are any answers for this quiz at all
const { data: allAnswers, error: allAnswersError } = await supabase
.from('quiz_answers')
.select('*');
if (allAnswersError) {
console.error('Error fetching all quiz answers:', allAnswersError);
return;
}
console.log('Total quiz answers in database:', allAnswers?.length);
// Check for both number and string types of quiz_id
const answersWithQuizId6 = allAnswers?.filter(a => {
if (typeof a.quiz_id === 'number') return a.quiz_id === 6;
if (typeof a.quiz_id === 'string') return a.quiz_id === '6';
return false;
});
console.log('Quiz answers with quiz_id=6 (any type):', answersWithQuizId6?.length);
// Now fetch the specific answers for this quiz
const { data, error } = await supabase
.from('quiz_answers')
.select('*')
.eq('quiz_id', quizId);
if (error) {
console.error('Error fetching quiz answers:', error);
return;
}
console.log('Fetched quiz answers for quiz_id', quizId, ':', data?.length);
// Try with string comparison as a fallback
if (!data || data.length === 0) {
console.log('Trying with string comparison...');
// Use 'or' filter instead of eq with string to avoid type issues
const { data: stringData, error: stringError } = await supabase
.from('quiz_answers')
.select('*')
.or(`quiz_id.eq.${quizId},quiz_id.eq.${String(quizId)}`);
if (stringError) {
console.error('Error fetching quiz answers with string comparison:', stringError);
} else {
console.log('Fetched quiz answers with string comparison:', stringData?.length);
if (stringData && stringData.length > 0) {
// Type assertion to handle nullable fields from Supabase
const typedAnswers = stringData.map(answer => ({
...answer,
team_name: answer.team_name || '',
answer: answer.answer || '',
question_number: answer.question_number || 0,
points: answer.points || 0,
correct: answer.correct || false
})) as QuizAnswer[];
setAnswers(typedAnswers);
return;
}
}
}
// Type assertion to handle nullable fields from Supabase
const typedAnswers = (data || []).map(answer => ({
...answer,
team_name: answer.team_name || '',
answer: answer.answer || '',
question_number: answer.question_number || 0,
points: answer.points || 0,
correct: answer.correct || false
})) as QuizAnswer[];
setAnswers(typedAnswers);
};
fetchAnswers();
// Subscribe to real-time changes
const channel = supabase
.channel('quiz_answers_channel')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'quiz_answers',
filter: `quiz_id=eq.${quizId}`
},
(payload) => {
console.log('Real-time update received:', payload);
if (payload.eventType === 'INSERT') {
const newAnswer = {
...payload.new,
team_name: payload.new.team_name || '',
answer: payload.new.answer || '',
question_number: payload.new.question_number || 0,
points: payload.new.points || 0,
correct: payload.new.correct || false
} as QuizAnswer;
setAnswers((prev) => [...prev, newAnswer]);
} else if (payload.eventType === 'UPDATE') {
setAnswers((prev) =>
prev.map((answer) =>
answer.id === payload.new.id
? {
...payload.new,
team_name: payload.new.team_name || '',
answer: payload.new.answer || '',
question_number: payload.new.question_number || 0,
points: payload.new.points || 0,
correct: payload.new.correct || false
} as QuizAnswer
: answer
)
);
} else if (payload.eventType === 'DELETE') {
setAnswers((prev) =>
prev.filter((answer) => answer.id !== payload.old.id)
);
}
}
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, [quizId, supabase]);
// Compute derived state
const groupedAnswers = groupByRound(answers);
const allTeams = Array.from(new Set(answers.map((a) => a.team_name)));
const teamTotals: Record<
string,
{ round1: number; round2: number; round3: number; round4: number }
> = {};
allTeams.forEach((team) => {
teamTotals[team] = { round1: 0, round2: 0, round3: 0, round4: 0 };
});
answers.forEach((answer) => {
if (!answer.correct) return;
const round = getRound(answer.question_number);
teamTotals[answer.team_name][`round${round}` as keyof typeof teamTotals[string]] +=
answer.points;
});
return (
<QuizContext.Provider
value={{
answers,
groupedAnswers,
allTeams,
teamTotals
}}
>
{children}
</QuizContext.Provider>
);
}
export function useQuiz() {
const context = useContext(QuizContext);
if (!context) {
throw new Error('useQuiz must be used within a QuizProvider');
}
return context;
}