From 0dfe644ff0a9e82ddc974d5b510cc1c885212073 Mon Sep 17 00:00:00 2001 From: Venisha Kalola Date: Thu, 23 Oct 2025 15:22:31 +0530 Subject: [PATCH] local high score persistence in Trivia --- src/components/CountryTrendChart.jsx | 5 + src/components/HeroSection.jsx | 19 ++ src/pages/Trivia.jsx | 311 ++++++++++++++++-- ....timestamp-1761116311330-9bd7f4dd0b574.mjs | 10 + 4 files changed, 311 insertions(+), 34 deletions(-) create mode 100644 src/components/CountryTrendChart.jsx create mode 100644 src/components/HeroSection.jsx create mode 100644 vite.config.js.timestamp-1761116311330-9bd7f4dd0b574.mjs diff --git a/src/components/CountryTrendChart.jsx b/src/components/CountryTrendChart.jsx new file mode 100644 index 0000000..a103b0a --- /dev/null +++ b/src/components/CountryTrendChart.jsx @@ -0,0 +1,5 @@ +import ChartPlaceholder from './ChartPlaceholder.jsx'; + +export default function CountryTrendChart({ slug }) { + return ; +} diff --git a/src/components/HeroSection.jsx b/src/components/HeroSection.jsx new file mode 100644 index 0000000..d9ef8cd --- /dev/null +++ b/src/components/HeroSection.jsx @@ -0,0 +1,19 @@ +export default function HeroSection({ image, title, subtitle }) { + return ( +
+
+ {image ? ( + + ) : null} +
+

{title}

+ {subtitle &&

{subtitle}

} +
+
+
+ ); +} diff --git a/src/pages/Trivia.jsx b/src/pages/Trivia.jsx index 99c2c17..0ba8591 100644 --- a/src/pages/Trivia.jsx +++ b/src/pages/Trivia.jsx @@ -9,7 +9,7 @@ * Medium: * - [ ] Timer per question + bonus points for speed * - [ ] Show progress bar (questions answered / total) - * - [ ] Local high score persistence + * - [x] Local high score persistence * - [ ] Review mode (see all answers after completion) * Advanced: * - [ ] Question set builder (choose amount, difficulty, category) @@ -21,69 +21,312 @@ import { useEffect, useState } from 'react'; import Loading from '../components/Loading.jsx'; import ErrorMessage from '../components/ErrorMessage.jsx'; import Card from '../components/Card.jsx'; +import HeroSection from '../components/HeroSection'; +import Quiz from '../Images/Quiz.jpg'; export default function Trivia() { const [questions, setQuestions] = useState([]); const [category, setCategory] = useState('18'); // Science: Computers + const [difficulty, setDifficulty] = useState('easy'); // Default to easy const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [score, setScore] = useState(0); + const [showReview, setShowReview] = useState(false); + const [highScore, setHighScore] = useState(0); + const [hasSavedForSet, setHasSavedForSet] = useState(false); - useEffect(() => { fetchQuestions(); }, [category]); + useEffect(() => { + fetchQuestions(); + }, [category, difficulty]); + + // Load high score once on mount + useEffect(() => { + try { + const stored = typeof window !== 'undefined' ? localStorage.getItem('triviaHighScore') : null; + if (stored !== null) setHighScore(parseInt(stored, 10) || 0); + } catch (_) { + // ignore storage errors + } + }, []); async function fetchQuestions() { try { - setLoading(true); setError(null); setScore(0); - const res = await fetch(`https://opentdb.com/api.php?amount=5&category=${category}&type=multiple`); + setLoading(true); + setError(null); + setScore(0); + setShowReview(false); + setHasSavedForSet(false); + + const res = await fetch( + `https://opentdb.com/api.php?amount=5&category=${category}&difficulty=${difficulty}&type=multiple` + ); if (!res.ok) throw new Error('Failed to fetch'); const json = await res.json(); - const qs = json.results.map(q => ({ + + const qs = json.results.map((q) => ({ ...q, answers: shuffle([q.correct_answer, ...q.incorrect_answers]), - picked: null + picked: null, })); setQuestions(qs); - } catch (e) { setError(e); } finally { setLoading(false); } + } catch (e) { + setError(e); + } finally { + setLoading(false); + } } - function shuffle(arr) { return arr.sort(() => Math.random() - 0.5); } + function shuffle(arr) { + return arr.sort(() => Math.random() - 0.5); + } function pick(qIndex, answer) { - setQuestions(qs => qs.map((q,i) => i===qIndex ? { ...q, picked: answer } : q)); - if (questions[qIndex].correct_answer === answer) setScore(s => s+1); + setQuestions((qs) => + qs.map((q, i) => (i === qIndex ? { ...q, picked: answer } : q)) + ); + if (questions[qIndex].correct_answer === answer) { + setScore((s) => s + 1); + } + } + + function decodeHtml(html) { + const txt = document.createElement('textarea'); + txt.innerHTML = html; + return txt.value; + } + + const answeredCount = questions.filter((q) => q.picked !== null).length; + const totalQuestions = questions.length; + const progressPercent = + totalQuestions > 0 ? (answeredCount / totalQuestions) * 100 : 0; + + const allAnswered = answeredCount === totalQuestions && totalQuestions > 0; + + // Persist high score at end of game (once per question set) + useEffect(() => { + if (!allAnswered || hasSavedForSet) return; + if (score > highScore) { + try { + if (typeof window !== 'undefined') localStorage.setItem('triviaHighScore', String(score)); + } catch (_) { + // ignore storage errors + } + setHighScore(score); + } + setHasSavedForSet(true); + }, [allAnswered, score, highScore, hasSavedForSet]); + + function resetHighScore() { + try { + if (typeof window !== 'undefined') localStorage.removeItem('triviaHighScore'); + } catch (_) { + // ignore storage errors + } + setHighScore(0); } return ( -
-

Trivia Quiz

- + <> + + Think Fast, Learn Faster + + } + subtitle="A trivia playground for curious minds, quick thinkers, and casual know-it-alls" +/> +
+
+

