feat(official-tournaments): implement reimport functionality for official tournaments
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 57s

This commit is contained in:
Torsten Schulz (local)
2026-09-09 13:16:04 +02:00
parent 0c9a898d61
commit ab4d99192e
4 changed files with 43 additions and 1 deletions

View File

@@ -16,6 +16,20 @@ export const saveTournamentSuggestion = async (req, res) => {
}
};
export const reimportOfficialTournament = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, id } = req.params;
await checkAccess(userToken, clubId);
const result = await officialTournamentService.reimportOfficialTournament(clubId, id);
if (!result) return res.status(404).json({ error: 'Turnier nicht gefunden.' });
return res.json(result);
} catch (error) {
console.error('[reimportOfficialTournament] Error:', error);
return res.status(error.status || 500).json({ error: error.message || 'Turnierdaten konnten nicht aktualisiert werden.' });
}
};
export const updateOfficialTournament = async (req, res) => {
try {
const { authcode: userToken } = req.headers;

View File

@@ -11,7 +11,7 @@ import {
listClubParticipations,
updateParticipantStatus,
autoRegisterOfficialTournamentParticipants
, listTacticPlans, saveTacticPlan, deleteTacticPlan, saveTournamentSuggestion
, listTacticPlans, saveTacticPlan, deleteTacticPlan, saveTournamentSuggestion, reimportOfficialTournament
} from '../controllers/officialTournamentController.js';
import { fetchTournamentSuggestions, listTournamentSuggestions, updateTournamentSuggestion } from '../controllers/tournamentSuggestionController.js';
@@ -31,6 +31,7 @@ router.get('/:clubId/:tournamentId/tactics', listTacticPlans);
router.post('/:clubId/:tournamentId/tactics', saveTacticPlan);
router.patch('/:clubId/:tournamentId/tactics/:planId', saveTacticPlan);
router.delete('/:clubId/:tournamentId/tactics/:planId', deleteTacticPlan);
router.post('/:clubId/:id/reimport', reimportOfficialTournament);
router.get('/:clubId/:id', getParsedTournament);
router.patch('/:clubId/:id', updateOfficialTournament);
router.delete('/:clubId/:id', deleteOfficialTournament);

View File

@@ -96,6 +96,18 @@ class OfficialTournamentService {
return repaired;
}
async reimportOfficialTournament(clubId, tournamentId) {
const tournament = await OfficialTournament.findOne({ where: { id: tournamentId, clubId } });
if (!tournament) return null;
const updatedCount = await this.repairTournamentFromSource(clubId, tournament);
if (!tournament.sourceUrl) {
const error = new Error('Für dieses Turnier ist keine myTischtennis-Quelle hinterlegt.');
error.status = 400;
throw error;
}
return { id: String(tournament.id), updatedCount };
}
async saveTournamentSuggestion(clubId, suggestionId) {
const suggestion = await TournamentSuggestion.findOne({ where: { id: suggestionId, clubId } });
if (!suggestion) return null;

View File

@@ -232,6 +232,9 @@
@click="generateMembersPdf">
{{ $t('officialTournaments.pdfForSelectedMembers') }}
</button>
<button class="btn-secondary" :disabled="reimporting" @click="reimportTournament">
{{ reimporting ? 'Aktualisiert ' : 'Daten aktualisieren' }}
</button>
<button
v-if="primaryHeaderAction !== 'auto-register'"
class="btn-secondary"
@@ -565,6 +568,7 @@
<div class="calendar-entry-empty">
<h3>Kalendereintrag geöffnet</h3>
<p>Für dieses Turnier sind noch keine Konkurrenzen hinterlegt. Importiere eine Ausschreibung als PDF, sobald du Teilnehmer oder Ergebnisse verwalten möchtest.</p>
<button class="btn-secondary" :disabled="reimporting" @click="reimportTournament">{{ reimporting ? 'Aktualisiert ' : 'Daten aktualisieren' }}</button>
</div>
</div>
<div v-else class="workspace-detail empty-workspace">
@@ -690,6 +694,7 @@ export default {
editingTournamentId: null,
editingTitle: '',
autoRegistering: false,
reimporting: false,
suggestions: [], fetchingSuggestions: false, suggestionError: '',
suggestionSearch: '', suggestionStatusFilter: 'new',
tacticPlans: [], tacticPlan: null, tacticPlansSnapshot: '', tacticsLoading: false, tacticSaving: false, tacticsError: '', tacticTournamentId: null, tacticLoadToken: 0, reloadToken: 0,
@@ -1678,6 +1683,16 @@ export default {
this.workspaceTab = 'saved';
} catch (error) { this.suggestionError = getSafeErrorMessage(error, 'Der Vorschlag konnte nicht gespeichert werden.'); }
},
async reimportTournament() {
if (!this.uploadedId || this.reimporting) return;
this.reimporting = true;
try {
const response = await apiClient.post(`/official-tournaments/${this.currentClub}/${this.uploadedId}/reimport`);
await Promise.all([this.reload(), this.loadList()]);
await this.showInfo('Turnierdaten aktualisiert', `${response.data?.updatedCount || 0} Datenbereiche wurden aus myTischtennis aktualisiert.`, '', 'success');
} catch (error) { await this.showInfo('Aktualisierung nicht möglich', getSafeErrorMessage(error, 'Die Turnierdaten konnten nicht aktualisiert werden.'), '', 'error'); }
finally { this.reimporting = false; }
},
buildParticipationMap(entries) {
const map = {};
for (const e of entries) {