import { useEffect, useRef, useState } from 'react'; import { QuestionsProgress } from './QuestionsProgress'; import { CheckCircle, SkipForward, Sparkles } from 'lucide-react'; import { QuestionCard } from './QuestionCard'; import { QuestionLoader } from './QuestionLoader'; import { isLoggedIn } from '../../lib/jwt'; import type { QuestionType } from '../../lib/question-group'; import { httpGet, httpPut } from '../../lib/http'; import { useToast } from '../../hooks/use-toast'; import { QuestionFinished } from './QuestionFinished'; import { Confetti } from '../Confetti'; type UserQuestionProgress = { know: string[]; dontKnow: string[]; skip: string[]; }; export type QuestionProgressType = keyof UserQuestionProgress; type QuestionsListProps = { groupId: string; questions: QuestionType[]; }; export function QuestionsList(props: QuestionsListProps) { const { questions: defaultQuestions, groupId } = props; const toast = useToast(); const [questions, setQuestions] = useState(defaultQuestions); const [isLoading, setIsLoading] = useState(true); const [showConfetti, setShowConfetti] = useState(false); const [currQuestionIndex, setCurrQuestionIndex] = useState(0); const [userProgress, setUserProgress] = useState(); const containerRef = useRef(null); async function fetchUserProgress(): Promise< UserQuestionProgress | undefined > { if (!isLoggedIn()) { return; } const { response, error } = await httpGet( `${ import.meta.env.PUBLIC_API_URL }/v1-get-user-question-progress/${groupId}`, ); if (error) { toast.error(error.message || 'Error fetching user progress'); return; } return response; } async function prepareProgress() { const userProgress = await fetchUserProgress(); setUserProgress(userProgress); const knownQuestions = userProgress?.know || []; const didNotKnowQuestions = userProgress?.dontKnow || []; const skipQuestions = userProgress?.skip || []; const pendingQuestionIndex = questions.findIndex((question) => { return ( !knownQuestions.includes(question.id) && !didNotKnowQuestions.includes(question.id) && !skipQuestions.includes(question.id) ); }); setCurrQuestionIndex(pendingQuestionIndex); setIsLoading(false); } async function resetProgress() { let knownQuestions = userProgress?.know || []; let didNotKnowQuestions = userProgress?.dontKnow || []; let skipQuestions = userProgress?.skip || []; if (!isLoggedIn()) { setQuestions(defaultQuestions); knownQuestions = []; didNotKnowQuestions = []; skipQuestions = []; } else { setIsLoading(true); const { response, error } = await httpPut( `${ import.meta.env.PUBLIC_API_URL }/v1-reset-question-progress/${groupId}`, { status: 'reset', }, ); if (error) { toast.error(error.message || 'Error resetting progress'); return; } knownQuestions = response?.know || []; didNotKnowQuestions = response?.dontKnow || []; skipQuestions = response?.skip || []; } setCurrQuestionIndex(0); setUserProgress({ know: knownQuestions, dontKnow: didNotKnowQuestions, skip: skipQuestions, }); setIsLoading(false); } async function updateQuestionStatus( status: QuestionProgressType, questionId: string, ) { setIsLoading(true); let newProgress = userProgress || { know: [], dontKnow: [], skip: [] }; if (!isLoggedIn()) { if (status === 'know') { newProgress.know.push(questionId); } else if (status == 'dontKnow') { newProgress.dontKnow.push(questionId); } else if (status == 'skip') { newProgress.skip.push(questionId); } } else { const { response, error } = await httpPut( `${ import.meta.env.PUBLIC_API_URL }/v1-update-question-status/${groupId}`, { status, questionId, questionGroupId: groupId, }, ); if (error || !response) { toast.error(error?.message || 'Error marking question status'); return; } newProgress = response; } const nextQuestionIndex = currQuestionIndex + 1; setUserProgress(newProgress); setIsLoading(false); if (!nextQuestionIndex || !questions[nextQuestionIndex]) { setShowConfetti(true); } setCurrQuestionIndex(nextQuestionIndex); } useEffect(() => { prepareProgress().then(() => null); }, [questions]); const knowCount = userProgress?.know.length || 0; const dontKnowCount = userProgress?.dontKnow.length || 0; const skipCount = userProgress?.skip.length || 0; const hasProgress = knowCount > 0 || dontKnowCount > 0 || skipCount > 0; const currQuestion = questions[currQuestionIndex]; const hasFinished = !isLoading && hasProgress && currQuestionIndex === -1; return (
{ resetProgress().finally(() => null); }} onNextClick={() => { if ( currQuestionIndex !== -1 && currQuestionIndex < questions.length - 1 ) { updateQuestionStatus('skip', currQuestion.id).finally(() => null); } }} onPrevClick={() => { if (currQuestionIndex > 0) { const prevQuestion = questions[currQuestionIndex - 1]; // remove last question from the progress of the user const tempUserProgress = { know: userProgress?.know.filter((id) => id !== prevQuestion.id) || [], dontKnow: userProgress?.dontKnow.filter((id) => id !== prevQuestion.id) || [], skip: userProgress?.skip.filter((id) => id !== prevQuestion.id) || [], }; setUserProgress(tempUserProgress); setCurrQuestionIndex(currQuestionIndex - 1); } }} /> {showConfetti && containerRef.current && ( { setShowConfetti(false); }} /> )}
{hasFinished && ( { resetProgress().finally(() => null); }} /> )} {!isLoading && currQuestion && } {isLoading && }
); }