]> git.localhorst.tv Git - alttp.git/commitdiff
clickable results
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 6 Apr 2022 13:35:15 +0000 (15:35 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 6 Apr 2022 13:35:15 +0000 (15:35 +0200)
resources/js/components/results/DetailDialog.js [new file with mode: 0644]
resources/js/components/results/Item.js
resources/js/helpers/Result.js
resources/js/i18n/de.js
resources/js/i18n/en.js
resources/sass/results.scss

diff --git a/resources/js/components/results/DetailDialog.js b/resources/js/components/results/DetailDialog.js
new file mode 100644 (file)
index 0000000..368575b
--- /dev/null
@@ -0,0 +1,95 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import { Button, Col, Form, Modal, Row } from 'react-bootstrap';
+import { withTranslation } from 'react-i18next';
+
+import Box from '../users/Box';
+import { getTime } from '../../helpers/Result';
+import { findResult } from '../../helpers/Participant';
+import { maySeeResults } from '../../helpers/permissions';
+import { withUser } from '../../helpers/UserContext';
+import i18n from '../../i18n';
+
+const getPlacement = result =>
+       `${result.placement}. (${i18n.t('results.points', { count: result.score })})`;
+
+const DetailDialog = ({
+       onHide,
+       participant,
+       round,
+       show,
+       tournament,
+       user,
+}) => {
+       const result = findResult(participant, round);
+       const maySee = maySeeResults(user, tournament, round);
+       return <Modal className="result-dialog" onHide={onHide} show={show}>
+               <Modal.Header closeButton>
+                       <Modal.Title>
+                               {i18n.t('results.details')}
+                       </Modal.Title>
+               </Modal.Header>
+               <Modal.Body>
+                       <Row>
+                               <Form.Group as={Col} sm={6}>
+                                       <Form.Label>{i18n.t('results.round')}</Form.Label>
+                                       <div>
+                                               #{round.number || '?'}
+                                               {' '}
+                                               {i18n.t('rounds.date', { date: new Date(round.created_at) })}
+                                       </div>
+                               </Form.Group>
+                               <Form.Group as={Col} sm={6}>
+                                       <Form.Label>{i18n.t('results.runner')}</Form.Label>
+                                       <div><Box user={participant.user} /></div>
+                               </Form.Group>
+                               <Form.Group as={Col} sm={6}>
+                                       <Form.Label>{i18n.t('results.result')}</Form.Label>
+                                       <div>
+                                               {maySee && result && result.has_finished
+                                                       ? getTime(result, maySee)
+                                                       : i18n.t('results.pending')}
+                                       </div>
+                               </Form.Group>
+                               <Form.Group as={Col} sm={6}>
+                                       <Form.Label>{i18n.t('results.placement')}</Form.Label>
+                                       <div>
+                                               {maySee && result && result.placement
+                                                       ? getPlacement(result)
+                                                       : i18n.t('results.pending')}
+                                       </div>
+                               </Form.Group>
+                               {maySee && result && result.comment ?
+                                       <Form.Group as={Col} sm={12}>
+                                               <Form.Label>{i18n.t('results.comment')}</Form.Label>
+                                               <div>{result.comment}</div>
+                                       </Form.Group>
+                               : null}
+                       </Row>
+               </Modal.Body>
+               <Modal.Footer>
+                       <Button onClick={onHide} variant="secondary">
+                               {i18n.t('button.close')}
+                       </Button>
+               </Modal.Footer>
+       </Modal>;
+};
+
+DetailDialog.propTypes = {
+       onHide: PropTypes.func,
+       participant: PropTypes.shape({
+               user: PropTypes.shape({
+               }),
+       }),
+       round: PropTypes.shape({
+               created_at: PropTypes.string,
+               number: PropTypes.number,
+       }),
+       show: PropTypes.bool,
+       tournament: PropTypes.shape({
+       }),
+       user: PropTypes.shape({
+       }),
+};
+
+export default withTranslation()(withUser(DetailDialog));
index 0d73105edd21ac13268223742196ca9bbdfe3daa..a2ee8e392b82a392b76236b8bab92e4e7806d6f7 100644 (file)
@@ -1,46 +1,14 @@
 import PropTypes from 'prop-types';
-import React from 'react';
-import { withTranslation } from 'react-i18next';
+import React, { useState } from 'react';
+import { Button } from 'react-bootstrap';
 
-import Icon from '../common/Icon';
+import DetailDialog from './DetailDialog';
 import Box from '../users/Box';
-import { formatTime } from '../../helpers/Result';
+import { getIcon, getTime } from '../../helpers/Result';
 import { findResult } from '../../helpers/Participant';
 import { maySeeResults } from '../../helpers/permissions';
 import { withUser } from '../../helpers/UserContext';
 
-const getIcon = (result, maySee) => {
-       if (!result || !result.has_finished) {
-               return <Icon.PENDING className="text-muted" size="lg" />;
-       }
-       if (result.forfeit && maySee) {
-               return <Icon.FORFEIT className="text-danger" size="lg" />;
-       }
-       if (result.placement === 1 && maySee) {
-               return <Icon.FIRST_PLACE className="text-gold" size="lg" />;
-       }
-       if (result.placement === 2 && maySee) {
-               return <Icon.SECOND_PLACE className="text-silver" size="lg" />;
-       }
-       if (result.placement === 3 && maySee) {
-               return <Icon.THIRD_PLACE className="text-bronze" size="lg" />;
-       }
-       return <Icon.FINISHED className="text-success" size="lg" />;
-};
-
-const getTime = (result, maySee) => {
-       if (!result || !maySee) {
-               return null;
-       }
-       if (result.time) {
-               return formatTime(result);
-       }
-       if (result.forfeit) {
-               return 'DNF';
-       }
-       return '?';
-};
-
 const getClassName = result => {
        const classNames = ['status'];
        if (result && result.has_finished) {
@@ -60,19 +28,28 @@ const Item = ({
        tournament,
        user,
 }) => {
+       const [showDialog, setShowDialog] = useState(false);
        const result = findResult(participant, round);
        const maySee = maySeeResults(user, tournament, round);
        return <div className="result">
                <Box user={participant.user} />
-               <div
+               <Button
                        className={getClassName(result)}
+                       onClick={() => setShowDialog(true)}
                        title={maySee && result && result.comment ? result.comment : null}
                >
                        <span className="time">
                                {getTime(result, maySee)}
                        </span>
                        {getIcon(result, maySee)}
-               </div>
+               </Button>
+               <DetailDialog
+                       onHide={() => setShowDialog(false)}
+                       participant={participant}
+                       round={round}
+                       show={showDialog}
+                       tournament={tournament}
+               />
        </div>;
 };
 
@@ -89,4 +66,4 @@ Item.propTypes = {
        }),
 };
 
-export default withTranslation()(withUser(Item));
+export default withUser(Item);
index 06ade091803e4a649a14914247e761ac01ad9619..205a42b27325050800c4fe55f728f43de1d0c8c0 100644 (file)
@@ -1,3 +1,6 @@
+import React from 'react';
+import Icon from '../components/common/Icon';
+
 export const formatTime = result => {
        const hours = `${Math.floor(result.time / 60 / 60)}`;
        let minutes = `${Math.floor((result.time / 60) % 60)}`;
@@ -11,6 +14,38 @@ export const formatTime = result => {
        return `${hours}:${minutes}:${seconds}`;
 };
 
+export const getIcon = (result, maySee) => {
+       if (!result || !result.has_finished) {
+               return <Icon.PENDING className="text-muted" size="lg" />;
+       }
+       if (result.forfeit && maySee) {
+               return <Icon.FORFEIT className="text-danger" size="lg" />;
+       }
+       if (result.placement === 1 && maySee) {
+               return <Icon.FIRST_PLACE className="text-gold" size="lg" />;
+       }
+       if (result.placement === 2 && maySee) {
+               return <Icon.SECOND_PLACE className="text-silver" size="lg" />;
+       }
+       if (result.placement === 3 && maySee) {
+               return <Icon.THIRD_PLACE className="text-bronze" size="lg" />;
+       }
+       return <Icon.FINISHED className="text-success" size="lg" />;
+};
+
+export const getTime = (result, maySee) => {
+       if (!result || !maySee) {
+               return null;
+       }
+       if (result.time) {
+               return formatTime(result);
+       }
+       if (result.forfeit) {
+               return 'DNF';
+       }
+       return '?';
+};
+
 export const parseTime = str => {
        if (!str) return null;
        return `${str}`.split(/[-.: ]+/).reduce((acc,time) => (60 * acc) + +time, 0);
@@ -18,5 +53,7 @@ export const parseTime = str => {
 
 export default {
        formatTime,
+       getIcon,
+       getTime,
        parseTime,
 };
index 09d6db7ea1690b0c244b58a37f68dcfe4f6697c3..322d0f2fd079765427a6dfe4049302338e9dc6a5 100644 (file)
@@ -144,14 +144,22 @@ export default {
                results: {
                        addComment: 'Kommentieren',
                        comment: 'Kommentar',
+                       details: 'Details',
                        edit: 'Ergebnis ändern',
                        editComment: 'Kommentar ändern',
                        forfeit: 'Aufgegeben',
+                       pending: 'Ausstehend',
+                       placement: 'Platzierung',
+                       points_one: '{{ count }} Punkt',
+                       points_other: '{{ count }} Punkte',
                        report: 'Ergebnis eintragen',
                        reportError: 'Fehler beim Eintragen :(',
                        reportPreview: 'Wird als {{ time }} festgehalten',
                        reportSuccess: 'Festgehalten',
                        reportTime: 'Zeit',
+                       result: 'Ergebnis',
+                       round: 'Runde',
+                       runner: 'Runner',
                        time: 'Zeit: {{ time }}',
                },
                rounds: {
index 72548f6a815801752217ea7bb687fd8211b9d1a6..acd3c48ac532aaa0f86ee48566c14ac1885197d8 100644 (file)
@@ -144,14 +144,22 @@ export default {
                results: {
                        addComment: 'Comment',
                        comment: 'Comment',
+                       details: 'Details',
                        edit: 'Change result',
                        editComment: 'Edit comment',
                        forfeit: 'Forfeit',
+                       pending: 'Pending',
+                       placement: 'Placement',
+                       points_one: '{{ count }} point',
+                       points_other: '{{ count }} points',
                        report: 'Report result',
                        reportError: 'Error saving :(',
                        reportPreview: 'Will be recorded as {{ time }}',
                        reportSuccess: 'Stored, thanks :)',
                        reportTime: 'Time',
+                       result: 'Result',
+                       round: 'Round',
+                       runner: 'Runner',
                        time: 'Time: {{ time }}',
                },
                rounds: {
index d78ebc53b47a8560a32d978509dc9a0c2a1f4661..3907e0ecd990f9d481737180b143b75cbcdd52e3 100644 (file)
@@ -8,18 +8,27 @@
 
                .status {
                        display: flex;
+                       position: relative;
                        align-items: center;
                        justify-content: space-between;
                        margin-top: 1ex;
                        padding: 0.5em;
+                       width: 100%;
                        min-width: 15ex;
+                       border: none;
                        border-radius: 1ex;
                        background: $dark;
                        color: $light;
 
+                       box-shadow: none;
+                       transition: top 0.15s ease-in-out;
                        &.has-comment {
                                box-shadow: 0 0.5ex 0 $info;
                        }
+                       &:active {
+                               box-shadow: none;
+                               top: 0.5ex;
+                       }
 
                        .time {
                                min-width: 9ex;