From caf64f3f55fc0d95be3b7e36b07efd46235d88ef Mon Sep 17 00:00:00 2001 From: Prisha Jain Date: Wed, 1 Jul 2026 23:15:54 +0530 Subject: [PATCH 1/4] Completed the final debugging and aligned the game to the existing design --- backend/package-lock.json | 61 --- frontend/package-lock.json | 33 +- frontend/src/components/GridMemoryGame.jsx | 326 ++++++++++++ .../InterviewPrep/components/PracticePage.jsx | 483 ++++++++++++------ 4 files changed, 667 insertions(+), 236 deletions(-) create mode 100644 frontend/src/components/GridMemoryGame.jsx diff --git a/backend/package-lock.json b/backend/package-lock.json index 8e49c6d..fe102fa 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1658,67 +1658,6 @@ "url": "https://opencollective.com/mongoose" } }, - "node_modules/mongoose/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/mongoose/node_modules/gaxios": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", - "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^5.0.0", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mongoose/node_modules/gcp-metadata": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", - "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "gaxios": "^5.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mongoose/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/mongoose/node_modules/mongodb": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 79ce58d..c368a53 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -27,8 +27,8 @@ }, "devDependencies": { "@eslint/js": "^9.33.0", - "@types/react": "^19.1.10", - "@types/react-dom": "^19.1.7", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^5.0.0", "eslint": "^9.33.0", "eslint-plugin-react-hooks": "^5.2.0", @@ -1752,23 +1752,30 @@ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/react": { - "version": "19.1.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", - "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", + "version": "18.3.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", "license": "MIT", "dependencies": { - "csstype": "^3.0.2" + "@types/prop-types": "*", + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", - "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^18.0.0" } }, "node_modules/@types/trusted-types": { @@ -2171,9 +2178,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/debug": { diff --git a/frontend/src/components/GridMemoryGame.jsx b/frontend/src/components/GridMemoryGame.jsx new file mode 100644 index 0000000..caa04b8 --- /dev/null +++ b/frontend/src/components/GridMemoryGame.jsx @@ -0,0 +1,326 @@ +import React, { useState, useEffect, useRef, useCallback } from "react"; +import { X, Grid3x3 } from "lucide-react"; + +// ─── Game config ─────────────────────────────────────────────────────────────── +const LEVELS = [ + { rows: 3, cols: 4, colored: 6, preview: 4, time: 20 }, + { rows: 3, cols: 5, colored: 9, preview: 4, time: 22 }, + { rows: 4, cols: 5, colored: 12, preview: 4, time: 25 }, + { rows: 4, cols: 6, colored: 15, preview: 3, time: 25 }, + { rows: 4, cols: 7, colored: 18, preview: 3, time: 28 }, + { rows: 5, cols: 7, colored: 22, preview: 3, time: 30 }, + { rows: 5, cols: 8, colored: 26, preview: 2, time: 30 }, + { rows: 6, cols: 8, colored: 30, preview: 2, time: 32 }, +]; + +function shuffle(arr) { + const a = [...arr]; + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [a[i], a[j]] = [a[j], a[i]]; + } + return a; +} + +function buildGrid(lvl) { + const { rows, cols, colored } = LEVELS[lvl]; + const total = rows * cols; + const positions = shuffle([...Array(total).keys()]); + const coloredSet = new Set(positions.slice(0, colored)); + return Array.from({ length: total }, (_, i) => ({ + colored: coloredSet.has(i), + clicked: false, + state: "idle", // idle | correct | wrong + })); +} + +// ─── Component ───────────────────────────────────────────────────────────────── +const GridMemoryGame = ({ onClose }) => { + const [phase, setPhase] = useState("start"); // start | preview | playing | won | lost | timeup + const [level, setLevel] = useState(0); + const [grid, setGrid] = useState([]); + const [remaining, setRemaining] = useState(0); + const [mistakes, setMistakes] = useState(0); + const [score, setScore] = useState(0); + const [timeLeft, setTimeLeft] = useState(0); + + const timerRef = useRef(null); + const previewRef = useRef(null); + + const clearTimers = () => { + clearTimeout(timerRef.current); + clearTimeout(previewRef.current); + }; + + const startLevel = useCallback((lvl) => { + clearTimers(); + const newGrid = buildGrid(lvl); + setLevel(lvl); + setGrid(newGrid); + setMistakes(0); + setScore(0); + setRemaining(LEVELS[lvl].colored); + setTimeLeft(LEVELS[lvl].time); + setPhase("preview"); + }, []); + + // Preview → playing + useEffect(() => { + if (phase !== "preview") return; + previewRef.current = setTimeout(() => { + setGrid((g) => g.map((c) => ({ ...c, state: "idle" }))); + setPhase("playing"); + }, LEVELS[level].preview * 1000); + return () => clearTimeout(previewRef.current); + }, [phase, level]); + + // Timer countdown + useEffect(() => { + if (phase !== "playing") return; + if (timeLeft <= 0) { setPhase("timeup"); return; } + timerRef.current = setTimeout(() => setTimeLeft((t) => t - 1), 1000); + return () => clearTimeout(timerRef.current); + }, [phase, timeLeft]); + + useEffect(() => () => clearTimers(), []); + + const handleCellClick = (i) => { + if (phase !== "playing") return; + const cell = grid[i]; + if (cell.clicked) return; + + setGrid((g) => + g.map((c, idx) => + idx === i ? { ...c, clicked: true, state: c.colored ? "correct" : "wrong" } : c + ) + ); + + if (cell.colored) { + const newRemaining = remaining - 1; + const newScore = score + 10; + setRemaining(newRemaining); + setScore(newScore); + if (newRemaining === 0) { clearTimers(); setPhase("won"); } + } else { + const newMistakes = mistakes + 1; + setMistakes(newMistakes); + if (newMistakes >= 3) { clearTimers(); setPhase("lost"); } + } + }; + + const lvl = LEVELS[level]; + const pct = lvl ? (timeLeft / lvl.time) * 100 : 0; + const CELL = 56; + const isLast = level >= LEVELS.length - 1; + + const cellStyle = (c) => { + if (phase === "preview") return c.colored ? "bg-violet-600" : "bg-gray-200 dark:bg-white/10"; + if (c.state === "correct") return "bg-emerald-500"; + if (c.state === "wrong") return "bg-red-500"; + return "bg-gray-200 dark:bg-white/10 hover:bg-violet-200 dark:hover:bg-violet-700/40 cursor-pointer"; + }; + + return ( +
+
+ + {/* Header */} +
+
+
+ +
+
+

+ Grid Memory +

+

Spatial recall challenge

+
+
+
+ {phase !== "start" && ( + + Level {level + 1} / {LEVELS.length} + + )} + +
+
+ +
+ + {/* START SCREEN */} + {phase === "start" && ( +
+
🧠
+

+ Grid Memory Challenge +

+

+ Purple cells flash briefly — memorise their positions, then click them all from memory. + 3 wrong clicks = game over. Beat all 8 levels! +

+ +
+ )} + + {/* IN-GAME */} + {phase !== "start" && ( + <> + {/* Progress dots */} +
+ {LEVELS.map((_, i) => ( +
+ ))} +
+ + {/* Stats row */} +
+ {[ + ["Mistakes", `${mistakes}/3`], + ["Remaining", remaining], + ["Score", score], + ["Time", `${timeLeft}s`], + ].map(([label, val]) => ( +
+ {label} + {val} +
+ ))} +
+ + {/* Timer bar */} + {(phase === "playing" || phase === "preview") && ( +
+
+
+ )} + + {/* Phase label */} +

+ {phase === "preview" + ? "Memorise the purple cells…" + : phase === "playing" + ? "Click all the purple cells!" + : ""} +

+ + {/* Grid */} +
+ {grid.map((c, i) => ( +
handleCellClick(i)} + className={`rounded-lg transition-all duration-150 ${cellStyle(c)} ${ + c.clicked ? "cursor-default" : "" + }`} + style={{ width: CELL, height: CELL }} + /> + ))} +
+ + )} + + {/* RESULT OVERLAY */} + {(phase === "won" || phase === "lost" || phase === "timeup") && ( +
+
+
+ {phase === "won" ? (isLast ? "🏆" : "🎉") : phase === "lost" ? "❌" : "⏰"} +
+

+ {phase === "won" + ? isLast + ? "All levels complete!" + : `Level ${level + 1} complete!` + : phase === "lost" + ? "Too many mistakes!" + : "Time's up!"} +

+

+ {phase === "won" + ? `Found all ${lvl.colored} cells with ${mistakes} mistake${mistakes !== 1 ? "s" : ""}. Score: ${score}` + : phase === "lost" + ? `You made 3 mistakes on Level ${level + 1}. Study the pattern and try again.` + : `Ran out of time on Level ${level + 1}. ${remaining} cell${remaining !== 1 ? "s" : ""} left.`} +

+
+ {phase === "won" && !isLast && ( + + )} + {phase === "won" && isLast && ( + + )} + + {phase !== "won" && ( + + )} +
+
+
+ )} +
+
+
+ ); +}; + +export default GridMemoryGame; diff --git a/frontend/src/pages/InterviewPrep/components/PracticePage.jsx b/frontend/src/pages/InterviewPrep/components/PracticePage.jsx index c21a2f9..fa1b4f0 100644 --- a/frontend/src/pages/InterviewPrep/components/PracticePage.jsx +++ b/frontend/src/pages/InterviewPrep/components/PracticePage.jsx @@ -1,4 +1,4 @@ -import React, { useState, useContext } from "react"; +import React, { useState, useEffect, useRef, useCallback, useContext } from "react"; import { useNavigate } from "react-router-dom"; import { UserContext } from "../../../context/userContext"; import AptitudeQuestionCard from "../../../components/Cards/AptitudeQuestionCard"; @@ -6,59 +6,236 @@ import Loader from "../../../components/Loader/Loader"; import axiosInstance from "../../../utils/axiosinstance"; import { API_PATHS } from "../../../utils/apiPaths"; -import { BrainCircuit, LineChart, Calculator, Dices, BookOpen, Puzzle } from "lucide-react"; +import { BrainCircuit, LineChart, Calculator, Dices, BookOpen, Puzzle, Grid3x3 } from "lucide-react"; +// ─── Grid Memory Game ────────────────────────────────────────────────────────── +const LEVELS = [ + { rows: 3, cols: 4, colored: 6, preview: 4, time: 20 }, + { rows: 3, cols: 5, colored: 9, preview: 4, time: 22 }, + { rows: 4, cols: 5, colored: 12, preview: 4, time: 25 }, + { rows: 4, cols: 6, colored: 15, preview: 3, time: 25 }, + { rows: 4, cols: 7, colored: 18, preview: 3, time: 28 }, + { rows: 5, cols: 7, colored: 22, preview: 3, time: 30 }, + { rows: 5, cols: 8, colored: 26, preview: 2, time: 30 }, + { rows: 6, cols: 8, colored: 30, preview: 2, time: 32 }, +]; + +function shuffle(arr) { + const a = [...arr]; + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [a[i], a[j]] = [a[j], a[i]]; + } + return a; +} + +function buildGrid(lvl) { + const { rows, cols, colored } = LEVELS[lvl]; + const total = rows * cols; + const positions = shuffle([...Array(total).keys()]); + const coloredSet = new Set(positions.slice(0, colored)); + return Array.from({ length: total }, (_, i) => ({ + colored: coloredSet.has(i), clicked: false, state: "idle", + })); +} + +const GridMemoryGame = () => { + const [phase, setPhase] = useState("start"); + const [level, setLevel] = useState(0); + const [grid, setGrid] = useState([]); + const [remaining, setRemaining] = useState(0); + const [mistakes, setMistakes] = useState(0); + const [score, setScore] = useState(0); + const [timeLeft, setTimeLeft] = useState(0); + const timerRef = useRef(null); + const previewRef = useRef(null); + + const clearTimers = () => { clearTimeout(timerRef.current); clearTimeout(previewRef.current); }; + + const startLevel = useCallback((lvl) => { + clearTimers(); + const newGrid = buildGrid(lvl); + setLevel(lvl); setGrid(newGrid); setMistakes(0); setScore(0); + setRemaining(LEVELS[lvl].colored); setTimeLeft(LEVELS[lvl].time); + setPhase("preview"); + }, []); + + useEffect(() => { + if (phase !== "preview") return; + previewRef.current = setTimeout(() => { + setGrid((g) => g.map((c) => ({ ...c, state: "idle" }))); + setPhase("playing"); + }, LEVELS[level].preview * 1000); + return () => clearTimeout(previewRef.current); + }, [phase, level]); + + useEffect(() => { + if (phase !== "playing") return; + if (timeLeft <= 0) { setPhase("timeup"); return; } + timerRef.current = setTimeout(() => setTimeLeft((t) => t - 1), 1000); + return () => clearTimeout(timerRef.current); + }, [phase, timeLeft]); + + useEffect(() => () => clearTimers(), []); + + const handleCellClick = (i) => { + if (phase !== "playing") return; + const cell = grid[i]; + if (cell.clicked) return; + setGrid((g) => g.map((c, idx) => idx === i ? { ...c, clicked: true, state: c.colored ? "correct" : "wrong" } : c)); + if (cell.colored) { + const nr = remaining - 1; const ns = score + 10; + setRemaining(nr); setScore(ns); + if (nr === 0) { clearTimers(); setPhase("won"); } + } else { + const nm = mistakes + 1; setMistakes(nm); + if (nm >= 3) { clearTimers(); setPhase("lost"); } + } + }; + + const lvl = LEVELS[level]; + const pct = lvl ? (timeLeft / lvl.time) * 100 : 0; + const isLast = level >= LEVELS.length - 1; + const CELL = 58; + + const cellClass = (c) => { + if (phase === "preview") return c.colored ? "bg-violet-600" : "bg-gray-200 dark:bg-white/10"; + if (c.state === "correct") return "bg-emerald-500"; + if (c.state === "wrong") return "bg-red-500"; + return "bg-gray-200 dark:bg-white/10 hover:bg-violet-300 dark:hover:bg-violet-700/50 cursor-pointer"; + }; + + // Start screen + if (phase === "start") { + return ( +
+
🧠
+

Grid Memory Challenge

+

+ Purple cells will flash briefly — memorise their positions, then click them all from memory. + 3 wrong clicks = game over. Beat all 8 levels! +

+ +
+ ); + } + + return ( +
+ {/* Level progress */} +
+ {LEVELS.map((_, i) => ( +
+ ))} +
+ + {/* Stats */} +
+ {[["Mistakes", `${mistakes}/3`], ["Remaining", remaining], ["Score", score], ["Time", `${timeLeft}s`]].map(([label, val]) => ( +
+ {label} {val} +
+ ))} +
+ Level {level + 1} / {LEVELS.length} +
+
+ + {/* Timer bar */} + {(phase === "playing" || phase === "preview") && ( +
+
+
+ )} + + {/* Phase label */} +

+ {phase === "preview" ? "Memorise the purple cells…" : phase === "playing" ? "Click all the purple cells!" : ""} +

+ + {/* Grid */} +
+ {grid.map((c, i) => ( +
handleCellClick(i)} + className={`rounded-lg transition-all duration-150 ${cellClass(c)} ${c.clicked ? "cursor-default" : ""}`} + style={{ width: CELL, height: CELL }} + /> + ))} +
+ + {/* Result panel */} + {(phase === "won" || phase === "lost" || phase === "timeup") && ( +
+
+ {phase === "won" ? (isLast ? "🏆" : "🎉") : phase === "lost" ? "❌" : "⏰"} +
+

+ {phase === "won" ? (isLast ? "All levels complete!" : `Level ${level + 1} complete!`) + : phase === "lost" ? "Too many mistakes!" : "Time's up!"} +

+

+ {phase === "won" + ? `Found all ${lvl.colored} cells with ${mistakes} mistake${mistakes !== 1 ? "s" : ""}. Score: ${score}` + : phase === "lost" + ? `You made 3 mistakes on Level ${level + 1}. Study the pattern and try again.` + : `Ran out of time on Level ${level + 1}. ${remaining} cell${remaining !== 1 ? "s" : ""} left to find.`} +

+
+ {phase === "won" && !isLast && ( + + )} + {phase === "won" && isLast && ( + + )} + + {phase !== "won" && ( + + )} +
+
+ )} +
+ ); +}; + +// ─── Topic data ──────────────────────────────────────────────────────────────── const topicsData = [ - { - name: "Logical Reasoning", - icon: BrainCircuit, - desc: "Test your analytical and logical thinking abilities.", - color: "from-blue-500 to-cyan-500", - bg: "bg-blue-50 dark:bg-blue-900/20", - text: "text-blue-600 dark:text-blue-400" - }, - { - name: "Data Interpretation", - icon: LineChart, - desc: "Analyze and interpret data from charts and graphs.", - color: "from-emerald-500 to-teal-500", - bg: "bg-emerald-50 dark:bg-emerald-900/20", - text: "text-emerald-600 dark:text-emerald-400" - }, - { - name: "Quantitative Aptitude", - icon: Calculator, - desc: "Sharpen your mathematical and numerical calculation skills.", - color: "from-violet-500 to-purple-500", - bg: "bg-violet-50 dark:bg-violet-900/20", - text: "text-violet-600 dark:text-violet-400" - }, - { - name: "Probability", - icon: Dices, - desc: "Master the concepts of chance, odds, and likelihood.", - color: "from-rose-500 to-pink-500", - bg: "bg-rose-50 dark:bg-rose-900/20", - text: "text-rose-600 dark:text-rose-400" - }, - { - name: "Verbal ability", - icon: BookOpen, - desc: "Improve your grammar, vocabulary, and comprehension.", - color: "from-amber-500 to-orange-500", - bg: "bg-amber-50 dark:bg-amber-900/20", - text: "text-amber-600 dark:text-amber-400" - }, - { - name: "Puzzles", - icon: Puzzle, - desc: "Solve complex brain teasers and lateral thinking puzzles.", - color: "from-indigo-500 to-blue-500", - bg: "bg-indigo-50 dark:bg-indigo-900/20", - text: "text-indigo-600 dark:text-indigo-400" - }, + { name: "Logical Reasoning", icon: BrainCircuit, desc: "Test your analytical and logical thinking abilities." }, + { name: "Data Interpretation", icon: LineChart, desc: "Analyze and interpret data from charts and graphs." }, + { name: "Quantitative Aptitude", icon: Calculator, desc: "Sharpen your mathematical and numerical calculation skills." }, + { name: "Probability", icon: Dices, desc: "Master the concepts of chance, odds, and likelihood." }, + { name: "Verbal ability", icon: BookOpen, desc: "Improve your grammar, vocabulary, and comprehension." }, + { name: "Puzzles", icon: Puzzle, desc: "Solve complex brain teasers and lateral thinking puzzles." }, + { name: "Grid Memory", icon: Grid3x3, desc: "Train spatial recall by memorising and clicking coloured cell patterns.", isGame: true }, ]; +// ─── PracticePage ────────────────────────────────────────────────────────────── const PracticePage = () => { const [selectedTopic, setSelectedTopic] = useState(null); const [questions, setQuestions] = useState([]); @@ -67,137 +244,119 @@ const PracticePage = () => { const navigate = useNavigate(); const handleTopicClick = async (topic) => { - if (!user) { - navigate("/login"); - return; - } - if (topic === "Career Specific Roadmap") { - navigate("/dashboard"); - return; - } + if (!user) { navigate("/login"); return; } + if (topic.isGame) { setSelectedTopic(topic); return; } setSelectedTopic(topic); setLoading(true); setQuestions([]); - try { - const res = await axiosInstance.get( - `${API_PATHS.APTITUDE.GENERATE}?topic=${topic}` - ); + const res = await axiosInstance.get(`${API_PATHS.APTITUDE.GENERATE}?topic=${topic.name}`); setQuestions(res.data); } catch (error) { console.error("Error fetching questions:", error); alert("Failed to generate questions."); } - setLoading(false); }; + const handleBack = () => { setSelectedTopic(null); setQuestions([]); }; + return ( - <> -
-
-
-

