]> git.localhorst.tv Git - alttp.git/commitdiff
result table view
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 26 Nov 2025 14:23:30 +0000 (15:23 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 26 Nov 2025 14:23:30 +0000 (15:23 +0100)
17 files changed:
app/Events/ResultChanged.php
app/Http/Controllers/ResultController.php
resources/js/components/groups/Item.jsx
resources/js/components/results/Item.jsx
resources/js/components/results/List.jsx
resources/js/components/results/Table.jsx [new file with mode: 0644]
resources/js/components/results/TableRow.jsx [new file with mode: 0644]
resources/js/components/results/VodLink.jsx [new file with mode: 0644]
resources/js/components/rounds/Item.jsx
resources/js/components/rounds/List.jsx
resources/js/components/tournament/Detail.jsx
resources/js/helpers/Result.jsx
resources/js/helpers/Round.js
resources/js/helpers/Tournament.js
resources/js/i18n/de.js
resources/js/i18n/en.js
resources/sass/results.scss

index da93768655a1f85845284f935869648d5f36929a..61207a0a06efaceba649e3b7c68fe40a60a5d6fb 100644 (file)
@@ -23,7 +23,7 @@ class ResultChanged implements ShouldBroadcast
        public function __construct(Result $result)
        {
                $this->result = $result;
-               $result->load('user');
+               $result->load(['user', 'verified_by']);
        }
 
        /**
index 004ba6b9972c5e619a1483afb064d207a44d74d3..6a4eb59b3072f95b0a3063a912af76a52fd18b3d 100644 (file)
@@ -55,7 +55,7 @@ class ResultController extends Controller
                                $request->user(),
                        );
                        DiscordBotCommand::queueResult($result);
-               } else if ($result->wasChanged(['comment', 'vod'])) {
+               } elseif ($result->wasChanged(['comment', 'vod'])) {
                        Protocol::resultCommented(
                                $round->tournament,
                                $result,
index f887e91d2a8a633b793433cee8e42695096d08f5..1322307e0a96255a31a180b364e644adc87b4c91 100644 (file)
@@ -24,8 +24,13 @@ const getStatusIcon = (round, result, t) => {
                        <Icon.WARNING title="" />
                </Button>;
        }
+       if (result.verified_at) {
+               return <Button className="group-status" title={t('groups.verified')} variant="info">
+                       <Icon.VERIFIED title="" />
+               </Button>;
+       }
        return <Button className="group-status" title={t('groups.complete')} variant="success">
-               <Icon.FINISHED />
+               <Icon.FINISHED title="" />
        </Button>;
 }
 
index 730bfeb1d83a4377d6b7fd6563c3f29671798d7b..527553a1c1fa197d1e8f3754caf4923eb7490713 100644 (file)
@@ -1,47 +1,19 @@
 import PropTypes from 'prop-types';
 import React from 'react';
-import { Button } from 'react-bootstrap';
-import { useTranslation } from 'react-i18next';
 
 import Badge from './Badge';
-import Icon from '../common/Icon';
+import VodLink from './VodLink';
 import Box from '../users/Box';
-import { maySeeResult } from '../../helpers/permissions';
+import { maySeeResult, mayVerifyResult } from '../../helpers/permissions';
 import { findResult } from '../../helpers/User';
 import { useUser } from '../../hooks/user';
 
-const twitchReg = /^https?:\/\/(www\.)?twitch\.tv/;
-const youtubeReg = /^https?:\/\/(www\.)?youtu(\.be|be\.)/;
-
-const getVoDVariant = result => {
-       if (!result || !result.vod) return 'outline-secondary';
-       if (twitchReg.test(result.vod)) {
-               return 'twitch';
-       }
-       if (youtubeReg.test(result.vod)) {
-               return 'outline-youtube';
-       }
-       return 'outline-secondary';
-};
-
-const getVoDIcon = result => {
-       const variant = getVoDVariant(result);
-       if (variant === 'twitch') {
-               return <Icon.TWITCH title="" />;
-       }
-       if (variant === 'outline-youtube') {
-               return <Icon.YOUTUBE title="" />;
-       }
-       return <Icon.VIDEO title="" />;
-};
-
 const Item = ({
        actions,
        round,
        tournament,
        user,
 }) => {
-       const { t } = useTranslation();
        const { user: authUser } = useUser();
 
        const result = React.useMemo(
@@ -52,22 +24,17 @@ const Item = ({
                () => maySeeResult(authUser, tournament, round, result),
                [authUser, result, round, tournament],
        );
+       const mayVerify = React.useMemo(
+               () => mayVerifyResult(authUser, tournament, round, result),
+               [authUser, result, round, tournament],
+       );
 
        return <div className="result">
                <Box user={user} />
                <div className="d-flex align-items-center justify-content-between">
                        <Badge actions={actions} round={round} tournament={tournament} user={user} />
-                       {maySee && result && result.vod ?
-                               <Button
-                                       className="vod-link"
-                                       href={result.vod}
-                                       size="sm"
-                                       target="_blank"
-                                       title={t('results.vod')}
-                                       variant={getVoDVariant(result)}
-                               >
-                                       {getVoDIcon(result)}
-                               </Button>
+                       {maySee || mayVerify ?
+                               <VodLink result={result} />
                        : null}
                </div>
        </div>;
index 0fa1e03f24537d8534665468892b4633c6b4ce4f..ac174eee11c39c0dc9a9252403abf0fc9b8992fc 100644 (file)
@@ -2,35 +2,14 @@ import PropTypes from 'prop-types';
 import React from 'react';
 
 import Item from './Item';
-import { sortByFinished, sortByResult } from '../../helpers/Participant';
-import { maySeeResults } from '../../helpers/permissions';
-import { getRunners, hasFixedRunners } from '../../helpers/Tournament';
-import { sortByTime, sortByUsername } from '../../helpers/Result';
+import { compileResults } from '../../helpers/Round';
 import { useUser } from '../../hooks/user';
 
 const List = ({ actions, round, tournament }) => {
        const { user } = useUser();
 
-       if (hasFixedRunners(tournament)) {
-               const runners = maySeeResults(user, tournament, round)
-                       ? sortByResult(getRunners(tournament), round)
-                       : sortByFinished(getRunners(tournament), round);
-               return <div className="results d-flex flex-wrap">
-                       {runners.map(participant =>
-                               <Item
-                                       actions={actions}
-                                       key={participant.id}
-                                       round={round}
-                                       tournament={tournament}
-                                       user={participant.user}
-                               />
-                       )}
-               </div>;
-       }
+       const results = React.useMemo(() => compileResults(tournament, round, user), [round, tournament, user]);
 
-       const results = maySeeResults(user, tournament, round)
-               ? sortByTime(round.results || [])
-               : sortByUsername(round.results || []);
        return <div className="results d-flex flex-wrap align-content-start">
                {results.map(result =>
                        <Item
diff --git a/resources/js/components/results/Table.jsx b/resources/js/components/results/Table.jsx
new file mode 100644 (file)
index 0000000..2a784f3
--- /dev/null
@@ -0,0 +1,66 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Table } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+
+import TableRow from './TableRow';
+import { maySeeResults } from '../../helpers/permissions';
+import { compileResults } from '../../helpers/Round';
+import { useUser } from '../../hooks/user';
+
+const ResultTable = ({ actions, round, tournament }) => {
+       const { t } = useTranslation();
+       const { user } = useUser();
+
+       const results = React.useMemo(
+               () => compileResults(tournament, round, user),
+               [round, tournament, user],
+       );
+       const maySee = React.useMemo(
+               () => maySeeResults(user, tournament, round),
+               [round, tournament, user],
+       );
+
+       return <Table className="result-table" striped hover>
+               <thead>
+                       <tr>
+                               <th className="result-runner">{t('results.runner')}</th>
+                               {maySee ?
+                                       <th className="result-placement">{t('results.placement')}</th>
+                               : null}
+                               <th className="result-vod">{t('results.vod')}</th>
+                               <th className="result-time">{t('results.result')}</th>
+                       </tr>
+               </thead>
+               <tbody>
+                       {results.map((result) => (
+                               <TableRow
+                                       actions={actions}
+                                       key={result.id}
+                                       round={round}
+                                       showPlacement={maySee}
+                                       tournament={tournament}
+                                       user={result.user}
+                               />
+                       ))}
+               </tbody>
+       </Table>;
+};
+
+ResultTable.propTypes = {
+       actions: PropTypes.shape({
+       }),
+       round: PropTypes.shape({
+               results: PropTypes.arrayOf(PropTypes.shape({
+               })),
+       }),
+       tournament: PropTypes.shape({
+               participants: PropTypes.arrayOf(PropTypes.shape({
+               })),
+               type: PropTypes.string,
+               users: PropTypes.arrayOf(PropTypes.shape({
+               })),
+       }),
+};
+
+export default ResultTable;
diff --git a/resources/js/components/results/TableRow.jsx b/resources/js/components/results/TableRow.jsx
new file mode 100644 (file)
index 0000000..c024bff
--- /dev/null
@@ -0,0 +1,69 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+import Badge from './Badge';
+import VodLink from './VodLink';
+import Box from '../users/Box';
+import { maySeeResult, mayVerifyResult } from '../../helpers/permissions';
+import { findResult } from '../../helpers/User';
+import { useUser } from '../../hooks/user';
+
+const TableRow = ({
+       actions,
+       round,
+       showPlacement = false,
+       tournament,
+       user,
+}) => {
+       const { t } = useTranslation();
+       const { user: authUser } = useUser();
+
+       const result = React.useMemo(
+               () => findResult(user, round),
+               [round, user],
+       );
+       const maySee = React.useMemo(
+               () => maySeeResult(authUser, tournament, round, result),
+               [authUser, result, round, tournament],
+       );
+       const mayVerify = React.useMemo(
+               () => mayVerifyResult(authUser, tournament, round, result),
+               [authUser, result, round, tournament],
+       );
+
+       return <tr>
+               <td className="result-runner">
+                       <Box user={user} />
+               </td>
+               {showPlacement ?
+                       <td className="result-placement">
+                               {maySee && result && result.placement
+                                       ? `${result.placement}. (${t('results.points', { count: result.score })})`
+                                       : t('results.pending')}
+                       </td>
+               : null}
+               <td className="result-vod">
+                       {maySee || mayVerify ?
+                               <VodLink result={result} withLabel />
+                       : null}
+               </td>
+               <td className="result-time">
+                       <Badge actions={actions} round={round} tournament={tournament} user={user} />
+               </td>
+       </tr>;
+};
+
+TableRow.propTypes = {
+       actions: PropTypes.shape({
+       }),
+       round: PropTypes.shape({
+       }),
+       showPlacement: PropTypes.bool,
+       tournament: PropTypes.shape({
+       }),
+       user: PropTypes.shape({
+       }),
+};
+
+export default TableRow;
diff --git a/resources/js/components/results/VodLink.jsx b/resources/js/components/results/VodLink.jsx
new file mode 100644 (file)
index 0000000..5eff825
--- /dev/null
@@ -0,0 +1,76 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Button } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+
+import Icon from '../common/Icon';
+
+const twitchReg = /^https?:\/\/(www\.)?twitch\.tv/;
+const youtubeReg = /^https?:\/\/(www\.)?youtu(\.be|be\.)/;
+
+const getVoDVariant = result => {
+       if (!result || !result.vod) return 'outline-secondary';
+       if (twitchReg.test(result.vod)) {
+               return 'twitch';
+       }
+       if (youtubeReg.test(result.vod)) {
+               return 'outline-youtube';
+       }
+       return 'outline-secondary';
+};
+
+const getVoDIcon = result => {
+       const variant = getVoDVariant(result);
+       if (variant === 'twitch') {
+               return <Icon.TWITCH title="" />;
+       }
+       if (variant === 'outline-youtube') {
+               return <Icon.YOUTUBE title="" />;
+       }
+       return <Icon.VIDEO title="" />;
+};
+
+const getVoDLabel = result => {
+       const variant = getVoDVariant(result);
+       if (variant === 'twitch') {
+               return 'icon.TwitchIcon';
+       }
+       if (variant === 'outline-youtube') {
+               return 'icon.YoutubeIcon';
+       }
+       return 'icon.VideoIcon';
+};
+
+const VodLink = ({
+       result,
+       withLabel = false,
+}) => {
+       const { t } = useTranslation();
+
+       if (!result?.vod) {
+               return null;
+       }
+
+       return <Button
+               className="vod-link"
+               href={result.vod}
+               size={withLabel ? 'md' : 'sm'}
+               target="_blank"
+               title={t('results.vod')}
+               variant={getVoDVariant(result)}
+       >
+               {getVoDIcon(result)}
+               {withLabel ?
+                       <span className="ms-2">{t(getVoDLabel(result))}</span>
+               : null}
+       </Button>;
+};
+
+VodLink.propTypes = {
+       result: PropTypes.shape({
+               vod: PropTypes.string,
+       }),
+       withLabel: PropTypes.bool,
+};
+
+export default VodLink;
index 45f2c853392620f8fca2fc75f91f2488040ed6ab..28c86b781a97122fd80b82018dce877ae37576a4 100644 (file)
@@ -11,15 +11,17 @@ import SeedRolledBy from './SeedRolledBy';
 import RoundProtocol from '../protocol/RoundProtocol';
 import List from '../results/List';
 import ReportButton from '../results/ReportButton';
+import Table from '../results/Table';
 import {
        mayDeleteRound,
        mayEditRound,
        mayReportResult,
+       mayVerifyResults,
        mayViewProtocol,
        isRunner,
 } from '../../helpers/permissions';
 import { formatNumber, isComplete } from '../../helpers/Round';
-import { hasFixedRunners } from '../../helpers/Tournament';
+import { hasAssignedGroups, hasFixedRunners, requiresVerification } from '../../helpers/Tournament';
 import { hasFinishedRound } from '../../helpers/User';
 import { useUser } from '../../hooks/user';
 
@@ -45,12 +47,18 @@ const getClassName = (round, tournament, user) => {
 
 const Item = ({
        actions,
+       resultView = 'list',
        round,
        tournament,
 }) => {
        const { t } = useTranslation();
        const { user } = useUser();
 
+       const mayVerify = React.useMemo(() => mayVerifyResults(user, tournament, round), [round, tournament, user]);
+       const unverifiedResults = React.useMemo(() => {
+               return (round.results || []).filter((result) => !result.verified_at).length;
+       }, [round]);
+
        return <li className={getClassName(round, tournament, user)}>
                {round.title ?
                        <h3>{round.title}</h3>
@@ -61,32 +69,40 @@ const Item = ({
                                        {formatNumber(tournament, round)}
                                        {t('rounds.date', { date: new Date(round.created_at) })}
                                </p>
-                               <p className="seed">
-                                       {round.code && round.code.length ?
-                                               <>
-                                                       <SeedCode code={round.code} game={round.game || 'alttpr'} />
-                                                       <br />
-                                               </>
-                                       : null}
-                                       <SeedButton
-                                               round={round}
-                                               tournament={tournament}
-                                       />
-                                       {' '}
-                                       <SeedRolledBy round={round} />
-                               </p>
-                               {mayReportResult(user, tournament) ?
-                                       <p className="report">
-                                               <ReportButton
+                               {!hasAssignedGroups(tournament) ? <>
+                                       <p className="seed">
+                                               {round.code && round.code.length ?
+                                                       <>
+                                                               <SeedCode code={round.code} game={round.game || 'alttpr'} />
+                                                               <br />
+                                                       </>
+                                               : null}
+                                               <SeedButton
                                                        round={round}
                                                        tournament={tournament}
-                                                       user={user}
                                                />
+                                               {' '}
+                                               <SeedRolledBy round={round} />
                                        </p>
-                               : null}
+                                       {mayReportResult(user, tournament) ?
+                                               <p className="report">
+                                                       <ReportButton
+                                                               round={round}
+                                                               tournament={tournament}
+                                                               user={user}
+                                                       />
+                                               </p>
+                                       : null}
+                               </> : null}
                                <div className="bottom-half">
                                        {!hasFixedRunners(tournament) && round.results && round.results.length ?
-                                               <p>{t('rounds.numberOfResults', { count: round.results.length })}</p>
+                                               <p>
+                                                       {t('rounds.numberOfResults', { count: round.results.length })}
+                                                       {mayVerify && unverifiedResults && requiresVerification(tournament) ? <>
+                                                               <br />
+                                                               {t('rounds.numberOfUnverifiedResults', { count: unverifiedResults })}
+                                                       </> : null}
+                                               </p>
                                        : null}
                                        <div className="button-bar">
                                                <LockButton round={round} tournament={tournament} />
@@ -102,7 +118,11 @@ const Item = ({
                                        </div>
                                </div>
                        </div>
-                       <List actions={actions} round={round} tournament={tournament} />
+                       {resultView === 'list' ?
+                               <List actions={actions} round={round} tournament={tournament} />
+                       :
+                               <Table actions={actions} round={round} tournament={tournament} />
+                       }
                </div>
        </li>;
 };
@@ -110,6 +130,7 @@ const Item = ({
 Item.propTypes = {
        actions: PropTypes.shape({
        }),
+       resultView: PropTypes.string,
        round: PropTypes.shape({
                code: PropTypes.arrayOf(PropTypes.string),
                created_at: PropTypes.string,
index c4a0293bfc2f48090f724256f5b2e6752c5bf2f6..dc343e18df6be78e286dfb1daca9de19fc807a2f 100644 (file)
@@ -9,6 +9,7 @@ import i18n from '../../i18n';
 
 const List = ({
        actions,
+       resultView,
        rounds,
        tournament,
 }) => rounds && rounds.length ? <>
@@ -17,6 +18,7 @@ const List = ({
                        <Item
                                actions={actions}
                                key={round.id}
+                               resultView={resultView}
                                round={round}
                                tournament={tournament}
                        />
@@ -35,6 +37,7 @@ List.propTypes = {
        actions: PropTypes.shape({
                moreRounds: PropTypes.func,
        }),
+       resultView: PropTypes.string,
        rounds: PropTypes.arrayOf(PropTypes.shape({
                id: PropTypes.number,
        })),
index af339beaf26ef80badd0b0b21a501cf54ee9beb4..747ed7a249d3778ed20fa9789c86e46c0fdc4cb4 100644 (file)
@@ -52,9 +52,19 @@ const Detail = ({
        actions,
        tournament,
 }) => {
+       const [resultView, setResultView] = React.useState(localStorage.getItem('tournaments.resultView') || 'list');
+
        const { t } = useTranslation();
        const { user } = useUser();
 
+       const toggleResultView = React.useCallback(() => {
+               setResultView((oldView) => {
+                       const newView = oldView === 'list' ? 'table' : 'list';
+                       localStorage.setItem('tournaments.resultView', newView);
+                       return newView;
+               })
+       }, []);
+
        return <Container className={getClassName(tournament, user)} fluid>
                <Row>
                        <Col lg={8} xl={9}>
@@ -143,15 +153,21 @@ const Detail = ({
                                ): null}
                                <div className="d-flex align-items-center justify-content-between">
                                        <h2>{t('rounds.heading')}</h2>
-                                       {actions.addRound && mayAddRounds(user, tournament) ?
-                                               <Button onClick={actions.addRound}>
-                                                       {t('rounds.new')}
+                                       <div className="button-bar">
+                                               <Button onClick={toggleResultView} variant="outline-secondary">
+                                                       {t(`results.${resultView}`)}
                                                </Button>
-                                       : null}
+                                               {actions.addRound && mayAddRounds(user, tournament) ?
+                                                       <Button onClick={actions.addRound}>
+                                                               {t('rounds.new')}
+                                                       </Button>
+                                               : null}
+                                       </div>
                                </div>
                                {tournament.rounds ?
                                        <Rounds
                                                actions={actions}
+                                               resultView={resultView}
                                                rounds={tournament.rounds}
                                                tournament={tournament}
                                        />
index ed16f3865d44f85fa16ed464b2fcc69bdd84941f..0024e9fec2bd2eaeeea6297828ef71b7d81d8454 100644 (file)
@@ -55,6 +55,9 @@ export const getIcon = (result, maySee) => {
        if (result.placement === 3 && maySee) {
                return <Icon.THIRD_PLACE className="text-bronze" size="lg" />;
        }
+       if (result.verified_at) {
+               return <Icon.VERIFIED className="text-info" size="lg" />;
+       }
        return <Icon.FINISHED className="text-success" size="lg" />;
 };
 
@@ -87,4 +90,6 @@ export default {
        getIcon,
        getTime,
        parseTime,
+       sortByTime,
+       sortByUsername,
 };
index dd349de7f5d7364af0de51a5ba744529e8054870..c12d41d66b3d3051f7a52648a1af24449fd50919 100644 (file)
@@ -1,6 +1,22 @@
 import Participant from './Participant';
+import { maySeeResults } from './permissions';
+import Result from './Result';
 import Tournament from './Tournament';
 
+export const compileResults = (tournament, round, user) => {
+       if (Tournament.hasFixedRunners(tournament)) {
+               const runners = Tournament.getRunners(tournament);
+               if (maySeeResults(user, tournament, round)) {
+                       return Participant.sortByResult(runners, round);
+               }
+               return Participant.sortByFinished(runners, round);
+       }
+       if (maySeeResults(user, tournament, round)) {
+               return Result.sortByTime(round.results || []);
+       }
+       return Result.sortByUsername(round.results || []);
+}
+
 export const formatNumberAlways = (tournament, round) => {
        const group = (tournament?.group_size > 1 && round?.group) || '';
        return round?.number ? `#${round.number}${group} ` : `X${round.id}`;
index c589ed4437519f58ed4f9f88dc89133e8c434641..206b15c8a0d380bcfe292c147e8d90eb345dae5e 100644 (file)
@@ -87,11 +87,15 @@ export const exportXlsx = async (tnmt) => {
        if (users.length > tournament.rounds.length) {
                summary.addRow([
                        i18n.t('results.runner'),
+                       i18n.t('users.discordId'),
+                       i18n.t('users.discordTag'),
                        ...tournament.rounds.map((round) => round.title || Round.formatNumberAlways(tournament, round)),
                ]);
                users.forEach((user) => {
                        summary.addRow([
                                User.getUserName(user),
+                               user.id,
+                               user.username,
                                ...tournament.rounds.map((round) => Result.getTime(User.findResult(user, round), true)),
                        ]);
                });
@@ -338,6 +342,8 @@ export const removeApplication = (tournament, id) => {
        };
 };
 
+export const requiresVerification = tournament => (tournament?.type === 'open-grouped-async');
+
 export const sortParticipants = tournament => {
        if (!tournament || !tournament.participants || !tournament.participants.length) {
                return tournament;
@@ -355,6 +361,8 @@ export default {
        getTournamentAdmins,
        getTournamentCrew,
        getTournamentMonitors,
+       hasAssignedGroups,
+       hasFixedRunners,
        hasRunners,
        hasScoreboard,
        hasSignup,
@@ -364,5 +372,6 @@ export default {
        patchResult,
        patchRound,
        patchUser,
+       requiresVerification,
        sortParticipants,
 };
index 4ee602bbf22e06f44d2162afca154e1217dee081..64393793a148de4c26a46808d34caa7feaccded0 100644 (file)
@@ -390,6 +390,7 @@ export default {
                        selfAssignError: 'Fehler beim Zuweisen',
                        selfAssignSuccess: 'Gruppen zugewiesen',
                        tournamentClosed: 'Dieses Turnier ist geschlossen.',
+                       verified: 'Bestätigt',
                },
                icon: {
                        AddIcon: 'Hinzufügen',
@@ -613,6 +614,7 @@ export default {
                        edit: 'Ergebnis ändern',
                        editComment: 'Kommentar ändern',
                        forfeit: 'Aufgegeben',
+                       list: 'Liste',
                        pending: 'Ausstehend',
                        placement: 'Platzierung',
                        points_one: '{{ count }} Punkt',
@@ -626,6 +628,7 @@ export default {
                        round: 'Runde',
                        runner: 'Runner',
                        score: 'Punkte',
+                       table: 'Tabelle',
                        time: 'Zeit: {{ time }}',
                        verification: 'Verifikation',
                        verificationPending: 'Noch nicht verifiziert',
@@ -653,6 +656,7 @@ export default {
                        noSeed: 'Noch kein Seed',
                        numberOfResults_one: '{{ count }} Ergebnis',
                        numberOfResults_other: '{{ count }} Ergebnisse',
+                       numberOfUnverifiedResults: '{{ count }} unbestätigt',
                        loadMore: 'weitere Runden laden',
                        lock: 'Runde sperren',
                        lockDescription: 'Wenn die Runde gesperrt wird, können Runner keine Änderungen an ihrem Ergebnis mehr vornehmen.',
@@ -1060,6 +1064,7 @@ export default {
                        p2: 'Für die Anzeige des Leaderboards wird eine Anfrage an alttp.localhorst.tv gesendet. Diese Anfrage wird anonymisiert protokolliert und nicht weiter verwertet.',
                },
                users: {
+                       discordId: 'Discord ID',
                        discordTag: 'Discord Tag',
                        editNickname: 'Name bearbeiten',
                        editStreamLink: 'Stream Link bearbeiten',
index 945b2a1fa0e96240618447919faeb9c384aa963c..ae5442dc898fb7430c8d4928e69760ff5d906020 100644 (file)
@@ -390,6 +390,7 @@ export default {
                        selfAssignError: 'Error assigning groups',
                        selfAssignSuccess: 'Groups assigned',
                        tournamentClosed: 'This tournament has closed.',
+                       verified: 'Verified',
                },
                icon: {
                        AddIcon: 'Add',
@@ -613,6 +614,7 @@ export default {
                        edit: 'Change result',
                        editComment: 'Edit comment',
                        forfeit: 'Forfeit',
+                       list: 'List',
                        pending: 'Pending',
                        placement: 'Placement',
                        points_one: '{{ count }} point',
@@ -626,6 +628,7 @@ export default {
                        round: 'Round',
                        runner: 'Runner',
                        score: 'Score',
+                       table: 'Table',
                        time: 'Time: {{ time }}',
                        verification: 'Verification',
                        verificationPending: 'Pending verification',
@@ -653,6 +656,7 @@ export default {
                        noSeed: 'No seed set',
                        numberOfResults_one: '{{ count }} submission',
                        numberOfResults_other: '{{ count }} submissions',
+                       numberOfUnverifiedResults: '{{ count }} unverified',
                        loadMore: 'load more rounds',
                        lock: 'Lock round',
                        lockDescription: 'When a round is locked, runners cannot submit or change results.',
@@ -1060,6 +1064,7 @@ export default {
                        p2: 'To display the leaderboard, a request is made to alttp.localhorst.tv. This request is logged anonymously and not further processed.',
                },
                users: {
+                       discordId: 'Discord ID',
                        discordTag: 'Discord tag',
                        editNickname: 'Edit name',
                        editStreamLink: 'Edit stream link',
index 99fa5cf324525063775bbc073065f8c4ffacd5ba..ea465873b948f97577837d350797e4d35ae23f80 100644 (file)
@@ -19,7 +19,7 @@
        transition: top 0.15s ease-in-out;
 
        &.has-comment {
-               box-shadow: 0 0.5ex 0 $info;
+               box-shadow: 0 0.5ex 0 $info !important;
        }
        &:active {
                box-shadow: none;
        }
 }
 
+.result-table {
+       width: 100%;
+
+       .result-time,
+       .result-vod {
+               text-align: right;
+               width: 15ex;
+       }
+}
+
 .results {
        .result {
                padding: 1ex;