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 = () => { } /> + + + + + + } + /> 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", + })); +} + +// ─── 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 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 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 to find.`} +

+
+ {phase === "won" && !isLast && ( + + )} + {phase === "won" && isLast && ( + + )} + + {phase !== "won" && ( + + )} +
+
+ )} +
+ ); +}; + +export default GridMemoryGame; 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 c21a2f9..c1616d7 100644 --- a/frontend/src/pages/InterviewPrep/components/PracticePage.jsx +++ b/frontend/src/pages/InterviewPrep/components/PracticePage.jsx @@ -8,57 +8,17 @@ import axiosInstance from "../../../utils/axiosinstance"; import { API_PATHS } from "../../../utils/apiPaths"; import { BrainCircuit, LineChart, Calculator, Dices, BookOpen, Puzzle } from "lucide-react"; +// ─── 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." }, ]; +// ─── PracticePage ────────────────────────────────────────────────────────────── const PracticePage = () => { const [selectedTopic, setSelectedTopic] = useState(null); const [questions, setQuestions] = useState([]); @@ -67,137 +27,111 @@ 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; } 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 */} + {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 ( - - ); - })} + {/* Quiz loading & questions */} + {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 */} -
- -
- - )} -
-
- + + )} +

+
); };