diff --git a/pages/list.js b/pages/list.js index 07dbc80..200e412 100644 --- a/pages/list.js +++ b/pages/list.js @@ -1,24 +1,87 @@ import Head from 'next/head'; import React from 'react'; +import { Card, CardBody, Container } from 'reactstrap'; +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 ( -
+
- Turnie.re - Turnierliste + Öffentliche Turniere: turnie.re -

Turnierliste

+ +
+ +
+
); } } + +class TournamentList extends React.Component { + constructor(props) { + super(props); + this.state = { + error: null, + isLoaded: false, + items: [] + }; + } + + componentDidMount() { + getRequest(getState(), '/tournaments?type=public') + .then( + response => { + this.setState({ + isLoaded: true, + items: response.data + }); + }, + error => { + this.setState({ + isLoaded: true, + error + }); + } + ); + } + + render() { + return ( + + + +

Öffentliche Turniere

+ {this.state.items.map(item => ( + //The code should be item.code but the api just supports it this way by now + + ))} +
+
+
+ ); + } +} + +function TournamentListEntry(props) { + return ( + + {props.name} + + ); +} diff --git a/pages/tournament.js b/pages/tournament.js index bb01189..c3905a6 100644 --- a/pages/tournament.js +++ b/pages/tournament.js @@ -1,8 +1,31 @@ import Head from 'next/head'; import React from 'react'; -import '../style.css'; +import { + Button, + Card, + CardBody, + Col, + Container, + Input, + InputGroup, + InputGroupAddon, + ListGroup, + ListGroupItem, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + Row, + Table +} from 'reactstrap'; +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, verifyCredentials } from '../js/api'; @@ -17,16 +40,345 @@ class TournamentPage extends React.Component { } render() { + // TODO: Change href-prop of the anchor tag to contain the tournament code return ( -
- - Turnie.re - Turnieranzeige - -

Turnieranzeige

-

Code: {this.props.query.code}

+
+ + 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 => + )} +
); } } -export default TournamentPage; +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 TournamentContainer(props) { + if (props.data === null) { + return null; + } else { + return ; + } +} + +function Stage(props) { + return (
+ +

{props.level}

+ + {props.matches.map((match => ( + + )))} + +
+
); +} + +class Match extends React.Component { + constructor(props) { + super(props); + this.state = { + modal: false + }; + this.toggleModal = this.toggleModal.bind(this); + } + + toggleModal() { + this.setState({modal: !this.state.modal}); + } + + render() { + let cardClass, smallMessage, borderClass; + //possible states: single_team not_ready not_started in_progress team1_won team2_won undecided + switch (this.props.match.state) { + case 'in_progress': + cardClass = 'table-warning'; + borderClass = 'border-warning'; + smallMessage = 'Spiel läuft'; + break; + case 'team1_won': + cardClass = 'table-success'; + borderClass = 'border-success'; + smallMessage = 'Gewinner: ' + this.props.match.team1; + break; + case 'team2_won': + cardClass = 'table-success'; + borderClass = 'border-success'; + smallMessage = 'Gewinner: ' + this.props.match.team2; + break; + case 'single_team': + cardClass = 'table-success'; + borderClass = 'border-success'; + smallMessage = 'kein Gegner, Team kommt weiter'; + break; + case 'not_ready': + smallMessage = 'Spiel kann noch nicht gestartet werden'; + break; + case 'not_started': + smallMessage = 'Spiel kann gestartet werden'; + break; + case 'undecided': + cardClass = 'table-success'; + borderClass = 'border-success'; + smallMessage = 'Spiel beendet, unentschieden'; + break; + } + return ( +
+ + + + + + {smallMessage} + +
+ ); + } +} + +function MatchModal(props) { + let title; + let actionButton = ''; + //possible states: single_team not_ready not_started in_progress team1_won team2_won undecided + switch (props.match.state) { + case 'in_progress': + title = 'Spiel läuft'; + actionButton = ; + break; + case 'team1_won': + title = 'Spiel beendet'; + break; + case 'team2_won': + title = 'Spiel beendet'; + break; + case 'single_team': + title = 'kein Gegner, Team kommt weiter'; + break; + case 'not_ready': + title = 'Spiel kann noch nicht gestartet werden'; + break; + case 'not_started': + title = 'Spiel kann gestartet werden'; + actionButton = ; + break; + case 'undecided': + title = 'Spiel beendet'; + break; + } + return ( + + {title} + + {props.match.state === 'in_progress' ? : + } + + + {actionButton} + + + + ); +} + +function MatchTable(props) { + let team1Class, team2Class; + //possible states: single_team not_ready not_started in_progress team1_won team2_won undecided + switch (props.match.state) { + case 'in_progress': + break; + case 'team1_won': + team1Class = 'font-weight-bold'; + team2Class = 'lost-team'; + break; + case 'team2_won': + team1Class = 'lost-team'; + team2Class = 'font-weight-bold'; + break; + case 'single_team': + team2Class = 'text-muted'; + break; + case 'not_ready': + break; + case 'not_started': + break; + case 'undecided': + break; + } + if(props.match.state === 'single_team'){ + return ( + + + + + + + + + +
{props.match.team1}
kein Gegner
+ ); + } else { + return ( + + + + + + + + + + + +
{props.match.scoreTeam1}{props.match.team1}
{props.match.scoreTeam2}{props.match.team2}
+ ); + } +} + +function EditableMatchTable(props) { + return ( + + + + + + + + + + + +
+ + {props.match.team1}
+ + {props.match.team2}
+ ); +} + +class ScoreInput extends React.Component { + constructor(props) { + super(props); + this.state = {score: this.props.score}; + this.updateScore = this.updateScore.bind(this); + this.increaseScore = this.increaseScore.bind(this); + this.decreaseScore = this.decreaseScore.bind(this); + } + + updateScore(event){ + this.setState({score: event.target.value}); + } + + increaseScore(){ + this.setState({score: Number(this.state.score) + 1}); + } + + decreaseScore(){ + this.setState({score: Number(this.state.score) - 1}); + } + + render() { + return ( + + + + ); + } +} + +function convertTournament(apiTournament) { + let groupStage = null; + let playoffStages = []; + for (let 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)) + }); + } + } + return { + id: apiTournament.id, + code: apiTournament.code, + description: apiTournament.description, + name: apiTournament.name, + public: 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)) + }; +} + +function convertMatch(apiMatch) { + return { + id: apiMatch.id, + state: apiMatch.state, + team1: apiMatch.match_scores[0].team.name, + team2: apiMatch.match_scores[1].team.name, + scoreTeam1: apiMatch.match_scores[0].points, + scoreTeam2: apiMatch.match_scores[1].points + }; +} + +class Main extends React.Component { + constructor(props) { + super(props); + const code = this.props.query.code; + getRequest(getState(), '/tournaments/' + code) + .then(response => { + this.setState({tournament: convertTournament(response.data)}); + }) + .catch(() => { /* TODO: Show some kind of error or smth */ }); + } + + render() { + const tournamentName = this.state === null ? 'Turnier' : this.state.tournament.name; + return ( +
+ + {tournamentName}: turnie.re + + + + +
+ ); + } +} + +export default Main; diff --git a/static/css/tournament.css b/static/css/tournament.css new file mode 100644 index 0000000..b00bdef --- /dev/null +++ b/static/css/tournament.css @@ -0,0 +1,28 @@ +.stage { + width: 1em; +} + +.lost-team { + text-decoration: line-through; +} + +.stages > div:nth-child(odd) { + background-color: #f8f8f8; +} + +.minw-25 { + min-width: 25%; +} + +.match:hover { + box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important; +} + +.match:hover > div { + border-width: 3px !important; + margin: -2px; +} + +.scoreInput { + width: 11rem; +} \ No newline at end of file