diff --git a/frontend/src/components/tournament/TournamentConfigTab.vue b/frontend/src/components/tournament/TournamentConfigTab.vue index 10271ebd..302ee3e3 100644 --- a/frontend/src/components/tournament/TournamentConfigTab.vue +++ b/frontend/src/components/tournament/TournamentConfigTab.vue @@ -1962,6 +1962,33 @@ export default { this.stageConfig.error = e?.message || String(e); } }, + async ensureStageConfigurationPersisted(existingAdvancements = null) { + const advancements = Array.isArray(existingAdvancements) ? existingAdvancements : null; + if (advancements && advancements.length > 0) return; + + const { stages, advancements: builtAdvancements } = this.buildPayload(); + if (!Array.isArray(builtAdvancements) || builtAdvancements.length === 0) { + throw new Error(this.$t('tournaments.stageConfigLoadError')); + } + + for (const adv of builtAdvancements) { + const hasPools = Array.isArray(adv?.config?.pools) && adv.config.pools.length > 0; + if (!hasPools) { + const label = `${adv.fromStageIndex}→${adv.toStageIndex}`; + throw new Error(this.$t('tournaments.atLeastOnePoolRule', { label })); + } + } + + const saveRes = await apiClient.put('/tournament/stages', { + clubId: Number(this.clubId), + tournamentId: Number(this.tournamentId), + stages, + advancements: builtAdvancements, + }); + if (saveRes.status >= 400) { + throw new Error(saveRes.data?.error || this.$t('messages.saveFailed')); + } + }, async advanceStage(fromStageIndex, toStageIndex) { this.stageConfig.error = null; this.stageConfig.success = null; @@ -1973,14 +2000,28 @@ export default { tournamentId: Number(this.tournamentId) } }); - const stages = Array.isArray(getRes?.data?.stages) ? getRes.data.stages : []; - const normalized = stages.map(s => ({ + const advancements = Array.isArray(getRes?.data?.advancements) ? getRes.data.advancements : []; + + // Alte Turniere haben teilweise Stages, aber noch keine gespeicherten Advancements. + // Dann ziehen wir die Konfiguration vor dem eigentlichen Start automatisch nach. + await this.ensureStageConfigurationPersisted(advancements); + + const refreshedRes = advancements.length > 0 + ? getRes + : await apiClient.get('/tournament/stages', { + params: { + clubId: Number(this.clubId), + tournamentId: Number(this.tournamentId) + } + }); + const refreshedStages = Array.isArray(refreshedRes?.data?.stages) ? refreshedRes.data.stages : []; + const refreshedNormalized = refreshedStages.map(s => ({ stageIndex: Number(s.stageIndex ?? s.index ?? s.id), stageId: Number(s.id ?? s.stageId ?? s.stageIndex), type: s.type || s.targetType || s.target })); - const from = normalized.find(s => s.stageIndex === Number(fromStageIndex)); - const to = normalized.find(s => s.stageIndex === Number(toStageIndex)); + const from = refreshedNormalized.find(s => s.stageIndex === Number(fromStageIndex)); + const to = refreshedNormalized.find(s => s.stageIndex === Number(toStageIndex)); const payload = { clubId: Number(this.clubId), diff --git a/frontend/src/components/tournament/TournamentResultsTab.vue b/frontend/src/components/tournament/TournamentResultsTab.vue index e8df15a6..84a7e5b5 100644 --- a/frontend/src/components/tournament/TournamentResultsTab.vue +++ b/frontend/src/components/tournament/TournamentResultsTab.vue @@ -14,6 +14,27 @@ {{ liveMatchCount }} {{ $t('tournaments.statusLive') }} {{ finishedMatchCount }} {{ $t('tournaments.statusFinished') }} +
+ + +