From 2ab05ab7927e8b21887ffa42d04eb9d4ff899382 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Fri, 25 Mar 2022 11:28:56 +0100 Subject: [PATCH 1/1] show placements on user profile --- app/Http/Controllers/UserController.php | 6 +++ app/Models/User.php | 51 +++++++++++++++++++ .../js/components/users/Participation.js | 27 +++++++++- resources/js/components/users/Profile.js | 29 +++++++++-- resources/js/components/users/Records.js | 50 ++++++++++++++++++ resources/js/i18n/de.js | 3 ++ resources/js/i18n/en.js | 3 ++ resources/sass/users.scss | 14 +++++ 8 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 resources/js/components/users/Records.js diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 8c451fc..692c458 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -43,6 +43,12 @@ class UserController extends Controller $this->authorize('view', $user); $user->load('participation'); $user->load('participation.tournament'); + $user->loadCount('round_first'); + $user->loadCount('round_second'); + $user->loadCount('round_third'); + $user->loadCount('tournament_first'); + $user->loadCount('tournament_second'); + $user->loadCount('tournament_third'); return $user->toJson(); } diff --git a/app/Models/User.php b/app/Models/User.php index 4f56ac1..b4950c3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -39,10 +39,61 @@ class User extends Authenticatable return false; } + public function participation() { return $this->hasMany(Participant::class); } + public function results() { + return $this->hasMany(Result::class); + } + + public function rounds() { + return $this->belongsToMany(Round::class, 'results'); + } + + public function tournaments() { + return $this->belongsToMany(Tournament::class, 'participants'); + } + + + public function round_first() { + return $this->rounds() + ->whereNot('locked') + ->wherePivot('placement', 1); + } + + public function round_second() { + return $this->rounds() + ->whereNot('locked') + ->wherePivot('placement', 2); + } + + public function round_third() { + return $this->rounds() + ->whereNot('locked') + ->wherePivot('placement', 3); + } + + public function tournament_first() { + return $this->tournaments() + ->whereNot('locked') + ->wherePivot('placement', 1); + } + + public function tournament_second() { + return $this->tournaments() + ->whereNot('locked') + ->wherePivot('placement', 2); + } + + public function tournament_third() { + return $this->tournaments() + ->whereNot('locked') + ->wherePivot('placement', 3); + } + + /** * The attributes that are mass assignable. * diff --git a/resources/js/components/users/Participation.js b/resources/js/components/users/Participation.js index d0d5d68..b12adff 100644 --- a/resources/js/components/users/Participation.js +++ b/resources/js/components/users/Participation.js @@ -4,8 +4,26 @@ import { Alert, Button, Table } from 'react-bootstrap'; import { withTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import Icon from '../common/Icon'; +import { isRunner } from '../../helpers/Participant'; import i18n from '../../i18n'; +const getIcon = participant => { + if (!isRunner(participant)) { + return '—'; + } + if (participant.placement === 1) { + return ; + } + if (participant.placement === 2) { + return ; + } + if (participant.placement === 3) { + return ; + } + return participant.placement; +}; + const Participation = ({ user }) => { const navigate = useNavigate(); @@ -14,10 +32,11 @@ const Participation = ({ user }) => { {i18n.t('users.participationEmpty')} ; } - return + return
+ @@ -31,6 +50,12 @@ const Participation = ({ user }) => { {p.tournament.title} + +

{i18n.t('users.discordTag')}

- +

{i18n.t('users.streamLink')}

{user.stream_link ? @@ -36,7 +37,23 @@ const Profile = ({ user }) =>

- + +

{i18n.t('users.tournamentRecords')}

+ + + +

{i18n.t('users.roundRecords')}

+ + +

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

@@ -47,7 +64,13 @@ Profile.propTypes = { user: PropTypes.shape({ participation: PropTypes.arrayOf(PropTypes.shape({ })), + round_first_count: PropTypes.number, + round_second_count: PropTypes.number, + round_third_count: PropTypes.number, stream_link: PropTypes.string, + tournament_first_count: PropTypes.number, + tournament_second_count: PropTypes.number, + tournament_third_count: PropTypes.number, username: PropTypes.string, }), }; diff --git a/resources/js/components/users/Records.js b/resources/js/components/users/Records.js new file mode 100644 index 0000000..5b110fe --- /dev/null +++ b/resources/js/components/users/Records.js @@ -0,0 +1,50 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { Col, Row } from 'react-bootstrap'; + +import Icon from '../common/Icon'; + +const Records = ({ + first, + second, + third, +}) => + +
+ + + + + {first} + +
+ + +
+ + + + + {second} + +
+ + +
+ + + + + {third} + +
+ +; + +Records.propTypes = { + first: PropTypes.number, + second: PropTypes.number, + third: PropTypes.number, +}; + +export default Records; diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index 9a7bb04..7f2c713 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -111,6 +111,7 @@ export default { participant: 'Teilnehmer', placement: 'Platzierung', placementShort: '#', + placementSubjectToChange: 'Das Turnier wurde noch nicht abgeschlossen, die Platzierung kann sich noch ändern.', roleNames: { admin: 'Administrator', runner: 'Runner', @@ -179,11 +180,13 @@ export default { editStreamLink: 'Stream Link bearbeiten', noStream: 'Kein Stream gesetzt', participationEmpty: 'Hat noch an keinen Turnieren teilgenommen.', + roundRecords: 'Renn-Platzierungen', setStreamLinkError: 'Konnte Stream Link nicht speichern', setStreamLinkSuccess: 'Stream Link gespeichert', stream: 'Stream', streamLink: 'Stream Link', tournaments: 'Turniere', + tournamentRecords: 'Turnier-Platzierungen', }, validation: { error: { diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index 9f64901..e790743 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -111,6 +111,7 @@ export default { participant: 'Participant', placement: 'Placement', placementShort: '#', + placementSubjectToChange: 'Tournament incomplete, placement subject to change.', roleNames: { admin: 'Administrator', runner: 'Runner', @@ -179,11 +180,13 @@ export default { editStreamLink: 'Edit stream link', noStream: 'No stream set', participationEmpty: 'Has not participated in any tourneys yet.', + roundRecords: 'Race records', setStreamLinkError: 'Could not save stream link', setStreamLinkSuccess: 'Stream link saved', stream: 'Stream', streamLink: 'Stream link', tournaments: 'Tournaments', + tournamentRecords: 'Tournament records', }, validation: { error: { diff --git a/resources/sass/users.scss b/resources/sass/users.scss index c6f7509..5847ae7 100644 --- a/resources/sass/users.scss +++ b/resources/sass/users.scss @@ -1,3 +1,17 @@ +.record-box { + display: flex; + align-items: center; + justify-content: space-around; + border: thin solid $secondary; + border-radius: 0.5rem; + padding: 1rem; + font-size: 1.5rem; + + .count { + margin-left: 0.5rem; + } +} + .user-box { padding: 0; color: inherit; -- 2.39.2
{i18n.t('participants.tournament')}{i18n.t('participants.placement')} {i18n.t('participants.roles')}
+ {getIcon(p)} + {!p.tournament.locked && isRunner(p) ? + * + : null} + {p.roles ? p.roles.map((role, index) => diff --git a/resources/js/components/users/Profile.js b/resources/js/components/users/Profile.js index 10c70db..e2fe2e7 100644 --- a/resources/js/components/users/Profile.js +++ b/resources/js/components/users/Profile.js @@ -4,6 +4,7 @@ import { Button, Col, Container, Row } from 'react-bootstrap'; import { withTranslation } from 'react-i18next'; import Box from './Box'; +import Records from './Records'; import EditStreamLinkButton from './EditStreamLinkButton'; import Participation from './Participation'; import Icon from '../common/Icon'; @@ -12,11 +13,11 @@ import i18n from '../../i18n'; const Profile = ({ user }) =>

{user.username}

-