diff --git a/frontend/public/cardPool.png b/frontend/public/cardPool.png new file mode 100644 index 0000000..f66b1f9 Binary files /dev/null and b/frontend/public/cardPool.png differ diff --git a/frontend/public/card_faces/back.jpeg b/frontend/public/card_faces/back.jpeg new file mode 100644 index 0000000..0e4ce5b Binary files /dev/null and b/frontend/public/card_faces/back.jpeg differ diff --git a/frontend/public/deckMat.png b/frontend/public/deckMat.png new file mode 100644 index 0000000..56f2bf5 Binary files /dev/null and b/frontend/public/deckMat.png differ diff --git a/frontend/public/playBackground.png b/frontend/public/playBackground.png new file mode 100644 index 0000000..dff9227 Binary files /dev/null and b/frontend/public/playBackground.png differ diff --git a/frontend/public/playerIcon.png b/frontend/public/playerIcon.png new file mode 100644 index 0000000..088d43f Binary files /dev/null and b/frontend/public/playerIcon.png differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b016e02..8f6b490 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -3,11 +3,11 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import Home from './pages/Home'; import AppLayout from './pages/AppLayout'; import Error from './pages/Error'; -import Game from './pages/Game'; import About from './pages/About'; import { AuthProvider } from './contexts/AuthContext'; import Login from './pages/Login'; import SignUp from './pages/SignUp'; +import { GameProvider } from './contexts/GameContext'; const router = createBrowserRouter([ { @@ -21,7 +21,7 @@ const router = createBrowserRouter([ }, { path: '/game', - element: , + element: , }, { path: '/about', element: }, { path: '/error', element: }, diff --git a/frontend/src/contexts/GameContext.tsx b/frontend/src/contexts/GameContext.tsx new file mode 100644 index 0000000..ed2cdfd --- /dev/null +++ b/frontend/src/contexts/GameContext.tsx @@ -0,0 +1,79 @@ +/* eslint-disable */ +import React, { createContext, useContext, useState, useEffect } from 'react'; +import Game from '../pages/Game'; +import { useLocation } from 'react-router-dom'; + +interface GameState { + players: { id: number; name: string; cards: string[] }[]; + cards: string[]; + currentTurn: number; + lastThrownCard: string; +} + +interface GameContextProps { + gameState: GameState | null; + setGameState: React.Dispatch>; +} + +const defaultGameState: GameState = { + players: [], + cards: [], + currentTurn: 0, + lastThrownCard: '', +}; + +const GameContext = createContext({ + gameState: null, + setGameState: () => {}, +}); + +export const GameProvider = () => { + const [gameState, setGameState] = useState( + defaultGameState + ); + + const location = useLocation(); + const backendUrl = process.env.REACT_APP_BACKEND_URL; + + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const gameType = queryParams.get('type'); + + fetch(`${backendUrl}/api/game?type=${gameType}`) + .then((response) => response.json()) + .then((data) => { + setGameState(data); + }); + + // polling + const interval = setInterval(() => { + fetch(`${backendUrl}/api/game/state`) + .then((response) => response.json()) + .then((data) => { + setGameState(data); + }); + }, 5000); + + return () => clearInterval(interval); + }, [location.search]); + + return ( + + {gameState ? ( + + ) : ( +
+ Loading... +
+ )} +
+ ); +}; + +export const useGameContext = () => { + const context = useContext(GameContext); + if (!context) { + throw new Error('useGameContext must be used within a GameProvider'); + } + return context; +}; diff --git a/frontend/src/pages/Game.tsx b/frontend/src/pages/Game.tsx index 233c599..661bf03 100644 --- a/frontend/src/pages/Game.tsx +++ b/frontend/src/pages/Game.tsx @@ -1,9 +1,109 @@ -function Game() { +import React from 'react'; +import { useGameContext } from '../contexts/GameContext'; +import Button from '../library/button'; + +const Game: React.FC = () => { + const { gameState } = useGameContext(); + + if (!gameState) { + return ( +
+ Loading... +
+ ); + } + + const playerPositions = [ + { top: '12%', left: '29%', transform: 'translate(-50%, -50%)' }, + { top: '12%', left: '71%', transform: 'translate(-50%, -50%)' }, + { top: '40%', left: '10%', transform: 'translate(-50%, -50%)' }, + { top: '40%', left: '90%', transform: 'translate(-50%, -50%)' }, + { top: '75%', left: '15%', transform: 'translate(-50%, -50%)' }, + { top: '75%', left: '85%', transform: 'translate(-50%, -50%)' }, + ]; + return ( -
-
X's turn
+
+
+ {/* Players */} + {gameState.players.slice(0, 6).map((player, index) => ( +
+
+ {player.cards.length} +
+
+ ))} + + {/* Center Deck and UNO Button */} +
+
+ Card Pool +
+ Card 1 + Last Thrown Card +
+
+
+ + {/* Player Mat */} +
+ Deck Mat +
+ {Array.from({ length: 7 }).map((_, index) => ( + {`Card + ))} +
+
+
); -} +}; export default Game; diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index bf0fe11..ba2da7b 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -11,7 +11,7 @@ const Home: React.FC = () => { const CreateGame = () => { // Logic to create a game console.log('Create Game'); - navigate('/error'); + navigate('/game'); }; const JoinGame = () => { diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index b605050..e6ecd90 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -18,6 +18,8 @@ export default { extend: { backgroundImage: { 'uno-bg': "url('/src/assets/bg.jpg')", + 'table-bg': "url('/playBackground.png')", + 'player-icon-bg': "url('/playerIcon.png')", }, }, },