diff --git a/ui/Dockerfile b/ui/Dockerfile index 996fb37..4b535fe 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -5,6 +5,7 @@ COPY package-lock.json . RUN npm install FROM base as dependencies +COPY utils /utils COPY pages /pages COPY public /public COPY styles /styles diff --git a/ui/api/game.api.ts b/ui/api/game.api.ts index ca4b769..018fb9b 100644 --- a/ui/api/game.api.ts +++ b/ui/api/game.api.ts @@ -1,5 +1,5 @@ import wretch from "wretch"; -import { Cell } from "../types"; +import { Cell, Position } from "../types"; const USER_ID_HEADER = "Player-id"; @@ -18,3 +18,27 @@ export const fetchGameState = async ( cellWidth: response.board[0].length, }; }; + +export const submitMove = async ( + playerId: string, + gameId: number, + piecePosition: Position, + movePosition: Position +): Promise<{ cells: Cell[] }> => { + console.log(piecePosition, movePosition); + const response: { board: Cell[][] } = await wretch(`/api/game/${gameId}/move`) + .headers({ + [USER_ID_HEADER]: playerId, + }) + .body({ + pieceRow: piecePosition.row, + pieceColumn: piecePosition.column, + moveRow: movePosition.row, + moveColumn: movePosition.column, + }) + .post() + .json(); + return { + cells: response.board.flat(), + }; +}; diff --git a/ui/components/board.tsx b/ui/components/board.tsx index f6f4537..86432c7 100644 --- a/ui/components/board.tsx +++ b/ui/components/board.tsx @@ -1,25 +1,33 @@ -import React from "react"; +import React, { useState } from "react"; +import cn from "classnames"; import { Cell } from "../types"; import styles from "../styles/board.module.css"; interface BoardProps { cells: Cell[]; cellWidth: number; + focusedCellIndex?: number; + onCellClick: (index: number) => void; } -const Board = (props: BoardProps) => { - return ( -
- {props.cells.map((cell) => ( -
{cell.piece}
- ))} -
- ); -}; +const Board = (props: BoardProps) => ( +
+ {props.cells.map((cell, i) => ( + + ))} +
+); export default Board; diff --git a/ui/components/game.tsx b/ui/components/game.tsx index e6283d4..78e3c3a 100644 --- a/ui/components/game.tsx +++ b/ui/components/game.tsx @@ -1,19 +1,42 @@ import React, { useState, useEffect } from "react"; -import { fetchGameState } from "../api/game.api"; +import { fetchGameState, submitMove } from "../api/game.api"; +import { convertIndexToPosition } from "../utils/position"; import { Cell } from "../types"; import Board from "./board"; +const DEFAULT_CLICKED_CELL = -1; + interface GameProps { gameId: number; + playerId: string; } const Game = (props: GameProps) => { const [isLoading, setLoading] = useState(false); - const [cellWidth, setCellWidth] = useState(4); + const [cellWidth, setCellWidth] = useState(0); const [cells, setCells] = useState([] as Cell[]); + const [focusedCellIndex, setFocusedCellIndex] = + useState(DEFAULT_CLICKED_CELL); + + const onCellClicked = (cellIndex: number): void => { + if (cellIndex === focusedCellIndex) { + setFocusedCellIndex(DEFAULT_CLICKED_CELL); + return; + } else if (focusedCellIndex === DEFAULT_CLICKED_CELL) { + setFocusedCellIndex(cellIndex); + return; + } + submitMove( + props.playerId, + props.gameId, + convertIndexToPosition(focusedCellIndex, cellWidth), + convertIndexToPosition(cellIndex, cellWidth) + ); + }; + useEffect(() => { setLoading(true); - fetchGameState("red", props.gameId).then( + fetchGameState(props.playerId, props.gameId).then( ({ cells: cellList, cellWidth: width }) => { setCellWidth(width); setCells(cellList); @@ -22,7 +45,14 @@ const Game = (props: GameProps) => { ); }, [props.gameId]); - return ; + return ( + + ); }; export default Game; diff --git a/ui/package-lock.json b/ui/package-lock.json index 227dea1..074eedd 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,6 +8,7 @@ "name": "ui", "version": "0.1.0", "dependencies": { + "classnames": "^2.3.1", "next": "12.1.0", "react": "17.0.2", "react-dom": "17.0.2", @@ -528,6 +529,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "node_modules/color-convert": { "version": "2.0.1", "dev": true, @@ -2842,6 +2848,11 @@ "supports-color": "^7.1.0" } }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "color-convert": { "version": "2.0.1", "dev": true, diff --git a/ui/package.json b/ui/package.json index fe3cab8..f99fa4f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "classnames": "^2.3.1", "next": "12.1.0", "react": "17.0.2", "react-dom": "17.0.2", diff --git a/ui/pages/index.tsx b/ui/pages/index.tsx index 0b16e8e..28a32e3 100644 --- a/ui/pages/index.tsx +++ b/ui/pages/index.tsx @@ -11,7 +11,7 @@ const Home: NextPage = () => (
- +
); diff --git a/ui/styles/board.module.css b/ui/styles/board.module.css index ddf1ee2..3fb899c 100644 --- a/ui/styles/board.module.css +++ b/ui/styles/board.module.css @@ -5,4 +5,9 @@ .gridCell { border: 1px solid black; padding-top: 100%; /* 100% is supposed to be 1:1 aspect ratio but it doesn't look like it */ + background: 0; +} + +.cellClicked { + border: 1px solid red; } \ No newline at end of file diff --git a/ui/types.ts b/ui/types.ts index 774c563..16036cd 100644 --- a/ui/types.ts +++ b/ui/types.ts @@ -3,4 +3,9 @@ export interface Cell { terrain: boolean; hidden: boolean; empty: boolean; +} + +export interface Position { + row: number; + column: number; } \ No newline at end of file diff --git a/ui/utils/position.ts b/ui/utils/position.ts new file mode 100644 index 0000000..0361e65 --- /dev/null +++ b/ui/utils/position.ts @@ -0,0 +1,9 @@ +import { Position } from "../types"; + +export const convertIndexToPosition = ( + index: number, + cellWidth: number +): Position => ({ + row: Math.floor(index / cellWidth), + column: index % cellWidth, +});