Skip to content

Commit 0356fdb

Browse files
authored
Merge pull request #14 from oslabs-beta/backend
Remix Compatibility + Optimize Backend
2 parents e4dd559 + 2d53e59 commit 0356fdb

37 files changed

+1541
-774
lines changed

demo-app-remix/.eslintrc.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/** @type {import('eslint').Linter.Config} */
2+
module.exports = {
3+
extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"],
4+
};

demo-app-remix/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
3+
/.cache
4+
/build
5+
/public/build
6+
.env

demo-app-remix/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Welcome to Remix!
2+
3+
- [Remix Docs](https://remix.run/docs)
4+
5+
## Development
6+
7+
Start the Remix development asset server and the Express server by running:
8+
9+
```sh
10+
npm run dev
11+
```
12+
13+
This starts your app in development mode, which will purge the server require cache when Remix rebuilds assets so you don't need a process manager restarting the express server.
14+
15+
## Deployment
16+
17+
First, build your app for production:
18+
19+
```sh
20+
npm run build
21+
```
22+
23+
Then run the app in production mode:
24+
25+
```sh
26+
npm start
27+
```
28+
29+
Now you'll need to pick a host to deploy it to.
30+
31+
### DIY
32+
33+
If you're familiar with deploying express applications you should be right at home just make sure to deploy the output of `remix build`
34+
35+
- `build/`
36+
- `public/build/`
37+
38+
### Using a Template
39+
40+
When you ran `npx create-remix@latest` there were a few choices for hosting. You can run that again to create a new project, then copy over your `app/` folder to the new project that's pre-configured for your target server.
41+
42+
```sh
43+
cd ..
44+
# create a new project, and pick a pre-configured host
45+
npx create-remix@latest
46+
cd my-new-remix-app
47+
# remove the new project's app (not the old one!)
48+
rm -rf app
49+
# copy your app over
50+
cp -R ../my-old-remix-app/app app
51+
```
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import React, { Component } from "react";
2+
import Row from "./Row";
3+
import type { BoardContent, Scoreboard, Player } from "../types/types";
4+
import type { BoardText } from "../types/types";
5+
6+
type BoardState = {
7+
board: BoardContent;
8+
currentPlayer: Player;
9+
gameOver: boolean;
10+
message: string;
11+
scoreboard: Scoreboard;
12+
};
13+
14+
class Board extends Component<{}, BoardState> {
15+
constructor(props: any) {
16+
super(props);
17+
this.state = {
18+
board: this.newBoard(),
19+
currentPlayer: "X",
20+
gameOver: false,
21+
message: "",
22+
scoreboard: { X: 0, O: 0 },
23+
};
24+
25+
this.resetBoard = this.resetBoard.bind(this);
26+
this.handleBoxClick = this.handleBoxClick.bind(this);
27+
}
28+
29+
componentDidUpdate() {
30+
this.checkForWinner();
31+
}
32+
33+
/**
34+
* @method newBoard
35+
* @description - returns a blank BoardContent array,
36+
* for the start of a new game
37+
*/
38+
newBoard(): BoardContent {
39+
return [
40+
["-", "-", "-"],
41+
["-", "-", "-"],
42+
["-", "-", "-"],
43+
];
44+
}
45+
46+
/**
47+
* @method resetBoard
48+
* @description - sets to board object to be all '-',
49+
* and sets gameOver and message to default state
50+
*/
51+
resetBoard(): void {
52+
this.setState({
53+
gameOver: false,
54+
board: this.newBoard(),
55+
message: "",
56+
});
57+
}
58+
59+
/**
60+
* @method checkForWinner
61+
* @description - checks to see if either player has filled a row
62+
* if so, ends the game and updates the message to declare winner
63+
*/
64+
checkForWinner(): void {
65+
const { board, gameOver, currentPlayer } = this.state;
66+
67+
const spacesLeft = (): boolean => {
68+
for (let i of board) {
69+
if (i.includes("-")) return true;
70+
}
71+
return false;
72+
};
73+
74+
if (!gameOver) {
75+
// win conditions: matching rows, columns, or diagonals, that are not empty('-')
76+
if (
77+
(board[0][0] === board[0][1] &&
78+
board[0][1] === board[0][2] &&
79+
board[0][2] !== "-") ||
80+
(board[1][0] === board[1][1] &&
81+
board[1][1] === board[1][2] &&
82+
board[1][2] !== "-") ||
83+
(board[2][0] === board[2][1] &&
84+
board[2][1] === board[2][2] &&
85+
board[2][2] !== "-") ||
86+
(board[0][0] === board[1][0] &&
87+
board[1][0] === board[2][0] &&
88+
board[2][0] !== "-") ||
89+
(board[0][1] === board[1][1] &&
90+
board[1][1] === board[2][1] &&
91+
board[2][1] !== "-") ||
92+
(board[0][2] === board[1][2] &&
93+
board[1][2] === board[2][2] &&
94+
board[2][2] !== "-") ||
95+
(board[0][0] === board[1][1] &&
96+
board[1][1] === board[2][2] &&
97+
board[2][2] !== "-") ||
98+
(board[2][0] === board[1][1] &&
99+
board[1][1] === board[0][2] &&
100+
board[0][2] !== "-")
101+
) {
102+
// winner is the person who's turn was previous
103+
const winner: Player = currentPlayer === "X" ? "O" : "X";
104+
105+
this.setState({
106+
gameOver: true,
107+
message: `Player ${winner} wins!`,
108+
});
109+
110+
// draw condition: no '-' remaining in board without above win condition triggering
111+
} else if (!spacesLeft()) {
112+
this.setState({
113+
gameOver: true,
114+
message: "Draw!",
115+
});
116+
}
117+
}
118+
}
119+
120+
handleBoxClick(row: number, column: number): void {
121+
const boardCopy: BoardContent = [
122+
[...this.state.board[0]],
123+
[...this.state.board[1]],
124+
[...this.state.board[2]],
125+
];
126+
boardCopy[row][column] = this.state.currentPlayer;
127+
const newPlayer: Player = this.state.currentPlayer === "X" ? "O" : "X";
128+
this.setState({ board: boardCopy, currentPlayer: newPlayer });
129+
}
130+
131+
render() {
132+
const rows: Array<JSX.Element> = [];
133+
for (let i = 0; i < 3; i++) {
134+
rows.push(
135+
<Row
136+
key={i}
137+
row={i}
138+
handleBoxClick={this.handleBoxClick}
139+
values={this.state.board[i]}
140+
/>
141+
);
142+
}
143+
const { X, O }: Scoreboard = this.state.scoreboard;
144+
145+
return (
146+
<div className="board">
147+
<h1>Tic Tac Toe</h1>
148+
{this.state.gameOver && <h4>{this.state.message}</h4>}
149+
{rows}
150+
<button id="reset" onClick={this.resetBoard}>
151+
Reset
152+
</button>
153+
</div>
154+
);
155+
}
156+
}
157+
158+
export default Board;

demo-app-remix/app/components/Box.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { BoardText } from "../types/types";
2+
3+
type BoxProps = {
4+
value: BoardText;
5+
row: number;
6+
column: number;
7+
handleBoxClick: (row: number, column: number) => void;
8+
};
9+
10+
const Box = (props: BoxProps) => {
11+
return (
12+
<button
13+
className="box"
14+
onClick={(e) => props.handleBoxClick(props.row, props.column)}
15+
>
16+
{props.value}
17+
</button>
18+
);
19+
};
20+
21+
export default Box;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Increment from './Increment';
2+
3+
function Buttons() {
4+
const buttons = [];
5+
for (let i = 0; i < 4; i++) {
6+
buttons.push(<Increment key={i} />);
7+
}
8+
9+
return (
10+
<div className='buttons'>
11+
<h1>Stateful Buttons</h1>
12+
<h4>
13+
These buttons are functional components that each manage their own state with the useState
14+
hook.
15+
</h4>
16+
{buttons}
17+
</div>
18+
);
19+
}
20+
21+
export default Buttons;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// import StateButton from "./StateButton";
2+
// import EffectButton from "./EffectButton";
3+
// import RefComponent from "./RefComponent";
4+
// import ContextButton from "./ContextButton";
5+
// import { useState, useEffect, useRef, createContext } from "react";
6+
7+
// export const ButtonContext: any = createContext(null);
8+
9+
// export default function Buttons() {
10+
// const [count, setCount] = useState(0);
11+
// const [effectCount, setEffectCount] = useState(0);
12+
// const renders = useRef(-1);
13+
14+
// useEffect(() => {
15+
// renders.current = renders.current + 1;
16+
// document.title = `You clicked ${effectCount} times`;
17+
// }, [count, effectCount]);
18+
19+
// return (
20+
// <div className="buttons">
21+
// <h1>React Hook Buttons</h1>
22+
// <h4 className="description">
23+
// These buttons are functional components that manage their own state with
24+
// different react hooks.
25+
// </h4>
26+
// {/* {buttons} */}
27+
// <StateButton count={count} setCount={setCount} />
28+
// <EffectButton effectCount={effectCount} setEffectCount={setEffectCount} />
29+
// <h4> A box renders once useRef count is 3</h4>
30+
// <div className={renders.current >= 3 ? "show" : "hide"}>
31+
// <RefComponent counter={renders.current} />
32+
// </div>
33+
// <ButtonContext.Provider value={{ value: [count, setCount] }}>
34+
// <ContextButton />
35+
// </ButtonContext.Provider>
36+
// </div>
37+
// );
38+
// }
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// import { validateHeaderValue } from "http";
2+
// import { useContext } from "react";
3+
// import { ButtonContext } from "./Buttons";
4+
5+
// export default function ContextButton() {
6+
// //const [count, setCount] = useState(0);
7+
8+
// // const [count, setCount] = useContext(ButtonContext);
9+
// const { value }: any = useContext(ButtonContext);
10+
// const [count, setCount] = value;
11+
12+
// return (
13+
// <div>
14+
// <h4>This button tests the useContext hook</h4>
15+
// <button className="buttonstyle" onClick={() => setCount(count + 1)}>
16+
// Click to increment: {count}
17+
// </button>
18+
// </div>
19+
// );
20+
// }
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// import React, { useState, useEffect, useRef } from "react";
2+
3+
// export default function StateButton(props: any) {
4+
// //const [count, setCount] = useState(0);
5+
6+
// return (
7+
// <div>
8+
// <h4>This button tests the UseEffect hook</h4>
9+
// <button
10+
// className="buttonstyle"
11+
// onClick={() => props.setEffectCount(props.effectCount + 1)}
12+
// >
13+
// {/* You clicked me {count} times. */}
14+
// Click to update doc title
15+
// </button>
16+
// </div>
17+
// );
18+
// }
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React, { useState } from 'react';
2+
3+
function Increment() {
4+
const [count, setCount] = useState(0);
5+
6+
return (
7+
<button className='increment' onClick={() => setCount(count + 1)}>
8+
You clicked me {count} times.
9+
</button>
10+
);
11+
}
12+
13+
export default Increment;

0 commit comments

Comments
 (0)