diff --git a/frontend/src/components/PDFGenerator.js b/frontend/src/components/PDFGenerator.js index 27ab7bb..65ea42b 100644 --- a/frontend/src/components/PDFGenerator.js +++ b/frontend/src/components/PDFGenerator.js @@ -1153,6 +1153,310 @@ class PDFGenerator { .join(', '); } + addTrainingDaySummary(clubName, trainingDate, trainingStart, trainingEnd, members, activities, activityMembersMap, groupActivityMembersMap, participantMapByMemberId, groups, trainingPlan = [], memberGroupsMap = {}) { + // Header + const formattedDate = new Date(trainingDate).toLocaleDateString('de-DE'); + const formattedStartTime = trainingStart ? trainingStart.slice(0, 5) : ''; + const formattedEndTime = trainingEnd ? trainingEnd.slice(0, 5) : ''; + + this.pdf.setFontSize(14); + this.pdf.setFont('helvetica', 'bold'); + this.pdf.text(`${clubName} - Trainingstag`, this.margin, this.cursorY); + this.cursorY += 8; + this.pdf.setFontSize(12); + this.pdf.setFont('helvetica', 'normal'); + this.pdf.text(`Datum: ${formattedDate}`, this.margin, this.cursorY); + this.cursorY += 7; + if (formattedStartTime && formattedEndTime) { + this.pdf.text(`Uhrzeit: ${formattedStartTime} - ${formattedEndTime}`, this.margin, this.cursorY); + this.cursorY += 7; + } + this.cursorY += 5; + + // Debug-Ausgaben + console.log('[addTrainingDaySummary] members:', members?.length || 0); + console.log('[addTrainingDaySummary] activities:', activities?.length || 0); + console.log('[addTrainingDaySummary] activityMembersMap:', activityMembersMap); + console.log('[addTrainingDaySummary] participantMapByMemberId:', participantMapByMemberId); + + // Erstelle Mapping: memberId -> Liste von Aktivitäten + const memberActivitiesMap = new Map(); + + // Initialisiere für alle Teilnehmer + members.forEach(member => { + if (member && member.id) { + memberActivitiesMap.set(member.id, []); + } + }); + + // Erstelle umgekehrtes Mapping: participantId -> memberId (einmalig) + const participantToMemberMap = {}; + if (participantMapByMemberId) { + Object.keys(participantMapByMemberId).forEach(memberId => { + const participantId = participantMapByMemberId[memberId]; + if (participantId) { + participantToMemberMap[participantId] = parseInt(memberId); + } + }); + } + + // Erstelle Mapping von activityId zu startTime aus trainingPlan + const activityIdToStartTime = new Map(); + trainingPlan.forEach(item => { + if (item && item.id && item.startTime) { + activityIdToStartTime.set(item.id, item.startTime); + } + }); + + // Durchlaufe alle Aktivitäten + let activitiesProcessed = 0; + let activitiesWithMembers = 0; + activities.forEach(activity => { + if (!activity || !activity.id) return; + activitiesProcessed++; + + const activityName = activity.predefinedActivity + ? activity.predefinedActivity.name + : (activity.description || 'Unbekannte Aktivität'); + // Verwende startTime aus trainingPlan, falls vorhanden, sonst leer + const activityStartTime = activityIdToStartTime.get(activity.id) || activity.startTime || ''; + const activityDuration = activity.duration ? `${activity.duration} Min` : ''; + + // Finde alle Teilnehmer dieser Aktivität + const participantIds = activityMembersMap[activity.id]; + let hasDirectAssignments = false; + + if (participantIds) { + // Handle both Set and Array + const idsArray = participantIds instanceof Set ? Array.from(participantIds) : (Array.isArray(participantIds) ? participantIds : []); + if (idsArray.length > 0) { + hasDirectAssignments = true; + activitiesWithMembers++; + idsArray.forEach(participantId => { + // Finde memberId für diesen participantId + const memberId = participantToMemberMap[participantId]; + if (memberId) { + if (!memberActivitiesMap.has(memberId)) { + memberActivitiesMap.set(memberId, []); + } + memberActivitiesMap.get(memberId).push({ + name: activityName, + startTime: activityStartTime, + duration: activityDuration + }); + } else { + console.log('[addTrainingDaySummary] Kein memberId gefunden für participantId:', participantId); + } + }); + } + } + + // Wenn keine direkten Zuordnungen vorhanden sind, verwende Gruppen-Zuordnung + if (!hasDirectAssignments) { + const activityGroupId = activity.groupActivity ? activity.groupActivity.id : null; + + if (activityGroupId) { + // Aktivität hat eine Gruppe: nehme alle Members dieser Gruppe + members.forEach(member => { + if (member && member.id) { + const memberGroupId = memberGroupsMap[member.id]; + // Prüfe ob memberGroupId als String oder Number vorliegt + const memberGroupIdStr = memberGroupId ? String(memberGroupId) : ''; + const activityGroupIdStr = String(activityGroupId); + + if (memberGroupIdStr === activityGroupIdStr) { + if (!memberActivitiesMap.has(member.id)) { + memberActivitiesMap.set(member.id, []); + } + memberActivitiesMap.get(member.id).push({ + name: activityName, + startTime: activityStartTime, + duration: activityDuration + }); + } + } + }); + } else { + // Aktivität hat keine Gruppe: nehme alle Teilnehmer + members.forEach(member => { + if (member && member.id) { + if (!memberActivitiesMap.has(member.id)) { + memberActivitiesMap.set(member.id, []); + } + memberActivitiesMap.get(member.id).push({ + name: activityName, + startTime: activityStartTime, + duration: activityDuration + }); + } + }); + } + } + }); + console.log('[addTrainingDaySummary] Aktivitäten verarbeitet:', activitiesProcessed, 'mit Mitgliedern:', activitiesWithMembers); + + // Durchlaufe alle Gruppenaktivitäten + // WICHTIG: Auch aus Zeitblöcken, daher trainingPlan verwenden statt activities + if (groupActivityMembersMap && Object.keys(groupActivityMembersMap).length > 0) { + // Finde Gruppenaktivitäten in trainingPlan (auch in Zeitblöcken) + trainingPlan.forEach(activity => { + if (!activity || !activity.groupActivities) return; + + activity.groupActivities.forEach(groupActivity => { + if (!groupActivity || !groupActivity.id) return; + + const activityName = groupActivity.groupPredefinedActivity + ? groupActivity.groupPredefinedActivity.name + : (groupActivity.description || 'Unbekannte Gruppenaktivität'); + // Verwende startTime aus trainingPlan für die übergeordnete Aktivität + const activityStartTime = activityIdToStartTime.get(activity.id) || activity.startTime || ''; + const activityDuration = activity.duration ? `${activity.duration} Min` : ''; + + // Finde alle Teilnehmer dieser Gruppenaktivität + const participantIds = groupActivityMembersMap[groupActivity.id]; + let hasDirectGroupActivityAssignments = false; + + if (participantIds) { + // Handle both Set and Array + const idsArray = participantIds instanceof Set ? Array.from(participantIds) : (Array.isArray(participantIds) ? participantIds : []); + if (idsArray.length > 0) { + hasDirectGroupActivityAssignments = true; + idsArray.forEach(participantId => { + // Finde memberId für diesen participantId + const memberId = participantToMemberMap[participantId]; + if (memberId) { + if (!memberActivitiesMap.has(memberId)) { + memberActivitiesMap.set(memberId, []); + } + memberActivitiesMap.get(memberId).push({ + name: activityName, + startTime: activityStartTime, + duration: activityDuration + }); + } + }); + } + } + + // Wenn keine direkten Zuordnungen vorhanden sind, verwende Gruppen-Zuordnung + if (!hasDirectGroupActivityAssignments) { + const groupActivityGroupId = groupActivity.groupsGroupActivity ? groupActivity.groupsGroupActivity.id : null; + + if (groupActivityGroupId) { + // Gruppenaktivität hat eine Gruppe: nehme alle Members dieser Gruppe + members.forEach(member => { + if (member && member.id) { + const memberGroupId = memberGroupsMap[member.id]; + // Prüfe ob memberGroupId als String oder Number vorliegt + const memberGroupIdStr = memberGroupId ? String(memberGroupId) : ''; + const groupActivityGroupIdStr = String(groupActivityGroupId); + + if (memberGroupIdStr === groupActivityGroupIdStr) { + if (!memberActivitiesMap.has(member.id)) { + memberActivitiesMap.set(member.id, []); + } + memberActivitiesMap.get(member.id).push({ + name: activityName, + startTime: activityStartTime, + duration: activityDuration + }); + } + } + }); + } else { + // Gruppenaktivität hat keine Gruppe: nehme alle Teilnehmer + members.forEach(member => { + if (member && member.id) { + if (!memberActivitiesMap.has(member.id)) { + memberActivitiesMap.set(member.id, []); + } + memberActivitiesMap.get(member.id).push({ + name: activityName, + startTime: activityStartTime, + duration: activityDuration + }); + } + }); + } + } + }); + }); + } + + // Sortiere Mitglieder alphabetisch + const sortedMembers = members + .filter(m => m && m.id && memberActivitiesMap.has(m.id)) + .sort((a, b) => { + const lastNameCompare = (a.lastName || '').localeCompare(b.lastName || ''); + if (lastNameCompare !== 0) return lastNameCompare; + return (a.firstName || '').localeCompare(b.firstName || ''); + }); + + console.log('[addTrainingDaySummary] Mitglieder mit Aktivitäten:', sortedMembers.length); + console.log('[addTrainingDaySummary] memberActivitiesMap:', Array.from(memberActivitiesMap.entries()).map(([id, acts]) => ({ id, count: acts.length }))); + + // Zeige für jeden Teilnehmer seine Aktivitäten + sortedMembers.forEach(member => { + const memberActivities = memberActivitiesMap.get(member.id); + if (!memberActivities || memberActivities.length === 0) return; + + // Prüfe ob neue Seite nötig + if (this.cursorY > 250) { + this.addNewPage(); + this.cursorY = this.margin; + } + + // Mitgliedsname + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(12); + const memberName = `${member.firstName || ''} ${member.lastName || ''}`.trim(); + this.pdf.text(memberName, this.margin, this.cursorY); + this.cursorY += 8; + + // Aktivitäten + this.pdf.setFont('helvetica', 'normal'); + this.pdf.setFontSize(10); + memberActivities.forEach(activity => { + if (this.cursorY > 280) { + this.addNewPage(); + this.cursorY = this.margin; + // Mitgliedsname erneut anzeigen bei Seitenwechsel + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(12); + this.pdf.text(memberName, this.margin, this.cursorY); + this.cursorY += 8; + this.pdf.setFont('helvetica', 'normal'); + this.pdf.setFontSize(10); + } + + let activityText = ` • ${activity.name}`; + if (activity.startTime) { + activityText += ` (${activity.startTime}`; + if (activity.duration) { + activityText += `, ${activity.duration}`; + } + activityText += ')'; + } else if (activity.duration) { + activityText += ` (${activity.duration})`; + } + + this.pdf.text(activityText, this.margin + 5, this.cursorY); + this.cursorY += 6; + }); + + // Abstand nach Mitglied + this.cursorY += 3; + }); + + // Falls keine Aktivitäten vorhanden + if (sortedMembers.length === 0) { + this.pdf.setFont('helvetica', 'normal'); + this.pdf.setFontSize(11); + this.pdf.text('Keine Aktivitäten für diesen Trainingstag erfasst.', this.margin, this.cursorY); + this.cursorY += 10; + } + } + } export default PDFGenerator; diff --git a/frontend/src/views/DiaryView.vue b/frontend/src/views/DiaryView.vue index 9e13d6f..b8c91bc 100644 --- a/frontend/src/views/DiaryView.vue +++ b/frontend/src/views/DiaryView.vue @@ -432,7 +432,11 @@
+
@@ -1919,6 +1923,72 @@ export default { pdf.addTrainingPlan(this.currentClubName, this.date.date, this.trainingStart, this.trainingEnd, this.trainingPlan); pdf.save('trainingsplan.pdf'); }, + async generateTrainingDayPDF() { + if (!this.date || !this.participants || this.participants.length === 0) { + this.showInfo('Fehler', 'Keine Teilnehmer für diesen Trainingstag vorhanden.', '', 'error'); + return; + } + + try { + // Lade alle Aktivitäts-Mitglieder-Zuordnungen + await this.loadAllActivityMembers(); + + // Filtere Mitglieder: nur die, die auch Teilnehmer sind + const participantMembers = this.members.filter(m => + m && m.id && this.participants.includes(m.id) + ); + + // Verwende trainingPlan statt activities, da die Aktivitäten dort sind + // Filtere Zeitblöcke komplett aus, da sie immer mehrere Gruppen umfassen + const activitiesForPDF = this.trainingPlan.filter(item => !item.isTimeblock); + + // Debug-Ausgaben + console.log('[generateTrainingDayPDF] participantMembers:', participantMembers.length); + console.log('[generateTrainingDayPDF] trainingPlan items:', this.trainingPlan.length); + console.log('[generateTrainingDayPDF] activitiesForPDF:', activitiesForPDF.length); + console.log('[generateTrainingDayPDF] activityMembersMap keys:', Object.keys(this.activityMembersMap || {})); + console.log('[generateTrainingDayPDF] groupActivityMembersMap keys:', Object.keys(this.groupActivityMembersMap || {})); + console.log('[generateTrainingDayPDF] participantMapByMemberId:', this.participantMapByMemberId); + + const pdf = new PDFGenerator(); + pdf.addTrainingDaySummary( + this.currentClubName, + this.date.date, + this.trainingStart, + this.trainingEnd, + participantMembers, + activitiesForPDF, + this.activityMembersMap, + this.groupActivityMembersMap, + this.participantMapByMemberId, + this.groups, + this.trainingPlan, + this.memberGroupsMap + ); + + const dateStr = new Date(this.date.date).toLocaleDateString('de-DE').replace(/\./g, '-'); + pdf.save(`trainingstag-${dateStr}.pdf`); + } catch (error) { + console.error('Fehler beim Generieren des Trainingstag-PDFs:', error); + this.showInfo('Fehler', 'Fehler beim Generieren des PDFs.', error.message || '', 'error'); + } + }, + async loadAllActivityMembers() { + // Lade für alle Aktivitäten im trainingPlan die Mitglieder-Zuordnungen + for (const activity of this.trainingPlan) { + if (activity && activity.id) { + await this.ensureActivityMembersLoaded(activity.id); + } + // Lade auch für Gruppenaktivitäten + if (activity && activity.groupActivities) { + for (const groupActivity of activity.groupActivities) { + if (groupActivity && groupActivity.id) { + await this.ensureGroupActivityMembersLoaded(groupActivity.id); + } + } + } + } + }, sortedMembers() { // Erstelle eine Kopie des Arrays, um Mutation zu vermeiden return [...this.members].sort((a, b) => {