aboutsummaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
Diffstat (limited to 'pages')
-rw-r--r--pages/_app.tsx6
-rw-r--r--pages/game.tsx84
-rw-r--r--pages/index.tsx198
-rw-r--r--pages/login.tsx66
-rw-r--r--pages/register.tsx93
-rw-r--r--pages/settings.tsx33
6 files changed, 480 insertions, 0 deletions
diff --git a/pages/_app.tsx b/pages/_app.tsx
new file mode 100644
index 0000000..7be2763
--- /dev/null
+++ b/pages/_app.tsx
@@ -0,0 +1,6 @@
+import '../styles/global.css';
+
+export default function VierOpEenRijWebsite({ Component, pageProps }) {
+ return <Component {...pageProps}/>
+}
+
diff --git a/pages/game.tsx b/pages/game.tsx
new file mode 100644
index 0000000..513fc7a
--- /dev/null
+++ b/pages/game.tsx
@@ -0,0 +1,84 @@
+import { CSSProperties } from 'react';
+
+import { NavBar } from '../components/navbar';
+import { CenteredPage } from '../components/page';
+import { VoerBord } from '../components/voerBord';
+import { DialogBox } from '../components/dialogBox';
+import { CurrentGameSettings } from '../components/gameSettings';
+import { Button, SearchBar } from '../components/ui';
+import { GameBar } from '../components/gameBar';
+
+import WifiTetheringRoundedIcon from '@material-ui/icons/WifiTetheringRounded';
+import LinkRoundedIcon from '@material-ui/icons/LinkRounded';
+
+var InviteButtonStyle: CSSProperties = {
+ backgroundColor: "var(--page-background)",
+ height: 160,
+ padding: 12
+}
+
+var InviteButtonIconStyle: CSSProperties = {
+ fontSize: 100,
+ position: "absolute",
+ top: 12,
+ left: "50%",
+ transform: "translateX(-50%)"
+}
+
+var InviteButtonLabelStyle: CSSProperties = {
+ position: "absolute",
+ bottom: 12,
+ left: "50%",
+ transform: "translateX(-50%)",
+ textAlign: "center",
+ color: "var(--text-alt)",
+ width: 136,
+ fontSize: 20,
+ userSelect: "none"
+}
+
+export default function GamePage() {
+ return (
+ <div>
+ <NavBar/>
+ <CenteredPage width={900} style={{ height: "100vh" }}>
+ <div style={{
+ position: "relative",
+ top: "50%",
+ transform: "translateY(-50%)",
+ maxWidth: "100vh",
+ margin: "0 auto"
+ }}>
+ <VoerBord width={7} height={6}/>
+ <GameBar/>
+ </div>
+ <DialogBox title="Nieuw spel">
+ <CurrentGameSettings/>
+ <div style={{
+ marginTop: 24,
+ display: "grid",
+ gridTemplateColumns: "1fr 1fr",
+ gridGap: 24
+ }}>
+ <Button style={InviteButtonStyle}>
+ <WifiTetheringRoundedIcon style={{
+ color: "var(--disk-b)",
+ ...InviteButtonIconStyle
+ }}/>
+ <h2 style={InviteButtonLabelStyle}>Willekeurige speler</h2>
+ </Button>
+ <Button style={InviteButtonStyle}>
+ <LinkRoundedIcon style={{
+ color: "var(--disk-a)",
+ ...InviteButtonIconStyle
+ }}/>
+ <h2 style={InviteButtonLabelStyle}>Uitnodigen via link</h2>
+ </Button>
+ </div>
+ <SearchBar label="Zoeken in vriendenlijst"/>
+ </DialogBox>
+ </CenteredPage>
+ </div>
+ );
+}
+
diff --git a/pages/index.tsx b/pages/index.tsx
new file mode 100644
index 0000000..ff4e2b7
--- /dev/null
+++ b/pages/index.tsx
@@ -0,0 +1,198 @@
+import { CSSProperties, Component } from 'react';
+import axios from 'axios';
+import { userInfo } from '../api/api';
+
+import { NavBar } from '../components/navbar';
+import { CenteredPage, PageTitle } from '../components/page';
+import { Vierkant, Button } from '../components/ui';
+import { AccountAvatar } from '../components/account';
+import { ToastArea, Toast } from '../components/toast';
+
+import VideogameAssetIcon from '@material-ui/icons/VideogameAsset';
+import ExtensionIcon from '@material-ui/icons/Extension';
+
+import Icon from '@mdi/react';
+import { mdiRobotExcited } from '@mdi/js';
+
+var GameModeIconStyle: CSSProperties = {
+ fontSize: 64,
+ width: 64,
+ height: 64,
+ display: "inline-block",
+ position: "absolute",
+ top: 24,
+ left: "50%",
+ transform: "translateX(-50%)"
+}
+
+var GameModeTextStyle: CSSProperties = {
+ whiteSpace: "nowrap",
+ display: "inline-block",
+ position: "absolute",
+ bottom: 24,
+ left: "50%",
+ transform: "translateX(-50%)",
+ userSelect: "none",
+ fontWeight: 500
+}
+
+var SquareSize: CSSProperties = {
+ width: 90,
+ height: 90
+}
+
+var LeftAlignedTableColumn: CSSProperties = {
+ textAlign: "left",
+ paddingLeft: 16
+}
+
+var RightAlignedTableColumn: CSSProperties = {
+ textAlign: "right",
+ paddingRight: 16
+}
+
+export default class HomePage extends Component {
+ state: {
+ info: userInfo,
+ loggedIn: boolean
+ } = {
+ info: {},
+ loggedIn: false
+ }
+
+ getUserInfo () {
+ axios.request<userInfo>({
+ method: "get",
+ url: `/api/user/info`,
+ headers: {"content-type": "application/json"}
+ })
+ .then(request => this.setState({
+ info: request.data,
+ loggedIn: request.status == 200
+ }))
+ .catch(console.log);
+ }
+
+ constructor(props: {}) {
+ super(props);
+ this.getUserInfo();
+ }
+
+ render () {
+ return <div>
+ <NavBar/>
+ <ToastArea>
+ <Toast text="Met icoon" icon={<VideogameAssetIcon style={{ fontSize: 32 }}/>}/>
+ <Toast text="Confirmation" type="confirmation"/>
+ <Toast text="Error" type="error"/>
+ </ToastArea>
+ <CenteredPage width={802}>
+ <PageTitle>4 op een rij</PageTitle>
+ <Vierkant href="/game">
+ <VideogameAssetIcon style={GameModeIconStyle}/>
+ <span style={GameModeTextStyle}>Nieuw spel</span>
+ <div style={SquareSize}></div>
+ </Vierkant>
+ <Vierkant href="/">
+ <ExtensionIcon style={GameModeIconStyle}/>
+ <span style={GameModeTextStyle}>Puzzels</span>
+ <div style={SquareSize}></div>
+ </Vierkant>
+ <Vierkant href="/">
+ <Icon path={mdiRobotExcited} style={GameModeIconStyle}/>
+ <span style={GameModeTextStyle}>Tegen computer</span>
+ <div style={SquareSize}></div>
+ </Vierkant>
+ <Vierkant href={ this.state.loggedIn ? "/account" : undefined } style={{ verticalAlign: "top" }}>
+ <div style={{
+ position: "relative",
+ width: 280,
+ height: 90,
+ textAlign: "center",
+ display: this.state.loggedIn ? "none" : "inline-block"
+ }}>
+ <span style={{
+ userSelect: "none",
+ display: "inline-block",
+ position: "absolute",
+ left: 0, right: 0, top: 0
+ }}>Log in of maak een account aan om je scores op te slaan en toegang te krijgen tot meer functies</span>
+ <div style={{
+ display: "grid",
+ gridGap: 24,
+ gridTemplateColumns: "1fr 1fr",
+ position: "absolute",
+ left: 0, right: 0, bottom: 0
+ }}>
+ <Button href="/register" text="Registreren" style={{ backgroundColor: "var(--background-alt)" }}/>
+ <Button href="/login" text="Inloggen"/>
+ </div>
+ </div>
+ <div style={{
+ position: "relative",
+ width: 280,
+ height: 90,
+ display: this.state.loggedIn ? "inline-block" : "none"
+ }}>
+ <div style={{
+ position: "absolute",
+ top: 0, left: 0,
+ ...SquareSize
+ }}>
+ <AccountAvatar size={90} image="url(https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fblogs.agu.org%2Fwildwildscience%2Ffiles%2F2017%2F09%2FCapture-1.png&f=1&nofb=1)"/>
+ </div>
+ <div style={{
+ position: "absolute",
+ top: 0, left: 102,
+ width: 178, height: 90
+ }}>
+ <h2 style={{
+ maxWidth: 178,
+ fontSize: 20,
+ whiteSpace: "nowrap",
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ }}>{this.state.info.username}</h2>
+ <p style={{ marginTop: 6 }}>Score: 400</p>
+ <p style={{ position: "absolute", bottom: 0, left: 0 }}>
+ <span style={{ color: "var(--disk-b-text)" }}>0 W</span>
+ <span style={{ margin: "0 3px" }}>/</span>
+ <span style={{ color: "var(--disk-a-text)" }}>0 V</span>
+ <span style={{ margin: "0 3px" }}>/</span>
+ <span style={{ opacity: .75 }}>0 G</span>
+ </p>
+ </div>
+ </div>
+ </Vierkant>
+ <Vierkant width="calc(100% - 12px)" style={{ display: this.state.loggedIn ? "block" : "none" }}>
+ <h2>Recente partijen</h2>
+ <table width="100%" style={{ marginTop: "16px", textAlign: "center" }}>
+ <tr>
+ <th style={{ width: "50%" }}>Tegenstander</th>
+ <th style={{ width: "20%" }}>Uitkomst</th>
+ <th style={{ width: "15%" }}>Zetten</th>
+ <th style={{ width: "15%" }}>Datum</th>
+ </tr>
+ <tr>
+ <td style={LeftAlignedTableColumn}>Naam hier</td>
+ <td style={{ color: "var(--disk-b-text)" }}>Gewonnen</td>
+ <td>7</td>
+ <td style={RightAlignedTableColumn}>Vandaag</td>
+ </tr>
+ <tr>
+ <td style={LeftAlignedTableColumn}>Nog meer namen</td>
+ <td style={{ opacity: .6 }}>Gelijkspel</td>
+ <td>42</td>
+ <td style={RightAlignedTableColumn}>Gisteren</td>
+ </tr>
+ </table>
+ </Vierkant>
+ <Vierkant width="calc(100% - 12px)">
+ <h2>Nieuws ofzo</h2>
+ <p style={{ margin: "6px 0" }}>Chess.com heeft heel veel troep waar niemand naar kijkt</p>
+ </Vierkant>
+ </CenteredPage>
+ </div>
+ }
+}
+
diff --git a/pages/login.tsx b/pages/login.tsx
new file mode 100644
index 0000000..0061c70
--- /dev/null
+++ b/pages/login.tsx
@@ -0,0 +1,66 @@
+import { v4 as uuidv4 } from 'uuid';
+import axios from 'axios';
+
+import { NavBar } from '../components/navbar';
+import { CenteredPage } from '../components/page';
+import { Vierkant, Input, Button } from '../components/ui';
+
+function submitLogin() {
+ var formData = {
+ email: (document.getElementById("email") as HTMLInputElement).value,
+ password: (document.getElementById("password") as HTMLInputElement).value
+ }
+
+ if ( !formData.email ||
+ !formData.password ) {
+ alert("Vul alsjeblieft alle velden in!");
+ return;
+ }
+
+ axios({
+ method: "post",
+ url: `${window.location.origin}/api/auth/login`,
+ headers: {"content-type": "application/json"},
+ data: formData
+ })
+ .then(() => window.location.pathname = "/")
+ .catch(error => {
+ if (error.response.status === 401) {
+ alert("Verkeerde gebruikersnaam/wachtwoord combinatie!")
+ return;
+ }
+ alert("Er is iets fout gegaan!");
+ });
+}
+
+export default function LoginPage() {
+ return (
+ <div>
+ <NavBar/>
+ <CenteredPage width={500} style={{ height: "100vh" }}>
+ <div style={{
+ position: "relative",
+ top: "50%",
+ transform: "translateY(-50%)",
+ margin: "0 auto",
+ textAlign: "center"
+ }}>
+ <Vierkant>
+ <Input id="email" label="email of gebruikersnaam" style={{ marginBottom: 12 }}></Input>
+ <Input id="password" label="wachtwoord" type="password"></Input>
+ <div style={{
+ marginTop: 24,
+ gridGap: 24,
+ display: "grid",
+ gridTemplateColumns: "1fr 1fr"
+ }}>
+ <Button href="/register" text="Registreren" style={{ backgroundColor: "var(--background-alt)"}}></Button>
+ <Button text="Inloggen" onclick={submitLogin}></Button>
+ </div>
+ </Vierkant>
+ </div>
+ </CenteredPage>
+ </div>
+ );
+}
+
diff --git a/pages/register.tsx b/pages/register.tsx
new file mode 100644
index 0000000..6e2c73f
--- /dev/null
+++ b/pages/register.tsx
@@ -0,0 +1,93 @@
+import { NavBar } from '../components/navbar';
+import { CenteredPage } from '../components/page';
+import { Vierkant, Input, Button } from '../components/ui';
+
+import { v4 as uuidv4 } from 'uuid';
+import { validate as validateEmail } from 'email-validator';
+import axios from 'axios';
+
+function submitRegister() {
+ var formData = {
+ username: (document.getElementById("username") as HTMLInputElement).value,
+ email: (document.getElementById("email") as HTMLInputElement).value,
+ password: (document.getElementById("password") as HTMLInputElement).value
+ }
+
+ if ( !formData.username ||
+ !formData.email ||
+ !formData.password ) {
+ alert("Vul alsjeblieft alle velden in!");
+ return;
+ }
+
+ var passwordRegex: RegExp = /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$/;
+ /*
+ * ^ Begin string
+ * (?=.*[A-Z]) Minimaal één hoofdletter
+ * (?=.*[a-z]) Minimaal één kleine letter
+ * (?=.*[0-9]) Minimaal één getal
+ * .{8,} Minimaal acht tekens in lengte
+ * $ Ende string
+ * https://stackoverflow.com/questions/5142103/regex-to-validate-password-strength
+ */
+
+ //TODO: alert -> react toast / material-ui snackbar
+ if ( formData.username.length > 35 ) {
+ alert("Je gebruikersnaam kan maximaal 35 tekens lang zijn!");
+ return;
+ }
+
+ if ( !validateEmail(formData.email) ) {
+ alert("Voer alsjeblieft een geldig e-mail adres in!");
+ return;
+ }
+
+ //TODO: wachtwoord max 72 tekens ivm bcrypt
+ if ( !formData.password.match(passwordRegex) ) {
+ alert("Je wachtwoord moet minimaal 8 tekens lang zijn en een hoofdletter, kleine letter, en een nummer bevatten!");
+ return;
+ }
+
+ axios({
+ method: "post",
+ url: `${window.location.origin}/api/auth/signup`,
+ headers: {"content-type": "application/json"},
+ data: formData
+ })
+ .then(() => {
+ //TODO: email verificatie
+ // redirect naar home, automatische login
+ window.location.pathname = "/";
+ })
+ .catch(error => {
+ alert("Er is iets fout gegaan!");
+ console.log(error);
+ });
+}
+
+export default function RegisterPage() {
+ return (
+ <div>
+ <NavBar/>
+ <CenteredPage width={500} style={{ height: "100vh" }}>
+ <div style={{
+ position: "relative",
+ top: "50%",
+ transform: "translateY(-50%)",
+ margin: "0 auto",
+ textAlign: "center"
+ }}>
+ <Vierkant>
+ <form>
+ <Input id="username" label="gebruikersnaam" style={{ marginBottom: 12 }}></Input>
+ <Input id="email" label="email" style={{ marginBottom: 12 }}></Input>
+ <Input id="password" label="wachtwoord" type="password"></Input>
+ <Button text="Registreren" style={{ marginTop: 24 }} onclick={submitRegister}></Button>
+ </form>
+ </Vierkant>
+ </div>
+ </CenteredPage>
+ </div>
+ );
+}
+
diff --git a/pages/settings.tsx b/pages/settings.tsx
new file mode 100644
index 0000000..6bb7c46
--- /dev/null
+++ b/pages/settings.tsx
@@ -0,0 +1,33 @@
+import { CSSProperties } from 'react';
+
+import { NavBar } from '../components/navbar';
+import { CenteredPage, PageTitle } from '../components/page';
+import { Vierkant } from '../components/ui';
+/* import { AccountAvatar } from '../components/account'; */
+import { CurrentGameSettings, EditGameSettings } from '../components/gameSettings';
+
+var SettingsSectionHeaderStyle: CSSProperties = {
+ marginBottom: 24
+}
+
+export default function SettingsPage() {
+ return (
+ <div>
+ <NavBar/>
+ <CenteredPage width={802}>
+ <PageTitle>Instellingen</PageTitle>
+ <Vierkant width="calc(100% - 12px)">
+ <h2 style={SettingsSectionHeaderStyle}>Account</h2>
+ </Vierkant>
+ <Vierkant width="calc(100% - 12px)">
+ <h2 style={SettingsSectionHeaderStyle}>Kleuren</h2>
+ </Vierkant>
+ <Vierkant width="calc(100% - 12px)">
+ <h2 style={SettingsSectionHeaderStyle}>Standaard spelregels</h2>
+ <CurrentGameSettings/>
+ </Vierkant>
+ </CenteredPage>
+ </div>
+ );
+}
+