Skip to content

Commit 8b2d72e

Browse files
committed
Add useQuizProgress hook for managing quiz state and progress, including saving and loading functionality
1 parent 8ec118c commit 8b2d72e

5 files changed

Lines changed: 231 additions & 60 deletions

File tree

src/components/quizz/QuizResults.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,9 @@ const QuizResults = ({ results, activeSetName, onRetry, shuffledQuestions }) =>
175175
<button
176176
id="retry-button"
177177
onClick={onRetry}
178-
className="flex items-center bg-indigo-100 text-indigo-700 hover:bg-indigo-200 font-medium py-2 px-6 rounded-lg transition-all duration-200"
178+
className="flex items-center gap-2 bg-indigo-100 text-indigo-700 hover:bg-indigo-200 font-medium py-2 px-6 rounded-lg transition-all duration-200"
179179
>
180-
<RefreshIcon className="mr-2" />
180+
<RefreshIcon/>
181181
Intentar de Nuevo
182182
</button>
183183
</div>

src/constants/localStorage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const defaultQuizData = [
4848
// LocalStorage keys for quizzes
4949
export const QUIZ_SETS_KEY = 'quizSetsReactGeneric';
5050
export const QUIZ_ACTIVE_SET_KEY = 'activeQuizSetNameReactGeneric';
51+
export const QUIZ_PROGRESS_KEY = 'quizProgressReactGeneric'; // Key for quiz progress
5152

5253
// --- Flashcard Constants ---
5354

src/hooks/useQuizProgress.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { useState, useEffect, useCallback } from 'react';
2+
import { saveQuizProgress, loadQuizProgress, clearQuizProgress } from '../services/storageManager';
3+
import { shuffleArray } from '../utils/helpers';
4+
5+
export const useQuizProgress = (activeQuizSetName, originalQuestions, isShuffleEnabled, isSubmitted) => {
6+
const [answers, setAnswers] = useState({});
7+
const [initialLoadedQuestionOrder, setInitialLoadedQuestionOrder] = useState(null);
8+
const [processedQuestions, setProcessedQuestions] = useState([]);
9+
10+
// Effect to load progress when the active quiz set name changes
11+
useEffect(() => {
12+
if (activeQuizSetName) {
13+
console.log(`useQuizProgress: Attempting to load progress for ${activeQuizSetName}`);
14+
const savedProgress = loadQuizProgress(activeQuizSetName);
15+
if (savedProgress && savedProgress.questionOrder) {
16+
setAnswers(savedProgress.answers || {});
17+
setInitialLoadedQuestionOrder(savedProgress.questionOrder);
18+
// console.log(`useQuizProgress: Loaded progress for ${activeQuizSetName}. Answers:`, savedProgress.answers, "Order:", savedProgress.questionOrder);
19+
} else {
20+
setAnswers({}); // Reset if no progress found or order is missing
21+
setInitialLoadedQuestionOrder(null);
22+
// console.log(`useQuizProgress: No valid progress found for ${activeQuizSetName}, resetting answers and order.`);
23+
}
24+
} else {
25+
setAnswers({}); // Reset if no active quiz set name
26+
setInitialLoadedQuestionOrder(null);
27+
// console.log("useQuizProgress: No activeQuizSetName, resetting answers and order.");
28+
}
29+
}, [activeQuizSetName]);
30+
31+
// Effect to determine and set the order of questions
32+
useEffect(() => {
33+
if (!originalQuestions || originalQuestions.length === 0) {
34+
setProcessedQuestions([]);
35+
return;
36+
}
37+
38+
if (initialLoadedQuestionOrder && initialLoadedQuestionOrder.length > 0) {
39+
// console.log("useQuizProgress: Using initial loaded question order", initialLoadedQuestionOrder);
40+
setProcessedQuestions(initialLoadedQuestionOrder);
41+
} else if (isShuffleEnabled) {
42+
// console.log("useQuizProgress: Shuffling original questions", originalQuestions);
43+
setProcessedQuestions(shuffleArray(originalQuestions));
44+
} else {
45+
// console.log("useQuizProgress: Using original question order", originalQuestions);
46+
setProcessedQuestions(originalQuestions);
47+
}
48+
}, [originalQuestions, initialLoadedQuestionOrder, isShuffleEnabled]);
49+
50+
51+
// Effect to save progress when answers or processedQuestions change,
52+
// if the quiz is active and not submitted.
53+
useEffect(() => {
54+
// Save progress if we have an active set, it's not submitted, and there are processed questions.
55+
if (activeQuizSetName && !isSubmitted && processedQuestions && processedQuestions.length > 0) {
56+
// console.log(`useQuizProgress: Saving progress for ${activeQuizSetName}. Answers:`, answers, "Order:", processedQuestions);
57+
saveQuizProgress(activeQuizSetName, { answers, questionOrder: processedQuestions });
58+
} else if (activeQuizSetName && !isSubmitted && (!processedQuestions || processedQuestions.length === 0)) {
59+
// This case might occur if a quiz is loaded that has no questions.
60+
// We probably don't want to save progress for an empty quiz or if the order is somehow lost.
61+
// console.log(`useQuizProgress: Not saving progress for ${activeQuizSetName} due to empty/invalid processed questions.`);
62+
}
63+
}, [answers, processedQuestions, activeQuizSetName, isSubmitted]);
64+
65+
const clearCurrentSavedProgress = useCallback(() => {
66+
if (activeQuizSetName) {
67+
// console.log(`useQuizProgress: Clearing saved progress for ${activeQuizSetName}`);
68+
clearQuizProgress(activeQuizSetName);
69+
setAnswers({});
70+
setInitialLoadedQuestionOrder(null); // This will trigger re-evaluation of processedQuestions
71+
}
72+
}, [activeQuizSetName]);
73+
74+
// Function to explicitly discard loaded order, e.g., when user toggles shuffle ON
75+
const discardLoadedOrderAndShuffle = useCallback(() => {
76+
setInitialLoadedQuestionOrder(null);
77+
// The useEffect for processedQuestions will then re-shuffle if isShuffleEnabled is true
78+
}, []);
79+
80+
return { answers, setAnswers, processedQuestions, clearCurrentSavedProgress, discardLoadedOrderAndShuffle };
81+
};

src/services/storageManager.js

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
FLASHCARD_SETS_KEY,
77
FLASHCARD_ACTIVE_SET_KEY,
88
QUIZ_SETS_KEY,
9-
QUIZ_ACTIVE_SET_KEY
9+
QUIZ_ACTIVE_SET_KEY,
10+
QUIZ_PROGRESS_KEY // Import the new key
1011
} from '../constants/localStorage';
1112

