From 1c3b922ba7143a548c8a7526f5e4384f336e2f1e Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Tue, 15 Mar 2022 20:43:24 +0100 Subject: [PATCH] switch participant list to scoreboard --- package-lock.json | 3 ++ package.json | 4 +- resources/js/components/tournament/Detail.js | 6 +-- .../js/components/tournament/Scoreboard.js | 33 ++++++++++++++ resources/js/helpers/Tournament.js | 43 +++++++++++++++++++ resources/js/i18n/de.js | 5 +++ 6 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 resources/js/components/tournament/Scoreboard.js diff --git a/package-lock.json b/package-lock.json index 42f84ad..398293a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3484,6 +3484,7 @@ "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", @@ -3557,6 +3558,7 @@ "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", "dev": true, "dependencies": { + "colors": "1.4.0", "string-width": "^4.2.0" }, "engines": { @@ -7064,6 +7066,7 @@ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { + "graceful-fs": "^4.1.6", "universalify": "^2.0.0" }, "optionalDependencies": { diff --git a/package.json b/package.json index abdb0d5..b03f12d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "eslintConfig": { "env": { "browser": true, - "node": true + "node": true }, "extends": [ "eslint:recommended", @@ -24,7 +24,7 @@ "sourceType": "module" }, "rules": { - "import/no-named-as-default-member": 0, + "import/no-named-as-default-member": 0, "max-len": [ "warn", { diff --git a/resources/js/components/tournament/Detail.js b/resources/js/components/tournament/Detail.js index 912d2a2..1dae1b3 100644 --- a/resources/js/components/tournament/Detail.js +++ b/resources/js/components/tournament/Detail.js @@ -3,7 +3,7 @@ import React from 'react'; 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 { @@ -25,10 +25,10 @@ const Detail = ({ : null}
-

{i18n.t('participants.heading')}

+

{i18n.t('tournaments.scoreboard')}

{tournament.participants ? - + : null}

{i18n.t('rounds.heading')}

diff --git a/resources/js/components/tournament/Scoreboard.js b/resources/js/components/tournament/Scoreboard.js new file mode 100644 index 0000000..008974b --- /dev/null +++ b/resources/js/components/tournament/Scoreboard.js @@ -0,0 +1,33 @@ +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 }) => + + + + + + + + + {calculateScores(tournament).map(score => + + + + + )} + +
{i18n.t('participants.participant')}{i18n.t('participants.score')}
{score.score}
; + +Scoreboard.propTypes = { + tournament: PropTypes.shape({ + }), +}; + +export default withTranslation()(Scoreboard); diff --git a/resources/js/helpers/Tournament.js b/resources/js/helpers/Tournament.js index 6b4ca28..2c145a8 100644 --- a/resources/js/helpers/Tournament.js +++ b/resources/js/helpers/Tournament.js @@ -1,6 +1,47 @@ 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; @@ -38,6 +79,8 @@ export const sortParticipants = tournament => { }; export default { + calculateScores, + compareScore, findParticipant, patchResult, sortParticipants, diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index de25089..97089e6 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -32,6 +32,8 @@ export default { participants: { empty: 'Noch keine Teilnehmer eingetragen', heading: 'Teilnehmer', + participant: 'Teilnehmer', + score: 'Punktzahl', }, protocol: { description: { @@ -65,6 +67,9 @@ export default { seed: 'Seed', setSeed: 'Seed eintragen', }, + tournaments: { + scoreboard: 'Scoreboard', + }, validation: { error: { required: 'Bitte ausfüllen', -- 2.39.2