- Practice Cognitive Skills +
+
+ + {/* Hero — hidden when a topic is selected */} +
+

+ Practice Cognitive Skills +

+

+ Sharpen your logical reasoning, quantitative, and verbal skills with curated aptitude tests and exercises. +

+
+ + {/* Topics grid */} + {!selectedTopic && ( +
+ {topicsData.map((topic) => { + const Icon = topic.icon; + return ( + + ); + })} +
+ )} + + {/* Active topic header — same for all topics including Grid Memory */} + {selectedTopic && ( +
+

+ Practicing: {selectedTopic.name}

-

- Sharpen your logical reasoning, quantitative, and verbal skills - with curated aptitude tests and exercises. -

+
+ )} - {/* Topics Grid */} - {!selectedTopic && ( -
- {topicsData.map((topic) => { - const Icon = topic.icon; - return ( + {/* Grid Memory game — inline, no modal */} + {selectedTopic?.isGame && } + + {/* Quiz loading & questions */} + {!selectedTopic?.isGame && ( + <> + {loading && } + {questions.length > 0 && ( + <> +
+ {questions.map((q, idx) => ( + + ))} +
+
- ); - })} -
- )} - - {/* Active Quiz Header */} - {selectedTopic && ( -
-

- Practicing: {selectedTopic} -

- -
- )} - - {/* Loading */} - {loading && } - - {/* Questions */} - {questions.length > 0 && ( - <> -
- {questions.map((q, idx) => ( - - ))} -
- - {/* Load More */} -
- -
- - )} -
-
- +

+ + )} + + )} +
+
); }; From cd26c8ef17256102c8825415381811a682e9f17f Mon Sep 17 00:00:00 2001 From: Prisha Jain Date: Sun, 5 Jul 2026 00:01:02 +0530 Subject: [PATCH 2/4] Revert unintended package-lock.json changes --- backend/package-lock.json | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/backend/package-lock.json b/backend/package-lock.json index fe102fa..8e49c6d 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1658,6 +1658,67 @@ "url": "https://opencollective.com/mongoose" } }, + "node_modules/mongoose/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mongoose/node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongoose/node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongoose/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/mongoose/node_modules/mongodb": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", From 62966137ddfa8295a55a4232195361225ddafd3d Mon Sep 17 00:00:00 2001 From: Prisha Jain Date: Sun, 5 Jul 2026 00:30:12 +0530 Subject: [PATCH 3/4] Added Cognitive Skills Section with Cognitive games inside it include the grid memory game --- frontend/src/App.jsx | 11 + frontend/src/components/GridMemoryGame.jsx | 355 +++++++----------- frontend/src/components/Layouts/Sidebar.jsx | 16 +- .../CognitiveGames/CognitiveGamesPage.jsx | 98 +++++ .../InterviewPrep/components/PracticePage.jsx | 283 ++------------ 5 files changed, 279 insertions(+), 484 deletions(-) create mode 100644 frontend/src/pages/CognitiveGames/CognitiveGamesPage.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1896b3c..4aeedea 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -20,6 +20,7 @@ import ProgressTrackerDashboard from "./pages/Home/ProgressTrackerDashboard"; import InterviewPrep from "./pages/InterviewPrep/InterviewPrep"; import AIHelper from "./components/AIHepler"; import PracticePage from "./pages/InterviewPrep/components/PracticePage"; +import CognitiveGamesPage from "./pages/CognitiveGames/CognitiveGamesPage"; import { useContext } from "react"; import { UserContext } from "./context/userContext"; import MainLayout from "./components/Layouts/MainLayout"; @@ -172,6 +173,16 @@ const App = () => { } /> + + + + + + } + /> ({ - colored: coloredSet.has(i), - clicked: false, - state: "idle", // idle | correct | wrong + colored: coloredSet.has(i), clicked: false, state: "idle", })); } -// ─── Component ───────────────────────────────────────────────────────────────── -const GridMemoryGame = ({ onClose }) => { - const [phase, setPhase] = useState("start"); // start | preview | playing | won | lost | timeup +// ─── Component (inline, used within a page — not a modal) ────────────────────── +const GridMemoryGame = () => { + const [phase, setPhase] = useState("start"); const [level, setLevel] = useState(0); const [grid, setGrid] = useState([]); const [remaining, setRemaining] = useState(0); const [mistakes, setMistakes] = useState(0); const [score, setScore] = useState(0); const [timeLeft, setTimeLeft] = useState(0); - const timerRef = useRef(null); const previewRef = useRef(null); - const clearTimers = () => { - clearTimeout(timerRef.current); - clearTimeout(previewRef.current); - }; + const clearTimers = () => { clearTimeout(timerRef.current); clearTimeout(previewRef.current); }; const startLevel = useCallback((lvl) => { clearTimers(); const newGrid = buildGrid(lvl); - setLevel(lvl); - setGrid(newGrid); - setMistakes(0); - setScore(0); - setRemaining(LEVELS[lvl].colored); - setTimeLeft(LEVELS[lvl].time); + setLevel(lvl); setGrid(newGrid); setMistakes(0); setScore(0); + setRemaining(LEVELS[lvl].colored); setTimeLeft(LEVELS[lvl].time); setPhase("preview"); }, []); - // Preview → playing useEffect(() => { if (phase !== "preview") return; previewRef.current = setTimeout(() => { @@ -74,7 +62,6 @@ const GridMemoryGame = ({ onClose }) => { return () => clearTimeout(previewRef.current); }, [phase, level]); - // Timer countdown useEffect(() => { if (phase !== "playing") return; if (timeLeft <= 0) { setPhase("timeup"); return; } @@ -88,237 +75,147 @@ const GridMemoryGame = ({ onClose }) => { if (phase !== "playing") return; const cell = grid[i]; if (cell.clicked) return; - - setGrid((g) => - g.map((c, idx) => - idx === i ? { ...c, clicked: true, state: c.colored ? "correct" : "wrong" } : c - ) - ); - + setGrid((g) => g.map((c, idx) => idx === i ? { ...c, clicked: true, state: c.colored ? "correct" : "wrong" } : c)); if (cell.colored) { - const newRemaining = remaining - 1; - const newScore = score + 10; - setRemaining(newRemaining); - setScore(newScore); - if (newRemaining === 0) { clearTimers(); setPhase("won"); } + const nr = remaining - 1; const ns = score + 10; + setRemaining(nr); setScore(ns); + if (nr === 0) { clearTimers(); setPhase("won"); } } else { - const newMistakes = mistakes + 1; - setMistakes(newMistakes); - if (newMistakes >= 3) { clearTimers(); setPhase("lost"); } + const nm = mistakes + 1; setMistakes(nm); + if (nm >= 3) { clearTimers(); setPhase("lost"); } } }; const lvl = LEVELS[level]; const pct = lvl ? (timeLeft / lvl.time) * 100 : 0; - const CELL = 56; const isLast = level >= LEVELS.length - 1; + const CELL = 58; - const cellStyle = (c) => { + const cellClass = (c) => { if (phase === "preview") return c.colored ? "bg-violet-600" : "bg-gray-200 dark:bg-white/10"; if (c.state === "correct") return "bg-emerald-500"; if (c.state === "wrong") return "bg-red-500"; - return "bg-gray-200 dark:bg-white/10 hover:bg-violet-200 dark:hover:bg-violet-700/40 cursor-pointer"; + return "bg-gray-200 dark:bg-white/10 hover:bg-violet-300 dark:hover:bg-violet-700/50 cursor-pointer"; }; + // Start screen + if (phase === "start") { + return ( +
+
🧠
+

