From 92ea5a1744d8943dba3566187dc45b181ccba11c Mon Sep 17 00:00:00 2001 From: Jerome Hardaway Date: Tue, 4 Feb 2025 19:39:12 -0500 Subject: [PATCH 1/2] Add Who's branch is it anyway game page Add a new page for the "Who's branch is it anyway" game. * **New Page and Components** - Add `src/pages/whos-branch.tsx` to create a new page component for the game. - Add `src/components/whos-branch/index.tsx` to implement the game logic and UI. - Add `src/components/whos-branch/question.tsx` to display game questions. - Add `src/components/whos-branch/score.tsx` to display the game score. * **Game Data** - Add `src/data/whos-branch.json` to store game data, including branches and questions. * **Navigation and Menu** - Modify `src/pages/index.tsx` to add a link to the new game page. - Modify `src/data/menu.ts` to add a menu item for the new game page. * **Testing** - Add `__tests__/pages/whos-branch.tests.tsx` to create tests for the game page, game logic, and UI. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/Vets-Who-Code/vets-who-code-app?shareId=XXXX-XXXX-XXXX-XXXX). --- __tests__/pages/whos-branch.tests.tsx | 49 +++++++++++++++ src/components/whos-branch/index.tsx | 38 ++++++++++++ src/components/whos-branch/question.tsx | 30 ++++++++++ src/components/whos-branch/score.tsx | 19 ++++++ src/data/menu.ts | 14 ++++- src/data/whos-branch.json | 79 +++++++++++++++++++++++++ src/pages/index.tsx | 6 ++ src/pages/whos-branch.tsx | 14 +++++ 8 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 __tests__/pages/whos-branch.tests.tsx create mode 100644 src/components/whos-branch/index.tsx create mode 100644 src/components/whos-branch/question.tsx create mode 100644 src/components/whos-branch/score.tsx create mode 100644 src/data/whos-branch.json create mode 100644 src/pages/whos-branch.tsx diff --git a/__tests__/pages/whos-branch.tests.tsx b/__tests__/pages/whos-branch.tests.tsx new file mode 100644 index 00000000..789bfaf6 --- /dev/null +++ b/__tests__/pages/whos-branch.tests.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import { render, screen, fireEvent } from "@testing-library/react"; +import "@testing-library/jest-dom/extend-expect"; +import WhosBranchPage from "@pages/whos-branch"; +import Game from "@components/whos-branch"; +import questions from "@data/whos-branch.json"; + +describe("WhosBranchPage", () => { + test("renders the breadcrumb", () => { + render(); + expect(screen.getByText("Home")).toBeInTheDocument(); + expect(screen.getByText("Who's Branch")).toBeInTheDocument(); + }); + + test("renders the game component", () => { + render(); + expect(screen.getByText(questions[0].question)).toBeInTheDocument(); + }); +}); + +describe("Game Component", () => { + test("renders the first question", () => { + render(); + expect(screen.getByText(questions[0].question)).toBeInTheDocument(); + }); + + test("handles correct answer", () => { + render(); + const correctOption = screen.getByText(questions[0].options[0]); + fireEvent.click(correctOption); + expect(screen.getByText(questions[1].question)).toBeInTheDocument(); + }); + + test("handles incorrect answer", () => { + render(); + const incorrectOption = screen.getByText(questions[0].options[1]); + fireEvent.click(incorrectOption); + expect(screen.getByText(questions[1].question)).toBeInTheDocument(); + }); + + test("displays score at the end", () => { + render(); + questions.forEach((question) => { + const option = screen.getByText(question.options[0]); + fireEvent.click(option); + }); + expect(screen.getByText("Your Score")).toBeInTheDocument(); + }); +}); diff --git a/src/components/whos-branch/index.tsx b/src/components/whos-branch/index.tsx new file mode 100644 index 00000000..223f09c3 --- /dev/null +++ b/src/components/whos-branch/index.tsx @@ -0,0 +1,38 @@ +import React, { useState } from "react"; +import questions from "@data/whos-branch.json"; +import Question from "./question"; +import Score from "./score"; + +const Game = () => { + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [score, setScore] = useState(0); + const [showScore, setShowScore] = useState(false); + + const handleAnswerOptionClick = (isCorrect: boolean) => { + if (isCorrect) { + setScore(score + 1); + } + + const nextQuestion = currentQuestionIndex + 1; + if (nextQuestion < questions.length) { + setCurrentQuestionIndex(nextQuestion); + } else { + setShowScore(true); + } + }; + + return ( +
+ {showScore ? ( + + ) : ( + + )} +
+ ); +}; + +export default Game; diff --git a/src/components/whos-branch/question.tsx b/src/components/whos-branch/question.tsx new file mode 100644 index 00000000..258e69bf --- /dev/null +++ b/src/components/whos-branch/question.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +interface QuestionProps { + question: { + question: string; + options: string[]; + answer: string; + }; + onAnswerOptionClick: (isCorrect: boolean) => void; +} + +const Question: React.FC = ({ question, onAnswerOptionClick }) => { + return ( +
+

{question.question}

+
+ {question.options.map((option, index) => ( + + ))} +
+
+ ); +}; + +export default Question; diff --git a/src/components/whos-branch/score.tsx b/src/components/whos-branch/score.tsx new file mode 100644 index 00000000..8e0dc0cb --- /dev/null +++ b/src/components/whos-branch/score.tsx @@ -0,0 +1,19 @@ +import React from "react"; + +interface ScoreProps { + score: number; + totalQuestions: number; +} + +const Score: React.FC = ({ score, totalQuestions }) => { + return ( +
+

Your Score

+

+ {score} out of {totalQuestions} +

+
+ ); +}; + +export default Score; diff --git a/src/data/menu.ts b/src/data/menu.ts index 8f2a5cdc..02df1314 100644 --- a/src/data/menu.ts +++ b/src/data/menu.ts @@ -1,4 +1,3 @@ -// Types for menu structure type MenuStatus = "hot" | "coming soon" | "new"; interface MenuItem { @@ -118,6 +117,19 @@ const navigation: NavigationItem[] = [ }, ], }, + { + id: 7, + label: "Games", + path: "#!", + submenu: [ + { + id: 71, + label: "Who's branch is it anyway", + path: "/whos-branch", + status: "new", + }, + ], + }, ]; export default navigation; diff --git a/src/data/whos-branch.json b/src/data/whos-branch.json new file mode 100644 index 00000000..685b3f53 --- /dev/null +++ b/src/data/whos-branch.json @@ -0,0 +1,79 @@ +{ + "branches": [ + { + "name": "Army", + "questions": [ + { + "question": "What is the primary color of the Army uniform?", + "options": ["Green", "Blue", "White", "Black"], + "answer": "Green" + }, + { + "question": "What is the motto of the Army?", + "options": ["Semper Fi", "This We'll Defend", "Aim High", "Always Ready"], + "answer": "This We'll Defend" + } + ] + }, + { + "name": "Navy", + "questions": [ + { + "question": "What is the primary color of the Navy uniform?", + "options": ["Green", "Blue", "White", "Black"], + "answer": "Blue" + }, + { + "question": "What is the motto of the Navy?", + "options": ["Semper Fi", "This We'll Defend", "Aim High", "Semper Fortis"], + "answer": "Semper Fortis" + } + ] + }, + { + "name": "Air Force", + "questions": [ + { + "question": "What is the primary color of the Air Force uniform?", + "options": ["Green", "Blue", "White", "Black"], + "answer": "Blue" + }, + { + "question": "What is the motto of the Air Force?", + "options": ["Semper Fi", "This We'll Defend", "Aim High", "Always Ready"], + "answer": "Aim High" + } + ] + }, + { + "name": "Marines", + "questions": [ + { + "question": "What is the primary color of the Marines uniform?", + "options": ["Green", "Blue", "White", "Black"], + "answer": "Blue" + }, + { + "question": "What is the motto of the Marines?", + "options": ["Semper Fi", "This We'll Defend", "Aim High", "Always Ready"], + "answer": "Semper Fi" + } + ] + }, + { + "name": "Coast Guard", + "questions": [ + { + "question": "What is the primary color of the Coast Guard uniform?", + "options": ["Green", "Blue", "White", "Black"], + "answer": "Blue" + }, + { + "question": "What is the motto of the Coast Guard?", + "options": ["Semper Fi", "This We'll Defend", "Aim High", "Always Ready"], + "answer": "Always Ready" + } + ] + } + ] +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index d9d66bd0..e358d2af 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,5 +1,6 @@ import type { NextPage } from "next"; import { GetStaticProps } from "next"; +import Link from "next/link"; import SEO from "@components/seo/page-seo"; import Layout from "@layout/layout-03"; import Wrapper from "@ui/wrapper/wrapper-02"; @@ -68,6 +69,11 @@ const Home: PageProps = ({ data }) => { + ); }; diff --git a/src/pages/whos-branch.tsx b/src/pages/whos-branch.tsx new file mode 100644 index 00000000..6787e381 --- /dev/null +++ b/src/pages/whos-branch.tsx @@ -0,0 +1,14 @@ +import React, { useState } from "react"; +import Breadcrumb from "@components/breadcrumb"; +import Game from "@components/whos-branch"; + +const WhosBranchPage = () => { + return ( + <> + + + + ); +}; + +export default WhosBranchPage; From 026050533feb2fe428f932f844851d5c2a300f9b Mon Sep 17 00:00:00 2001 From: Jerome Hardaway Date: Tue, 4 Feb 2025 21:37:39 -0500 Subject: [PATCH 2/2] Remove Who's Branch Is It Anyway game components and related data --- src/components/whos-branch/index.tsx | 38 ---- src/components/whos-branch/question.tsx | 30 --- src/components/whos-branch/score.tsx | 19 -- .../whos-branch.json | 7 +- src/lib/whos-branch.ts | 14 ++ src/pages/index.tsx | 6 +- src/pages/subjects/[slug].tsx | 4 +- src/pages/whos-branch.tsx | 14 -- src/pages/whos-branch/[branch].tsx | 181 ++++++++++++++++++ src/pages/whos-branch/index.tsx | 76 ++++++++ 10 files changed, 281 insertions(+), 108 deletions(-) delete mode 100644 src/components/whos-branch/index.tsx delete mode 100644 src/components/whos-branch/question.tsx delete mode 100644 src/components/whos-branch/score.tsx rename src/data/{ => whos-branch-is-it-anyway}/whos-branch.json (95%) create mode 100644 src/lib/whos-branch.ts delete mode 100644 src/pages/whos-branch.tsx create mode 100644 src/pages/whos-branch/[branch].tsx create mode 100644 src/pages/whos-branch/index.tsx diff --git a/src/components/whos-branch/index.tsx b/src/components/whos-branch/index.tsx deleted file mode 100644 index 223f09c3..00000000 --- a/src/components/whos-branch/index.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useState } from "react"; -import questions from "@data/whos-branch.json"; -import Question from "./question"; -import Score from "./score"; - -const Game = () => { - const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); - const [score, setScore] = useState(0); - const [showScore, setShowScore] = useState(false); - - const handleAnswerOptionClick = (isCorrect: boolean) => { - if (isCorrect) { - setScore(score + 1); - } - - const nextQuestion = currentQuestionIndex + 1; - if (nextQuestion < questions.length) { - setCurrentQuestionIndex(nextQuestion); - } else { - setShowScore(true); - } - }; - - return ( -
- {showScore ? ( - - ) : ( - - )} -
- ); -}; - -export default Game; diff --git a/src/components/whos-branch/question.tsx b/src/components/whos-branch/question.tsx deleted file mode 100644 index 258e69bf..00000000 --- a/src/components/whos-branch/question.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; - -interface QuestionProps { - question: { - question: string; - options: string[]; - answer: string; - }; - onAnswerOptionClick: (isCorrect: boolean) => void; -} - -const Question: React.FC = ({ question, onAnswerOptionClick }) => { - return ( -
-

{question.question}

-
- {question.options.map((option, index) => ( - - ))} -
-
- ); -}; - -export default Question; diff --git a/src/components/whos-branch/score.tsx b/src/components/whos-branch/score.tsx deleted file mode 100644 index 8e0dc0cb..00000000 --- a/src/components/whos-branch/score.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from "react"; - -interface ScoreProps { - score: number; - totalQuestions: number; -} - -const Score: React.FC = ({ score, totalQuestions }) => { - return ( -
-

Your Score

-

- {score} out of {totalQuestions} -

-
- ); -}; - -export default Score; diff --git a/src/data/whos-branch.json b/src/data/whos-branch-is-it-anyway/whos-branch.json similarity index 95% rename from src/data/whos-branch.json rename to src/data/whos-branch-is-it-anyway/whos-branch.json index 685b3f53..38989c49 100644 --- a/src/data/whos-branch.json +++ b/src/data/whos-branch-is-it-anyway/whos-branch.json @@ -1,4 +1,7 @@ -{ +// data/questions.ts +import { GameData } from '../types'; + +export const gameData: GameData = { "branches": [ { "name": "Army", @@ -76,4 +79,4 @@ ] } ] -} +}; \ No newline at end of file diff --git a/src/lib/whos-branch.ts b/src/lib/whos-branch.ts new file mode 100644 index 00000000..d0e0c1ad --- /dev/null +++ b/src/lib/whos-branch.ts @@ -0,0 +1,14 @@ +// lib/whos-branch.ts +import fs from "fs"; +import path from "path"; + +export const getAllBranches = () => { + const jsonPath = path.join(process.cwd(), "src/data/whos-branch-is-it-anyway/whos-branch.json"); + const jsonData = JSON.parse(fs.readFileSync(jsonPath, "utf8")); + return jsonData.branches; +}; + +export const getBranchByName = (name: string) => { + const branches = getAllBranches(); + return branches.find((branch: any) => branch.name.toLowerCase() === name.toLowerCase()); +}; \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index e358d2af..b75f5f73 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -69,9 +69,9 @@ const Home: PageProps = ({ data }) => { -
- - Play "Who's branch is it anyway" +
+ + Play "Who's branch is it anyway"
diff --git a/src/pages/subjects/[slug].tsx b/src/pages/subjects/[slug].tsx index f0682d09..c551720a 100644 --- a/src/pages/subjects/[slug].tsx +++ b/src/pages/subjects/[slug].tsx @@ -30,13 +30,13 @@ const SingleCourse: PageProps = ({ data: { course, instructor, relatedCourses } type: "website", images: [ { - url: `https://maxcoach-react.pages.dev${course.thumbnail.src}`, + url: `https://vetswhocode.io${course.thumbnail.src}`, width: 800, height: 600, alt: course.title, }, { - url: `https://maxcoach-react.pages.dev${course.thumbnail.src}`, + url: `https://vetswhocode.io${course.thumbnail.src}`, width: 900, height: 800, alt: course.title, diff --git a/src/pages/whos-branch.tsx b/src/pages/whos-branch.tsx deleted file mode 100644 index 6787e381..00000000 --- a/src/pages/whos-branch.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React, { useState } from "react"; -import Breadcrumb from "@components/breadcrumb"; -import Game from "@components/whos-branch"; - -const WhosBranchPage = () => { - return ( - <> - - - - ); -}; - -export default WhosBranchPage; diff --git a/src/pages/whos-branch/[branch].tsx b/src/pages/whos-branch/[branch].tsx new file mode 100644 index 00000000..21f8caf6 --- /dev/null +++ b/src/pages/whos-branch/[branch].tsx @@ -0,0 +1,181 @@ +// pages/whos-branch/[branch].tsx +import type { GetStaticPaths, NextPage } from "next"; +import { useEffect, useState } from "react"; +import { useRouter } from "next/router"; +import { getAllBranches, getBranchByName } from "../../lib/whos-branch"; + +interface Question { + question: string; + options: string[]; + answer: string; +} + +interface Branch { + name: string; + questions: Question[]; +} + +type TProps = { + data: { + branch: Branch; + }; +}; + +const BranchQuiz: NextPage = ({ data: { branch } }) => { + const router = useRouter(); + const [players, setPlayers] = useState([]); + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); + const [selectedPlayer, setSelectedPlayer] = useState(null); + const [scores, setScores] = useState>({}); + const [feedback, setFeedback] = useState(""); + const [showNextButton, setShowNextButton] = useState(false); + + useEffect(() => { + // Get players from sessionStorage + const storedPlayers = sessionStorage.getItem("players"); + if (!storedPlayers) { + router.push("/whos-branch"); + return; + } + const playersList = JSON.parse(storedPlayers); + setPlayers(playersList); + + // Initialize scores + const initialScores: Record = {}; + playersList.forEach((player: string) => { + initialScores[player] = 0; + }); + setScores(initialScores); + }, [router]); + + const handlePlayerSelect = (player: string) => { + setSelectedPlayer(player); + }; + + const handleAnswerClick = (selectedAnswer: string) => { + if (!selectedPlayer) { + setFeedback("Please select a player first!"); + return; + } + + const currentQuestion = branch.questions[currentQuestionIndex]; + const isCorrect = selectedAnswer === currentQuestion.answer; + + if (isCorrect) { + setScores((prev) => ({ + ...prev, + [selectedPlayer]: prev[selectedPlayer] + 1, + })); + setFeedback("Correct!"); + } else { + setFeedback(`Wrong! The correct answer is ${currentQuestion.answer}.`); + } + + setShowNextButton(true); + }; + + const handleNextQuestion = () => { + if (currentQuestionIndex + 1 < branch.questions.length) { + setCurrentQuestionIndex((prev) => prev + 1); + setSelectedPlayer(null); + setFeedback(""); + setShowNextButton(false); + } else { + // Game over logic + setFeedback("Game Over!"); + } + }; + + if (!branch) return null; + + return ( +
+

Who's Branch Is It Anyway?

+ +
+

Select the Player:

+
+ {players.map((player) => ( + + ))} +
+ +

{branch.questions[currentQuestionIndex].question}

+ +
+ {branch.questions[currentQuestionIndex].options.map((option) => ( + + ))} +
+ +

{feedback}

+ + {showNextButton && ( + + )} + +
+ {Object.entries(scores).map(([player, score]) => ( +

+ {player}: {score} +

+ ))} +
+
+
+ ); +}; + +export const getStaticPaths: GetStaticPaths = async () => { + const branches = await getAllBranches(); + + return { + paths: branches.map((branch) => ({ + params: { + branch: branch.name.toLowerCase(), + }, + })), + fallback: false, + }; +}; + +interface StaticProps { + params: { + branch: string; + }; +} + +export const getStaticProps = async ({ params }: StaticProps) => { + const branch = await getBranchByName(params.branch); + + if (!branch) { + return { + notFound: true, + }; + } + + return { + props: { + data: { + branch, + }, + }, + }; +}; + +export default BranchQuiz; diff --git a/src/pages/whos-branch/index.tsx b/src/pages/whos-branch/index.tsx new file mode 100644 index 00000000..cc4f590a --- /dev/null +++ b/src/pages/whos-branch/index.tsx @@ -0,0 +1,76 @@ +// pages/whos-branch/index.tsx +import type { NextPage } from "next"; +import { useState } from "react"; +import { useRouter } from "next/router"; +import { getAllBranches } from "../../lib/whos-branch"; + +type TProps = { + data: { + branches: { + name: string; + }[]; + }; +}; + +const WhoBranchIndex: NextPage = ({ data: { branches } }) => { + const router = useRouter(); + const [players, setPlayers] = useState(["", "", "", ""]); + + const handleStartGame = () => { + const validPlayers = players.filter(name => name.trim() !== ""); + if (validPlayers.length === 0) { + alert("Please enter at least one player name"); + return; + } + + // Store players in sessionStorage + sessionStorage.setItem('players', JSON.stringify(validPlayers)); + + // Redirect to first branch + if (branches.length > 0) { + router.push(`/whos-branch/${branches[0].name.toLowerCase()}`); + } + }; + + const handlePlayerChange = (index: number, value: string) => { + const newPlayers = [...players]; + newPlayers[index] = value; + setPlayers(newPlayers); + }; + + return ( +
+

Who's Branch Is It Anyway?

+ +
+

Enter Player Names:

+ {players.map((player, index) => ( + handlePlayerChange(index, e.target.value)} + placeholder={`Player ${index + 1}`} + /> + ))} + +
+
+ ); +}; + +export const getStaticProps = async () => { + const branches = await getAllBranches(); + + return { + props: { + data: { + branches, + }, + }, + }; +}; + +export default WhoBranchIndex; \ No newline at end of file