Create a first draft of the edit-tournament-page

This commit is contained in:
JP1998 2018-12-12 15:43:34 +01:00
parent d81a8f8cc6
commit 909faff65c
7 changed files with 463 additions and 78 deletions

View File

@ -40,6 +40,28 @@ const defaultstate_userinfo = {
uid : null 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) { export function postRequest(state, url, data) {
return axios.post(api_url + url, data, { return axios.post(api_url + url, data, {
headers : generateHeaders(state) headers : generateHeaders(state)
@ -58,6 +80,9 @@ export function deleteRequest(state, url) {
}); });
} }
// PATCH /teams/{ id }
// { 'name' : ... }
function generateHeaders(state) { function generateHeaders(state) {
if(state.userinfo.isSignedIn) { if(state.userinfo.isSignedIn) {
return { return {
@ -224,12 +249,59 @@ 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((error) => {
console.log(error);
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:
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 = { const reducers = {
userinfo: reducer_userinfo userinfo: reducer_userinfo,
tournamentinfo: reducer_tournamentinfo
}; };
const default_applicationstate = { const default_applicationstate = {
userinfo : defaultstate_userinfo userinfo : defaultstate_userinfo,
tournamentinfo: defaultstate_tournamentinfo
}; };
var __store; var __store;
@ -287,6 +359,18 @@ export function logout() {
}); });
} }
export function requestTournament(code, successCallback, errorCallback) {
__store.dispatch({
type: actiontypes_tournamentinfo.REQUEST_TOURNAMENT,
parameters: {
code: code,
successCallback: successCallback,
errorCallback: errorCallback
},
state: __store.getState()
});
}
function rehydrateApplicationState() { function rehydrateApplicationState() {
const persistedState = localStorage.getItem('reduxState') ? const persistedState = localStorage.getItem('reduxState') ?
JSON.parse(localStorage.getItem('reduxState')) : JSON.parse(localStorage.getItem('reduxState')) :
@ -297,6 +381,10 @@ function rehydrateApplicationState() {
type : actiontypes_userinfo.REHYDRATE, type : actiontypes_userinfo.REHYDRATE,
parameters : Object.assign({}, persistedState.userinfo, {}) parameters : Object.assign({}, persistedState.userinfo, {})
}); });
__store.dispatch({
type : actiontypes_tournamentinfo.REHYDRATE,
parameters : Object.assign({}, persistedState.tournamentinfo, {})
});
} }
} }

View File

@ -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 (
<div>
<Head>
<title>turnie.re - Error {this.props.statusCode}</title>
</Head>
<TurniereNavigation/>
<ErrorPage statusCode={this.props.statusCode}/>
<Footer/>
</div>
);
}
}
function ErrorPage(props){
return (
<Container className="mb-5">
<div className="row mb-5">
<div className="col-lg text-center">
<img src="/static/images/logo-questionmark.png" className="w-75 img-fluid"/>
</div>
<div className="col-lg error-code-box">
<h1 className="custom-font py-5">{props.statusCode}</h1>
</div>
</div>
<ErrorMessage code={props.statusCode}/>
</Container>
);
}
function ErrorMessage(props) {
switch (props.code) {
case 400:
return (<div className="running-text">
<h2>Deine Anfrage ist fehlerhaft.</h2>
<p>
Wir empfehlen, die eingegebene Seite über die <a href="/">Startseite</a> zu suchen.
</p>
</div>);
case 403:
return (<div className="running-text">
<h2>Du bist nicht autorisiert, diese Seite aufzurufen.</h2>
<p>
Bitte stelle sicher, dass Du angemeldet bist und auf dieses Turnier oder dieses Match zugreifen darfst.
</p>
<p>
Wir empfehlen, die eingegebene Seite über die <a href="/">Startseite</a> zu suchen.
</p>
</div>);
case 404:
return (<div className="running-text">
<h2>Die aufgerufene Seite wurde leider nicht gefunden.</h2>
<p>
Entweder hast Du dich vertippt, oder die gesuchte Seite gibt es nicht.
</p>
<p>
Wir empfehlen, die eingegebene Seite über die <a href="/">Startseite</a> zu suchen.
</p>
</div>);
case 500:
return (<div className="running-text">
<h2>Diese Seite funktioniert nicht.</h2>
<p>
turnie.re kann Deine Anfrage im Moment nicht verarbeiten. Bitte versuche es später erneut.
</p>
</div>);
default:
return (<div>
<h2>Ein unbekannter Fehler ist aufgetreten.</h2>
</div>);
}
}

