diff --git a/js/api.js b/js/api.js index 3c5db29..83f281b 100644 --- a/js/api.js +++ b/js/api.js @@ -40,6 +40,28 @@ const defaultstate_userinfo = { uid : null }; +const actiontypes_tournamentinfo = { + 'REQUEST_TOURNAMENT' : 'REQUEST_TOURNAMENT', + 'REQUEST_TOURNAMENT_SUCCESS' : 'REQUEST_TOURNAMENT_SUCCESS', + + 'MODIFY_TOURNAMENT' : 'MODIFY_TOURNAMENT', + 'MODIFY_TOURNAMENT_SUCCESS' : 'MODIFY_TOURNAMENT_SUCCESS', + 'MODIFY_TOURNAMENT_ERROR' : 'MODIFY_TOURNAMENT_ERROR', + + 'REHYDRATE' : 'TOURNAMENTINFO_REHYDRATE', + 'CLEAR' : 'TOURNAMENTINFO_CLEAR', +}; + +const defaultstate_tournamentinfo = { + code : '', + description : '', + id : -1, + name : '', + isPublic : '', + stages: [], + teams : [] +}; + export function postRequest(state, url, data) { return axios.post(api_url + url, data, { headers : generateHeaders(state) @@ -58,12 +80,18 @@ export function deleteRequest(state, url) { }); } +export function patchRequest(state, url, data) { + return axios.patch(api_url + url, data, { + headers : generateHeaders(state) + }); +} + function generateHeaders(state) { - if(state.isSignedIn) { + if(state.userinfo.isSignedIn) { return { - 'access-token' : state.accesstoken, - 'client' : state.client, - 'uid' : state.uid + 'access-token' : state.userinfo.accesstoken, + 'client' : state.userinfo.client, + 'uid' : state.userinfo.uid }; } else { return {}; @@ -102,7 +130,7 @@ function checkForAuthenticationHeaders(response) { const reducer_userinfo = (state = defaultstate_userinfo, action) => { switch(action.type) { case actiontypes_userinfo.REGISTER: - postRequest(state, '/users', { + postRequest(action.state, '/users', { 'username' : action.parameters.username, 'email' : action.parameters.email, 'password' : action.parameters.password @@ -143,7 +171,7 @@ const reducer_userinfo = (state = defaultstate_userinfo, action) => { errorMessages : action.parameters.errorMessages }); case actiontypes_userinfo.LOGIN: - postRequest(state, '/users/sign_in', { + postRequest(action.state, '/users/sign_in', { email : action.parameters.email, password : action.parameters.password }).then((resp) => { @@ -186,7 +214,7 @@ const reducer_userinfo = (state = defaultstate_userinfo, action) => { errorMessages : action.parameters.errorMessages }); case actiontypes_userinfo.LOGOUT: - deleteRequest(state, '/users/sign_out').then(() => { + deleteRequest(action.state, '/users/sign_out').then(() => { __store.dispatch({ type : actiontypes_userinfo.CLEAR }); }).catch(() => { __store.dispatch({ type : actiontypes_userinfo.CLEAR }); @@ -200,7 +228,7 @@ const reducer_userinfo = (state = defaultstate_userinfo, action) => { uid : action.parameters.uid }); case actiontypes_userinfo.VERIFY_CREDENTIALS: - getRequest(state, '/users/validate_token').then((resp) => { + getRequest(action.state, '/users/validate_token').then((resp) => { storeOptionalToken(resp); }).catch(() => { __store.dispatch({ type: actiontypes_userinfo.CLEAR }); @@ -224,12 +252,68 @@ const reducer_userinfo = (state = defaultstate_userinfo, action) => { } }; +const reducer_tournamentinfo = (state = defaultstate_tournamentinfo, action) => { + switch(action.type) { + case actiontypes_tournamentinfo.REQUEST_TOURNAMENT: + getRequest(action.state, '/tournaments/' + action.parameters.code).then((resp) => { + __store.dispatch({ + type: actiontypes_tournamentinfo.REQUEST_TOURNAMENT_SUCCESS, + parameters: resp.data + }); + storeOptionalToken(resp); + action.parameters.successCallback(); + }).catch(() => { + action.parameters.errorCallback(); + }); + return Object.assign({}, state, {}); + case actiontypes_tournamentinfo.REQUEST_TOURNAMENT_SUCCESS: + return Object.assign({}, state, { + code : action.parameters.code, + description : action.parameters.description, + id : action.parameters.id, + name : action.parameters.name, + isPublic : action.parameters.public, + stages: action.parameters.stages, + teams : action.parameters.teams + }); + case actiontypes_tournamentinfo.MODIFY_TOURNAMENT: + patchRequest(action.state, '/teams/' + action.parameters.teamid, { + name: action.parameters.name + }).then((resp) => { + storeOptionalToken(resp); + action.parameters.onSuccess(); + }).catch((error) => { + if(error.response) { + storeOptionalToken(error.response); + } + action.parameters.onError(); + }); + return Object.assign({}, state, {}); + case actiontypes_tournamentinfo.MODIFY_TOURNAMENT_SUCCESS: + + return Object.assign({}, state, {}); + case actiontypes_tournamentinfo.MODIFY_TOURNAMENT_ERROR: + + return Object.assign({}, state, {}); + + case actiontypes_tournamentinfo.REHYDRATE: + + return Object.assign({}, state, {}); + case actiontypes_tournamentinfo.CLEAR: + + return Object.assign({}, state, {}); + default: return state; + } +}; + const reducers = { - userinfo: reducer_userinfo + userinfo: reducer_userinfo, + tournamentinfo: reducer_tournamentinfo }; const default_applicationstate = { - userinfo : defaultstate_userinfo + userinfo : defaultstate_userinfo, + tournamentinfo: defaultstate_tournamentinfo }; var __store; @@ -250,7 +334,10 @@ export function verifyCredentials() { rehydrateApplicationState(); if(__store.getState().userinfo.isSignedIn) { - __store.dispatch({ type: actiontypes_userinfo.VERIFY_CREDENTIALS }); + __store.dispatch({ + type: actiontypes_userinfo.VERIFY_CREDENTIALS, + state: __store.getState() + }); } } @@ -261,7 +348,8 @@ export function register(username, email, password) { username: username, email: email, password: password - } + }, + state: __store.getState() }); } @@ -271,12 +359,41 @@ export function login(email, password) { parameters: { email: email, password: password - } + }, + state: __store.getState() }); } export function logout() { - __store.dispatch({ type : actiontypes_userinfo.LOGOUT }); + __store.dispatch({ + type : actiontypes_userinfo.LOGOUT, + state: __store.getState() + }); +} + +export function requestTournament(code, successCallback, errorCallback) { + __store.dispatch({ + type: actiontypes_tournamentinfo.REQUEST_TOURNAMENT, + parameters: { + code: code, + successCallback: successCallback, + errorCallback: errorCallback + }, + state: __store.getState() + }); +} + +export function updateTeamName(team, successCB, errorCB) { + __store.dispatch({ + type: actiontypes_tournamentinfo.MODIFY_TOURNAMENT, + parameters: { + teamid: team.id, + name: team.name, + onSuccess : successCB, + onError : errorCB + }, + state: __store.getState() + }); } export function getState() { @@ -293,6 +410,10 @@ function rehydrateApplicationState() { type : actiontypes_userinfo.REHYDRATE, parameters : Object.assign({}, persistedState.userinfo, {}) }); + __store.dispatch({ + type : actiontypes_tournamentinfo.REHYDRATE, + parameters : Object.assign({}, persistedState.tournamentinfo, {}) + }); } } diff --git a/js/components/ErrorComponents.js b/js/components/ErrorComponents.js new file mode 100644 index 0000000..d33190b --- /dev/null +++ b/js/components/ErrorComponents.js @@ -0,0 +1,86 @@ +import Head from 'next/head'; +import React from 'react'; +import {Footer, TurniereNavigation} from '../CommonComponents'; +import 'bootstrap/dist/css/bootstrap.min.css'; +import {Container} from 'reactstrap'; +import '../../static/everypage.css'; +import '../../static/css/error.css'; + +export class ErrorPageComponent extends React.Component { + + static getInitialProps({ statusCode }) { + return { statusCode }; + } + + render() { + return ( +
+ + turnie.re - Error {this.props.statusCode} + + + +
+ ); + } +} + +function ErrorPage(props){ + return ( + +
+
+ +
+
+

{props.statusCode}

+
+
+ +
+ ); +} + +function ErrorMessage(props) { + switch (props.code) { + case 400: + return (
+

Deine Anfrage ist fehlerhaft.

+

+ Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. +

+
); + case 403: + return (
+

Du bist nicht autorisiert, diese Seite aufzurufen.

+

+ Bitte stelle sicher, dass Du angemeldet bist und auf dieses Turnier oder dieses Match zugreifen darfst. +

+

+ Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. +

+
); + case 404: + return (
+

Die aufgerufene Seite wurde leider nicht gefunden.

+

+ Entweder hast Du dich vertippt, oder die gesuchte Seite gibt es nicht. +

+

+ Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. +

+
); + case 500: + return (
+

Diese Seite funktioniert nicht.

+

+ turnie.re kann Deine Anfrage im Moment nicht verarbeiten. Bitte versuche es später erneut. +

+
); + default: + return (
+

Ein unbekannter Fehler ist aufgetreten.

+
); + } +} diff --git a/package.json b/package.json index 50ad571..601fd1b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "next": "^7.0.2", "react": "^16.6.1", "react-dom": "^16.6.1", + "react-notify-toast": "^0.5.0", "react-redux": "^5.1.1", "reactstrap": "^6.5.0", "redux": "^4.0.1", diff --git a/pages/_app.js b/pages/_app.js index 3bf91cd..39529b0 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -2,18 +2,15 @@ import App, {Container} from 'next/app'; import React from 'react'; import { Provider } from 'react-redux'; import withReduxStore from '../js/redux/reduxStoreBinder'; -import { verifyCredentials } from '../js/api'; +import Notifications from 'react-notify-toast'; class TurniereApp extends App { - componentDidMount() { - verifyCredentials(); - } - render () { const {Component, pageProps, reduxStore} = this.props; return ( + diff --git a/pages/_error.js b/pages/_error.js index 202f1ca..c0babdc 100644 --- a/pages/_error.js +++ b/pages/_error.js @@ -1,10 +1,9 @@ -import Head from 'next/head'; +import { ErrorPageComponent } from '../js/components/ErrorComponents.js'; import React from 'react'; -import {Footer, TurniereNavigation} from '../js/CommonComponents'; -import 'bootstrap/dist/css/bootstrap.min.css'; -import {Container} from 'reactstrap'; -import '../static/everypage.css'; -import '../static/css/error.css'; + +import { + verifyCredentials +} from '../js/api'; export default class Error extends React.Component { static getInitialProps({ res, err }) { @@ -12,75 +11,13 @@ export default class Error extends React.Component { return { statusCode }; } + componentDidMount() { + verifyCredentials(); + } + render() { return ( -
- - turnie.re - Error {this.props.statusCode} - - - -
-
+ ); } } - -function ErrorPage(props){ - return ( - -
-
- -
-
-

{props.statusCode}

-
-
- -
- ); -} - -function ErrorMessage(props) { - switch (props.code) { - case 400: - return (
-

Deine Anfrage ist fehlerhaft.

-

- Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. -

-
); - case 403: - return (
-

Du bist nicht autorisiert, diese Seite aufzurufen.

-

- Bitte stelle sicher, dass Du angemeldet bist und auf dieses Turnier oder dieses Match zugreifen darfst. -

-

- Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. -

-
); - case 404: - return (
-

Die aufgerufene Seite wurde leider nicht gefunden.

-

- Entweder hast Du dich vertippt, oder die gesuchte Seite gibt es nicht. -

-

- Wir empfehlen, die eingegebene Seite über die Startseite zu suchen. -

-
); - case 500: - return (
-

Diese Seite funktioniert nicht.

-

- turnie.re kann Deine Anfrage im Moment nicht verarbeiten. Bitte versuche es später erneut. -

-
); - default: - return (
-

Ein unbekannter Fehler ist aufgetreten.

-
); - } -} \ No newline at end of file diff --git a/pages/create.js b/pages/create.js index d2ee8db..b1606f4 100644 --- a/pages/create.js +++ b/pages/create.js @@ -16,20 +16,34 @@ import { Label } from 'reactstrap'; +import { + verifyCredentials +} from '../js/api'; + import EditableStringList from '../js/EditableStringList'; -export default () => ( -
- - Turnier erstellen: turnie.re - - -
- -
-
-
-); +export default class CreatePage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Turnier erstellen: turnie.re + + +
+ +
+
+
+ ); + } +} + function CreateTournamentCard() { return ( diff --git a/pages/faq.js b/pages/faq.js index 57e3bdb..d747e7d 100644 --- a/pages/faq.js +++ b/pages/faq.js @@ -5,6 +5,10 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js'; import '../static/everypage.css'; +import { + verifyCredentials +} from '../js/api'; + function Main() { return (
@@ -216,14 +220,23 @@ function TournamentFaq() { ); } -export default () => ( -
- - FAQ: turnie.re - - - -
-
-
-); +export default class FaqPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + FAQ: turnie.re + + + +
+
+
+ ); + } +} diff --git a/pages/imprint.js b/pages/imprint.js index feec607..34fb942 100644 --- a/pages/imprint.js +++ b/pages/imprint.js @@ -5,6 +5,10 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import {BigImage, Footer, TurniereNavigation} from '../js/CommonComponents.js'; import '../static/everypage.css'; +import { + verifyCredentials +} from '../js/api'; + function Main() { return (
@@ -69,14 +73,23 @@ function ImprintText(){ } -export default () => ( -
- - Impressum: turnie.re - - - -
-
-
-); +export default class ImprintPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Impressum: turnie.re + + + +
+
+
+ ); + } +} diff --git a/pages/index.js b/pages/index.js index dee8a02..8dfc2ea 100644 --- a/pages/index.js +++ b/pages/index.js @@ -12,6 +12,10 @@ import '../static/css/index.css'; import { connect } from 'react-redux'; +import { + verifyCredentials +} from '../js/api'; + function Main() { return (
@@ -114,7 +118,7 @@ function PromotedLinkTournamentCode() {
- +

Gib hier einen Turnier Code ein, um direkt zum entsprechenden Turnier zu gelangen.

@@ -165,6 +169,11 @@ function PromotedLinkCreateTournament() { class Index extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + render () { return (
diff --git a/pages/list.js b/pages/list.js index 8e03fa4..200e412 100644 --- a/pages/list.js +++ b/pages/list.js @@ -1,22 +1,37 @@ import Head from 'next/head'; -import '../static/everypage.css'; -import { Footer, TurniereNavigation } from '../js/CommonComponents'; import React from 'react'; import { Card, CardBody, Container } from 'reactstrap'; -import { getRequest, getState } from '../js/api'; -export default () => ( -
- - Öffentliche Turniere: turnie.re - - -
- -
-
-
-); +import { Footer, TurniereNavigation } from '../js/CommonComponents'; +import { + getRequest, + getState, + verifyCredentials +} from '../js/api'; + +import '../static/everypage.css'; + +export default class ListPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Öffentliche Turniere: turnie.re + + +
+ +
+
+
+ ); + } +} class TournamentList extends React.Component { constructor(props) { diff --git a/pages/login.js b/pages/login.js index 6acc489..1a58e49 100644 --- a/pages/login.js +++ b/pages/login.js @@ -6,18 +6,31 @@ import { Button, Card, CardBody, Container, Form, FormGroup, Input, Label } from import { login } from '../js/api'; import { connect } from 'react-redux'; -export default () => ( -
- - Login: turnie.re - - -
- -
-
-
-); +import { + verifyCredentials +} from '../js/api'; + +export default class LoginPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Login: turnie.re + + +
+ +
+
+
+ ); + } +} function Login() { return ( diff --git a/pages/privacy.js b/pages/privacy.js index 9f0c2b9..e4263eb 100644 --- a/pages/privacy.js +++ b/pages/privacy.js @@ -5,6 +5,10 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js'; import '../static/everypage.css'; +import { + verifyCredentials +} from '../js/api'; + function Main() { return (
@@ -488,14 +492,23 @@ function PrivacyText(){ } -export default () => ( -
- - Datenschutzerklärung: turnie.re - - - -
-
-
-); +export default class PrivacyPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Datenschutzerklärung: turnie.re + + + +
+
+
+ ); + } +} diff --git a/pages/register.js b/pages/register.js index cf0365f..096d6c1 100644 --- a/pages/register.js +++ b/pages/register.js @@ -6,19 +6,32 @@ import { Button, Card, CardBody, Container, Form, FormGroup, FormText, Input, La import { register } from '../js/api'; import { connect } from 'react-redux'; -export default () => ( -
- - Registrieren: turnie.re - - -
- - -
-
-
-); +import { + verifyCredentials +} from '../js/api'; + +export default class RegisterPage extends React.Component { + + componentDidMount() { + verifyCredentials(); + } + + render() { + return ( +
+ + Registrieren: turnie.re + + +
+ + +
+
+
+ ); + } +} function Register() { return ( diff --git a/pages/tournament-edit.js b/pages/tournament-edit.js new file mode 100644 index 0000000..0c89d91 --- /dev/null +++ b/pages/tournament-edit.js @@ -0,0 +1,290 @@ +import Head from 'next/head'; +import React from 'react'; +import 'bootstrap/dist/css/bootstrap.min.css'; +import { connect } from 'react-redux'; +import { notify } from 'react-notify-toast'; + +import { requestTournament } from '../js/api'; +import { BigImage, Footer, TurniereNavigation } from '../js/CommonComponents.js'; +import { ErrorPageComponent } from '../js/components/ErrorComponents.js'; + +import { + Container, + Button, + Card, + CardBody, + Table +} from 'reactstrap'; + +import { + verifyCredentials, + updateTeamName +} from '../js/api'; + +import '../static/everypage.css'; +import '../static/css/index.css'; + +class EditTournamentPage extends React.Component { + + static async getInitialProps({query}) { + return {query}; + } + + constructor(props) { + super(props); + + this.state = { + validCode: true + }; + } + + componentDidMount() { + verifyCredentials(); + requestTournament(this.props.query.code, () => { + this.setState({ validCode: true }); + this._edittournamentcontent.notifyOfContentUpdate(); + }, () => { + this.setState({ validCode: false }); + }); + } + + render() { + const { validCode } = this.state; + const { name } = this.props; + + if(validCode) { + return ( +
+ + Turnie.re - Turnier bearbeiten + + + + + { this._edittournamentcontent = edittournamentcontent; }}/> + +
+
+ ); + } else { + return ( + + ); + } + } +} + +function mapStateToTournamentInfo(state) { + const { name } = state.tournamentinfo; + return { name }; +} + +export default connect( + mapStateToTournamentInfo +)(EditTournamentPage); + +class EditTournamentContent extends React.Component { + + render() { + return ( +
+ + { this._edittournamentpropertiesfield = field; }}/> + { this._editteamfield = field; }}/> +
+ ); + } + + notifyOfContentUpdate() { + this._edittournamentpropertiesfield.notifyOfContentUpdate(); + this._editteamfield.notifyOfContentUpdate(); + } +} + +function ReturnToTournamentButton() { + return ( + + + + ); +} + +class EditTournamentPropertiesField extends React.Component { + + render() { + return ( + + +

Turnier-Eigenschaften ändern

+ { this._visibleedittournamentform = form; }}/> +
+
+ ); + } + + notifyOfContentUpdate() { + this._visibleedittournamentform.getWrappedInstance().notifyOfContentUpdate(); + } +} + +class EditTournamentForm extends React.Component { + + constructor(props) { + super(props); + + this.state = { + name : '', + description : '', + isPublic : false + }; + } + + render() { + const { name, description, isPublic } = this.state; + + return ( +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ ); + } + + notifyOfContentUpdate() { + const { name, description, isPublic } = this.props; + + this.setState({ + name : name? name : '', + description : description? description : '', + isPublic : isPublic + }); + } + + handleClick() { + // TODO: Apply changes to the tournament properties + } + + handleNameInput(input) { + this.setState({ name : input.target.value }); + } + + handleDescriptionInput(input) { + this.setState({ description : input.target.value }); + } + + handlePublicInput(input) { + this.setState({ public : input.target.value }); + } +} + +function mapStateToTournamentFormProps(state) { + const { name, description, isPublic } = state.tournamentinfo; + return { name, description, isPublic }; +} + +const VisibleEditTournamentForm = connect( + mapStateToTournamentFormProps, + null, null, { withRef : true} +)(EditTournamentForm); + +class EditTeamField extends React.Component { + + render() { + return ( + + +

Team-Namen ändern

+ { this._visibleeditteamnamesform = form; }}/> +
+
+ ); + } + + notifyOfContentUpdate() { + this._visibleeditteamnamesform.getWrappedInstance().notifyOfContentUpdate(); + } +} + +class EditTeamNamesForm extends React.Component { + + constructor(props) { + super(props); + + this.state = { + teams : [] + }; + } + + render() { + const { teams } = this.state; + + return ( +
+ + + { + teams.map((team, index) => + + + + + ) + } + +
+
+ ); + } + + notifyOfContentUpdate() { + const { teams } = this.props; + + this.setState({ + teams : teams + }); + } + + handleNameInput(index, input) { + var team = this.state.teams.slice(); + + team[index].name = input.target.value; + + this.setState({ + teams : team + }); + } + + handleClick(index) { + updateTeamName(this.state.teams[index], () => { + notify.show('Team Name wurde erfolgreich geändert.', 'success', 5000); + }, () => { + notify.show('Team Name konnte nicht geändert werden.', 'warning', 5000); + }); + } +} + +function mapStateToTeamFormProps(state) { + const { teams } = state.tournamentinfo; + return { teams }; +} + +const VisibleEditTeamNamesForm = connect( + mapStateToTeamFormProps, + null, null, { withRef : true } +)(EditTeamNamesForm); + diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js index 09cde55..21fddff 100644 --- a/pages/tournament-fullscreen.js +++ b/pages/tournament-fullscreen.js @@ -1,11 +1,19 @@ import Head from 'next/head'; import React from 'react'; +import { + verifyCredentials +} from '../js/api'; + class FullscreenTournamentPage extends React.Component { static async getInitialProps({query}) { return {query}; } + + componentDidMount() { + verifyCredentials(); + } render() { return ( diff --git a/pages/tournament.js b/pages/tournament.js index d864dd8..40753f5 100644 --- a/pages/tournament.js +++ b/pages/tournament.js @@ -22,29 +22,39 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import {BigImage, Footer, TurniereNavigation} from '../js/CommonComponents.js'; import '../static/everypage.css'; import '../static/css/tournament.css'; -import { getRequest, getState } from '../js/api'; -function Tournament(props) { - // TODO: Change href-prop of the anchor tag to contain the tournament code - return ( -
- - Turnier bearbeiten -

{props.tournament.description}

- - - {props.tournament.isPublic ? 'Das Turnier ist öffentlich.' : 'Das Turnier ist privat.'} - - Turnier-Code: {props.tournament.code} - von {props.tournament.ownerUsername} - -
-
- {props.tournament.playoffStages.map(stage => - )} +import { + getRequest, + getState, + verifyCredentials +} from '../js/api'; + +class TournamentPage extends React.Component { + + render() { + const { id, description, isPublic, code, ownerUsername, playoffStages } = this.props.tournament; + + // TODO: Change href-prop of the anchor tag to contain the tournament code + return ( +
+ + Turnier bearbeiten +

{description}

+ + + {isPublic ? 'Das Turnier ist öffentlich.' : 'Das Turnier ist privat.'} + + Turnier-Code: {code} + von {ownerUsername} + +
+
+ {playoffStages.map(stage => + )} +
-
- ); + ); + } } function getLevelName(levelNumber) { @@ -57,10 +67,12 @@ function getLevelName(levelNumber) { } function TournamentContainer(props) { - if (props.data === null) { + const { tournament } = props.data; + + if (tournament === null) { return null; } else { - return ; + return ; } } @@ -339,9 +351,24 @@ function convertMatch(apiMatch) { } class Main extends React.Component { + + static async getInitialProps({query}) { + return {query}; + } + constructor(props) { super(props); + + this.state = { + tournament : null + }; + } + + componentDidMount() { + verifyCredentials(); + const code = this.props.query.code; + getRequest(getState(), '/tournaments/' + code) .then(response => { this.setState({tournament: convertTournament(response.data)}); @@ -349,12 +376,9 @@ class Main extends React.Component { .catch(() => { /* TODO: Show some kind of error or smth */ }); } - static async getInitialProps({query}) { - return {query}; - } render() { - const tournamentName = this.state === null ? 'Turnier' : this.state.tournament.name; + const tournamentName = this.state.tournament === null ? 'Turnier' : this.state.tournament.name; return (
diff --git a/server.js b/server.js index daccc7d..188d268 100644 --- a/server.js +++ b/server.js @@ -8,7 +8,7 @@ const handle = app.getRequestHandler(); app.prepare() .then(() => { const server = express(); - + server.get('/t/:code', (req, res) => { const actualPage = '/tournament'; const queryParam = { code: req.params.code }; @@ -21,6 +21,12 @@ app.prepare() app.render(req, res, actualPage, queryParam); }); + server.get('/t/:code/edit', (req, res) => { + const actualPage = '/tournament-edit'; + const queryParam = { code: req.params.code }; + app.render(req, res, actualPage, queryParam); + }); + server.get('*', (req, res) => { return handle(req, res); }); diff --git a/static/css/index.css b/static/css/index.css index 0ae4d38..665ccab 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -1,4 +1,8 @@ .index-cards { background: url("/static/images/tennis-blurred.jpg") no-repeat center; background-size: cover; +} + +h2, h3, h4 { + font-family: Halt, sans-serif; } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 042a220..912caed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5635,7 +5635,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -6388,6 +6388,14 @@ react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-notify-toast@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/react-notify-toast/-/react-notify-toast-0.5.0.tgz#b00cf50a3cc97a1d222ecd7d7a8e7f14bef5fa67" + integrity sha1-sAz1CjzJeh0iLs19eo5/FL71+mc= + dependencies: + object-assign "^4.0.0" + prop-types "^15.5.8" + react-popper@^0.10.4: version "0.10.4" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-0.10.4.tgz#af2a415ea22291edd504678d7afda8a6ee3295aa"