]> git.localhorst.tv Git - alttp.git/commitdiff
lookup getseed on result post
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 26 Nov 2025 15:44:39 +0000 (16:44 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 26 Nov 2025 15:44:39 +0000 (16:44 +0100)
app/Http/Controllers/ResultController.php
app/Models/Result.php
database/migrations/2025_11_26_142512_result_got_seed_at.php [new file with mode: 0644]
resources/js/components/results/DetailDialog.jsx
resources/js/helpers/Result.jsx
resources/js/helpers/Tournament.js
resources/js/i18n/de.js
resources/js/i18n/en.js
resources/sass/results.scss

index 6a4eb59b3072f95b0a3063a912af76a52fd18b3d..ec732b6d987d6d9c260dc8bfca79549bd5a0f17b 100644 (file)
@@ -42,6 +42,17 @@ class ResultController extends Controller
                if (!$result->verified_at) {
                        $result->vod = !empty($validatedData['vod']) ? $validatedData['vod'] : null;
                }
+               if (!$result->got_seed_at) {
+                       // try to find earliest access to the seed
+                       $entry = $round->protocols()
+                               ->where('user_id', '=', $result->user_id)
+                               ->where('type', '=', 'round.getseed')
+                               ->oldest()
+                               ->first();
+                       if ($entry) {
+                               $result->got_seed_at = $entry->created_at;
+                       }
+               }
                $result->save();
 
                if ($result->wasChanged()) {
index 77cb7f9c7173f71bf37f8e13ffa409e46b3f8001..a85dbc7a83f84708355b8f257b4333b4fe16b8e0 100644 (file)
@@ -104,8 +104,11 @@ class Result extends Model
 
        protected $casts = [
                'forfeit' => 'boolean',
+               'got_seed_at' => 'datetime',
                'time' => 'double',
                'user_id' => 'string',
+               'verified_at' => 'datetime',
+               'verified_by_id' => 'string',
        ];
 
        protected $appends = [
diff --git a/database/migrations/2025_11_26_142512_result_got_seed_at.php b/database/migrations/2025_11_26_142512_result_got_seed_at.php
new file mode 100644 (file)
index 0000000..d326c74
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+       /**
+        * Run the migrations.
+        */
+       public function up(): void
+       {
+               Schema::table('results', function (Blueprint $table) {
+                       $table->timestamp('got_seed_at')->nullable()->default(null);
+               });
+       }
+
+       /**
+        * Reverse the migrations.
+        */
+       public function down(): void
+       {
+               Schema::table('results', function (Blueprint $table) {
+                       $table->dropColumn('got_seed_at');
+               });
+       }
+};
index 17141fb8425a67c4b3772f8b9aae82cc38797adc..388d57d648cb803ecdc289bf8e3bbe0d2988efd5 100644 (file)
@@ -1,3 +1,4 @@
+import moment from 'moment';
 import PropTypes from 'prop-types';
 import React from 'react';
 import { Button, Col, Form, Modal, Row } from 'react-bootstrap';
@@ -5,7 +6,7 @@ import { useTranslation } from 'react-i18next';
 
 import Verification from './Verification';
 import Box from '../users/Box';
-import { getTime } from '../../helpers/Result';
+import { formatTime, getTime } from '../../helpers/Result';
 import { formatNumberAlways } from '../../helpers/Round';
 import { maySeeResult, mayVerifyResult } from '../../helpers/permissions';
 import { findResult } from '../../helpers/User';
@@ -92,6 +93,32 @@ const DetailDialog = ({
                </Modal.Body>
                {result?.verified_at || (mayVerify && actions?.verifyResult) ? (
                        <Modal.Body className="verification">
+                               {mayVerify ?
+                                       <Row className="mb-3">
+                                               <Form.Group as={Col} sm={4}>
+                                                       <Form.Label>{t('results.createdAt')}</Form.Label>
+                                                       <div>{t('results.createdAtFormat', { date: new Date(result.created_at) })}</div>
+                                               </Form.Group>
+                                               <Form.Group as={Col} sm={4}>
+                                                       <Form.Label>{t('results.gotSeedAt')}</Form.Label>
+                                                       <div>
+                                                               {result.got_seed_at
+                                                                       ? t('results.gotSeedAtFormat', { date: new Date(result.got_seed_at) })
+                                                                       : <em>{t('general.unknown')}</em>
+                                                               }
+                                                       </div>
+                                               </Form.Group>
+                                               <Form.Group as={Col} sm={4}>
+                                                       <Form.Label>{t('general.difference')}</Form.Label>
+                                                       <div>
+                                                               {result.got_seed_at
+                                                                       ? formatTime({ time: moment(result.created_at).diff(result.got_seed_at, 'seconds') })
+                                                                       : '—'
+                                                               }
+                                                       </div>
+                                               </Form.Group>
+                                       </Row>
+                               : null}
                                <Verification actions={actions} result={result} round={round} tournament={tournament} />
                        </Modal.Body>
                ) : null}
index 0024e9fec2bd2eaeeea6297828ef71b7d81d8454..833368a78e81047bf2afbc9d76e988be4a19e6ec 100644 (file)
@@ -27,16 +27,18 @@ export const compareResult = (a, b) => {
 };
 
 export const formatTime = result => {
-       const hours = `${Math.floor(result.time / 60 / 60)}`;
-       let minutes = `${Math.floor((result.time / 60) % 60)}`;
-       let seconds = `${Math.floor(result.time % 60)}`;
+       const time = result?.time < 0 ? result.time * -1 : result.time;
+       const sign = result?.time < 0 ? '-' : '';
+       const hours = `${Math.floor(time / 60 / 60)}`;
+       let minutes = `${Math.floor((time / 60) % 60)}`;
+       let seconds = `${Math.floor(time % 60)}`;
        while (minutes.length < 2) {
                minutes = `0${minutes}`;
        }
        while (seconds.length < 2) {
                seconds = `0${seconds}`;
        }
-       return `${hours}:${minutes}:${seconds}`;
+       return `${sign}${hours}:${minutes}:${seconds}`;
 };
 
 export const getIcon = (result, maySee) => {
index 206b15c8a0d380bcfe292c147e8d90eb345dae5e..605b9473ff5aa2a14da298ac9649f5607a87a6c7 100644 (file)
@@ -1,4 +1,5 @@
 import axios from 'axios';
+import moment from 'moment';
 
 import Application from './Application';
 import downloadBlob from './downloadBlob';
@@ -85,12 +86,15 @@ export const exportXlsx = async (tnmt) => {
                views: [{ state: 'frozen', xSplit: 1, ySplit: 1 }],
        });
        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)),
-               ]);
+               summary.columns = [
+                       { header: i18n.t('results.runner'), width: 15 },
+                       { header: i18n.t('users.discordId'), width: 20 },
+                       { header: i18n.t('users.discordTag'), width: 15 },
+                       ...tournament.rounds.map((round) => ({
+                               header: round.title || Round.formatNumberAlways(tournament, round),
+                               width: 15,
+                       })),
+               ];
                users.forEach((user) => {
                        summary.addRow([
                                User.getUserName(user),
@@ -100,10 +104,13 @@ export const exportXlsx = async (tnmt) => {
                        ]);
                });
        } else {
-               summary.addRow([
-                       i18n.t('results.round'),
-                       ...users.map(User.getUserName),
-               ]);
+               summary.columns = [
+                       { header: i18n.t('results.round'), width: 20 },
+                       ...users.map((user) => ({
+                               header: User.getUserName(user),
+                               width: 15,
+                       })),
+               ];
                tournament.rounds.forEach((round) => {
                        summary.addRow([
                                round.title || Round.formatNumberAlways(tournament, round),
@@ -116,26 +123,32 @@ export const exportXlsx = async (tnmt) => {
                const worksheet = workbook.addWorksheet(getRoundWorksheetTitle(tournament, round), {
                        views: [{ state: 'frozen', xSplit: 1, ySplit: 1 }],
                });
-               worksheet.addRow([
-                       i18n.t('results.runner'),
-                       i18n.t('results.forfeit'),
-                       i18n.t('results.reportTime'),
-                       i18n.t('results.placement'),
-                       i18n.t('results.score'),
-                       i18n.t('results.comment'),
-                       i18n.t('results.vod'),
-                       i18n.t('general.created_at'),
-               ]);
+               worksheet.columns = [
+                       { header: i18n.t('results.runner'), width: 15 },
+                       { header: i18n.t('results.forfeit'), width: 12 },
+                       { header: i18n.t('results.reportTime'), width: 10 },
+                       { header: i18n.t('results.placement'), width: 12 },
+                       { header: i18n.t('results.score'), width: 8 },
+                       { header: i18n.t('results.comment'), width: 20 },
+                       { header: i18n.t('results.vod'), width: 20 },
+                       { header: i18n.t('general.created_at'), width: 18 },
+                       { header: i18n.t('results.gotSeedAt'), width: 18 },
+                       { header: i18n.t('general.difference'), width: 10 },
+               ];
                round.results.forEach((result) => {
                        worksheet.addRow([
                                User.getUserName(result.user),
                                result.forfeit ? 'x' : '-',
-                               result.time,
+                               Result.formatTime(result),
                                result.placement,
                                result.score,
                                result.comment || '',
                                result.vod || '',
-                               result.created_at,
+                               moment(result.created_at).format('L LT'),
+                               result.got_seed_at ? moment(result.got_seed_at).format('L LT') : i18n.t('general.unknown'),
+                               result.got_seed_at
+                                       ? Result.formatTime({ time: moment(result.created_at).diff(result.got_seed_at, 'seconds') })
+                                       : '—',
                        ]);
                });
        });
index 64393793a148de4c26a46808d34caa7feaccded0..10797ccbe587a799ee7831c32a59ce9eff40cacd 100644 (file)
@@ -359,6 +359,7 @@ export default {
                        appName: 'ALttP',
                        copied: 'Kopiert',
                        created_at: 'Erstellt am',
+                       difference: 'Differenz',
                        exportError: 'Fehler beim Exportieren',
                        languages: {
                                de: 'Deutsch',
@@ -375,6 +376,7 @@ export default {
                        saveError: 'Fehler beim Speichern',
                        saveSuccess: 'Gespeichert',
                        summary: 'Zusammenfassung',
+                       unknown: 'Unbekannt',
                        upload: 'Datei hochladen',
                        uploadError: 'Fehler beim Hochladen',
                        uploading: 'Am Hochladen...',
@@ -609,11 +611,15 @@ export default {
                },
                results: {
                        addComment: 'Kommentieren',
+                       createdAt: 'Eingetragen am',
+                       createdAtFormat: '{{ date, L LT }}',
                        comment: 'Kommentar',
                        details: 'Details',
                        edit: 'Ergebnis ändern',
                        editComment: 'Kommentar ändern',
                        forfeit: 'Aufgegeben',
+                       gotSeedAt: 'Seed geladen',
+                       gotSeedAtFormat: '{{ date, L LT }}',
                        list: 'Liste',
                        pending: 'Ausstehend',
                        placement: 'Platzierung',
index ae5442dc898fb7430c8d4928e69760ff5d906020..0491e31087d00c56edf58b0e84ae2eadf8613f12 100644 (file)
@@ -359,6 +359,7 @@ export default {
                        appName: 'ALttP',
                        copied: 'Copied',
                        created_at: 'Created at',
+                       difference: 'Difference',
                        exportError: 'Error during export',
                        languages: {
                                de: 'German',
@@ -375,6 +376,7 @@ export default {
                        saveError: 'Error saving',
                        saveSuccess: 'Saved successfully',
                        summary: 'Summary',
+                       unknown: 'Unknown',
                        upload: 'Upload file',
                        uploadError: 'Error uploading',
                        uploading: 'Uploading...',
@@ -610,10 +612,14 @@ export default {
                results: {
                        addComment: 'Comment',
                        comment: 'Comment',
+                       createdAt: 'Entry from',
+                       createdAtFormat: '{{ date, L LT }}',
                        details: 'Details',
                        edit: 'Change result',
                        editComment: 'Edit comment',
                        forfeit: 'Forfeit',
+                       gotSeedAt: 'Got seed at',
+                       gotSeedAtFormat: '{{ date, L LT }}',
                        list: 'List',
                        pending: 'Pending',
                        placement: 'Placement',
index ea465873b948f97577837d350797e4d35ae23f80..00659a9a246dd43cb9559af32b37ba9708212aaa 100644 (file)
@@ -46,7 +46,7 @@
        .result-time,
        .result-vod {
                text-align: right;
-               width: 15ex;
+               width: 18ex;
        }
 }