diff --git a/ui/.prettierrc b/ui/.prettierrc
new file mode 100644
index 0000000..a5cd819
--- /dev/null
+++ b/ui/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true
+}
diff --git a/ui/Dockerfile b/ui/Dockerfile
index 1a9d0d8..996fb37 100644
--- a/ui/Dockerfile
+++ b/ui/Dockerfile
@@ -8,9 +8,11 @@ FROM base as dependencies
COPY pages /pages
COPY public /public
COPY styles /styles
+COPY api /api
COPY components /components
COPY next* /
COPY tsconfig.json /
+COPY types.ts /
EXPOSE 3000
RUN npm run build
diff --git a/ui/api/game.api.ts b/ui/api/game.api.ts
new file mode 100644
index 0000000..ca4b769
--- /dev/null
+++ b/ui/api/game.api.ts
@@ -0,0 +1,20 @@
+import wretch from "wretch";
+import { Cell } from "../types";
+
+const USER_ID_HEADER = "Player-id";
+
+export const fetchGameState = async (
+ playerId: string,
+ gameId: number
+): Promise<{ cells: Cell[]; cellWidth: number }> => {
+ const response: { board: Cell[][] } = await wretch(`/api/game/${gameId}`)
+ .headers({
+ [USER_ID_HEADER]: playerId,
+ })
+ .get()
+ .json();
+ return {
+ cells: response.board.flat(),
+ cellWidth: response.board[0].length,
+ };
+};
diff --git a/ui/components/board.tsx b/ui/components/board.tsx
index 52086a7..f6f4537 100644
--- a/ui/components/board.tsx
+++ b/ui/components/board.tsx
@@ -1,7 +1,25 @@
-import React from 'react'
+import React from "react";
+import { Cell } from "../types";
+import styles from "../styles/board.module.css";
-const Board = () => (
-
-)
+interface BoardProps {
+ cells: Cell[];
+ cellWidth: number;
+}
-export default Board;
\ No newline at end of file
+const Board = (props: BoardProps) => {
+ return (
+
+ {props.cells.map((cell) => (
+
{cell.piece}
+ ))}
+
+ );
+};
+
+export default Board;
diff --git a/ui/components/game.tsx b/ui/components/game.tsx
new file mode 100644
index 0000000..e6283d4
--- /dev/null
+++ b/ui/components/game.tsx
@@ -0,0 +1,28 @@
+import React, { useState, useEffect } from "react";
+import { fetchGameState } from "../api/game.api";
+import { Cell } from "../types";
+import Board from "./board";
+
+interface GameProps {
+ gameId: number;
+}
+
+const Game = (props: GameProps) => {
+ const [isLoading, setLoading] = useState(false);
+ const [cellWidth, setCellWidth] = useState(4);
+ const [cells, setCells] = useState([] as Cell[]);
+ useEffect(() => {
+ setLoading(true);
+ fetchGameState("red", props.gameId).then(
+ ({ cells: cellList, cellWidth: width }) => {
+ setCellWidth(width);
+ setCells(cellList);
+ setLoading(false);
+ }
+ );
+ }, [props.gameId]);
+
+ return ;
+};
+
+export default Game;
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 1adaf33..227dea1 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -10,7 +10,8 @@
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
- "react-dom": "17.0.2"
+ "react-dom": "17.0.2",
+ "wretch": "^1.7.9"
},
"devDependencies": {
"@types/node": "17.0.21",
@@ -2527,6 +2528,11 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/wretch": {
+ "version": "1.7.9",
+ "resolved": "https://registry.npmjs.org/wretch/-/wretch-1.7.9.tgz",
+ "integrity": "sha512-uUSze1Z72RiQjyoqr7r1KW+05WDNeqqKOeyJDPhw6EVEaOgp9RQNrr8AQt3OF7qylQbh2iVtT9r0nXIHlbJgqQ=="
+ },
"node_modules/yallist": {
"version": "4.0.0",
"dev": true,
@@ -4060,6 +4066,11 @@
"version": "1.0.2",
"dev": true
},
+ "wretch": {
+ "version": "1.7.9",
+ "resolved": "https://registry.npmjs.org/wretch/-/wretch-1.7.9.tgz",
+ "integrity": "sha512-uUSze1Z72RiQjyoqr7r1KW+05WDNeqqKOeyJDPhw6EVEaOgp9RQNrr8AQt3OF7qylQbh2iVtT9r0nXIHlbJgqQ=="
+ },
"yallist": {
"version": "4.0.0",
"dev": true
diff --git a/ui/package.json b/ui/package.json
index a9ac96c..fe3cab8 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -11,7 +11,8 @@
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
- "react-dom": "17.0.2"
+ "react-dom": "17.0.2",
+ "wretch": "^1.7.9"
},
"devDependencies": {
"@types/node": "17.0.21",
diff --git a/ui/pages/index.tsx b/ui/pages/index.tsx
index 3d7c124..0b16e8e 100644
--- a/ui/pages/index.tsx
+++ b/ui/pages/index.tsx
@@ -1,19 +1,19 @@
-import type { NextPage } from 'next'
-import Head from 'next/head'
-import Image from 'next/image'
-import Board from '../components/board'
-import styles from '../styles/Home.module.css'
+import type { NextPage } from "next";
+import Head from "next/head";
+import Image from "next/image";
+import Game from "../components/game";
+import styles from "../styles/BoardPage.module.css";
const Home: NextPage = () => (
- <>
+
Free Go Game
-
+
- >
-)
+
+);
-export default Home
\ No newline at end of file
+export default Home;
diff --git a/ui/styles/BoardPage.module.css b/ui/styles/BoardPage.module.css
new file mode 100644
index 0000000..1e69efb
--- /dev/null
+++ b/ui/styles/BoardPage.module.css
@@ -0,0 +1,4 @@
+.main {
+ max-width: 900px;
+ margin: auto;
+}
\ No newline at end of file
diff --git a/ui/styles/board.module.css b/ui/styles/board.module.css
new file mode 100644
index 0000000..ddf1ee2
--- /dev/null
+++ b/ui/styles/board.module.css
@@ -0,0 +1,8 @@
+.gridContainer {
+ display: grid;
+}
+
+.gridCell {
+ border: 1px solid black;
+ padding-top: 100%; /* 100% is supposed to be 1:1 aspect ratio but it doesn't look like it */
+}
\ No newline at end of file
diff --git a/ui/types.ts b/ui/types.ts
new file mode 100644
index 0000000..774c563
--- /dev/null
+++ b/ui/types.ts
@@ -0,0 +1,6 @@
+export interface Cell {
+ piece?: string;
+ terrain: boolean;
+ hidden: boolean;
+ empty: boolean;
+}
\ No newline at end of file