summaryrefslogtreecommitdiff
path: root/client/src
diff options
context:
space:
mode:
authorSOGYO\bvdoord <bvdoord@sogyo.nl>2021-01-26 11:22:58 +0100
committerSOGYO\bvdoord <bvdoord@sogyo.nl>2021-01-26 11:22:58 +0100
commitf382ec567e1292cedee7c01e721ddc38bdc4dc4e (patch)
tree3012b3343d24c4d52f4370ca1f2efc4c78dbd84f /client/src
parent9fb9913bc5ccc49d738481fa71c5ba75c43ed532 (diff)
Front-end met Snowpack toegevoegd
Diffstat (limited to 'client/src')
-rw-r--r--client/src/About/About.tsx12
-rw-r--r--client/src/App.css9
-rw-r--r--client/src/App.tsx29
-rw-r--r--client/src/Header/Header.css30
-rw-r--r--client/src/Header/Header.tsx20
-rw-r--r--client/src/Header/logo.jpgbin0 -> 5184 bytes
-rw-r--r--client/src/Mancala/Mancala.css1
-rw-r--r--client/src/Mancala/Mancala.tsx24
-rw-r--r--client/src/Mancala/Play.css1
-rw-r--r--client/src/Mancala/Play.tsx17
-rw-r--r--client/src/Mancala/StartGame.css10
-rw-r--r--client/src/Mancala/StartGame.tsx74
-rw-r--r--client/src/gameState.ts19
-rw-r--r--client/src/index.tsx15
14 files changed, 261 insertions, 0 deletions
diff --git a/client/src/About/About.tsx b/client/src/About/About.tsx
new file mode 100644
index 0000000..e7a9c69
--- /dev/null
+++ b/client/src/About/About.tsx
@@ -0,0 +1,12 @@
+import React from "react";
+
+export function About() {
+ return <div>
+ <h2>Mancala</h2>
+ <p>
+ Mancala is one of the oldest known board games. It has many different variations and is known under several different names.
+ The objective of the game is obtaining as many stones, beads or seeds as possible.
+ It is a game of skill that does not rely on any sort of randomness, making it similar to chess.
+ </p>
+ </div>
+} \ No newline at end of file
diff --git a/client/src/App.css b/client/src/App.css
new file mode 100644
index 0000000..8eebe3a
--- /dev/null
+++ b/client/src/App.css
@@ -0,0 +1,9 @@
+body {
+ margin: 0px;
+ padding: 0px;
+ background-color: #eeeeee;
+}
+
+.main-content {
+ padding: 8px;
+} \ No newline at end of file
diff --git a/client/src/App.tsx b/client/src/App.tsx
new file mode 100644
index 0000000..edd9504
--- /dev/null
+++ b/client/src/App.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
+import { Header } from "./Header/Header";
+import { About } from "./About/About";
+import { Mancala } from "./Mancala/Mancala";
+import "./App.css";
+
+export function App() {
+ return (
+ <Router>
+ {/* The header with navigation options is always on top of every page */}
+ <Header />
+
+ <div className="main-content">
+ <Switch>
+ {/* If the user goes to the url /about, show the about page */}
+ <Route path="/about">
+ <About />
+ </Route>
+
+ {/* If the user goes to any other url, show the play page */}
+ <Route path="/">
+ <Mancala />
+ </Route>
+ </Switch>
+ </div>
+ </Router>
+ )
+} \ No newline at end of file
diff --git a/client/src/Header/Header.css b/client/src/Header/Header.css
new file mode 100644
index 0000000..6744f32
--- /dev/null
+++ b/client/src/Header/Header.css
@@ -0,0 +1,30 @@
+.main-header {
+ background-color: white;
+ color: green;
+ font-size: 2rem;
+ margin-bottom: 8px;
+}
+
+.main-navigation {
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ background-color: green;
+ font-size: 1.3rem;
+ height: 45px;
+}
+
+.main-navigation a {
+ color: white;
+ text-decoration: none;
+}
+
+.main-header div {
+ display: flex;
+ align-items: center;
+}
+
+.main-header img {
+ height: 50px;
+ margin: 6px;
+} \ No newline at end of file
diff --git a/client/src/Header/Header.tsx b/client/src/Header/Header.tsx
new file mode 100644
index 0000000..9f96d0c
--- /dev/null
+++ b/client/src/Header/Header.tsx
@@ -0,0 +1,20 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import "./Header.css";
+import urlLogo from "./logo.jpg";
+
+/**
+ * A Header component with a Sogyo logo, the name of the application and several links to different pages
+ */
+export function Header() {
+ return <header className="main-header">
+ <div>
+ <img src={urlLogo} />
+ Mancala
+ </div>
+ <div className="main-navigation">
+ <Link to="/">Play</Link>
+ <Link to="/about">About</Link>
+ </div>
+ </header>
+} \ No newline at end of file
diff --git a/client/src/Header/logo.jpg b/client/src/Header/logo.jpg
new file mode 100644
index 0000000..9104922
--- /dev/null
+++ b/client/src/Header/logo.jpg
Binary files differ
diff --git a/client/src/Mancala/Mancala.css b/client/src/Mancala/Mancala.css
new file mode 100644
index 0000000..8f73945
--- /dev/null
+++ b/client/src/Mancala/Mancala.css
@@ -0,0 +1 @@
+/** Add some styles */ \ No newline at end of file
diff --git a/client/src/Mancala/Mancala.tsx b/client/src/Mancala/Mancala.tsx
new file mode 100644
index 0000000..42c31a2
--- /dev/null
+++ b/client/src/Mancala/Mancala.tsx
@@ -0,0 +1,24 @@
+import React, { useState } from "react";
+import { StartGame } from "./StartGame";
+import { Play } from "./Play";
+import type { GameState } from "../gameState";
+import "./Mancala.css";
+
+/**
+ * The base component for the Mancala game. If there's no active game, the `StartGame` component allows
+ * users to enter two player names and start a new game.
+ * If there's an active game this component holds the game state. This game state can be passed as a prop
+ * to child components as needed.
+ *
+ * Child components can modify the game state by calling the setGameState (which they recieve as prop.)
+ */
+export function Mancala() {
+
+ const [ gameState, setGameState ] = useState<GameState | undefined>(undefined);
+
+ if (!gameState) {
+ return <StartGame setGameState={setGameState} />
+ }
+
+ return <Play gameState={gameState} setGameState={setGameState} />
+} \ No newline at end of file
diff --git a/client/src/Mancala/Play.css b/client/src/Mancala/Play.css
new file mode 100644
index 0000000..8f73945
--- /dev/null
+++ b/client/src/Mancala/Play.css
@@ -0,0 +1 @@
+/** Add some styles */ \ No newline at end of file
diff --git a/client/src/Mancala/Play.tsx b/client/src/Mancala/Play.tsx
new file mode 100644
index 0000000..3b8ff25
--- /dev/null
+++ b/client/src/Mancala/Play.tsx
@@ -0,0 +1,17 @@
+import React from "react";
+import type { GameState } from "../gameState";
+import "./Play.css";
+
+type PlayProps = {
+ gameState: GameState;
+ setGameState(newGameState: GameState): void;
+}
+
+export function Play({ gameState, setGameState }: PlayProps) {
+ return (
+ <div>
+ <p>{gameState.players[0].name} vs {gameState.players[1].name}</p>
+ To do...
+ </div>
+ )
+} \ No newline at end of file
diff --git a/client/src/Mancala/StartGame.css b/client/src/Mancala/StartGame.css
new file mode 100644
index 0000000..3ab5872
--- /dev/null
+++ b/client/src/Mancala/StartGame.css
@@ -0,0 +1,10 @@
+.startGameButton {
+ font-size: 2em;
+ background-color: lightgreen;
+ border: 2px solid black;
+}
+
+.errorMessage {
+ height: 1em;
+ color: red;
+} \ No newline at end of file
diff --git a/client/src/Mancala/StartGame.tsx b/client/src/Mancala/StartGame.tsx
new file mode 100644
index 0000000..9d5be2c
--- /dev/null
+++ b/client/src/Mancala/StartGame.tsx
@@ -0,0 +1,74 @@
+import React, { useState } from "react";
+import type { GameState } from "../gameState";
+import "./StartGame.css";
+
+type StartGameProps = {
+ setGameState(newGameState: GameState): void;
+}
+
+/**
+ * Allows the players to enter their name. A name is required for both players. They can't have the same names.
+ */
+export function StartGame({ setGameState }: StartGameProps) {
+
+ const [errorMessage, setErrorMessage] = useState("");
+ const [playerOne, setPlayerOne] = useState("");
+ const [playerTwo, setPlayerTwo] = useState("");
+
+ async function tryStartGame(e: React.FormEvent) {
+ e.preventDefault(); // Prevent default browser behavior of submitting forms
+ if (!playerOne) {
+ setErrorMessage("A name is required for player 1");
+ return;
+ }
+ if (!playerTwo) {
+ setErrorMessage("A name is required for player 2");
+ return;
+ }
+ if (playerOne === playerTwo) {
+ setErrorMessage("Each player should have a unique name");
+ return;
+ }
+ setErrorMessage("");
+
+ try {
+ const response = await fetch('mancala/api/start', {
+ method: 'POST',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ nameplayer1: playerOne, nameplayer2: playerTwo })
+ });
+
+ if (response.ok) {
+ const gameState = await response.json();
+ setGameState(gameState);
+ } else {
+ console.error(response.statusText);
+ }
+ } catch (error) {
+ console.error(error.toString());
+ }
+ }
+
+ return (
+ <form onSubmit={(e) => tryStartGame(e)}>
+ <input value={playerOne}
+ placeholder="Player 1 name"
+ onChange={(e) => setPlayerOne(e.target.value)}
+ />
+
+ <input value={playerTwo}
+ placeholder="Player 2 name"
+ onChange={(e) => setPlayerTwo(e.target.value)}
+ />
+
+ <p className="errorMessage">{errorMessage}</p>
+
+ <button className="startGameButton" type="submit">
+ Play Mancala!
+ </button>
+ </form>
+ )
+} \ No newline at end of file
diff --git a/client/src/gameState.ts b/client/src/gameState.ts
new file mode 100644
index 0000000..2d5ec3e
--- /dev/null
+++ b/client/src/gameState.ts
@@ -0,0 +1,19 @@
+
+export interface GameState {
+ players: [ Player, Player ]; // a player array contains exactly two Players
+ gameStatus: {
+ endOfGame: boolean;
+ };
+}
+
+interface Player {
+ name: string;
+ pits: Pit[];
+ type: "player1" | "player2"; // only "player1" and "player2" are valid options for this string
+ hasTurn: boolean;
+}
+
+interface Pit {
+ index: number;
+ nrOfStones: number;
+}
diff --git a/client/src/index.tsx b/client/src/index.tsx
new file mode 100644
index 0000000..c92ef7e
--- /dev/null
+++ b/client/src/index.tsx
@@ -0,0 +1,15 @@
+import * as React from "react";
+import ReactDOM from "react-dom";
+import { App } from "./App";
+
+ReactDOM.render(
+ <App/>,
+ document.getElementById("root")
+)
+
+// Hot Module Replacement (HMR) - Remove this snippet to remove HMR.
+// Learn more: https://www.snowpack.dev/#hot-module-replacement
+const hotMudleReplacement = import.meta.hot;
+if (hotMudleReplacement) {
+ hotMudleReplacement.accept();
+}