From 156f4d692193fca942d0439372474816332a1de6 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 13 Nov 2025 17:32:29 +0100 Subject: [PATCH] Add member change event handling for real-time updates This commit introduces a new socket event for member changes, allowing real-time updates when members are created or updated. The backend now emits a 'member:changed' event upon successful member modifications, while the frontend listens for this event to refresh the member list in the DiaryView component. This enhances the interactivity and responsiveness of the application, ensuring users receive immediate feedback on member changes. --- backend/controllers/memberController.js | 7 +++++ backend/services/socketService.js | 5 ++++ frontend/src/services/socketService.js | 17 ++++++++++++ frontend/src/views/DiaryView.vue | 37 +++++++++++++++++++++++-- 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/backend/controllers/memberController.js b/backend/controllers/memberController.js index 36ddbc1..b146803 100644 --- a/backend/controllers/memberController.js +++ b/backend/controllers/memberController.js @@ -1,5 +1,6 @@ import MemberService from "../services/memberService.js"; import MemberTransferService from "../services/memberTransferService.js"; +import { emitMemberChanged } from '../services/socketService.js'; import { devLog } from '../utils/logger.js'; const getClubMembers = async(req, res) => { @@ -32,6 +33,12 @@ const setClubMembers = async (req, res) => { const { authcode: userToken } = req.headers; const addResult = await MemberService.setClubMember(userToken, clubId, memberId, firstName, lastName, street, city, postalCode, birthdate, phone, email, active, testMembership, picsInInternetAllowed, gender, ttr, qttr, memberFormHandedOver, contacts); + + // Emit Socket-Event wenn Member erfolgreich erstellt/aktualisiert wurde + if (addResult.status === 200) { + emitMemberChanged(clubId); + } + res.status(addResult.status || 500).json(addResult.response); } catch (error) { console.error('[setClubMembers] - Error:', error); diff --git a/backend/services/socketService.js b/backend/services/socketService.js index 7edfce6..a1e0238 100644 --- a/backend/services/socketService.js +++ b/backend/services/socketService.js @@ -104,3 +104,8 @@ export const emitActivityChanged = (clubId, dateId) => { emitToClub(clubId, 'activity:changed', { dateId }); }; +// Event für Member-Änderungen (erstellen, aktualisieren) +export const emitMemberChanged = (clubId) => { + emitToClub(clubId, 'member:changed', { clubId }); +}; + diff --git a/frontend/src/services/socketService.js b/frontend/src/services/socketService.js index ae1571c..2a3574a 100644 --- a/frontend/src/services/socketService.js +++ b/frontend/src/services/socketService.js @@ -150,6 +150,17 @@ export const onActivityChanged = (callback) => { } }; +export const onMemberChanged = (callback) => { + if (socket) { + socket.on('member:changed', (data) => { + console.log('📡 [Socket] member:changed empfangen:', data); + callback(data); + }); + } else { + console.warn('⚠️ [Socket] onMemberChanged: Socket nicht verbunden'); + } +}; + // Event-Listener entfernen export const offParticipantAdded = (callback) => { if (socket) { @@ -223,3 +234,9 @@ export const offActivityChanged = (callback) => { } }; +export const offMemberChanged = (callback) => { + if (socket) { + socket.off('member:changed', callback); + } +}; + diff --git a/frontend/src/views/DiaryView.vue b/frontend/src/views/DiaryView.vue index c1f8427..7684e37 100644 --- a/frontend/src/views/DiaryView.vue +++ b/frontend/src/views/DiaryView.vue @@ -579,6 +579,7 @@ import { onActivityMemberAdded, onActivityMemberRemoved, onActivityChanged, + onMemberChanged, offParticipantAdded, offParticipantRemoved, offParticipantUpdated, @@ -589,7 +590,8 @@ import { offDiaryDateUpdated, offActivityMemberAdded, offActivityMemberRemoved, - offActivityChanged + offActivityChanged, + offMemberChanged } from '../services/socketService.js'; export default { @@ -1054,7 +1056,9 @@ export default { async loadMembers() { const response = await apiClient.get(`/clubmembers/get/${this.currentClub}/false`); - this.members = response.data; + // Erstelle ein neues Array, um Vue-Reaktivität sicherzustellen + this.members = Array.isArray(response.data) ? [...response.data] : []; + console.log('📡 [DiaryView] loadMembers: Mitglieder geladen, Anzahl:', this.members?.length || 0); }, async loadParticipants(dateId) { @@ -1615,7 +1619,8 @@ export default { pdf.save('trainingsplan.pdf'); }, sortedMembers() { - return this.members.sort((a, b) => { + // Erstelle eine Kopie des Arrays, um Mutation zu vermeiden + return [...this.members].sort((a, b) => { const firstNameComparison = a.firstName.localeCompare(b.firstName); if (firstNameComparison === 0) { return a.lastName.localeCompare(b.lastName); @@ -2451,6 +2456,9 @@ export default { onActivityMemberAdded(this.handleActivityMemberAdded); onActivityMemberRemoved(this.handleActivityMemberRemoved); onActivityChanged(this.handleActivityChanged); + + // Event-Handler für Member-Änderungen + onMemberChanged(this.handleMemberChanged); console.log('✅ [DiaryView] Alle Event-Handler registriert'); }, @@ -2466,6 +2474,7 @@ export default { offActivityMemberAdded(this.handleActivityMemberAdded); offActivityMemberRemoved(this.handleActivityMemberRemoved); offActivityChanged(this.handleActivityChanged); + offMemberChanged(this.handleMemberChanged); }, async handleParticipantAdded(data) { @@ -2624,6 +2633,28 @@ export default { console.log('⚠️ [DiaryView] Datum stimmt nicht überein oder kein Datum ausgewählt'); } }, + + async handleMemberChanged(data) { + console.log('📡 [DiaryView] handleMemberChanged aufgerufen:', data); + console.log('📡 [DiaryView] Aktueller Club:', this.currentClub, 'Event Club:', data.clubId); + // Prüfe, ob der Club übereinstimmt + if (data.clubId && String(data.clubId) === String(this.currentClub)) { + console.log('✅ [DiaryView] Club stimmt überein, lade Mitgliederliste neu'); + console.log('📡 [DiaryView] Aktuelle Mitgliederanzahl vor Reload:', this.members?.length || 0); + // Lade Mitgliederliste neu + try { + await this.loadMembers(); + console.log('✅ [DiaryView] Mitgliederliste neu geladen, neue Anzahl:', this.members?.length || 0); + console.log('📡 [DiaryView] Mitgliederliste:', this.members); + // Force Vue update + this.$forceUpdate(); + } catch (error) { + console.error('❌ [DiaryView] Fehler beim Neuladen der Mitgliederliste:', error); + } + } else { + console.log('⚠️ [DiaryView] Club stimmt nicht überein - Event Club:', data.clubId, 'Aktueller Club:', this.currentClub); + } + }, }, async mounted() { await this.init();