diff options
Diffstat (limited to 'pages')
-rw-r--r-- | pages/_app.tsx | 6 | ||||
-rw-r--r-- | pages/game.tsx | 84 | ||||
-rw-r--r-- | pages/index.tsx | 198 | ||||
-rw-r--r-- | pages/login.tsx | 66 | ||||
-rw-r--r-- | pages/register.tsx | 93 | ||||
-rw-r--r-- | pages/settings.tsx | 33 |
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> + ); +} + |