diff --git a/src/components/CountryTrendChart.jsx b/src/components/CountryTrendChart.jsx
index d0cdd0a..73f359d 100644
--- a/src/components/CountryTrendChart.jsx
+++ b/src/components/CountryTrendChart.jsx
@@ -1,216 +1,216 @@
-import { useEffect, useState, useRef, useMemo, useState as useLocalState } from 'react';
+import { useEffect, useState, useRef, useMemo } from 'react';
import Loading from './Loading.jsx';
import ChartPlaceholder from './ChartPlaceholder.jsx';
import ErrorMessage from './ErrorMessage.jsx';
function SimpleLineChart({ data, width = 600, height = 250 }) {
- const [hover, setHover] = useLocalState(null);
- if (!data || data.length === 0) return ;
-
- const padding = 40;
- const dates = data.map(d => new Date(d.date));
- const values = data.map(d => d.value);
-
- const minY = Math.min(...values);
- const maxY = Math.max(...values);
- const yRange = maxY - minY || 1;
-
- const x = i => padding + (i / (data.length - 1)) * (width - padding * 2);
- const y = v => height - padding - ((v - minY) / yRange) * (height - padding * 2);
-
- // Smooth path using quadratic curves
- const path = data.reduce((acc, d, i, arr) => {
- const px = x(i);
- const py = y(d.value);
- if (i === 0) return `M ${px} ${py}`;
- const prevX = x(i - 1);
- const prevY = y(arr[i - 1].value);
- const midX = (px + prevX) / 2;
- const midY = (py + prevY) / 2;
- return acc + ` Q ${prevX} ${prevY}, ${midX} ${midY}`;
- }, '');
-
- const yTicks = 5;
- const yStep = yRange / yTicks;
- const yLabels = Array.from({ length: yTicks + 1 }, (_, i) => minY + i * yStep);
-
- return (
-
- );
+ const [hover, setHover] = useState(null);
+ if (!data || data.length === 0) return ;
+
+ const padding = 40;
+ const dates = data.map(d => new Date(d.date));
+ const values = data.map(d => d.value);
+
+ const minY = Math.min(...values);
+ const maxY = Math.max(...values);
+ const yRange = maxY - minY || 1;
+
+ const x = i => padding + (i / (data.length - 1)) * (width - padding * 2);
+ const y = v => height - padding - ((v - minY) / yRange) * (height - padding * 2);
+
+ // Smooth path using quadratic curves
+ const path = data.reduce((acc, d, i, arr) => {
+ const px = x(i);
+ const py = y(d.value);
+ if (i === 0) return `M ${px} ${py}`;
+ const prevX = x(i - 1);
+ const prevY = y(arr[i - 1].value);
+ const midX = (px + prevX) / 2;
+ const midY = (py + prevY) / 2;
+ return acc + ` Q ${prevX} ${prevY}, ${midX} ${midY}`;
+ }, '');
+
+ const yTicks = 5;
+ const yStep = yRange / yTicks;
+ const yLabels = Array.from({ length: yTicks + 1 }, (_, i) => minY + i * yStep);
+
+ return (
+
+ );
}
export default function CountryTrendChart({ slug }) {
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState(null);
- const [series, setSeries] = useState([]);
- const abortRef = useRef();
- const lastSlugRef = useRef(slug);
-
- useEffect(() => {
- if (!slug) {
- lastSlugRef.current = slug;
- setSeries([]);
- setError(null);
- setLoading(false);
- if (abortRef.current) abortRef.current.abort();
- return;
- }
-
- const controller = new AbortController();
- abortRef.current = controller;
- let mounted = true;
-
- async function load() {
- setLoading(true);
- setError(null);
- setSeries([]);
- try {
- const res = await fetch(
- `https://disease.sh/v3/covid-19/historical/${slug}?lastdays=all`,
- { signal: controller.signal }
- );
- if (!res.ok) throw new Error('Failed to fetch country trends');
- const json = await res.json();
-
- if (!mounted || lastSlugRef.current !== slug) return;
-
- const cases = json.timeline?.cases || {};
- const seriesData = Object.entries(cases).map(([date, value]) => ({
- date,
- value: Number(value || 0),
- }));
-
- seriesData.sort((a, b) => new Date(a.date) - new Date(b.date));
-
- setSeries(seriesData);
- } catch (e) {
- if (e.name === 'AbortError') return;
- setError(e);
- } finally {
- if (mounted) setLoading(false);
- }
- }
-
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [series, setSeries] = useState([]);
+ const abortRef = useRef();
+ const lastSlugRef = useRef(slug);
+
+ useEffect(() => {
+ if (!slug) {
lastSlugRef.current = slug;
- load();
-
- return () => {
- mounted = false;
- controller.abort();
- };
- }, [slug]);
-
- if (!slug) return ;
- if (loading && series.length === 0) return ;
- if (error) return ;
-
- return (
-
-
Confirmed Cases — Cumulative
-
-
- );
+ setSeries([]);
+ setError(null);
+ setLoading(false);
+ if (abortRef.current) abortRef.current.abort();
+ return;
+ }
+
+ const controller = new AbortController();
+ abortRef.current = controller;
+ let mounted = true;
+
+ async function load() {
+ setLoading(true);
+ setError(null);
+ setSeries([]);
+ try {
+ const res = await fetch(
+ `https://disease.sh/v3/covid-19/historical/${slug}?lastdays=all`,
+ { signal: controller.signal }
+ );
+ if (!res.ok) throw new Error('Failed to fetch country trends');
+ const json = await res.json();
+
+ if (!mounted || lastSlugRef.current !== slug) return;
+
+ const cases = json.timeline?.cases || {};
+ const seriesData = Object.entries(cases).map(([date, value]) => ({
+ date,
+ value: Number(value || 0),
+ }));
+
+ seriesData.sort((a, b) => new Date(a.date) - new Date(b.date));
+
+ setSeries(seriesData);
+ } catch (e) {
+ if (e.name === 'AbortError') return;
+ setError(e);
+ } finally {
+ if (mounted) setLoading(false);
+ }
+ }
+
+ lastSlugRef.current = slug;
+ load();
+
+ return () => {
+ mounted = false;
+ controller.abort();
+ };
+ }, [slug]);
+
+ if (!slug) return ;
+ if (loading && series.length === 0) return ;
+ if (error) return ;
+
+ return (
+
+
Confirmed Cases — Cumulative
+
+
+ );
}
diff --git a/src/components/HeroSection.jsx b/src/components/HeroSection.jsx
index f1be622..52b95f3 100644
--- a/src/components/HeroSection.jsx
+++ b/src/components/HeroSection.jsx
@@ -1,21 +1,31 @@
-
import React from 'react';
-
-const HeroSection = ({ image, title, subtitle }) => {
+export default function HeroSection({ image, title, subtitle }) {
return (
-
-
-
{title}
- {subtitle &&
{subtitle}
}
+
+
+ {image && (
+

+ )}
+
+
+ {title}
+
+ {subtitle && (
+
+ {subtitle}
+
+ )}
);
-};
-
-export default HeroSection;
+}
diff --git a/src/pages/Trivia.jsx b/src/pages/Trivia.jsx
index 084a78e..429af08 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)
@@ -32,17 +32,33 @@ export default function Trivia() {
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, 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);
setShowReview(false);
+ setHasSavedForSet(false);
const res = await fetch(
`https://opentdb.com/api.php?amount=5&category=${category}&difficulty=${difficulty}&type=multiple`
@@ -89,195 +105,249 @@ export default function Trivia() {
const allAnswered = answeredCount === totalQuestions && totalQuestions > 0;
- return (
- <>
-
- 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)}
-
-
+ // 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]);
- {/* Category Selector */}
-
-
- {/* Difficulty Selector */}
-
-
+ function resetHighScore() {
+ try {
+ if (typeof window !== 'undefined')
+ localStorage.removeItem('triviaHighScore');
+ } catch (_) {
+ // ignore storage errors
+ }
+ setHighScore(0);
+ }
- {/* Loading / Error */}
- {loading &&
}
-
+ return (
+ <>
+
+ Think Fast, Learn Faster
+ >
+ }
+ subtitle="A trivia playground for curious minds, quick thinkers, and casual know-it-alls"
+ />
- {/* Progress Bar */}
- {totalQuestions > 0 && (
-
-
- Progress: {answeredCount} / {totalQuestions} answered
-
-
+
+
Trivia Quiz
+
+ {difficulty.charAt(0).toUpperCase() + difficulty.slice(1)}
+
+
+
+ {/* Category + Difficulty Selector */}
+
+
+
+
+
+ {/* Loading / Error */}
+ {loading &&
}
+
+
+ {/* Progress Bar */}
+ {totalQuestions > 0 && (
+
+
+ Progress: {answeredCount} / {totalQuestions} answered
+
+ >
+
+
-
- )}
+ )}
- {/* Score + Review Button */}
-
-
Score: {score}
-
+
+ {/* Quiz Cards */}
+ {questions.map((q, idx) => (
+
+
+ {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 (
+ -
+ pick(idx, a)}
+ style={{
+ margin: '5px',
+ padding: '8px 12px',
+ borderRadius: '6px',
+ cursor: q.picked || showReview ? 'default' : 'pointer',
+ border:
+ btnClass === 'correct'
+ ? '2px solid green'
+ : btnClass === 'wrong'
+ ? '2px solid red'
+ : '1px solid #ccc',
+ background:
+ btnClass === 'correct'
+ ? '#c8e6c9'
+ : btnClass === 'wrong'
+ ? '#ffcdd2'
+ : '#fff',
+ color: 'black',
+ fontWeight: '500',
+ }}
+ >
+ {decodeHtml(a)}
+
+
+ );
+ })}
+
+
+ ))}
+
+ {/* Review Section */}
+ {showReview && (
+
+
🎯 Quiz Complete!
+
+ Final Score: {score} / {totalQuestions}
+
+
Best Score: {highScore}
+
+ Play Again
+
+
+ )}
+
>
);
}
-
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=