"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
+ "fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==",
"dev": true,
"dependencies": {
+ "colors": "1.4.0",
"string-width": "^4.2.0"
},
"engines": {
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"dependencies": {
+ "graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
},
"optionalDependencies": {
"eslintConfig": {
"env": {
"browser": true,
- "node": true
+ "node": true
},
"extends": [
"eslint:recommended",
"sourceType": "module"
},
"rules": {
- "import/no-named-as-default-member": 0,
+ "import/no-named-as-default-member": 0,
"max-len": [
"warn",
{
import { Button, Container } from 'react-bootstrap';
import { withTranslation } from 'react-i18next';
-import Participants from '../participants/List';
+import Scoreboard from './Scoreboard';
import Protocol from '../protocol/Protocol';
import Rounds from '../rounds/List';
import {
: null}
</div>
<div className="d-flex align-items-center justify-content-between">
- <h2>{i18n.t('participants.heading')}</h2>
+ <h2>{i18n.t('tournaments.scoreboard')}</h2>
</div>
{tournament.participants ?
- <Participants participants={tournament.participants} tournament={tournament} />
+ <Scoreboard tournament={tournament} />
: null}
<div className="d-flex align-items-center justify-content-between">
<h2>{i18n.t('rounds.heading')}</h2>
--- /dev/null
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Table } from 'react-bootstrap';
+import { withTranslation } from 'react-i18next';
+
+import Box from '../users/Box';
+import { calculateScores } from '../../helpers/Tournament';
+import i18n from '../../i18n';
+
+const Scoreboard = ({ tournament }) =>
+<Table striped className="scoreboard">
+ <thead>
+ <tr>
+ <th>{i18n.t('participants.participant')}</th>
+ <th className="text-end">{i18n.t('participants.score')}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {calculateScores(tournament).map(score =>
+ <tr className="score" key={score.participant.id}>
+ <td><Box user={score.participant.user} /></td>
+ <td className="text-end text-lg">{score.score}</td>
+ </tr>
+ )}
+ </tbody>
+</Table>;
+
+Scoreboard.propTypes = {
+ tournament: PropTypes.shape({
+ }),
+};
+
+export default withTranslation()(Scoreboard);
import Participant from './Participant';
import Round from './Round';
+export const calculateScores = tournament => {
+ if (!tournament || !tournament.participants || !tournament.participants.length) return [];
+ const scores = tournament.participants.map(participant => ({ participant, score: 0 }));
+ if (!tournament.rounds || !tournament.rounds.length) return scores;
+ tournament.rounds.forEach(round => {
+ const filtered = Participant
+ .sortByResult(tournament.participants, round)
+ .map(p => ({ participant: p, result: Participant.findResult(p, round) }))
+ .filter(r => r.result && (r.result.time || r.result.forfeit))
+ .reverse();
+ let running = 0;
+ let bonus = 1;
+ let lastResult = null;
+ for (let i = 0; i < filtered.length; ++i) {
+ const score = scores.find(s => s.participant.id === filtered[i].participant.id);
+ if (!score) return;
+ const result = filtered[i].result;
+ const betterThanLast = lastResult === null || result.time < lastResult;
+ if (!result.forfeit && betterThanLast) {
+ running += bonus;
+ lastResult = result.time;
+ bonus = 1;
+ } else {
+ ++bonus;
+ }
+ if (!result.forfeit) {
+ score.score += running;
+ }
+ }
+ });
+ return scores.sort(compareScore).reverse();
+};
+
+export const compareScore = (a, b) => {
+ const a_score = a && a.score ? a.score : 0;
+ const b_score = b && b.score ? b.score : 0;
+ if (a_score < b_score) return -1;
+ if (b_score < a_score) return 1;
+ return 0;
+};
+
export const findParticipant = (tournament, user) => {
if (!tournament || !tournament.participants || !tournament.participants.length) return null;
if (!user || !user.id) return null;
};
export default {
+ calculateScores,
+ compareScore,
findParticipant,
patchResult,
sortParticipants,
participants: {
empty: 'Noch keine Teilnehmer eingetragen',
heading: 'Teilnehmer',
+ participant: 'Teilnehmer',
+ score: 'Punktzahl',
},
protocol: {
description: {
seed: 'Seed',
setSeed: 'Seed eintragen',
},
+ tournaments: {
+ scoreboard: 'Scoreboard',
+ },
validation: {
error: {
required: 'Bitte ausfüllen',