diff --git a/backend/controllers/groupController.js b/backend/controllers/groupController.js index 5ddb95a..c1bdfea 100644 --- a/backend/controllers/groupController.js +++ b/backend/controllers/groupController.js @@ -1,5 +1,7 @@ import HttpError from '../exceptions/HttpError.js'; import groupService from '../services/groupService.js'; +import { emitActivityChanged, emitGroupChanged } from '../services/socketService.js'; +import DiaryDate from '../models/DiaryDates.js'; import { devLog } from '../utils/logger.js'; const addGroup = async(req, res) => { @@ -7,6 +9,15 @@ const addGroup = async(req, res) => { const { authcode: userToken } = req.headers; const { clubid: clubId, dateid: dateId, name, lead } = req.body; const result = await groupService.addGroup(userToken, clubId, dateId, name, lead); + + // Emit Socket-Event für Gruppen-Änderungen + if (dateId) { + const diaryDate = await DiaryDate.findByPk(dateId); + if (diaryDate?.clubId) { + emitGroupChanged(diaryDate.clubId, dateId); + } + } + res.status(201).json(result); } catch (error) { console.error('[addGroup] - Error:', error); @@ -33,6 +44,15 @@ const changeGroup = async(req, res) => { const { groupId } = req.params; const { clubid: clubId, dateid: dateId, name, lead } = req.body; const result = await groupService.changeGroup(userToken, groupId, clubId, dateId, name, lead); + + // Emit Socket-Event für Gruppen-Änderungen + if (dateId) { + const diaryDate = await DiaryDate.findByPk(dateId); + if (diaryDate?.clubId) { + emitGroupChanged(diaryDate.clubId, dateId); + } + } + res.status(200).json(result); } catch (error) { console.error('[changeGroup] - Error:', error); @@ -40,4 +60,27 @@ const changeGroup = async(req, res) => { } } -export { addGroup, getGroups, changeGroup}; \ No newline at end of file +const deleteGroup = async(req, res) => { + try { + const { authcode: userToken } = req.headers; + const { groupId } = req.params; + const { clubid: clubId, dateid: dateId } = req.body; + const result = await groupService.deleteGroup(userToken, groupId, clubId, dateId); + + // Emit Socket-Events für Gruppen- und Aktivitäts-Änderungen (Gruppen werden in Aktivitäten verwendet) + if (dateId) { + const diaryDate = await DiaryDate.findByPk(dateId); + if (diaryDate?.clubId) { + emitGroupChanged(diaryDate.clubId, dateId); + emitActivityChanged(diaryDate.clubId, dateId); + } + } + + res.status(200).json(result); + } catch (error) { + console.error('[deleteGroup] - Error:', error); + res.status(error.statusCode || 500).json({ error: error.message }); + } +} + +export { addGroup, getGroups, changeGroup, deleteGroup}; \ No newline at end of file diff --git a/backend/controllers/tournamentController.js b/backend/controllers/tournamentController.js index dd197f1..dd5e36f 100644 --- a/backend/controllers/tournamentController.js +++ b/backend/controllers/tournamentController.js @@ -1,5 +1,6 @@ // controllers/tournamentController.js import tournamentService from "../services/tournamentService.js"; +import { emitTournamentChanged } from '../services/socketService.js'; // 1. Alle Turniere eines Vereins export const getTournaments = async (req, res) => { @@ -20,9 +21,13 @@ export const addTournament = async (req, res) => { const { clubId, tournamentName, date } = req.body; try { const tournament = await tournamentService.addTournament(token, clubId, tournamentName, date); + // Emit Socket-Event + if (clubId && tournament && tournament.id) { + emitTournamentChanged(clubId, tournament.id); + } res.status(201).json(tournament); } catch (error) { - console.error(error); + console.error('[addTournament] Error:', error); res.status(500).json({ error: error.message }); } }; @@ -32,11 +37,16 @@ export const addParticipant = async (req, res) => { const { authcode: token } = req.headers; const { clubId, tournamentId, participant: participantId } = req.body; try { + if (!participantId) { + return res.status(400).json({ error: 'Teilnehmer-ID ist erforderlich' }); + } await tournamentService.addParticipant(token, clubId, tournamentId, participantId); const participants = await tournamentService.getParticipants(token, clubId, tournamentId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json(participants); } catch (error) { - console.error(error); + console.error('[addParticipant] Error:', error); res.status(500).json({ error: error.message }); } }; @@ -60,6 +70,8 @@ export const setModus = async (req, res) => { const { clubId, tournamentId, type, numberOfGroups, advancingPerGroup } = req.body; try { await tournamentService.setModus(token, clubId, tournamentId, type, numberOfGroups, advancingPerGroup); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.sendStatus(204); } catch (error) { console.error(error); @@ -73,6 +85,8 @@ export const createGroups = async (req, res) => { const { clubId, tournamentId } = req.body; try { await tournamentService.createGroups(token, clubId, tournamentId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.sendStatus(204); } catch (error) { console.error(error); @@ -86,6 +100,8 @@ export const fillGroups = async (req, res) => { const { clubId, tournamentId } = req.body; try { const updatedMembers = await tournamentService.fillGroups(token, clubId, tournamentId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json(updatedMembers); } catch (error) { console.error(error); @@ -138,6 +154,8 @@ export const addMatchResult = async (req, res) => { const { clubId, tournamentId, matchId, set, result } = req.body; try { await tournamentService.addMatchResult(token, clubId, tournamentId, matchId, set, result); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json({ message: "Result added successfully" }); } catch (error) { console.error(error); @@ -151,6 +169,8 @@ export const finishMatch = async (req, res) => { const { clubId, tournamentId, matchId } = req.body; try { await tournamentService.finishMatch(token, clubId, tournamentId, matchId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json({ message: "Match finished successfully" }); } catch (error) { console.error(error); @@ -164,6 +184,8 @@ export const startKnockout = async (req, res) => { try { await tournamentService.startKnockout(token, clubId, tournamentId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json({ message: "K.o.-Runde erfolgreich gestartet" }); } catch (error) { const status = /Gruppenmodus|Zu wenige Qualifikanten/.test(error.message) ? 400 : 500; @@ -190,6 +212,8 @@ export const manualAssignGroups = async (req, res) => { numberOfGroups, // neu maxGroupSize // neu ); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json(groupsWithParts); } catch (error) { console.error('Error in manualAssignGroups:', error); @@ -202,6 +226,8 @@ export const resetGroups = async (req, res) => { const { clubId, tournamentId } = req.body; try { await tournamentService.resetGroups(token, clubId, tournamentId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.sendStatus(204); } catch (err) { console.error(err); @@ -214,6 +240,8 @@ export const resetMatches = async (req, res) => { const { clubId, tournamentId } = req.body; try { await tournamentService.resetMatches(token, clubId, tournamentId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.sendStatus(204); } catch (err) { console.error(err); @@ -227,6 +255,8 @@ export const removeParticipant = async (req, res) => { try { await tournamentService.removeParticipant(token, clubId, tournamentId, participantId); const participants = await tournamentService.getParticipants(token, clubId, tournamentId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json(participants); } catch (err) { console.error(err); @@ -245,6 +275,8 @@ export const deleteMatchResult = async (req, res) => { matchId, set ); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json({ message: 'Einzelsatz gelöscht' }); } catch (error) { console.error('Error in deleteMatchResult:', error); @@ -258,6 +290,8 @@ export const reopenMatch = async (req, res) => { const { clubId, tournamentId, matchId } = req.body; try { await tournamentService.reopenMatch(token, clubId, tournamentId, matchId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); // Gib optional das aktualisierte Match zurück res.status(200).json({ message: "Match reopened" }); } catch (error) { @@ -271,6 +305,8 @@ export const deleteKnockoutMatches = async (req, res) => { const { clubId, tournamentId } = req.body; try { await tournamentService.resetKnockout(token, clubId, tournamentId); + // Emit Socket-Event + emitTournamentChanged(clubId, tournamentId); res.status(200).json({ message: "K.o.-Runde gelöscht" }); } catch (error) { console.error("Error in deleteKnockoutMatches:", error); diff --git a/backend/routes/groupRoutes.js b/backend/routes/groupRoutes.js index 5cc30d6..693a97f 100644 --- a/backend/routes/groupRoutes.js +++ b/backend/routes/groupRoutes.js @@ -1,6 +1,6 @@ import express from 'express'; import { authenticate } from '../middleware/authMiddleware.js'; -import { addGroup, getGroups, changeGroup } from '../controllers/groupController.js'; +import { addGroup, getGroups, changeGroup, deleteGroup } from '../controllers/groupController.js'; const router = express.Router(); @@ -9,5 +9,6 @@ router.use(authenticate); router.post('/', addGroup); router.get('/:clubId/:dateId', getGroups); router.put('/:groupId', changeGroup); +router.delete('/:groupId', deleteGroup); export default router; diff --git a/backend/services/groupService.js b/backend/services/groupService.js index 13f5a70..4f47e7b 100644 --- a/backend/services/groupService.js +++ b/backend/services/groupService.js @@ -58,6 +58,22 @@ class GroupService { await group.save(); return group; } + + async deleteGroup(userToken, groupId, clubId, dateId) { + await checkAccess(userToken, clubId); + await this.checkDiaryDateToClub(clubId, dateId); + const group = await Group.findOne({ + where: { + id: groupId, + diaryDateId: dateId + } + }); + if (!group) { + throw new HttpError('Gruppe nicht gefunden oder passt nicht zum angegebenen Datum und Verein', 404); + } + await group.destroy(); + return { success: true }; + } } export default new GroupService(); \ No newline at end of file diff --git a/backend/services/socketService.js b/backend/services/socketService.js index 0defb3b..e49cd9c 100644 --- a/backend/services/socketService.js +++ b/backend/services/socketService.js @@ -110,3 +110,13 @@ export const emitMemberChanged = (clubId) => { emitToClub(clubId, 'member:changed', { clubId }); }; +// Event für Gruppen-Änderungen (erstellen, aktualisieren, löschen) +export const emitGroupChanged = (clubId, dateId) => { + emitToClub(clubId, 'group:changed', { dateId }); +}; + +// Event für Tournament-Änderungen (alle Aktionen) +export const emitTournamentChanged = (clubId, tournamentId) => { + emitToClub(clubId, 'tournament:changed', { tournamentId }); +}; + diff --git a/frontend/src/services/socketService.js b/frontend/src/services/socketService.js index f6fea2a..5c7b342 100644 --- a/frontend/src/services/socketService.js +++ b/frontend/src/services/socketService.js @@ -166,6 +166,28 @@ export const onMemberChanged = (callback) => { } }; +export const onGroupChanged = (callback) => { + if (socket) { + socket.on('group:changed', (data) => { + console.log('📡 [Socket] group:changed empfangen:', data); + callback(data); + }); + } else { + console.warn('⚠️ [Socket] onGroupChanged: Socket nicht verbunden'); + } +}; + +export const onTournamentChanged = (callback) => { + if (socket) { + socket.on('tournament:changed', (data) => { + console.log('📡 [Socket] tournament:changed empfangen:', data); + callback(data); + }); + } else { + console.warn('⚠️ [Socket] onTournamentChanged: Socket nicht verbunden'); + } +}; + // Event-Listener entfernen export const offParticipantAdded = (callback) => { if (socket) { @@ -245,3 +267,15 @@ export const offMemberChanged = (callback) => { } }; +export const offGroupChanged = (callback) => { + if (socket) { + socket.off('group:changed', callback); + } +}; + +export const offTournamentChanged = (callback) => { + if (socket) { + socket.off('tournament:changed', callback); + } +}; + diff --git a/frontend/src/views/DiaryView.vue b/frontend/src/views/DiaryView.vue index d96c54b..641ad9b 100644 --- a/frontend/src/views/DiaryView.vue +++ b/frontend/src/views/DiaryView.vue @@ -96,6 +96,10 @@ + @@ -104,11 +108,11 @@