Trivia Quiz

+ + {difficulty.charAt(0).toUpperCase() + difficulty.slice(1)} + +
+ + {/* Category Selector */} +
+ + {/* Difficulty Selector */} + +
+ + {/* Loading / Error */} {loading && } -

Score: {score}

+ + {/* Progress Bar */} + {totalQuestions > 0 && ( +
+

+ Progress: {answeredCount} / {totalQuestions} answered +

+
+
+
+
+ )} + + {/* Score + Review Button */} +
+

Score: {score} | Best: {highScore}

+ + +
+ + {/* Quiz Cards */} {questions.map((q, idx) => ( - +
    - {q.answers.map(a => ( -
  • - -
  • - ))} + {q.answers.map((a) => { + const isPicked = q.picked === a; + const isCorrect = a === q.correct_answer; + let btnClass = ''; + + if (showReview) { + btnClass = isCorrect + ? 'correct' + : isPicked + ? 'wrong' + : 'neutral'; + } else if (isPicked) { + btnClass = isCorrect ? 'correct' : 'wrong'; + } + + return ( +
  • + +
  • + ); + })}
))} - {/* TODO: Add scoreboard persistence */} + + {/* Review Section */} + {showReview && ( +
+

🎯 Quiz Complete!

+

+ Final Score: {score} / {totalQuestions} +

+

Best Score: {highScore}

+ +
+ )}
+ ); -} - -function decodeHtml(html) { - const txt = document.createElement('textarea'); - txt.innerHTML = html; - return txt.value; -} +} \ No newline at end of file diff --git a/vite.config.js.timestamp-1761116311330-9bd7f4dd0b574.mjs b/vite.config.js.timestamp-1761116311330-9bd7f4dd0b574.mjs new file mode 100644 index 0000000..a9084ad --- /dev/null +++ b/vite.config.js.timestamp-1761116311330-9bd7f4dd0b574.mjs @@ -0,0 +1,10 @@ +// vite.config.js +import { defineConfig } from "file:///Users/venishakalola/Desktop/hacktoberfest/react-verse/node_modules/vite/dist/node/index.js"; +import react from "file:///Users/venishakalola/Desktop/hacktoberfest/react-verse/node_modules/@vitejs/plugin-react/dist/index.js"; +var vite_config_default = defineConfig({ + plugins: [react()] +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvdmVuaXNoYWthbG9sYS9EZXNrdG9wL2hhY2t0b2JlcmZlc3QvcmVhY3QtdmVyc2VcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9Vc2Vycy92ZW5pc2hha2Fsb2xhL0Rlc2t0b3AvaGFja3RvYmVyZmVzdC9yZWFjdC12ZXJzZS92aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vVXNlcnMvdmVuaXNoYWthbG9sYS9EZXNrdG9wL2hhY2t0b2JlcmZlc3QvcmVhY3QtdmVyc2Uvdml0ZS5jb25maWcuanNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJztcbmltcG9ydCByZWFjdCBmcm9tICdAdml0ZWpzL3BsdWdpbi1yZWFjdCc7XG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtyZWFjdCgpXSxcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFvVixTQUFTLG9CQUFvQjtBQUNqWCxPQUFPLFdBQVc7QUFFbEIsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUyxDQUFDLE1BQU0sQ0FBQztBQUNuQixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=