Grid Memory Challenge

+

+ Purple cells will flash briefly — memorise their positions, then click them all from memory. + 3 wrong clicks = game over. Beat all 8 levels! +

+ +
+ ); + } + return ( -
-
+
+ {/* Level progress */} +
+ {LEVELS.map((_, i) => ( +
+ ))} +
- {/* Header */} -
-
-
- -
-
-

- Grid Memory -

-

Spatial recall challenge

-
-
-
- {phase !== "start" && ( - - Level {level + 1} / {LEVELS.length} - - )} - + {/* Stats */} +
+ {[["Mistakes", `${mistakes}/3`], ["Remaining", remaining], ["Score", score], ["Time", `${timeLeft}s`]].map(([label, val]) => ( +
+ {label} {val}
+ ))} +
+ Level {level + 1} / {LEVELS.length}
+
-
+ {/* Timer bar */} + {(phase === "playing" || phase === "preview") && ( +
+
+
+ )} + + {/* Phase label */} +

+ {phase === "preview" ? "Memorise the purple cells…" : phase === "playing" ? "Click all the purple cells!" : ""} +

+ + {/* Grid */} +
+ {grid.map((c, i) => ( +
handleCellClick(i)} + className={`rounded-lg transition-all duration-150 ${cellClass(c)} ${c.clicked ? "cursor-default" : ""}`} + style={{ width: CELL, height: CELL }} + /> + ))} +
- {/* START SCREEN */} - {phase === "start" && ( -
-
🧠
-

- Grid Memory Challenge -

-

- Purple cells flash briefly — memorise their positions, then click them all from memory. - 3 wrong clicks = game over. Beat all 8 levels! -

- -
- )} - - {/* IN-GAME */} - {phase !== "start" && ( - <> - {/* Progress dots */} -
- {LEVELS.map((_, i) => ( -
- ))} -
- - {/* Stats row */} -
- {[ - ["Mistakes", `${mistakes}/3`], - ["Remaining", remaining], - ["Score", score], - ["Time", `${timeLeft}s`], - ].map(([label, val]) => ( -
- {label} - {val} -
- ))} -
- - {/* Timer bar */} - {(phase === "playing" || phase === "preview") && ( -
-
-
- )} - - {/* Phase label */} -

- {phase === "preview" - ? "Memorise the purple cells…" - : phase === "playing" - ? "Click all the purple cells!" - : ""} -

- - {/* Grid */} -
- {grid.map((c, i) => ( -
handleCellClick(i)} - className={`rounded-lg transition-all duration-150 ${cellStyle(c)} ${ - c.clicked ? "cursor-default" : "" - }`} - style={{ width: CELL, height: CELL }} - /> - ))} -
- - )} - - {/* RESULT OVERLAY */} - {(phase === "won" || phase === "lost" || phase === "timeup") && ( -
-
-
- {phase === "won" ? (isLast ? "🏆" : "🎉") : phase === "lost" ? "❌" : "⏰"} -
-

- {phase === "won" - ? isLast - ? "All levels complete!" - : `Level ${level + 1} complete!` - : phase === "lost" - ? "Too many mistakes!" - : "Time's up!"} -

-

- {phase === "won" - ? `Found all ${lvl.colored} cells with ${mistakes} mistake${mistakes !== 1 ? "s" : ""}. Score: ${score}` - : phase === "lost" - ? `You made 3 mistakes on Level ${level + 1}. Study the pattern and try again.` - : `Ran out of time on Level ${level + 1}. ${remaining} cell${remaining !== 1 ? "s" : ""} left.`} -

-
- {phase === "won" && !isLast && ( - - )} - {phase === "won" && isLast && ( - - )} - - {phase !== "won" && ( - - )} -
-
-
- )} + )} + {phase === "won" && isLast && ( + + )} + + {phase !== "won" && ( + + )} +
-
+ )}
); }; diff --git a/frontend/src/components/Layouts/Sidebar.jsx b/frontend/src/components/Layouts/Sidebar.jsx index e61fde0..9236b06 100644 --- a/frontend/src/components/Layouts/Sidebar.jsx +++ b/frontend/src/components/Layouts/Sidebar.jsx @@ -26,6 +26,7 @@ import { BookMarked, CalendarDays, ScrollText, + Grid3x3, } from "lucide-react"; const Sidebar = () => { @@ -61,6 +62,19 @@ const Sidebar = () => { }, ], }, + { + id: "cognitive-skills", + title: "Cognitive Skills", + isHeader: true, + items: [ + { + id: "cognitive-games", + title: "Cognitive Games", + path: "/cognitive-games", + icon: Grid3x3, + }, + ], + }, { id: "dsa", title: "DSA", @@ -185,7 +199,7 @@ const Sidebar = () => { ]; const handleServiceClick = (item) => { - if (item.title === "Cognitive Builder" && !user) { + if ((item.title === "Cognitive Builder" || item.title === "Cognitive Games") && !user) { setShowLoginModal(true); } else { navigate(item.path); diff --git a/frontend/src/pages/CognitiveGames/CognitiveGamesPage.jsx b/frontend/src/pages/CognitiveGames/CognitiveGamesPage.jsx new file mode 100644 index 0000000..fcc9dc5 --- /dev/null +++ b/frontend/src/pages/CognitiveGames/CognitiveGamesPage.jsx @@ -0,0 +1,98 @@ +import React, { useState, useContext } from "react"; +import { useNavigate } from "react-router-dom"; +import { UserContext } from "../../context/userContext"; +import GridMemoryGame from "../../components/GridMemoryGame"; +import { Grid3x3 } from "lucide-react"; + +// ─── Games data ──────────────────────────────────────────────────────────────── +// Add more brain-training games here in the future — each just needs a +// name/icon/desc and a matching component rendered below. +const gamesData = [ + { + name: "Grid Memory", + icon: Grid3x3, + desc: "Train spatial recall by memorising and clicking coloured cell patterns.", + component: GridMemoryGame, + }, +]; + +// ─── CognitiveGamesPage ──────────────────────────────────────────────────────── +const CognitiveGamesPage = () => { + const [selectedGame, setSelectedGame] = useState(null); + const { user } = useContext(UserContext); + const navigate = useNavigate(); + + const handleGameClick = (game) => { + if (!user) { navigate("/login"); return; } + setSelectedGame(game); + }; + + const handleBack = () => setSelectedGame(null); + + const ActiveGame = selectedGame?.component; + + return ( +
+
+ + {/* Hero — hidden when a game is selected */} +
+

+ Cognitive Games +

+

+ Sharpen your memory, focus, and spatial recall with quick, playful brain-training games. +

+
+ + {/* Games grid */} + {!selectedGame && ( +
+ {gamesData.map((game) => { + const Icon = game.icon; + return ( + + ); + })} +
+ )} + + {/* Active game header */} + {selectedGame && ( +
+

+ Playing: {selectedGame.name} +

+ +
+ )} + + {/* Active game — inline, no modal */} + {selectedGame && ActiveGame && } +
+
+ ); +}; + +export default CognitiveGamesPage; diff --git a/frontend/src/pages/InterviewPrep/components/PracticePage.jsx b/frontend/src/pages/InterviewPrep/components/PracticePage.jsx index fa1b4f0..c1616d7 100644 --- a/frontend/src/pages/InterviewPrep/components/PracticePage.jsx +++ b/frontend/src/pages/InterviewPrep/components/PracticePage.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef, useCallback, useContext } from "react"; +import React, { useState, useContext } from "react"; import { useNavigate } from "react-router-dom"; import { UserContext } from "../../../context/userContext"; import AptitudeQuestionCard from "../../../components/Cards/AptitudeQuestionCard"; @@ -6,223 +6,7 @@ import Loader from "../../../components/Loader/Loader"; import axiosInstance from "../../../utils/axiosinstance"; import { API_PATHS } from "../../../utils/apiPaths"; -import { BrainCircuit, LineChart, Calculator, Dices, BookOpen, Puzzle, Grid3x3 } from "lucide-react"; - -// ─── Grid Memory Game ────────────────────────────────────────────────────────── -const LEVELS = [ - { rows: 3, cols: 4, colored: 6, preview: 4, time: 20 }, - { rows: 3, cols: 5, colored: 9, preview: 4, time: 22 }, - { rows: 4, cols: 5, colored: 12, preview: 4, time: 25 }, - { rows: 4, cols: 6, colored: 15, preview: 3, time: 25 }, - { rows: 4, cols: 7, colored: 18, preview: 3, time: 28 }, - { rows: 5, cols: 7, colored: 22, preview: 3, time: 30 }, - { rows: 5, cols: 8, colored: 26, preview: 2, time: 30 }, - { rows: 6, cols: 8, colored: 30, preview: 2, time: 32 }, -]; - -function shuffle(arr) { - const a = [...arr]; - for (let i = a.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [a[i], a[j]] = [a[j], a[i]]; - } - return a; -} - -function buildGrid(lvl) { - const { rows, cols, colored } = LEVELS[lvl]; - const total = rows * cols; - const positions = shuffle([...Array(total).keys()]); - const coloredSet = new Set(positions.slice(0, colored)); - return Array.from({ length: total }, (_, i) => ({ - colored: coloredSet.has(i), clicked: false, state: "idle", - })); -} - -const GridMemoryGame = () => { - const [phase, setPhase] = useState("start"); - const [level, setLevel] = useState(0); - const [grid, setGrid] = useState([]); - const [remaining, setRemaining] = useState(0); - const [mistakes, setMistakes] = useState(0); - const [score, setScore] = useState(0); - const [timeLeft, setTimeLeft] = useState(0); - const timerRef = useRef(null); - const previewRef = useRef(null); - - const clearTimers = () => { clearTimeout(timerRef.current); clearTimeout(previewRef.current); }; - - const startLevel = useCallback((lvl) => { - clearTimers(); - const newGrid = buildGrid(lvl); - setLevel(lvl); setGrid(newGrid); setMistakes(0); setScore(0); - setRemaining(LEVELS[lvl].colored); setTimeLeft(LEVELS[lvl].time); - setPhase("preview"); - }, []); - - useEffect(() => { - if (phase !== "preview") return; - previewRef.current = setTimeout(() => { - setGrid((g) => g.map((c) => ({ ...c, state: "idle" }))); - setPhase("playing"); - }, LEVELS[level].preview * 1000); - return () => clearTimeout(previewRef.current); - }, [phase, level]); - - useEffect(() => { - if (phase !== "playing") return; - if (timeLeft <= 0) { setPhase("timeup"); return; } - timerRef.current = setTimeout(() => setTimeLeft((t) => t - 1), 1000); - return () => clearTimeout(timerRef.current); - }, [phase, timeLeft]); - - useEffect(() => () => clearTimers(), []); - - const handleCellClick = (i) => { - if (phase !== "playing") return; - const cell = grid[i]; - if (cell.clicked) return; - setGrid((g) => g.map((c, idx) => idx === i ? { ...c, clicked: true, state: c.colored ? "correct" : "wrong" } : c)); - if (cell.colored) { - const nr = remaining - 1; const ns = score + 10; - setRemaining(nr); setScore(ns); - if (nr === 0) { clearTimers(); setPhase("won"); } - } else { - const nm = mistakes + 1; setMistakes(nm); - if (nm >= 3) { clearTimers(); setPhase("lost"); } - } - }; - - const lvl = LEVELS[level]; - const pct = lvl ? (timeLeft / lvl.time) * 100 : 0; - const isLast = level >= LEVELS.length - 1; - const CELL = 58; - - const cellClass = (c) => { - if (phase === "preview") return c.colored ? "bg-violet-600" : "bg-gray-200 dark:bg-white/10"; - if (c.state === "correct") return "bg-emerald-500"; - if (c.state === "wrong") return "bg-red-500"; - return "bg-gray-200 dark:bg-white/10 hover:bg-violet-300 dark:hover:bg-violet-700/50 cursor-pointer"; - }; - - // Start screen - if (phase === "start") { - return ( -
-
🧠
-

Grid Memory Challenge

-

- Purple cells will flash briefly — memorise their positions, then click them all from memory. - 3 wrong clicks = game over. Beat all 8 levels! -

- -
- ); - } - - return ( -
- {/* Level progress */} -
- {LEVELS.map((_, i) => ( -
- ))} -
- - {/* Stats */} -
- {[["Mistakes", `${mistakes}/3`], ["Remaining", remaining], ["Score", score], ["Time", `${timeLeft}s`]].map(([label, val]) => ( -
- {label} {val} -
- ))} -
- Level {level + 1} / {LEVELS.length} -
-
- - {/* Timer bar */} - {(phase === "playing" || phase === "preview") && ( -
-
-
- )} - - {/* Phase label */} -

- {phase === "preview" ? "Memorise the purple cells…" : phase === "playing" ? "Click all the purple cells!" : ""} -

- - {/* Grid */} -
- {grid.map((c, i) => ( -
handleCellClick(i)} - className={`rounded-lg transition-all duration-150 ${cellClass(c)} ${c.clicked ? "cursor-default" : ""}`} - style={{ width: CELL, height: CELL }} - /> - ))} -
- - {/* Result panel */} - {(phase === "won" || phase === "lost" || phase === "timeup") && ( -
-
- {phase === "won" ? (isLast ? "🏆" : "🎉") : phase === "lost" ? "❌" : "⏰"} -
-

- {phase === "won" ? (isLast ? "All levels complete!" : `Level ${level + 1} complete!`) - : phase === "lost" ? "Too many mistakes!" : "Time's up!"} -

-

- {phase === "won" - ? `Found all ${lvl.colored} cells with ${mistakes} mistake${mistakes !== 1 ? "s" : ""}. Score: ${score}` - : phase === "lost" - ? `You made 3 mistakes on Level ${level + 1}. Study the pattern and try again.` - : `Ran out of time on Level ${level + 1}. ${remaining} cell${remaining !== 1 ? "s" : ""} left to find.`} -

-
- {phase === "won" && !isLast && ( - - )} - {phase === "won" && isLast && ( - - )} - - {phase !== "won" && ( - - )} -
-
- )} -
- ); -}; +import { BrainCircuit, LineChart, Calculator, Dices, BookOpen, Puzzle } from "lucide-react"; // ─── Topic data ──────────────────────────────────────────────────────────────── const topicsData = [ @@ -232,7 +16,6 @@ const topicsData = [ { name: "Probability", icon: Dices, desc: "Master the concepts of chance, odds, and likelihood." }, { name: "Verbal ability", icon: BookOpen, desc: "Improve your grammar, vocabulary, and comprehension." }, { name: "Puzzles", icon: Puzzle, desc: "Solve complex brain teasers and lateral thinking puzzles." }, - { name: "Grid Memory", icon: Grid3x3, desc: "Train spatial recall by memorising and clicking coloured cell patterns.", isGame: true }, ]; // ─── PracticePage ────────────────────────────────────────────────────────────── @@ -245,7 +28,6 @@ const PracticePage = () => { const handleTopicClick = async (topic) => { if (!user) { navigate("/login"); return; } - if (topic.isGame) { setSelectedTopic(topic); return; } setSelectedTopic(topic); setLoading(true); setQuestions([]); @@ -303,7 +85,7 @@ const PracticePage = () => {
)} - {/* Active topic header — same for all topics including Grid Memory */} + {/* Active topic header */} {selectedTopic && (

@@ -318,41 +100,34 @@ const PracticePage = () => {

)} - {/* Grid Memory game — inline, no modal */} - {selectedTopic?.isGame && } - {/* Quiz loading & questions */} - {!selectedTopic?.isGame && ( + {loading && } + {questions.length > 0 && ( <> - {loading && } - {questions.length > 0 && ( - <> -
- {questions.map((q, idx) => ( - - ))} -
-
- -
- - )} +
+ {questions.map((q, idx) => ( + + ))} +
+
+ +
)} From e150ebae2041b161ca9131a814f639d67163eda6 Mon Sep 17 00:00:00 2001 From: Prisha Jain Date: Sun, 5 Jul 2026 00:33:50 +0530 Subject: [PATCH 4/4] Revert unintended package-lock.json changes --- frontend/package-lock.json | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c368a53..79ce58d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -27,8 +27,8 @@ }, "devDependencies": { "@eslint/js": "^9.33.0", - "@types/react": "^18.3.1", - "@types/react-dom": "^18.3.1", + "@types/react": "^19.1.10", + "@types/react-dom": "^19.1.7", "@vitejs/plugin-react": "^5.0.0", "eslint": "^9.33.0", "eslint-plugin-react-hooks": "^5.2.0", @@ -1752,30 +1752,23 @@ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "license": "MIT" - }, "node_modules/@types/react": { - "version": "18.3.31", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", - "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", + "version": "19.1.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", + "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", "license": "MIT", "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.2.2" + "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", + "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^18.0.0" + "@types/react": "^19.0.0" } }, "node_modules/@types/trusted-types": { @@ -2178,9 +2171,9 @@ } }, "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, "node_modules/debug": {