feat(tournament-suggestions): implement save functionality for tournament suggestions
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 8m44s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 8m44s
This commit is contained in:
@@ -2,6 +2,20 @@ import { checkAccess } from '../utils/userUtils.js';
|
||||
import officialTournamentService from '../services/officialTournamentService.js';
|
||||
import clickTtTournamentRegistrationService from '../services/clickTtTournamentRegistrationService.js';
|
||||
|
||||
export const saveTournamentSuggestion = async (req, res) => {
|
||||
try {
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { clubId, id } = req.params;
|
||||
await checkAccess(userToken, clubId);
|
||||
const result = await officialTournamentService.saveTournamentSuggestion(clubId, id);
|
||||
if (!result) return res.status(404).json({ error: 'Turniervorschlag nicht gefunden.' });
|
||||
return res.status(result.created ? 201 : 200).json(result);
|
||||
} catch (error) {
|
||||
console.error('[saveTournamentSuggestion] Error:', error);
|
||||
return res.status(error.status || 500).json({ error: error.message || 'Turniervorschlag konnte nicht gespeichert werden.' });
|
||||
}
|
||||
};
|
||||
|
||||
export const updateOfficialTournament = async (req, res) => {
|
||||
try {
|
||||
const { authcode: userToken } = req.headers;
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
listClubParticipations,
|
||||
updateParticipantStatus,
|
||||
autoRegisterOfficialTournamentParticipants
|
||||
, listTacticPlans, saveTacticPlan, deleteTacticPlan
|
||||
, listTacticPlans, saveTacticPlan, deleteTacticPlan, saveTournamentSuggestion
|
||||
} from '../controllers/officialTournamentController.js';
|
||||
import { fetchTournamentSuggestions, listTournamentSuggestions, updateTournamentSuggestion } from '../controllers/tournamentSuggestionController.js';
|
||||
|
||||
@@ -23,6 +23,7 @@ router.use(authenticate);
|
||||
router.get('/:clubId', listOfficialTournaments);
|
||||
router.get('/:clubId/suggestions', listTournamentSuggestions);
|
||||
router.post('/:clubId/suggestions/fetch', fetchTournamentSuggestions);
|
||||
router.post('/:clubId/suggestions/:id/save', saveTournamentSuggestion);
|
||||
router.patch('/:clubId/suggestions/:id', updateTournamentSuggestion);
|
||||
router.get('/:clubId/participations/summary', listClubParticipations);
|
||||
router.post('/:clubId/upload', upload.single('pdf'), uploadTournamentPdf);
|
||||
|
||||
@@ -7,10 +7,36 @@ import OfficialTournament from '../models/OfficialTournament.js';
|
||||
import OfficialCompetition from '../models/OfficialCompetition.js';
|
||||
import OfficialCompetitionMember from '../models/OfficialCompetitionMember.js';
|
||||
import OfficialTournamentTacticPlan from '../models/OfficialTournamentTacticPlan.js';
|
||||
import TournamentSuggestion from '../models/TournamentSuggestion.js';
|
||||
import Member from '../models/Member.js';
|
||||
import OfficialTournamentParserService from './officialTournamentParserService.js';
|
||||
|
||||
class OfficialTournamentService {
|
||||
async saveTournamentSuggestion(clubId, suggestionId) {
|
||||
const suggestion = await TournamentSuggestion.findOne({ where: { id: suggestionId, clubId } });
|
||||
if (!suggestion) return null;
|
||||
|
||||
let tournament = await OfficialTournament.findOne({
|
||||
where: { clubId, title: suggestion.title, eventDate: suggestion.eventDate },
|
||||
});
|
||||
const created = !tournament;
|
||||
if (!tournament) {
|
||||
tournament = await OfficialTournament.create({
|
||||
clubId,
|
||||
title: suggestion.title,
|
||||
eventDate: suggestion.eventDate,
|
||||
organizer: suggestion.organizer,
|
||||
host: suggestion.organizer,
|
||||
venues: JSON.stringify(suggestion.location ? [suggestion.location] : []),
|
||||
competitionTypes: JSON.stringify([]),
|
||||
registrationDeadlines: JSON.stringify([]),
|
||||
entryFees: JSON.stringify({}),
|
||||
});
|
||||
}
|
||||
await suggestion.update({ status: 'reviewed' });
|
||||
return { id: String(tournament.id), created };
|
||||
}
|
||||
|
||||
async listTacticPlans(clubId, tournamentId) {
|
||||
const tournament = await OfficialTournament.findOne({ where: { id: tournamentId, clubId } });
|
||||
if (!tournament) return null;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
<p v-if="suggestionError" class="suggestion-error">{{ suggestionError }}</p>
|
||||
<ul v-if="filteredSuggestions.length" class="event-list suggestion-list">
|
||||
<li v-for="suggestion in filteredSuggestions" :key="suggestion.id" class="event-item"><div class="suggestion-main"><strong>{{ suggestion.title }}</strong><span>{{ suggestion.eventDate || 'Termin offen' }}<template v-if="suggestion.location"> · {{ suggestion.location }}</template></span></div><a class="btn-secondary" :href="suggestion.sourceUrl" target="_blank" rel="noopener">Öffnen</a><template v-if="suggestion.status === 'dismissed'"><button class="btn-secondary" @click="updateSuggestionStatus(suggestion, 'new')">Einblenden</button></template><template v-else><button class="btn-secondary" @click="updateSuggestionStatus(suggestion, suggestion.status === 'reviewed' ? 'new' : 'reviewed')">{{ suggestion.status === 'reviewed' ? 'Als neu markieren' : 'Geprüft' }}</button><button class="btn-secondary" @click="updateSuggestionStatus(suggestion, 'dismissed')">Ausblenden</button></template></li>
|
||||
<li v-for="suggestion in filteredSuggestions" :key="suggestion.id" class="event-item"><div class="suggestion-main"><strong>{{ suggestion.title }}</strong><span>{{ suggestion.eventDate || 'Termin offen' }}<template v-if="suggestion.location"> · {{ suggestion.location }}</template></span></div><a class="btn-secondary" :href="suggestion.sourceUrl" target="_blank" rel="noopener">Öffnen</a><template v-if="suggestion.status === 'dismissed'"><button class="btn-secondary" @click="updateSuggestionStatus(suggestion, 'new')">Einblenden</button></template><template v-else><button class="btn-secondary" @click="saveSuggestion(suggestion)">{{ suggestion.status === 'reviewed' ? 'Speichern' : 'Geprüft & speichern' }}</button><button class="btn-secondary" @click="updateSuggestionStatus(suggestion, suggestion.status === 'reviewed' ? 'new' : 'reviewed')">{{ suggestion.status === 'reviewed' ? 'Als neu markieren' : 'Nur geprüft' }}</button><button class="btn-secondary" @click="updateSuggestionStatus(suggestion, 'dismissed')">Ausblenden</button></template></li>
|
||||
</ul>
|
||||
<p v-else class="empty-state compact">{{ suggestionSearch ? 'Keine passenden Turniervorschläge gefunden.' : 'Keine Turniervorschläge in dieser Ansicht. „Jetzt abrufen“ aktualisiert den Kalender.' }}</p>
|
||||
</section>
|
||||
@@ -1642,6 +1642,15 @@ export default {
|
||||
try { await apiClient.patch(`/official-tournaments/${this.currentClub}/suggestions/${suggestion.id}`, { status }); await this.loadSuggestions(); }
|
||||
catch (error) { this.suggestionError = getSafeErrorMessage(error, 'Der Vorschlag konnte nicht aktualisiert werden.'); }
|
||||
},
|
||||
async saveSuggestion(suggestion) {
|
||||
this.suggestionError = '';
|
||||
try {
|
||||
const response = await apiClient.post(`/official-tournaments/${this.currentClub}/suggestions/${suggestion.id}/save`);
|
||||
await Promise.all([this.loadSuggestions(), this.loadList()]);
|
||||
this.uploadedId = String(response.data.id);
|
||||
this.workspaceTab = 'saved';
|
||||
} catch (error) { this.suggestionError = getSafeErrorMessage(error, 'Der Vorschlag konnte nicht gespeichert werden.'); }
|
||||
},
|
||||
buildParticipationMap(entries) {
|
||||
const map = {};
|
||||
for (const e of entries) {
|
||||
|
||||
Reference in New Issue
Block a user