-
{props.name}
+
{props.name}
diff --git a/js/redux/tournamentApi.js b/js/redux/tournamentApi.js
index 84e1085..572e325 100644
--- a/js/redux/tournamentApi.js
+++ b/js/redux/tournamentApi.js
@@ -25,6 +25,27 @@ export function getStage(stageId, successCallback, errorCallback) {
.catch(errorCallback);
}
+export function getTournamentMeta(tournamentId, successCallback, errorCallback) {
+ getRequest(getState(), '/tournaments/' + tournamentId + '?simple=true')
+ .then(response => {
+ successCallback(response.status, response.data);
+ })
+ .catch(errorCallback);
+}
+
+export function getTournamentMatches(tournamentId, successCallback, errorCallback, matchState=null) {
+ let matchFilter = '';
+ if (matchState) {
+ matchFilter = '?state=' + matchState;
+ }
+ getRequest(getState(), '/tournaments/' + tournamentId + '/matches' + matchFilter)
+ .then(response => {
+ successCallback(response.status, response.data.sort((a, b) => a.position > b.position).map(match => convertMatch(match)));
+ })
+ .catch(errorCallback);
+}
+
+
function convertTournament(apiTournament) {
let groupStage = null;
const playoffStages = [];
@@ -51,7 +72,7 @@ function convertTournament(apiTournament) {
function convertPlayoffStage(apiStage) {
return {
- id: apiStage.id, level: apiStage.level, matches: apiStage.matches.map(match => convertMatch(match, false))
+ id: apiStage.id, level: apiStage.level, matches: apiStage.matches.sort((a, b) => a.position > b.position).map(match => convertMatch(match, false))
};
}
@@ -59,8 +80,8 @@ function convertGroup(apiGroup) {
return {
id: apiGroup.id,
number: apiGroup.number,
- scores: apiGroup.group_scores,
- matches: apiGroup.matches.map(match => convertMatch(match, true))
+ scores: apiGroup.group_scores.sort((a, b) => b.group_points > a.group_points),
+ matches: apiGroup.matches.sort((a, b) => a.position > b.position).map(match => convertMatch(match, true))
};
}
diff --git a/package.json b/package.json
index d732053..2c46b03 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"eslint-config-google": "^0.14.0",
"express": "^4.18.1",
"next": "^12.1.6",
+ "qrcode.react": "^3.1.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-notify-toast": "^0.5.1",
diff --git a/pages/tournament-fullscreen-groups.js b/pages/tournament-fullscreen-groups.js
new file mode 100644
index 0000000..1b8f7b4
--- /dev/null
+++ b/pages/tournament-fullscreen-groups.js
@@ -0,0 +1,151 @@
+import Head from 'next/head';
+import React from 'react';
+import {ErrorPageComponent} from '../js/components/ErrorComponents';
+import {getTournament} from '../js/redux/tournamentApi';
+import {
+ Col,
+ Container, Navbar, NavbarBrand, NavItem, Row, Spinner
+} from 'reactstrap';
+import {QRCodeSVG} from 'qrcode.react';
+import {Group} from '../js/components/GroupStage';
+
+
+function FullscreenPage(props) {
+ let logo;
+ if (props.showLogo) {
+ logo =
+
+

+
+ ;
+ } else {
+ logo =
;
+ }
+ return (
+
+
+ {props.groups.map(group => )}
+
+
+
+
+
+ {logo}
+
+
+
);
+}
+
+function FullscreenPageHeader(props) {
+ return (
+ {props.title}
+ {props.page}/{props.maxPage}
+ );
+}
+
+
+class Main extends React.Component {
+ static async getInitialProps({query}) {
+ return {query};
+ }
+
+ constructor(props) {
+ super(props);
+ this.groupsPerPage = 11;
+ this.pages = 2;
+ this.page = 0;
+ this.backgroundColors = ['#a8e6cf', '#dcedc1', '#ffd3b6'];
+
+ this.state = {
+ groups: [], tournament: null, loadedTournament: false, loadingStatus: null, page: 0,
+ showLogo: false, backgroundColor: 'white'
+ };
+ this.onTournamentRequestSuccess = this.onTournamentRequestSuccess.bind(this);
+ this.onTournamentRequestError = this.onTournamentRequestError.bind(this);
+ this.increasePage = this.increasePage.bind(this);
+ }
+
+ componentDidMount() {
+ this.updateTournament();
+ const intervalIdPage = setInterval(this.increasePage, 3000);
+ this.setState({intervalIdPage: intervalIdPage});
+ }
+
+ increasePage() {
+ if (this.page >= this.pages) {
+ this.page = 0;
+ } else {
+ this.page = this.page + 1;
+ }
+ this.updateTournament();
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.state.intervalIdPage);
+ }
+
+ updateTournament() {
+ getTournament(this.props.query.code, this.onTournamentRequestSuccess, this.onTournamentRequestError);
+ }
+
+ onTournamentRequestSuccess(requestStatus, tournament) {
+ // filter groups by page
+ const groups = tournament.groupStage.groups;
+ const start = this.page * this.groupsPerPage;
+ const end = (this.page + 1) * this.groupsPerPage;
+
+ this.setState({
+ loadingStatus: requestStatus, tournament: tournament,
+ groups: groups.slice(start, end), loadedTournament: true,
+ page: this.page, showLogo: this.page == this.pages,
+ backgroundColor: this.backgroundColors[this.page]
+ });
+ }
+
+ onTournamentRequestError(error) {
+ if (error.response) {
+ this.setState({loadingStatus: error.response.status, loadedTournament: true});
+ } else {
+ this.setState({loadingStatus: -1, loadedTournament: true});
+ }
+ }
+
+ render() {
+ const {groups, tournament, loadedTournament, loadingStatus, page, showLogo, backgroundColor} = this.state;
+ if (!loadedTournament) {
+ return (
+
+
Vollbild-Ansicht: turnie.re
+
+
+
+ lade Vollbild-Ansicht
+
+ );
+ }
+ if (loadingStatus === 200) {
+ return (
+
+
{tournament.name}: turnie.re
+
+
+
+ );
+ } else {
+ return
;
+ }
+ }
+}
+
+export default Main;
diff --git a/pages/tournament-fullscreen.js b/pages/tournament-fullscreen.js
index 3df8089..16204bf 100644
--- a/pages/tournament-fullscreen.js
+++ b/pages/tournament-fullscreen.js
@@ -1,20 +1,179 @@
import Head from 'next/head';
import React from 'react';
+import {ErrorPageComponent} from '../js/components/ErrorComponents';
+import {getTournamentMatches, getTournamentMeta} from '../js/redux/tournamentApi';
+import {
+ Col, Container, DropdownItem, DropdownMenu, DropdownToggle, Navbar, NavbarBrand, NavItem, Row, UncontrolledDropdown,
+ Spinner
+} from 'reactstrap';
+import {Match} from '../js/components/Match';
-class FullscreenTournamentPage extends React.Component {
+
+function FullscreenPage(props) {
+ return (
+
+
+
);
+}
+
+function Matches(props) {
+ let matches;
+ if (props.matches == null) {
+ matches = (
+
+ lade Matches
+
);
+ } else if (props.matches.length === 0) {
+ matches = (keine Matches
);
+ } else {
+ matches = (
+ {props.matches.map(match => )}
+
);
+ }
+ return (
+ {matches}
+
);
+}
+
+function FilterDropdown(props) {
+ return (
+ Match-Filter:
+
+ {props.selected.label}
+
+
+ {Object.keys(matchFilters).map(matchFilter => props.select(matchFilters[matchFilter])}>
+ {matchFilters[matchFilter].label}
+ )}
+
+ );
+}
+
+
+function FullscreenPageHeader(props) {
+ return (
+
+ {props.title}
+
+ Turnier-Code: {props.code}
+
+ );
+}
+
+const matchFilters = {
+ 'all': {backend: null, label: 'alle'},
+ 'in_progress': {backend: 'in_progress', label: 'laufend'},
+ 'upcoming': {backend: 'upcoming', label: 'kommend'},
+ 'not_started': {backend: 'not_started', label: 'bereit zum Starten'},
+ 'finished': {backend: 'finished', label: 'beendet'},
+ 'single_team': {backend: 'single_team', label: 'ohne Gegner'},
+ 'not_ready': {backend: 'not_ready', label: 'noch nicht festgelegt'}
+};
+
+class Main extends React.Component {
static async getInitialProps({query}) {
return {query};
}
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ tournamentMeta: null, matches: [], matchFilter: matchFilters.all, loadedMeta: false, loadedMatches: false
+ };
+ this.onTournamentRequestSuccess = this.onTournamentRequestSuccess.bind(this);
+ this.onTournamentRequestError = this.onTournamentRequestError.bind(this);
+ this.onTournamentMatchesRequestSuccess = this.onTournamentMatchesRequestSuccess.bind(this);
+ this.onTournamentMatchesRequestError = this.onTournamentMatchesRequestError.bind(this);
+ this.updateMatches = this.updateMatches.bind(this);
+ this.selectFilter = this.selectFilter.bind(this);
+ }
+
+ selectFilter(filter) {
+ this.setState({matchFilter: filter, loadedMatches: false});
+ this.updateMatches();
+ }
+
+ componentDidMount() {
+ const tournamentId = this.props.query.code;
+ getTournamentMeta(tournamentId, this.onTournamentRequestSuccess, this.onTournamentRequestError);
+ this.updateMatches();
+ const intervalId = setInterval(this.updateMatches, 3000);
+ this.setState({intervalId: intervalId});
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.state.intervalId);
+ }
+
+ updateMatches() {
+ const tournamentId = this.props.query.code;
+ getTournamentMatches(tournamentId, this.onTournamentMatchesRequestSuccess, this.onTournamentMatchesRequestError,
+ this.state.matchFilter.backend);
+ }
+
+
+ onTournamentRequestSuccess(requestStatus, tournament) {
+ this.setState({metaStatus: requestStatus, tournamentMeta: tournament, loadedMeta: true});
+ }
+
+ onTournamentRequestError(error) {
+ if (error.response) {
+ this.setState({metaStatus: error.response.status, loadedMeta: true});
+ } else {
+ this.setState({metaStatus: -1, loadedMeta: true});
+ }
+ }
+
+ onTournamentMatchesRequestSuccess(requestStatus, matches) {
+ this.setState({matchesStatus: requestStatus, matches: matches, loadedMatches: true});
+ }
+
+ onTournamentMatchesRequestError(error) {
+ if (error.response) {
+ this.setState({matchesStatus: error.response.status, loadedMatches: true});
+ } else {
+ this.setState({matchesStatus: -1, loadedMatches: true});
+ }
+ }
+
+
render() {
- return (
-
-
Turnie.re - Turnieranzeige (Vollbild)
-
-
Turnieranzeige (Vollbild)
-
Code: {this.props.query.code}
-
);
+ const {metaStatus, matchesStatus, tournamentMeta, matches} = this.state;
+ const filter = {
+ selected: this.state.matchFilter, select: this.selectFilter
+ };
+ if (!this.state.loadedMeta) {
+ return (
+
+
Vollbild-Ansicht: turnie.re
+
+
+
+ lade Vollbild-Ansicht
+
+ );
+ }
+ if (!this.state.loadedMatches) {
+ return (
+
+
{tournamentMeta.name}: turnie.re
+
+
+ );
+ }
+ if (metaStatus === 200 && matchesStatus === 200) {
+ return (
+
+
{tournamentMeta.name}: turnie.re
+
+
+ );
+ } else {
+ return ;
+ }
}
}
-export default FullscreenTournamentPage;
+export default Main;
diff --git a/pages/tournament-statistics.js b/pages/tournament-statistics.js
index d0ddf18..6f9682e 100644
--- a/pages/tournament-statistics.js
+++ b/pages/tournament-statistics.js
@@ -1,7 +1,7 @@
import Head from 'next/head';
import React from 'react';
import {connect} from 'react-redux';
-import {ButtonGroup, Col, Container, Row, NavbarBrand} from 'reactstrap';
+import {ButtonGroup, Col, Container, Row} from 'reactstrap';
import {TurniereNavigation} from '../js/components/Navigation';
import {StandingsTable} from '../js/components/StandingsTable';
@@ -44,6 +44,9 @@ class StatisticsTournamentPage extends React.Component {
zurück zum Turnier
+
+ Turnier-Vollbild-Ansicht
+
diff --git a/pages/tournament.js b/pages/tournament.js
index 333904a..b5b656c 100644
--- a/pages/tournament.js
+++ b/pages/tournament.js
@@ -41,6 +41,8 @@ function StatusBar(props) {
+
+ Vollbild-Ansicht Gruppen
);
}
@@ -51,6 +53,12 @@ function StatisticsButton(props) {
);
}
+function FullscreenButton(props) {
+ return (
+ Vollbild-Ansicht
+ );
+}
+
function mapStateToTournamentPageProperties(state) {
const {isSignedIn, username} = state.userinfo;
diff --git a/public/static/css/everypage.css b/public/static/css/everypage.css
index 0a2c339..1741101 100644
--- a/public/static/css/everypage.css
+++ b/public/static/css/everypage.css
@@ -17,7 +17,7 @@
}
.big-image {
- background: url("/static/images/landingpage-background.jpg") no-repeat center;
+ background: url("/static/images/bpwstr_banner.jpg") no-repeat center;
background-size: cover;
padding: 3vw 5vw;
text-align: center;
diff --git a/public/static/css/tournament.css b/public/static/css/tournament.css
index 63b476a..5855f3f 100644
--- a/public/static/css/tournament.css
+++ b/public/static/css/tournament.css
@@ -11,7 +11,7 @@
}
.minw-25 {
- min-width: 25%;
+ min-width: 350px;
}
.match:hover {
diff --git a/public/static/images/bpwstr_banner.jpg b/public/static/images/bpwstr_banner.jpg
new file mode 100644
index 0000000..1f3dc92
Binary files /dev/null and b/public/static/images/bpwstr_banner.jpg differ
diff --git a/public/static/images/bpwstr_logo.png b/public/static/images/bpwstr_logo.png
new file mode 100644
index 0000000..8081fc3
Binary files /dev/null and b/public/static/images/bpwstr_logo.png differ
diff --git a/server.js b/server.js
index 7dc0784..d17d936 100644
--- a/server.js
+++ b/server.js
@@ -21,6 +21,12 @@ app.prepare()
app.render(req, res, actualPage, queryParam);
});
+ server.get('/t/:code/fullscreen-groups', (req, res) => {
+ const actualPage = '/tournament-fullscreen-groups';
+ const queryParam = {code: req.params.code};
+ app.render(req, res, actualPage, queryParam);
+ });
+
server.get('/t/:code/edit', (req, res) => {
const actualPage = '/tournament-edit';
const queryParam = {code: req.params.code};
diff --git a/yarn.lock b/yarn.lock
index b2bd013..2a8a372 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5715,6 +5715,11 @@ punycode@^2.1.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+qrcode.react@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8"
+ integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==
+
qs@6.10.3:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"