feat(TournamentConfigTab, TournamentResultsTab): enhance stage configuration and knockout actions
- Added ensureStageConfigurationPersisted method to validate and save stage advancements, ensuring proper configuration before advancing stages. - Updated advanceStage method to call ensureStageConfigurationPersisted, improving the flow of tournament advancement. - Introduced primary action buttons in TournamentResultsTab for starting and resetting knockout rounds, enhancing user interaction during tournament management. - Styled results-primary-actions for better visibility and usability in the results tab.
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -14,6 +14,27 @@
|
||||
<span v-if="liveMatchCount > 0" class="results-chip results-chip-live">{{ liveMatchCount }} {{ $t('tournaments.statusLive') }}</span>
|
||||
<span v-if="finishedMatchCount > 0" class="results-chip">{{ finishedMatchCount }} {{ $t('tournaments.statusFinished') }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="(canStartKnockout && !showKnockout && numberOfGroupsForSelectedClass > 1) || (showKnockout && canResetKnockout && numberOfGroupsForSelectedClass > 1)"
|
||||
class="results-primary-actions"
|
||||
>
|
||||
<button
|
||||
v-if="canStartKnockout && !showKnockout && numberOfGroupsForSelectedClass > 1"
|
||||
@click="$emit('start-knockout')"
|
||||
class="btn-primary"
|
||||
:disabled="knockoutOperationInProgress"
|
||||
>
|
||||
{{ $t('tournaments.startKORound') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="showKnockout && canResetKnockout && numberOfGroupsForSelectedClass > 1"
|
||||
@click="$emit('reset-knockout')"
|
||||
class="trash-btn"
|
||||
:disabled="knockoutOperationInProgress"
|
||||
>
|
||||
🗑️ {{ $t('tournaments.deleteKORound') }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="numberOfTables && (filteredGroupMatches.length || filteredKnockoutMatches.length)" class="distribute-tables-bar">
|
||||
<button @click="$emit('distribute-tables')" class="btn-primary">
|
||||
{{ $t('tournaments.distributeTables') }}
|
||||
@@ -646,6 +667,18 @@ export default {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.results-primary-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.9rem 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
background: var(--surface-color, #ffffff);
|
||||
}
|
||||
|
||||
.results-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user