1213
// --- Generic LocalStorage Interaction ---
@@ -145,6 +146,76 @@ export const saveActiveSetName = (activeSetKey, name) => {
145146
saveToLocalStorage(activeSetKey, name);
146147
};
147148

149+
// --- Quiz Progress Management ---
150+
151+
/**
152+
* Saves the current quiz progress (e.g., answers and question order) to localStorage.
153+
* @param {string} quizSetName - The name of the quiz set for which progress is being saved.
154+
* @param {object} progressData - The progress data to save (e.g., { answers: {...}, questionOrder: [] }).
155+
*/
156+
export const saveQuizProgress = (quizSetName, progressData) => {
157+
if (!quizSetName) {
158+
console.error("Cannot save quiz progress without a quizSetName.");
159+
return;
160+
}
161+
const allProgress = loadFromLocalStorage(QUIZ_PROGRESS_KEY, {});
162+
// Ensure progressData contains answers and questionOrder
163+
if (!progressData || typeof progressData.answers === 'undefined' || typeof progressData.questionOrder === 'undefined') {
164+
console.error("Attempted to save progress without answers or questionOrder:", progressData);
165+
// Potentially clear progress if it's in an invalid state or just return
166+
// For now, let's prevent saving incomplete progress data.
167+
return;
168+
}
169+
allProgress[quizSetName] = progressData;
170+
saveToLocalStorage(QUIZ_PROGRESS_KEY, allProgress);
171+
console.log(`Progress for quiz '${quizSetName}' saved with answers and question order.`);
172+
};
173+
174+
/**
175+
* Loads the quiz progress (answers and question order) for a specific quiz set from localStorage.
176+
* @param {string} quizSetName - The name of the quiz set to load progress for.
177+
* @returns {object|null} The saved progress data { answers, questionOrder }, or null if not found or error.
178+
*/
179+
export const loadQuizProgress = (quizSetName) => {
180+
if (!quizSetName) {
181+
console.warn("Cannot load quiz progress without a quizSetName.");
182+
return null;
183+
}
184+
const allProgress = loadFromLocalStorage(QUIZ_PROGRESS_KEY, {});
185+
if (allProgress && allProgress[quizSetName]) {
186+
// Validate that the loaded progress has the expected structure
187+
if (typeof allProgress[quizSetName].answers !== 'undefined' && Array.isArray(allProgress[quizSetName].questionOrder)) {
188+
console.log(`Progress for quiz '${quizSetName}' loaded (includes answers and questionOrder).`);
189+
return allProgress[quizSetName];
190+
} else {
191+
console.warn(`Loaded progress for quiz '${quizSetName}' has incorrect structure. Discarding.`);
192+
// Optionally, clear this malformed progress
193+
// delete allProgress[quizSetName];
194+
// saveToLocalStorage(QUIZ_PROGRESS_KEY, allProgress);
195+
return null;
196+
}
197+
}
198+
console.log(`No saved progress found for quiz '${quizSetName}'.`);
199+
return null;
200+
};
201+
202+
/**
203+
* Clears the quiz progress for a specific quiz set from localStorage.
204+
* @param {string} quizSetName - The name of the quiz set to clear progress for.
205+
*/
206+
export const clearQuizProgress = (quizSetName) => {
207+
if (!quizSetName) {
208+
console.warn("Cannot clear quiz progress without a quizSetName.");
209+
return;
210+
}
211+
const allProgress = loadFromLocalStorage(QUIZ_PROGRESS_KEY, {});
212+
if (allProgress && allProgress[quizSetName]) {
213+
delete allProgress[quizSetName];
214+
saveToLocalStorage(QUIZ_PROGRESS_KEY, allProgress);
215+
console.log(`Progress for quiz '${quizSetName}' cleared.`);
216+
}
217+
};
218+
148219
/**
149220
* Gets the appropriate default data based on type.
150221
* @param {string} type - 'quiz' or 'flashcard'

0 commit comments

Comments
 (0)