View File

@ -1,10 +1,4 @@
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';
export default class Error extends React.Component { export default class Error extends React.Component {
static getInitialProps({ res, err }) { static getInitialProps({ res, err }) {
@ -14,73 +8,7 @@ export default class Error extends React.Component {
render() { render() {
return ( return (
<div> <ErrorPageComponent statusCode={this.props.statusCode}/>
<Head>
<title>turnie.re - Error {this.props.statusCode}</title>
</Head>
<TurniereNavigation/>
<ErrorPage statusCode={this.props.statusCode}/>
<Footer/>
</div>
); );
} }
} }
function ErrorPage(props){
return (
<Container className="mb-5">
<div className="row mb-5">
<div className="col-lg text-center">
<img src="/static/images/logo-questionmark.png" className="w-75 img-fluid"/>
</div>
<div className="col-lg error-code-box">
<h1 className="custom-font py-5">{props.statusCode}</h1>
</div>
</div>
<ErrorMessage code={props.statusCode}/>
</Container>
);
}
function ErrorMessage(props) {
switch (props.code) {
case 400:
return (<div className="running-text">
<h2>Deine Anfrage ist fehlerhaft.</h2>
<p>
Wir empfehlen, die eingegebene Seite über die <a href="/">Startseite</a> zu suchen.
</p>
</div>);
case 403:
return (<div className="running-text">
<h2>Du bist nicht autorisiert, diese Seite aufzurufen.</h2>
<p>
Bitte stelle sicher, dass Du angemeldet bist und auf dieses Turnier oder dieses Match zugreifen darfst.
</p>
<p>
Wir empfehlen, die eingegebene Seite über die <a href="/">Startseite</a> zu suchen.
</p>
</div>);
case 404:
return (<div className="running-text">
<h2>Die aufgerufene Seite wurde leider nicht gefunden.</h2>
<p>
Entweder hast Du dich vertippt, oder die gesuchte Seite gibt es nicht.
</p>
<p>
Wir empfehlen, die eingegebene Seite über die <a href="/">Startseite</a> zu suchen.
</p>
</div>);
case 500:
return (<div className="running-text">
<h2>Diese Seite funktioniert nicht.</h2>
<p>
turnie.re kann Deine Anfrage im Moment nicht verarbeiten. Bitte versuche es später erneut.
</p>
</div>);
default:
return (<div>
<h2>Ein unbekannter Fehler ist aufgetreten.</h2>
</div>);
}
}

View File

@ -114,7 +114,7 @@ function PromotedLinkTournamentCode() {
<CardBody className="row"> <CardBody className="row">
<form id="turniercode-form" className="col-lg-4" action="/t" method="get"> <form id="turniercode-form" className="col-lg-4" action="/t" method="get">
<input className="form-control" type="search" name="code" placeholder="Turnier-Code"/> <input className="form-control" type="search" name="code" placeholder="Turnier-Code"/>
<button className="btn btn-outline-success w-100 my-2" type="submit">Turnier-Code öffnen</button> <Button className="btn btn-outline-success w-100 my-2" type="submit">Turnier-Code öffnen</Button>
</form> </form>
<div className="col-lg-8"> <div className="col-lg-8">
<p>Gib hier einen Turnier Code ein, um direkt zum entsprechenden Turnier zu gelangen.</p> <p>Gib hier einen Turnier Code ein, um direkt zum entsprechenden Turnier zu gelangen.</p>

273
pages/tournament-edit.js Normal file
View File

@ -0,0 +1,273 @@
import Head from 'next/head';
import React from 'react';
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,
CardTitle,
Table
} from 'reactstrap';
import { connect } from 'react-redux';
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() {
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 (
<div>
<Head>
<title>Turnie.re - Turnier bearbeiten</title>
</Head>
<TurniereNavigation/>
<BigImage text={ name }/>
<EditTournamentContent ref={(edittournamentcontent) => { this._edittournamentcontent = edittournamentcontent; }}/>
<Footer/>
</div>
);
} else {
return (
<ErrorPageComponent statusCode={ 404 }/>
);
}
}
}
function mapStateToTournamentInfo(state) {
const { name } = state.tournamentinfo;
return { name };
}
export default connect(
mapStateToTournamentInfo
)(EditTournamentPage);
class EditTournamentContent extends React.Component {
render() {
const { code } = this.props;
return (
<div>
<ReturnToTournamentButton/>
<EditTournamentPropertiesField ref={(field) => { this._edittournamentpropertiesfield = field; }}/>
<EditTeamField ref={(field) => { this._editteamfield = field; }}/>
</div>
);
}
notifyOfContentUpdate() {
this._edittournamentpropertiesfield.notifyOfContentUpdate();
this._editteamfield.notifyOfContentUpdate();
}
}
function ReturnToTournamentButton() {
return (
<Container className="px-0">
<Button color="secondary" className="mb-5 w-100" href="./">Zurück zum Turnier</Button>
</Container>
);
}
class EditTournamentPropertiesField extends React.Component {
render() {
return (
<Card className="container">
<CardBody>
<h2>Turnier-Eigenschaften ändern</h2>
<VisibleEditTournamentForm ref={(form) => { this._visibleedittournamentform = form; }}/>
</CardBody>
</Card>
);
}
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 (
<div>
<div className="form-group">
<label htmlFor="name">Turnier-Name</label>
<input className="form-control" type="text" name="name" id="name" value={ name } placeholder={ name } onChange={ this.handleNameInput.bind(this) } />
</div>
<div className="form-group">
<label htmlFor="name">Turnier-Beschreibung</label>
<input className="form-control" type="text" name="name" id="name" value={ description } placeholder={ description } onChange={ this.handleDescriptionInput.bind(this) } />
</div>
<div className="form-group custom-control custom-checkbox">
<input className="custom-control-input" type="checkbox" name="isPublic" id="isPublic" value={ isPublic } onChange={ this.handlePublicInput.bind(this) } />
<label htmlFor="isPublic" className="custom-control-label">Das Turnier öffentlich anzeigen</label>
</div>
<div className="form-group">
<div className="input-group">
<Button color="success" className="px-5" onClick={ this.handleClick.bind(this) }>Ändern</Button>
</div>
</div>
</div>
);
}
notifyOfContentUpdate() {
const { name, description, isPublic } = this.props;
this.setState({
name : name? name : '',
description : description? description : '',
isPublic : isPublic
});
}
handleClick(input) {
// 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 (
<Card className="container my-4">
<CardBody>
<h2>Team-Namen ändern</h2>
<VisibleEditTeamNamesForm ref={(form) => { this._visibleeditteamnamesform = form; }}/>
</CardBody>
</Card>
);
}
notifyOfContentUpdate() {
this._visibleeditteamnamesform.getWrappedInstance().notifyOfContentUpdate();
}
}
class EditTeamNamesForm extends React.Component {
constructor(props) {
super(props);
this.state = {
teams : []
};
}
render() {
const { teams } = this.state;
return (
<div>
<Table className="table-striped mt-3">
<tbody>
{
teams.map((team, index) => {
})
}
</tbody>
</Table>
</div>
);
}
notifyOfContentUpdate() {
const { teams } = this.props;
this.setState({
teams : teams
});
}
handleClick(input) {
// TODO: Apply changes to the tournament properties
}
handleNameInput(input) {
this.setState({ name : input.target.value });
}
}
function mapStateToTeamFormProps(state) {
const { teams } = state.tournamentinfo;
return { teams };
}
const VisibleEditTeamNamesForm = connect(
mapStateToTeamFormProps,
null, null, { withRef : true }
)(EditTeamNamesForm);

View File

@ -8,7 +8,7 @@ const handle = app.getRequestHandler();
app.prepare() app.prepare()
.then(() => { .then(() => {
const server = express(); const server = express();
server.get('/t/:code', (req, res) => { server.get('/t/:code', (req, res) => {
const actualPage = '/tournament'; const actualPage = '/tournament';
const queryParam = { code: req.params.code }; const queryParam = { code: req.params.code };
@ -21,6 +21,12 @@ app.prepare()
app.render(req, res, actualPage, queryParam); 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) => { server.get('*', (req, res) => {
return handle(req, res); return handle(req, res);
}); });

View File

@ -1,4 +1,8 @@
.index-cards { .index-cards {
background: url("/static/images/tennis-blurred.jpg") no-repeat center; background: url("/static/images/tennis-blurred.jpg") no-repeat center;
background-size: cover; background-size: cover;
}
h2, h3, h4 {
font-family: Halt, sans-serif;
} }