From c59d0714d62f9028135cc9cff829d16b91e5fb4f Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Fri, 11 Mar 2022 23:48:45 +0100 Subject: [PATCH] result display --- app/Models/Result.php | 8 ++++++++ resources/js/components/common/Icon.js | 2 ++ resources/js/components/results/Item.js | 23 +++++++++++++++++------ resources/js/helpers/Round.js | 8 ++++++++ resources/js/helpers/permissions.js | 8 ++++++++ resources/sass/app.scss | 1 + resources/sass/results.scss | 24 ++++++++++++++++++++++++ resources/sass/rounds.scss | 4 ++++ 8 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 resources/sass/results.scss diff --git a/app/Models/Result.php b/app/Models/Result.php index e5c9f7e..e495419 100644 --- a/app/Models/Result.php +++ b/app/Models/Result.php @@ -17,6 +17,14 @@ class Result extends Model return $this->belongsTo(Participant::class); } + public function getHasFinishedAttribute() { + return $this->time > 0; + } + + protected $appends = [ + 'has_finished', + ]; + protected $fillable = [ 'round_id', 'time', diff --git a/resources/js/components/common/Icon.js b/resources/js/components/common/Icon.js index c9bb3bc..66d2ac9 100644 --- a/resources/js/components/common/Icon.js +++ b/resources/js/components/common/Icon.js @@ -61,7 +61,9 @@ const makePreset = (presetDisplayName, presetName) => { Icon.DISCORD = makePreset('DiscordIcon', ['fab', 'discord']); Icon.EDIT = makePreset('EditIcon', 'edit'); +Icon.FINISHED = makePreset('FinishedIcon', 'square-check'); Icon.LOGOUT = makePreset('LogoutIcon', 'sign-out-alt'); +Icon.PENDING = makePreset('PendingIcon', 'clock'); Icon.PROTOCOL = makePreset('ProtocolIcon', 'file-alt'); export default Icon; diff --git a/resources/js/components/results/Item.js b/resources/js/components/results/Item.js index 771c7b6..cc61dd0 100644 --- a/resources/js/components/results/Item.js +++ b/resources/js/components/results/Item.js @@ -2,23 +2,34 @@ import PropTypes from 'prop-types'; import React from 'react'; import { withTranslation } from 'react-i18next'; +import Icon from '../common/Icon'; import Box from '../users/Box'; import { formatTime } from '../../helpers/Result'; import { findResult } from '../../helpers/Participant'; -import i18n from '../../i18n'; +import { maySeeResults } from '../../helpers/permissions'; +import { withUser } from '../../helpers/UserContext'; const Item = ({ participant, round, + tournament, + user, }) => { const result = findResult(participant, round); return (
-
- {result ? - {i18n.t('results.time', { time: formatTime(result) })} - : null} +
+ + {result && maySeeResults(user, tournament, round) ? + formatTime(result) + : null} + + {result && result.has_finished ? + + : + + }
); @@ -37,4 +48,4 @@ Item.propTypes = { }), }; -export default withTranslation()(Item); +export default withTranslation()(withUser(Item)); diff --git a/resources/js/helpers/Round.js b/resources/js/helpers/Round.js index 8a8b6ad..086517e 100644 --- a/resources/js/helpers/Round.js +++ b/resources/js/helpers/Round.js @@ -1,3 +1,10 @@ +export const isComplete = (tournament, round) => { + if (!tournament || !tournament.participants) return false; + if (!round || !round.results) return false; + return tournament.participants.length === round.results.length && + round.results.filter(r => !r.has_finished).length === 0; +}; + export const patchResult = (round, result) => { if (!round) return round; if (!round.results || !round.results.length) { @@ -13,5 +20,6 @@ export const patchResult = (round, result) => { }; export default { + isComplete, patchResult, }; diff --git a/resources/js/helpers/permissions.js b/resources/js/helpers/permissions.js index 8918031..2565d44 100644 --- a/resources/js/helpers/permissions.js +++ b/resources/js/helpers/permissions.js @@ -1,6 +1,8 @@ /// NOTE: These permissions are for UI cosmetics only! /// They should be in sync with the backend Policies. +import Round from './Round'; + export const isAdmin = user => user && user.role === 'admin'; export const isSameUser = (user, subject) => user && subject && user.id === subject.id; @@ -11,9 +13,15 @@ export const isParticipant = (user, tournament) => user && tournament && tournament.participants && tournament.participants.find(p => p.user && p.user.id == user.id); +export const hasFinished = (user, round) => + user && round && round.results && + round.results.find(r => r.user_id == user.id && r.has_finished); + export const mayAddRounds = (user, tournament) => isAdmin(user) || isParticipant(user, tournament); export const mayViewProtocol = user => isAdmin(user); +export const maySeeResults = (user, tournament, round) => + isAdmin(user) || hasFinished(user, round) || Round.isComplete(tournament, round); diff --git a/resources/sass/app.scss b/resources/sass/app.scss index ff99a53..cbb7c67 100644 --- a/resources/sass/app.scss +++ b/resources/sass/app.scss @@ -13,5 +13,6 @@ // Custom @import 'common'; @import 'participants'; +@import 'results'; @import 'rounds'; @import 'users'; diff --git a/resources/sass/results.scss b/resources/sass/results.scss new file mode 100644 index 0000000..c5a7ce6 --- /dev/null +++ b/resources/sass/results.scss @@ -0,0 +1,24 @@ +.results { + .result { + padding: 1ex; + + .status { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 1ex; + padding: 0.5em; + border-radius: 1ex; + background: $dark; + color: $light; + + .time { + min-width: 9ex; + height: 1.4em; + background: $secondary; + border-radius: 0.5ex; + padding: 0 0.5ex; + } + } + } +} diff --git a/resources/sass/rounds.scss b/resources/sass/rounds.scss index 8b58071..83949c9 100644 --- a/resources/sass/rounds.scss +++ b/resources/sass/rounds.scss @@ -7,5 +7,9 @@ border: thin solid $secondary; border-radius: 1ex; padding: 1ex; + + .info { + padding-right: 1rem; + } } } -- 2.39.2