refactor(TournamentStats): remove InternalTournamentStats dialog and streamline state management
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 36s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 36s
- Removed the InternalTournamentStats component from App.vue and its associated state management in Vuex. - Updated the DialogManager to include InternalTournamentStats, allowing for better dialog handling. - Refactored the TournamentsView to utilize a new method for opening the InternalTournamentStats dialog. - Enhanced the InternalTournamentStats component by simplifying its template and removing unnecessary props and methods. - Improved the logic for displaying tournament statistics based on club selection, ensuring a cleaner user experience.
This commit is contained in:
@@ -181,11 +181,6 @@
|
||||
<!-- Dialog Manager -->
|
||||
<DialogManager />
|
||||
|
||||
<InternalTournamentStats
|
||||
v-if="showInternalTournamentStatsDialog"
|
||||
v-model="internalTournamentStatsDialogOpen"
|
||||
/>
|
||||
|
||||
<footer class="app-footer">
|
||||
<div class="footer-content">
|
||||
<router-link to="/impressum" class="footer-link">Impressum</router-link>
|
||||
@@ -228,13 +223,10 @@ import BaseDialog from './components/BaseDialog.vue';
|
||||
import { buildInfoConfig, buildConfirmConfig } from './utils/dialogUtils.js';
|
||||
|
||||
const DialogManager = defineAsyncComponent(() => import('./components/DialogManager.vue'));
|
||||
import InternalTournamentStats from './components/tournament/InternalTournamentStats.vue';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
DialogManager,
|
||||
InternalTournamentStats,
|
||||
BaseDialog,
|
||||
InfoDialog,
|
||||
ConfirmDialog,
|
||||
@@ -295,21 +287,6 @@ export default {
|
||||
viewReloadKey() {
|
||||
return `${this.$route.fullPath}|${this.currentClub || 'no-club'}`;
|
||||
},
|
||||
internalTournamentStatsDialogOpen: {
|
||||
get() {
|
||||
return this.$store.state.internalTournamentStatsOpen;
|
||||
},
|
||||
set(v) {
|
||||
this.$store.commit('setInternalTournamentStatsOpen', v);
|
||||
},
|
||||
},
|
||||
showInternalTournamentStatsDialog() {
|
||||
return (
|
||||
this.isAuthenticated &&
|
||||
!!this.currentClub &&
|
||||
this.hasPermission('tournaments', 'read')
|
||||
);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentClub(newVal) {
|
||||
|
||||
@@ -65,6 +65,9 @@ const MemberTransferSettingsView = defineAsyncComponent(() => import('../views/M
|
||||
const LogsView = defineAsyncComponent(() => import('../views/LogsView.vue'));
|
||||
const ClickTtView = defineAsyncComponent(() => import('../views/ClickTtView.vue'));
|
||||
const PersonalSettings = defineAsyncComponent(() => import('../views/PersonalSettings.vue'));
|
||||
const InternalTournamentStats = defineAsyncComponent(() =>
|
||||
import('./tournament/InternalTournamentStats.vue'),
|
||||
);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -77,7 +80,8 @@ export default {
|
||||
MemberTransferSettingsView,
|
||||
LogsView,
|
||||
ClickTtView,
|
||||
PersonalSettings
|
||||
PersonalSettings,
|
||||
InternalTournamentStats,
|
||||
},
|
||||
name: 'DialogManager',
|
||||
computed: {
|
||||
@@ -97,7 +101,8 @@ export default {
|
||||
'MemberTransferSettingsView': MemberTransferSettingsView,
|
||||
'LogsView': LogsView,
|
||||
'ClickTtView': ClickTtView,
|
||||
'PersonalSettings': PersonalSettings
|
||||
'PersonalSettings': PersonalSettings,
|
||||
InternalTournamentStats,
|
||||
};
|
||||
const component = components[componentName] || null;
|
||||
return component;
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
<template>
|
||||
<BaseDialog
|
||||
:model-value="modelValue"
|
||||
:title="$t('tournaments.internalStatsTitle')"
|
||||
size="large"
|
||||
:is-modal="false"
|
||||
:position="dialogPosition"
|
||||
:closable="true"
|
||||
:close-on-overlay="false"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
@update:position="dialogPosition = $event"
|
||||
@close="$emit('update:modelValue', false)"
|
||||
>
|
||||
<div class="internal-tournament-stats" v-if="clubId">
|
||||
<!-- Inhalt für DialogManager: Rahmen, Minimieren und Schließen liefert der Manager -->
|
||||
<div v-if="!clubId" class="internal-tournament-stats internal-tournament-stats--embed stats-no-club">
|
||||
<p class="stats-empty">{{ $t('club.selectPlaceholder') }}</p>
|
||||
</div>
|
||||
<div v-else class="internal-tournament-stats internal-tournament-stats--embed">
|
||||
<div class="stats-toolbar">
|
||||
<button
|
||||
type="button"
|
||||
@@ -122,7 +114,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -130,24 +121,14 @@ import { mapState } from 'vuex';
|
||||
import jsPDF from 'jspdf';
|
||||
import autoTable from 'jspdf-autotable';
|
||||
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,
|
||||
loading: false,
|
||||
error: null,
|
||||
dialogPosition: { x: 80, y: 80 },
|
||||
stats: {
|
||||
tournamentCount: 0,
|
||||
ageClassOptions: [],
|
||||
@@ -213,24 +194,13 @@ export default {
|
||||
this.selectedBandKeys = [];
|
||||
this.genderScope = 'all';
|
||||
this.pendingResetAgeSelection = false;
|
||||
if (this.modelValue) this.load();
|
||||
},
|
||||
modelValue(open) {
|
||||
if (open) {
|
||||
this.placeDialog();
|
||||
if (this.clubId) this.load();
|
||||
}
|
||||
if (this.clubId) this.load();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.clubId) this.load();
|
||||
},
|
||||
methods: {
|
||||
placeDialog() {
|
||||
if (typeof window === 'undefined') return;
|
||||
const panelW = Math.min(900, window.innerWidth - 32);
|
||||
this.dialogPosition = {
|
||||
x: Math.max(16, Math.floor((window.innerWidth - panelW) / 2)),
|
||||
y: Math.max(24, Math.floor(window.innerHeight * 0.08)),
|
||||
};
|
||||
},
|
||||
periodLabel() {
|
||||
const key =
|
||||
this.months === 12
|
||||
@@ -500,6 +470,13 @@ export default {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.internal-tournament-stats--embed {
|
||||
padding: 1rem 1.25rem 1.25rem;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.stats-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -43,8 +43,6 @@ const store = createStore({
|
||||
// Browser-Sprache wird in i18n/index.js erkannt
|
||||
return null;
|
||||
})(),
|
||||
/** Turnierstatistik-Einzel: Dialog bleibt beim Seitenwechsel offen (App.vue) */
|
||||
internalTournamentStatsOpen: false,
|
||||
},
|
||||
mutations: {
|
||||
setToken(state, token) {
|
||||
@@ -74,7 +72,6 @@ const store = createStore({
|
||||
safeSessionStorage.setItem('currentClub', club);
|
||||
} else {
|
||||
safeSessionStorage.removeItem('currentClub');
|
||||
state.internalTournamentStatsOpen = false;
|
||||
}
|
||||
},
|
||||
setClubsMutation(state, clubs) {
|
||||
@@ -104,7 +101,6 @@ const store = createStore({
|
||||
state.token = null;
|
||||
safeSessionStorage.removeItem('token');
|
||||
safeSessionStorage.removeItem('currentClub');
|
||||
state.internalTournamentStatsOpen = false;
|
||||
},
|
||||
clearUsername(state) {
|
||||
state.username = '';
|
||||
@@ -125,6 +121,9 @@ const store = createStore({
|
||||
closeDialog(state, dialogId) {
|
||||
state.dialogs = state.dialogs.filter(dialog => dialog.id !== dialogId);
|
||||
},
|
||||
clearAllDialogs(state) {
|
||||
state.dialogs = [];
|
||||
},
|
||||
minimizeDialog(state, dialogId) {
|
||||
const dialog = state.dialogs.find(d => d.id === dialogId);
|
||||
if (dialog) {
|
||||
@@ -147,9 +146,6 @@ const store = createStore({
|
||||
dialog.zIndex = maxZIndex + 1;
|
||||
}
|
||||
},
|
||||
setInternalTournamentStatsOpen(state, open) {
|
||||
state.internalTournamentStatsOpen = !!open;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async login({ commit }, { token, username }) {
|
||||
@@ -160,7 +156,7 @@ const store = createStore({
|
||||
commit('setClubsMutation', response.data);
|
||||
},
|
||||
logout({ commit }) {
|
||||
commit('setInternalTournamentStatsOpen', false);
|
||||
commit('clearAllDialogs');
|
||||
commit('clearToken');
|
||||
commit('clearUsername');
|
||||
commit('clearPermissions');
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
<td class="player-name">{{ stat.firstName }} {{ stat.lastName }}</td>
|
||||
<td class="stat-value">
|
||||
<span v-if="memberById[stat.memberId]">
|
||||
{{ memberById[stat.memberId].qttr ?? memberById[stat.memberId].ttr ?? '–' }}
|
||||
{{ getMemberLineupRatingLabel(memberById[stat.memberId]) }}
|
||||
</span>
|
||||
<span v-else>–</span>
|
||||
</td>
|
||||
@@ -889,17 +889,26 @@ export default {
|
||||
return t('teamManagement.eligibilityRegular');
|
||||
};
|
||||
|
||||
/**
|
||||
* Nur für Meldungsreihenfolge / ±30-Punkte-Regel: zählt **QTTR > 0**.
|
||||
* Kein QTTR, 0 oder ungültig → wie „unbewertet“ (frei verschiebbar bzgl. der Regel).
|
||||
*/
|
||||
const getMemberLineupRatingValue = (member) => {
|
||||
const qttr = Number(member?.qttr);
|
||||
if (Number.isFinite(qttr)) return qttr;
|
||||
const ttr = Number(member?.ttr);
|
||||
if (Number.isFinite(ttr)) return ttr;
|
||||
return Number.NEGATIVE_INFINITY;
|
||||
if (member == null) return Number.NEGATIVE_INFINITY;
|
||||
const raw = member.qttr;
|
||||
if (raw == null || raw === '') return Number.NEGATIVE_INFINITY;
|
||||
const qttr = Number(raw);
|
||||
if (!Number.isFinite(qttr) || qttr === 0) return Number.NEGATIVE_INFINITY;
|
||||
return qttr;
|
||||
};
|
||||
|
||||
/** Anzeige Meldung/Statistik: nur QTTR (>0); TTR ist für die Einordnung nicht relevant */
|
||||
const getMemberLineupRatingLabel = (member) => {
|
||||
const rating = getMemberLineupRatingValue(member);
|
||||
return Number.isFinite(rating) ? String(rating) : '–';
|
||||
const rawQ = member?.qttr;
|
||||
if (rawQ == null || rawQ === '') return '–';
|
||||
const q = Number(rawQ);
|
||||
if (!Number.isFinite(q) || q === 0) return '–';
|
||||
return String(q);
|
||||
};
|
||||
|
||||
const lineupProposalGroups = computed(() => {
|
||||
@@ -1770,7 +1779,7 @@ export default {
|
||||
const map = {};
|
||||
clubMembers.value = membersResp.data || [];
|
||||
for (const m of clubMembers.value) {
|
||||
map[m.id] = { ttr: m.ttr ?? null, qttr: m.qttr ?? null };
|
||||
map[m.id] = { qttr: m.qttr ?? null };
|
||||
}
|
||||
memberById.value = map;
|
||||
} catch (e) {
|
||||
@@ -2371,6 +2380,7 @@ export default {
|
||||
removeMemberFromLineup,
|
||||
moveLineupMember,
|
||||
memberById,
|
||||
getMemberLineupRatingLabel,
|
||||
schedulerJobs,
|
||||
formatJobDate,
|
||||
loadSchedulerJobsInfo,
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
v-if="activeMode === 'internal'"
|
||||
type="button"
|
||||
class="stats-open-button"
|
||||
@click="$store.commit('setInternalTournamentStatsOpen', true)"
|
||||
@click="openInternalTournamentStatsDialog"
|
||||
>
|
||||
📊 {{ $t('tournaments.internalStatsOpenButton') }}
|
||||
</button>
|
||||
@@ -51,6 +51,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import TournamentTab from './TournamentTab.vue';
|
||||
|
||||
export default {
|
||||
@@ -64,6 +65,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['dialogs']),
|
||||
currentModeDescription() {
|
||||
if (this.activeMode === 'mini') return this.$t('tournaments.miniChampionships');
|
||||
if (this.activeMode === 'external') return this.$t('tournaments.openTournaments');
|
||||
@@ -71,9 +73,20 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['openDialog', 'closeDialog']),
|
||||
switchMode(mode) {
|
||||
this.activeMode = mode;
|
||||
},
|
||||
openInternalTournamentStatsDialog() {
|
||||
this.dialogs
|
||||
.filter((d) => d.component === 'InternalTournamentStats')
|
||||
.forEach((d) => this.closeDialog(d.id));
|
||||
this.openDialog({
|
||||
title: this.$t('tournaments.internalStatsTitle'),
|
||||
component: 'InternalTournamentStats',
|
||||
props: {},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user