Create a first draft of the edit-tournament-page
This commit is contained in:
parent
d81a8f8cc6
commit
909faff65c
92
js/api.js
92
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,6 +80,9 @@ export function deleteRequest(state, url) {
|
|||
});
|
||||
}
|
||||
|
||||
// PATCH /teams/{ id }
|
||||
// { 'name' : ... }
|
||||
|
||||
function generateHeaders(state) {
|
||||
if(state.userinfo.isSignedIn) {
|
||||
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 = {
|
||||
userinfo: reducer_userinfo
|
||||
userinfo: reducer_userinfo,
|
||||
tournamentinfo: reducer_tournamentinfo
|
||||
};
|
||||
|
||||
const default_applicationstate = {
|
||||
userinfo : defaultstate_userinfo
|
||||
userinfo : defaultstate_userinfo,
|
||||
tournamentinfo: defaultstate_tournamentinfo
|
||||
};
|
||||
|
||||
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() {
|
||||
const persistedState = localStorage.getItem('reduxState') ?
|
||||
JSON.parse(localStorage.getItem('reduxState')) :
|
||||
|
|
@ -297,6 +381,10 @@ function rehydrateApplicationState() {
|
|||
type : actiontypes_userinfo.REHYDRATE,
|
||||
parameters : Object.assign({}, persistedState.userinfo, {})
|
||||
});
|
||||
__store.dispatch({
|
||||
type : actiontypes_tournamentinfo.REHYDRATE,
|
||||
parameters : Object.assign({}, persistedState.tournamentinfo, {})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,4 @@
|
|||
import Head from 'next/head';
|
||||
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 { ErrorPageComponent } from '../js/components/ErrorComponents.js';
|
||||
|
||||
export default class Error extends React.Component {
|
||||
static getInitialProps({ res, err }) {
|
||||
|
|
@ -14,73 +8,7 @@ export default class Error extends React.Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>turnie.re - Error {this.props.statusCode}</title>
|
||||
</Head>
|
||||
<TurniereNavigation/>
|
||||
<ErrorPage statusCode={this.props.statusCode}/>
|
||||
<Footer/>
|
||||
</div>
|
||||
<ErrorPageComponent statusCode={this.props.statusCode}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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>);
|
||||
}
|
||||
}
|
||||
|
|
@ -114,7 +114,7 @@ function PromotedLinkTournamentCode() {
|
|||
<CardBody className="row">
|
||||
<form id="turniercode-form" className="col-lg-4" action="/t" method="get">
|
||||
<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>
|
||||
<div className="col-lg-8">
|
||||
<p>Gib hier einen Turnier Code ein, um direkt zum entsprechenden Turnier zu gelangen.</p>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue