diff --git a/js/components/GroupStage.js b/js/components/GroupStage.js new file mode 100644 index 0000000..76f371e --- /dev/null +++ b/js/components/GroupStage.js @@ -0,0 +1,100 @@ +import {Button, Card, CardBody, Col, Collapse, Row, Table} from 'reactstrap'; +import {Match} from './Match'; +import React, {Component} from 'react'; +import {getGroup} from '../redux/tournamentApi'; +import {notify} from 'react-notify-toast'; + +export default class GroupStage extends Component { + constructor(props) { + super(props); + this.state = {showMatches: this.props.showMatches}; + this.toggleShowMatches = this.toggleShowMatches.bind(this); + } + + toggleShowMatches() { + this.setState({showMatches: !this.state.showMatches}); + } + + render() { + return (
+

+ Gruppenphase + +

+ + {this.props.groups.map(group => )} + +
); + } +} + +function ShowMatchesToggleButton(props) { + return (); +} + +class Group extends Component { + constructor(props) { + super(props); + this.state = props.group; + this.reload = this.reload.bind(this); + this.onReloadSuccess = this.onReloadSuccess.bind(this); + this.onReloadError = this.onReloadError.bind(this); + } + + reload() { + getGroup(this.state.id, this.onReloadSuccess, this.onReloadError); + } + + onReloadSuccess(status, updatedGroup) { + this.setState(updatedGroup); + } + + onReloadError() { + notify.show('Die Gruppe konnte nicht aktualisiert werden.', 'warning', 2000); + } + + render() { + return ( + + +

Gruppe {this.state.number}

+ + {this.state.matches.map((match => ( + )))} + + +
+
+ ); + } +} + +function GroupScoresTable(props) { + return ( + + + + + + + + + + {props.scores.map(groupScore => )} + +
TeamPunkteerzieltkassiert
); +} + + +function GroupScoresTableRow(props) { + return ( + {props.score.team.name} + {props.score.group_points} + {props.score.received_points} + {props.score.scored_points} + ); +} diff --git a/js/components/Match.js b/js/components/Match.js index 95293d8..e9e01b8 100644 --- a/js/components/Match.js +++ b/js/components/Match.js @@ -70,6 +70,7 @@ export class Match extends React.Component { updatedMatch.team1.score = scoreTeam1; updatedMatch.team2.score = scoreTeam2; this.setState({match: updatedMatch}); + this.props.onChange !== undefined && this.props.onChange(); } getMatchFinishedMessage() { diff --git a/js/components/PlayoffStages.js b/js/components/PlayoffStages.js new file mode 100644 index 0000000..5387bab --- /dev/null +++ b/js/components/PlayoffStages.js @@ -0,0 +1,18 @@ +import {Stage} from './Stage'; +import React from 'react'; + +export function PlayoffStages(props) { + return (
+ {props.playoffStages.map(stage => )} +
); +} + +function getLevelName(levelNumber) { + const names = ['Finale', 'Halbfinale', 'Viertelfinale', 'Achtelfinale']; + if (levelNumber < names.length) { + return names[levelNumber]; + } else { + return Math.pow(2, levelNumber) + 'tel-Finale'; + } +} diff --git a/js/components/Stage.js b/js/components/Stage.js new file mode 100644 index 0000000..32a128c --- /dev/null +++ b/js/components/Stage.js @@ -0,0 +1,18 @@ +import {Col, Container, Row} from 'reactstrap'; +import {Match} from './Match'; +import React from 'react'; + +export function Stage(props) { + const {isSignedIn, isOwner} = props; + + return (
+ +

{props.level}

+ + {props.matches.map((match => ( + )))} + +
+
); +} diff --git a/js/redux/tournamentApi.js b/js/redux/tournamentApi.js new file mode 100644 index 0000000..92e7d72 --- /dev/null +++ b/js/redux/tournamentApi.js @@ -0,0 +1,100 @@ +import {getRequest} from './backendApi'; +import {getState} from '../api'; + +export function getTournament(code, successCallback, errorCallback) { + getRequest(getState(), '/tournaments/' + code) + .then(response => { + successCallback(response.status, convertTournament(response.data)); + }) + .catch(errorCallback); +} + +export function getGroup(groupId, successCallback, errorCallback) { + getRequest(getState(), '/groups/' + groupId) + .then(response => { + successCallback(response.status, convertGroup(response.data)); + }) + .catch(errorCallback); +} + +function convertTournament(apiTournament) { + let groupStage = null; + const playoffStages = []; + for (const stage of apiTournament.stages) { + if (stage.groups.length > 0) { + // group stage + groupStage = {groups: stage.groups.map(group => convertGroup(group))}; + } else { + // playoff stage + playoffStages.push({ + id: stage.id, level: stage.level, matches: stage.matches.map(match => convertMatch(match, false)) + }); + } + } + return { + id: apiTournament.id, + code: apiTournament.code, + description: apiTournament.description, + name: apiTournament.name, + isPublic: apiTournament.public, + ownerUsername: apiTournament.owner_username, + groupStage: groupStage, + playoffStages: playoffStages + }; +} + +function convertGroup(apiGroup) { + return { + id: apiGroup.id, + number: apiGroup.number, + scores: apiGroup.group_scores, + matches: apiGroup.matches.map(match => convertMatch(match, true)) + }; +} + +function convertMatch(apiMatch, allowUndecided) { + const result = { + id: apiMatch.id, state: apiMatch.state, allowUndecided: allowUndecided, + winnerTeamId: apiMatch.winner === null ? null : apiMatch.winner.id + }; + + if (apiMatch.match_scores.length === 2) { + result.team1 = { + name: apiMatch.match_scores[0].team.name, + id: apiMatch.match_scores[0].team.id, + score: apiMatch.match_scores[0].points, + scoreId: apiMatch.match_scores[0].id + }; + result.team2 = { + name: apiMatch.match_scores[1].team.name, + id: apiMatch.match_scores[1].team.id, + score: apiMatch.match_scores[1].points, + scoreId: apiMatch.match_scores[1].id + }; + } else if (apiMatch.match_scores.length === 1) { + result.team1 = { + name: apiMatch.match_scores[0].team.name, + id: apiMatch.match_scores[0].team.id, + score: apiMatch.match_scores[0].points, + scoreId: apiMatch.match_scores[0].id + }; + result.team2 = { + name: 'TBD', + id: null, + score: 0 + }; + } else { + result.team1 = { + name: 'TBD', + id: null, + score: 0 + }; + result.team2 = { + name: 'TBD', + id: null, + score: 0 + }; + } + + return result; +} diff --git a/pages/tournament.js b/pages/tournament.js index bd0de1b..fcffa8e 100644 --- a/pages/tournament.js +++ b/pages/tournament.js @@ -1,25 +1,26 @@ import Head from 'next/head'; import React from 'react'; import {connect} from 'react-redux'; -import {Col, Container, ListGroup, ListGroupItem, Row} from 'reactstrap'; +import {Container, ListGroup, ListGroupItem} from 'reactstrap'; import {ErrorPageComponent} from '../js/components/ErrorComponents'; import {Footer} from '../js/components/Footer'; import {TurniereNavigation} from '../js/components/Navigation'; import {BigImage} from '../js/components/BigImage'; -import {getState} from '../js/api'; -import {getRequest} from '../js/redux/backendApi'; import 'bootstrap/dist/css/bootstrap.min.css'; import '../static/css/everypage.css'; import '../static/css/tournament.css'; -import {Match} from '../js/components/Match'; +import {getTournament} from '../js/redux/tournamentApi'; +import {PlayoffStages} from '../js/components/PlayoffStages'; +import GroupStage from '../js/components/GroupStage'; class PrivateTournamentPage extends React.Component { render() { - const {id, description, isPublic, code, ownerUsername, playoffStages} = this.props.tournament; + const {id, description, isPublic, code, ownerUsername, playoffStages, groupStage} = this.props.tournament; const {isSignedIn, username} = this.props; + const isOwner = username === ownerUsername; // TODO: Change href-prop of the anchor tag to contain the tournament code return (
@@ -35,9 +36,11 @@ class PrivateTournamentPage extends React.Component {
- {playoffStages.map(stage => )} + {groupStage != null && +
} +
); } @@ -60,112 +63,6 @@ function EditButton(props) { } } -function getLevelName(levelNumber) { - const names = ['Finale', 'Halbfinale', 'Viertelfinale', 'Achtelfinale']; - if (levelNumber < names.length) { - return names[levelNumber]; - } else { - return Math.pow(2, levelNumber) + 'tel-Finale'; - } -} - -function Stage(props) { - const {isSignedIn, isOwner} = props; - - return (
- -

{props.level}

- - {props.matches.map((match => ( - )))} - -
-
); -} - -function convertTournament(apiTournament) { - let groupStage = null; - const playoffStages = []; - for (const stage of apiTournament.stages) { - if (stage.groups.length > 0) { - // group stage - groupStage = {groups: stage.groups.map(group => convertGroup(group))}; - } else { - // playoff stage - playoffStages.push({ - id: stage.id, level: stage.level, matches: stage.matches.map(match => convertMatch(match, false)) - }); - } - } - return { - id: apiTournament.id, - code: apiTournament.code, - description: apiTournament.description, - name: apiTournament.name, - isPublic: apiTournament.public, - ownerUsername: apiTournament.owner_username, - groupStage: groupStage, - playoffStages: playoffStages - }; -} - -function convertGroup(apiGroup) { - return { - id: apiGroup.id, - number: apiGroup.number, - scores: apiGroup.group_scores, - matches: apiGroup.matches.map(match => convertMatch(match, true)) - }; -} - -function convertMatch(apiMatch, allowUndecided) { - const result = { - id: apiMatch.id, state: apiMatch.state, allowUndecided: allowUndecided, - winnerTeamId: apiMatch.winner === null ? null : apiMatch.winner.id - }; - - if (apiMatch.match_scores.length === 2) { - result.team1 = { - name: apiMatch.match_scores[0].team.name, - id: apiMatch.match_scores[0].team.id, - score: apiMatch.match_scores[0].points, - scoreId: apiMatch.match_scores[0].id - }; - result.team2 = { - name: apiMatch.match_scores[1].team.name, - id: apiMatch.match_scores[1].team.id, - score: apiMatch.match_scores[1].points, - scoreId: apiMatch.match_scores[1].id - }; - } else if (apiMatch.match_scores.length === 1) { - result.team1 = { - name: apiMatch.match_scores[0].team.name, - id: apiMatch.match_scores[0].team.id, - score: apiMatch.match_scores[0].points, - scoreId: apiMatch.match_scores[0].id - }; - result.team2 = { - name: 'TBD', - id: null, - score: 0 - }; - } else { - result.team1 = { - name: 'TBD', - id: null, - score: 0 - }; - result.team2 = { - name: 'TBD', - id: null, - score: 0 - }; - } - - return result; -} - class Main extends React.Component { static async getInitialProps({query}) { return {query}; @@ -177,22 +74,24 @@ class Main extends React.Component { this.state = { tournament: null }; + this.onTournamentRequestSuccess = this.onTournamentRequestSuccess.bind(this); + this.onTournamentRequestError = this.onTournamentRequestError.bind(this); } componentDidMount() { - const code = this.props.query.code; + getTournament(this.props.query.code, this.onTournamentRequestSuccess, this.onTournamentRequestError); + } - getRequest(getState(), '/tournaments/' + code) - .then(response => { - this.setState({status: response.status, tournament: convertTournament(response.data)}); - }) - .catch(err => { - if (err.response) { - this.setState({status: err.response.status}); - } else { - this.setState({status: -1}); - } - }); + onTournamentRequestSuccess(requestStatus, tournament) { + this.setState({status: requestStatus, tournament: tournament}); + } + + onTournamentRequestError(error) { + if (error.response) { + this.setState({status: error.response.status}); + } else { + this.setState({status: -1}); + } } diff --git a/static/css/everypage.css b/static/css/everypage.css index 4943630..5c46239 100644 --- a/static/css/everypage.css +++ b/static/css/everypage.css @@ -7,6 +7,10 @@ font-family: Halt, sans-serif; } +.default-font-family { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + .navbar-brand { font-family: Halt, sans-serif; font-size: 2em; @@ -67,4 +71,4 @@ footer { background: url("/static/images/tennis-blurred.jpg") no-repeat top; background-size: cover; min-height: 100vh; -} \ No newline at end of file +} diff --git a/static/css/tournament.css b/static/css/tournament.css index b00bdef..63b476a 100644 --- a/static/css/tournament.css +++ b/static/css/tournament.css @@ -6,7 +6,7 @@ text-decoration: line-through; } -.stages > div:nth-child(odd) { +.stages > div > div:nth-child(odd) { background-color: #f8f8f8; } @@ -25,4 +25,4 @@ .scoreInput { width: 11rem; -} \ No newline at end of file +}