feat(TournamentStats): update internal tournament scoring logic and UI enhancements
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 36s

- Revised scoring system for internal tournaments to assign points based on placement (1st = 100, 2nd = 99, etc.), with adjustments for tied ranks and a cap at 101 points.
- Refactored `groupPointsFromRankings` function for improved clarity and efficiency.
- Enhanced the InternalTournamentStats component UI, integrating a dialog for better user interaction and accessibility.
- Updated localization strings across multiple languages to reflect the new scoring system and UI changes, improving user understanding and experience.
This commit is contained in:
Torsten Schulz (local)
2026-04-08 11:02:34 +02:00
parent 4a53801a54
commit 43dbd5442a
18 changed files with 170 additions and 109 deletions

View File

@@ -1,7 +1,8 @@
/**
* Statistik-Punkte für interne Einzel-Turniere (Gruppenphase + K.-o.-Runde).
*
* Gruppe: Letzter in der Gruppe = 1 Punkt, Vorletzter = 2, … Gleiche Platzierung = gleiche Punkte.
* Gruppe: 1. Platz = 100, 2. = 99, 3. = 98, … (Platzierung aus API: 1 = bestes Ergebnis).
* Gleiche Platzierung = gleiche Punkte. Ab Platz 101 wird mit 0 gekappt.
* K.-o.: Wer die K.-o.-Runde erreicht: höchste Gruppenpunkte dieser Klasse + 1,
* danach +1 pro gewonnenes K.-o.-Spiel.
*/
@@ -50,24 +51,18 @@ export function parseWinnerFromMatch(match) {
/**
* @param {Array<{ position: number, id: number }>} rankings Platz 1 = bestes Ergebnis; id = Turnier-Mitglied-ID
* @returns {Map<number, number>} tournamentMemberId -> Punkte (von unten gezählt)
* @returns {Map<number, number>} tournamentMemberId -> Punkte (1. Platz 100, 2. 99, …)
*/
export function groupPointsFromRankings(rankings) {
const map = new Map();
if (!rankings || rankings.length === 0) return map;
const sorted = [...rankings].sort((a, b) => Number(b.position) - Number(a.position));
let pts = 1;
let i = 0;
while (i < sorted.length) {
const pos = Number(sorted[i].position);
let j = i;
while (j < sorted.length && Number(sorted[j].position) === pos) {
const id = sorted[j].id;
if (id != null) map.set(Number(id), pts);
j += 1;
}
i = j;
pts += 1;
for (const r of rankings) {
const pos = Number(r.position);
if (!Number.isFinite(pos) || pos < 1) continue;
const id = r.id;
if (id == null) continue;
const pts = Math.max(0, 101 - pos);
map.set(Number(id), pts);
}
return map;
}

View File

@@ -1,74 +1,92 @@
<template>
<section class="internal-tournament-stats" v-if="clubId">
<div class="stats-header">
<h3>{{ $t('tournaments.internalStatsTitle') }}</h3>
<label class="period-label">
<span>{{ $t('tournaments.internalStatsPeriod') }}</span>
<select v-model="months" @change="load" :disabled="loading">
<option :value="12">{{ $t('tournaments.internalStatsLast12Months') }}</option>
<option :value="6">{{ $t('tournaments.internalStatsLast6Months') }}</option>
<option :value="3">{{ $t('tournaments.internalStatsLast3Months') }}</option>
</select>
</label>
</div>
<p class="stats-meta">
{{ $t('tournaments.internalStatsTournamentsInPeriod', { count: stats.tournamentCount || 0 }) }}
</p>
<p class="stats-explain">{{ $t('tournaments.internalStatsPointsExplain') }}</p>
<BaseDialog
:model-value="modelValue"
:title="$t('tournaments.internalStatsTitle')"
size="large"
:closable="true"
:close-on-overlay="true"
@update:model-value="$emit('update:modelValue', $event)"
@close="$emit('update:modelValue', false)"
>
<div class="internal-tournament-stats" v-if="clubId">
<div class="stats-toolbar">
<label class="period-label">
<span>{{ $t('tournaments.internalStatsPeriod') }}</span>
<select v-model="months" @change="load" :disabled="loading">
<option :value="12">{{ $t('tournaments.internalStatsLast12Months') }}</option>
<option :value="6">{{ $t('tournaments.internalStatsLast6Months') }}</option>
<option :value="3">{{ $t('tournaments.internalStatsLast3Months') }}</option>
</select>
</label>
</div>
<p class="stats-meta">
{{ $t('tournaments.internalStatsTournamentsInPeriod', { count: stats.tournamentCount || 0 }) }}
</p>
<p class="stats-explain">{{ $t('tournaments.internalStatsPointsExplain') }}</p>
<div v-if="loading" class="stats-loading">{{ $t('common.loading') }}</div>
<div v-else-if="error" class="stats-error">{{ error }}</div>
<div v-else class="stats-grid">
<div class="stats-column">
<h4>{{ $t('tournaments.internalStatsAbsoluteRank') }}</h4>
<table class="stats-table">
<thead>
<tr>
<th class="col-rank">#</th>
<th>{{ $t('tournaments.player') }}</th>
<th>{{ $t('tournaments.internalStatsPoints') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in stats.absoluteRanking" :key="'a-' + row.memberId">
<td class="col-rank">{{ i + 1 }}</td>
<td>{{ row.firstName }} {{ row.lastName }}</td>
<td>{{ row.totalPoints }}</td>
</tr>
</tbody>
</table>
<p v-if="!stats.absoluteRanking?.length" class="stats-empty">{{ $t('tournaments.internalStatsEmpty') }}</p>
</div>
<div class="stats-column">
<h4>{{ $t('tournaments.internalStatsAverageRank') }}</h4>
<table class="stats-table">
<thead>
<tr>
<th class="col-rank">#</th>
<th>{{ $t('tournaments.player') }}</th>
<th>{{ $t('tournaments.internalStatsAvgPoints') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in stats.averageRanking" :key="'v-' + row.memberId">
<td class="col-rank">{{ i + 1 }}</td>
<td>{{ row.firstName }} {{ row.lastName }}</td>
<td>{{ formatAvg(row.averagePoints) }}</td>
</tr>
</tbody>
</table>
<p v-if="!stats.averageRanking?.length" class="stats-empty">{{ $t('tournaments.internalStatsEmpty') }}</p>
<div v-if="loading" class="stats-loading">{{ $t('common.loading') }}</div>
<div v-else-if="error" class="stats-error">{{ error }}</div>
<div v-else class="stats-grid">
<div class="stats-column">
<h4>{{ $t('tournaments.internalStatsAbsoluteRank') }}</h4>
<table class="stats-table">
<thead>
<tr>
<th class="col-rank">#</th>
<th>{{ $t('tournaments.player') }}</th>
<th>{{ $t('tournaments.internalStatsPoints') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in stats.absoluteRanking" :key="'a-' + row.memberId">
<td class="col-rank">{{ i + 1 }}</td>
<td>{{ row.firstName }} {{ row.lastName }}</td>
<td>{{ row.totalPoints }}</td>
</tr>
</tbody>
</table>
<p v-if="!stats.absoluteRanking?.length" class="stats-empty">{{ $t('tournaments.internalStatsEmpty') }}</p>
</div>
<div class="stats-column">
<h4>{{ $t('tournaments.internalStatsAverageRank') }}</h4>
<table class="stats-table">
<thead>
<tr>
<th class="col-rank">#</th>
<th>{{ $t('tournaments.player') }}</th>
<th>{{ $t('tournaments.internalStatsAvgPoints') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in stats.averageRanking" :key="'v-' + row.memberId">
<td class="col-rank">{{ i + 1 }}</td>
<td>{{ row.firstName }} {{ row.lastName }}</td>
<td>{{ formatAvg(row.averagePoints) }}</td>
</tr>
</tbody>
</table>
<p v-if="!stats.averageRanking?.length" class="stats-empty">{{ $t('tournaments.internalStatsEmpty') }}</p>
</div>
</div>
</div>
</section>
</BaseDialog>
</template>
<script>
import { mapState } from 'vuex';
import apiClient from '../../apiClient.js';
import BaseDialog from '../BaseDialog.vue';
export default {
name: 'InternalTournamentStats',
components: { BaseDialog },
props: {
modelValue: {
type: Boolean,
default: false,
},
},
emits: ['update:modelValue'],
data() {
return {
months: 12,
@@ -88,11 +106,13 @@ export default {
},
watch: {
clubId() {
this.load();
if (this.modelValue) this.load();
},
modelValue(open) {
if (open && this.clubId) {
this.load();
}
},
},
mounted() {
this.load();
},
methods: {
formatAvg(v) {
@@ -121,27 +141,18 @@ export default {
<style scoped>
.internal-tournament-stats {
margin-bottom: 1.5rem;
padding: 1rem 1.25rem;
border: 1px solid var(--border-color, #e5e7eb);
border-radius: var(--border-radius, 8px);
background: var(--background-light, #fafafa);
min-width: 0;
}
.stats-header {
.stats-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
justify-content: flex-end;
gap: 0.75rem;
margin-bottom: 0.5rem;
}
.stats-header h3 {
margin: 0;
font-size: 1.15rem;
}
.period-label {
display: flex;
align-items: center;

View File

@@ -176,12 +176,13 @@
},
"tournaments": {
"internalStatsTitle": "Statistik interne Turniere (Einzel)",
"internalStatsOpenButton": "Turnierstatistik (Einzel)",
"internalStatsPeriod": "Ziitruum",
"internalStatsLast12Months": "Letscht 12 Mönet",
"internalStatsLast6Months": "Letscht 6 Mönet",
"internalStatsLast3Months": "Letscht 3 Mönet",
"internalStatsTournamentsInPeriod": "{count} Turnier(e) im Ziitruum (ohni Minimeisterschafte).",
"internalStatsPointsExplain": "Pünkt: I jede Gruppe chunnt dr Letscht 1 Punkt, dr Vorletzt 2, usw.; gliichi Platzierig = gliichi Pünkt. Wer d K.-o.-Rundi erreicht, bechunnt di höchschte Gruppepünkt vo de Chlass plus 1, deno no 1 Punkt pro gwunne K.-o.-Spiel. Nur Veräinsmitglieder (Einzel).",
"internalStatsPointsExplain": "Pünkt: Pro Gruppe 1. Platz = 100, 2. = 99, 3. = 98 usw.; gliichi Platzierig = gliichi Pünkt. Wer d K.-o.-Rundi erreicht, bechunnt di höchschte Gruppepünkt vo de Chlass plus 1, deno no 1 Punkt pro gwunne K.-o.-Spiel. Nur Veräinsmitglieder (Einzel).",
"internalStatsAbsoluteRank": "Rangliste Gsamtpünkt",
"internalStatsAverageRank": "Rangliste Durchschnitt (Pünkt pro Turnier)",
"internalStatsPoints": "Pünkt",

View File

@@ -407,12 +407,13 @@
},
"tournaments": {
"internalStatsTitle": "Statistik interne Turniere (Einzel)",
"internalStatsOpenButton": "Turnierstatistik (Einzel)",
"internalStatsPeriod": "Zeitraum",
"internalStatsLast12Months": "Letzte 12 Monate",
"internalStatsLast6Months": "Letzte 6 Monate",
"internalStatsLast3Months": "Letzte 3 Monate",
"internalStatsTournamentsInPeriod": "{count} Turnier(e) im Zeitraum (ohne Minimeisterschaften).",
"internalStatsPointsExplain": "Punkte: In jeder Gruppe erhält der Letzte 1 Punkt, der Vorletzte 2, usw.; gleiche Platzierung = gleiche Punkte. Wer die K.-o.-Runde erreicht, erhält die höchsten Gruppenpunkte der Klasse plus 1, danach je weiteren gewonnenen K.-o.-Spiel einen Punkt. Nur Vereinsmitglieder (Einzel).",
"internalStatsPointsExplain": "Punkte: Pro Gruppe gilt 1. Platz = 100, 2. = 99, 3. = 98 usw.; gleiche Platzierung = gleiche Punkte. Wer die K.-o.-Runde erreicht, erhält die höchsten Gruppenpunkte der Klasse plus 1, danach je weiteren gewonnenen K.-o.-Spiel einen Punkt. Nur Vereinsmitglieder (Einzel).",
"internalStatsAbsoluteRank": "Rangliste Gesamtpunkte",
"internalStatsAverageRank": "Rangliste Durchschnitt (Punkte pro Turnier)",
"internalStatsPoints": "Punkte",

View File

@@ -699,12 +699,13 @@
"tournaments": {
"internalTournaments": "Interne Turniere",
"internalStatsTitle": "Statistik interne Turniere (Einzel)",
"internalStatsOpenButton": "Turnierstatistik (Einzel)",
"internalStatsPeriod": "Zeitraum",
"internalStatsLast12Months": "Letzte 12 Monate",
"internalStatsLast6Months": "Letzte 6 Monate",
"internalStatsLast3Months": "Letzte 3 Monate",
"internalStatsTournamentsInPeriod": "{count} Turnier(e) im Zeitraum (ohne Minimeisterschaften).",
"internalStatsPointsExplain": "Punkte: In jeder Gruppe erhält der Letzte 1 Punkt, der Vorletzte 2, usw.; gleiche Platzierung = gleiche Punkte. Wer die K.-o.-Runde erreicht, erhält die höchsten Gruppenpunkte der Klasse plus 1, danach je weiteren gewonnenen K.-o.-Spiel einen Punkt. Nur Vereinsmitglieder (Einzel).",
"internalStatsPointsExplain": "Punkte: Pro Gruppe gilt 1. Platz = 100, 2. = 99, 3. = 98 usw.; gleiche Platzierung = gleiche Punkte. Wer die K.-o.-Runde erreicht, erhält die höchsten Gruppenpunkte der Klasse plus 1, danach je weiteren gewonnenen K.-o.-Spiel einen Punkt. Nur Vereinsmitglieder (Einzel).",
"internalStatsAbsoluteRank": "Rangliste Gesamtpunkte",
"internalStatsAverageRank": "Rangliste Durchschnitt (Punkte pro Turnier)",
"internalStatsPoints": "Punkte",

View File

@@ -176,12 +176,13 @@
},
"tournaments": {
"internalStatsTitle": "Internal tournaments statistics (singles)",
"internalStatsOpenButton": "Tournament statistics (singles)",
"internalStatsPeriod": "Period",
"internalStatsLast12Months": "Last 12 months",
"internalStatsLast6Months": "Last 6 months",
"internalStatsLast3Months": "Last 3 months",
"internalStatsTournamentsInPeriod": "{count} tournament(s) in this period (excluding mini championships).",
"internalStatsPointsExplain": "Points: In each group, last place scores 1, second-to-last 2, and so on; tied positions share the same points. Players who reach the knockout stage get the highest group points in that class plus 1, then one point per knockout match won. Club members in singles classes only.",
"internalStatsPointsExplain": "Points: Per group, 1st place = 100, 2nd = 99, 3rd = 98, and so on; tied ranks share the same score. Players who reach the knockout stage get the highest group score in that class plus 1, then one point per knockout match won. Club members in singles classes only.",
"internalStatsAbsoluteRank": "Total points ranking",
"internalStatsAverageRank": "Average points per tournament",
"internalStatsPoints": "Points",

View File

@@ -357,12 +357,13 @@
},
"tournaments": {
"internalStatsTitle": "Internal tournaments statistics (singles)",
"internalStatsOpenButton": "Tournament statistics (singles)",
"internalStatsPeriod": "Period",
"internalStatsLast12Months": "Last 12 months",
"internalStatsLast6Months": "Last 6 months",
"internalStatsLast3Months": "Last 3 months",
"internalStatsTournamentsInPeriod": "{count} tournament(s) in this period (excluding mini championships).",
"internalStatsPointsExplain": "Points: In each group, last place scores 1, second-to-last 2, and so on; tied positions share the same points. Players who reach the knockout stage get the highest group points in that class plus 1, then one point per knockout match won. Club members in singles classes only.",
"internalStatsPointsExplain": "Points: Per group, 1st place = 100, 2nd = 99, 3rd = 98, and so on; tied ranks share the same score. Players who reach the knockout stage get the highest group score in that class plus 1, then one point per knockout match won. Club members in singles classes only.",
"internalStatsAbsoluteRank": "Total points ranking",
"internalStatsAverageRank": "Average points per tournament",
"internalStatsPoints": "Points",

View File

@@ -176,12 +176,13 @@
},
"tournaments": {
"internalStatsTitle": "Internal tournaments statistics (singles)",
"internalStatsOpenButton": "Tournament statistics (singles)",
"internalStatsPeriod": "Period",
"internalStatsLast12Months": "Last 12 months",
"internalStatsLast6Months": "Last 6 months",
"internalStatsLast3Months": "Last 3 months",
"internalStatsTournamentsInPeriod": "{count} tournament(s) in this period (excluding mini championships).",
"internalStatsPointsExplain": "Points: In each group, last place scores 1, second-to-last 2, and so on; tied positions share the same points. Players who reach the knockout stage get the highest group points in that class plus 1, then one point per knockout match won. Club members in singles classes only.",
"internalStatsPointsExplain": "Points: Per group, 1st place = 100, 2nd = 99, 3rd = 98, and so on; tied ranks share the same score. Players who reach the knockout stage get the highest group score in that class plus 1, then one point per knockout match won. Club members in singles classes only.",
"internalStatsAbsoluteRank": "Total points ranking",
"internalStatsAverageRank": "Average points per tournament",
"internalStatsPoints": "Points",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "Estadísticas de torneos internos (individual)",
"internalStatsOpenButton": "Estadísticas de torneos (individual)",
"internalStatsPeriod": "Periodo",
"internalStatsLast12Months": "Últimos 12 meses",
"internalStatsLast6Months": "Últimos 6 meses",
"internalStatsLast3Months": "Últimos 3 meses",
"internalStatsTournamentsInPeriod": "{count} torneo(s) en el periodo (sin minicampeonatos).",
"internalStatsPointsExplain": "Puntos: en cada grupo, el último suma 1, el penúltimo 2, etc.; empate en la clasificación = mismos puntos. Quien llega al KO recibe los puntos de grupo más altos de la clase más 1, y después 1 punto por partido de KO ganado. Solo socios en individual.",
"internalStatsPointsExplain": "Puntos: por grupo, 1.º = 100, 2.º = 99, 3.º = 98, etc.; empate = mismos puntos. En KO: máximo de grupo de la clase + 1, más 1 por partido KO ganado. Solo socios en individual.",
"internalStatsAbsoluteRank": "Clasificación por puntos totales",
"internalStatsAverageRank": "Clasificación por media (puntos por torneo)",
"internalStatsPoints": "Puntos",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "Estadistika ng internal na paligsahan (singles)",
"internalStatsOpenButton": "Buksan ang estadistika (singles)",
"internalStatsPeriod": "Saklaw",
"internalStatsLast12Months": "Huling 12 buwan",
"internalStatsLast6Months": "Huling 6 na buwan",
"internalStatsLast3Months": "Huling 3 buwan",
"internalStatsTournamentsInPeriod": "{count} paligsahan sa panahon (hindi kasama ang mini championships).",
"internalStatsPointsExplain": "Puntos: Sa bawat grupo, pinakahuli = 1, bago sa huli = 2, atbp.; parehong ranggo = parehong puntos. Ang mga pumasok sa knockout ay tumatanggap ng pinakamataas na grupo puntos ng klase plus 1, at +1 bawat panalong laro sa KO. Mga miyembro lamang, singles.",
"internalStatsPointsExplain": "Puntos: kada grupo, 1 = 100, 2 = 99, 3 = 98, atbp.; parehong ranggo = parehong puntos. Sa KO: pinakamataas na grupo ng klase + 1, +1 bawat panalong laro sa KO. Mga miyembro lamang (singles).",
"internalStatsAbsoluteRank": "Ranggo sa kabuuang puntos",
"internalStatsAverageRank": "Ranggo sa average (puntos bawat paligsahan)",
"internalStatsPoints": "Puntos",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "Statistiques des tournois internes (simple)",
"internalStatsOpenButton": "Statistiques (simple)",
"internalStatsPeriod": "Période",
"internalStatsLast12Months": "12 derniers mois",
"internalStatsLast6Months": "6 derniers mois",
"internalStatsLast3Months": "3 derniers mois",
"internalStatsTournamentsInPeriod": "{count} tournoi(s) sur la période (hors mini-championnats).",
"internalStatsPointsExplain": "Points : dans chaque poule, le dernier reçoit 1, lavant-dernier 2, etc. ; même place = mêmes points. Les joueurs en phase KO reçoivent le meilleur total de poule de la catégorie plus 1, puis 1 point par match KO gagné. Membres du club en simple uniquement.",
"internalStatsPointsExplain": "Points : par poule, 1er = 100, 2e = 99, 3e = 98, etc. ; ex æquo = même score. En KO : meilleur score de poule de la catégorie + 1, puis +1 par match KO gagné. Membres du club en simple uniquement.",
"internalStatsAbsoluteRank": "Classement des points totaux",
"internalStatsAverageRank": "Classement par moyenne (points par tournoi)",
"internalStatsPoints": "Points",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "Statistiche tornei interni (singolo)",
"internalStatsOpenButton": "Statistiche tornei (singolo)",
"internalStatsPeriod": "Periodo",
"internalStatsLast12Months": "Ultimi 12 mesi",
"internalStatsLast6Months": "Ultimi 6 mesi",
"internalStatsLast3Months": "Ultimi 3 mesi",
"internalStatsTournamentsInPeriod": "{count} torneo/i nel periodo (esclusi i mini-campionati).",
"internalStatsPointsExplain": "Punti: in ogni girone lultimo ha 1, il penultimo 2, ecc.; stesso posto = stessi punti. Chi raggiunge il KO ottiene il massimo punti girone della classe più 1, poi 1 punto per ogni partita KO vinta. Solo soci al singolo.",
"internalStatsPointsExplain": "Punti: per girone, 1° = 100, 2° = 99, 3° = 98, ecc.; stesso posto = stessi punti. In KO: massimo girone della classe + 1, poi +1 per partita KO vinta. Solo soci al singolo.",
"internalStatsAbsoluteRank": "Classifica punti totali",
"internalStatsAverageRank": "Classifica media (punti per torneo)",
"internalStatsPoints": "Punti",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "内部大会の統計(シングルス)",
"internalStatsOpenButton": "統計を表示(シングルス)",
"internalStatsPeriod": "期間",
"internalStatsLast12Months": "過去12か月",
"internalStatsLast6Months": "過去6か月",
"internalStatsLast3Months": "過去3か月",
"internalStatsTournamentsInPeriod": "期間内の大会 {count} 件(ミニ選手権は除く)。",
"internalStatsPointsExplain": "得点:各グループで最下位が1点、その上が2点…同順位は同点。ックアウトに進んだ選手はクラス最高のグループ得点に1を加え、KOで勝った試合ごとにさらに1点。クラブ会員シングルのみ。",
"internalStatsPointsExplain": "得点:各グループで1位100点、2位99点、3位98点…同順位は同点。KO進出者はクラス最高のグループ得点1、KO勝利ごとに1。クラブ会員シングルのみ。",
"internalStatsAbsoluteRank": "総得点ランキング",
"internalStatsAverageRank": "平均得点ランキング(大会あたり)",
"internalStatsPoints": "得点",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "Statystyki turniejów wewnętrznych (singel)",
"internalStatsOpenButton": "Statystyki turniejów (singel)",
"internalStatsPeriod": "Okres",
"internalStatsLast12Months": "Ostatnie 12 miesięcy",
"internalStatsLast6Months": "Ostatnie 6 miesięcy",
"internalStatsLast3Months": "Ostatnie 3 miesiące",
"internalStatsTournamentsInPeriod": "{count} turniej(ów) w okresie (bez mini-mistrzostw).",
"internalStatsPointsExplain": "Punkty: w każdej grupie ostatni ma 1, przedostatni 2 itd.; ten sam ranking = te same punkty. Gracze w fazie pucharowej: maks. punkty grupowe klasy plus 1, potem 1 pkt za każdy wygrany mecz KO. Tylko członkowie klubu, gra pojedyncza.",
"internalStatsPointsExplain": "Punkty: w grupie miejsce 1 = 100, 2 = 99, 3 = 98 itd.; remis = te same punkty. W KO: maks. punktów grupowych klasy + 1, potem +1 za wygrany mecz KO. Tylko członkowie klubu, singel.",
"internalStatsAbsoluteRank": "Ranking sumy punktów",
"internalStatsAverageRank": "Ranking średniej (punkty na turniej)",
"internalStatsPoints": "Punkty",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "สถิติการแข่งขันภายใน (เดี่ยว)",
"internalStatsOpenButton": "เปิดสถิติ (เดี่ยว)",
"internalStatsPeriod": "ช่วงเวลา",
"internalStatsLast12Months": "12 เดือนล่าสุด",
"internalStatsLast6Months": "6 เดือนล่าสุด",
"internalStatsLast3Months": "3 เดือนล่าสุด",
"internalStatsTournamentsInPeriod": "{count} การแข่งขันในช่วงเวลา (ไม่รวมมินิแชมเปียนชิป)",
"internalStatsPointsExplain": "คะแนน: ในแต่ละกลุ่ม อันดับสุดท้ายได้ 1 รองสุดท้ายได้ 2 ต่อไปเรื่อยๆ อันดับเดียวกันได้คะแนนเท่ากัน ผู้เข้ารอบน็อกเอาต์ได้คะแนนกลุ่มสูงสุดของคลาสบวก 1 แล้วบวก 1 ต่อการชนะแมตช์ KO หนึ่งนัด เฉพาะสมาชิกสโมสร ประเภทเดี่ยว",
"internalStatsPointsExplain": "คะแนน: ต่อกลุ่ม อันดับ 1 = 100, 2 = 99, 3 = 98 … อันดับเดียวกันได้คะแนนเท่ากัน รอบ KO: คะแนนกลุ่มสูงสุดของคลาส +1 แล้ว +1 ต่อชนะแมตช์ KO เฉพาะสมาชิก เดี่ยว",
"internalStatsAbsoluteRank": "อันดับคะแนนรวม",
"internalStatsAverageRank": "อันดับค่าเฉลี่ย (คะแนนต่อการแข่งขัน)",
"internalStatsPoints": "คะแนน",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "Istatistika ng internal na tournament (singles)",
"internalStatsOpenButton": "Buksan ang istatistika (singles)",
"internalStatsPeriod": "Saklaw",
"internalStatsLast12Months": "Huling 12 buwan",
"internalStatsLast6Months": "Huling 6 na buwan",
"internalStatsLast3Months": "Huling 3 buwan",
"internalStatsTournamentsInPeriod": "{count} tournament sa panahon (hindi kasama ang mini championships).",
"internalStatsPointsExplain": "Puntos: Sa bawat grupo, ang huli ay 1, bago sa huli ay 2, atbp.; parehong ranggo = parehong puntos. Ang mga nakapasok sa knockout ay nakakakuha ng pinakamataas na grupo puntos ng klase plus 1, at +1 bawat panalong laro sa KO. Mga miyembro lamang sa singles.",
"internalStatsPointsExplain": "Puntos: bawat grupo, 1 = 100, 2 = 99, 3 = 98, atbp.; parehong ranggo = parehong puntos. Sa KO: pinakamataas na grupo ng klase + 1, +1 bawat panalong laro sa KO. Mga miyembro lamang (singles).",
"internalStatsAbsoluteRank": "Ranggo sa kabuuang puntos",
"internalStatsAverageRank": "Ranggo sa average (puntos bawat tournament)",
"internalStatsPoints": "Puntos",

View File

@@ -175,12 +175,13 @@
},
"tournaments": {
"internalStatsTitle": "内部锦标赛统计(单打)",
"internalStatsOpenButton": "打开单打统计数据",
"internalStatsPeriod": "时间范围",
"internalStatsLast12Months": "过去 12 个月",
"internalStatsLast6Months": "过去 6 个月",
"internalStatsLast3Months": "过去 3 个月",
"internalStatsTournamentsInPeriod": "该期间共 {count} 场锦标赛(不含迷你锦标赛)。",
"internalStatsPointsExplain": "计分:每组中最后一名得 1 分,倒数第二名得 2 分,以此类推;相同名次得分相同。进入淘汰赛的选手获得该级别最高小组赛分数加 1之后每赢一场淘汰赛再加 1 分。仅统计俱乐部成员单打。",
"internalStatsPointsExplain": "计分:每组第 1 名 100 分,第 2 名 99 分,第 3 名 98 分,以此类推;相同名次得分相同。进入淘汰赛的选手获得该级别最高小组赛分数加 1之后每赢一场淘汰赛再加 1 分。仅统计俱乐部成员单打。",
"internalStatsAbsoluteRank": "总积分榜",
"internalStatsAverageRank": "平均分榜(每场锦标赛)",
"internalStatsPoints": "分数",

View File

@@ -26,11 +26,21 @@
</div>
</div>
<div class="workspace-summary">
{{ currentModeDescription }}
<div class="workspace-summary-row">
<div class="workspace-summary">
{{ currentModeDescription }}
</div>
<button
v-if="activeMode === 'internal'"
type="button"
class="stats-open-button"
@click="internalStatsOpen = true"
>
📊 {{ $t('tournaments.internalStatsOpenButton') }}
</button>
</div>
<InternalTournamentStats v-if="activeMode === 'internal'" />
<InternalTournamentStats v-if="activeMode === 'internal'" v-model="internalStatsOpen" />
<div class="tab-content">
<TournamentTab
@@ -55,6 +65,7 @@ export default {
data() {
return {
activeMode: 'internal',
internalStatsOpen: false,
};
},
computed: {
@@ -120,9 +131,38 @@ export default {
color: #166534;
}
.workspace-summary {
.workspace-summary-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 20px;
}
.workspace-summary {
margin: 0;
color: #6b7280;
flex: 1;
min-width: 0;
}
.stats-open-button {
flex-shrink: 0;
background: #f3f4f6;
border: 1px solid #d1d5db;
border-radius: 999px;
padding: 8px 14px;
font-size: 0.9rem;
font-weight: 600;
color: #374151;
cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease;
}
.stats-open-button:hover {
background: #e5e7eb;
border-color: #9ca3af;
}
.tab-content {