diff --git a/frontend/src/components/PDFGenerator.js b/frontend/src/components/PDFGenerator.js index 725657e..195eeec 100644 --- a/frontend/src/components/PDFGenerator.js +++ b/frontend/src/components/PDFGenerator.js @@ -264,30 +264,116 @@ class PDFGenerator { }); } - addMemberCompetitions(tournamentTitle, memberName, rows) { + addMemberCompetitions(tournamentTitle, memberName, recommendedRows = [], otherRows = []) { let y = this.margin; this.pdf.setFont('helvetica', 'bold'); this.pdf.setFontSize(14); this.pdf.text(tournamentTitle || 'Offizielles Turnier', this.margin, y); y += 9; - this.pdf.setFont('helvetica', 'normal'); + this.pdf.setFont('helvetica', 'bold'); this.pdf.setFontSize(12); this.pdf.text(`Mitglied: ${memberName}`, this.margin, y); y += 8; - this.pdf.setFont('helvetica', 'bold'); - this.pdf.text('Wettbewerb', this.margin, y); - this.pdf.text('Datum', this.margin + 110, y); - this.pdf.text('Startzeit', this.margin + 150, y); - y += 7; - this.pdf.setFont('helvetica', 'normal'); - for (const r of rows) { - this.pdf.text(r.name || '', this.margin, y); - this.pdf.text(r.date || '–', this.margin + 110, y); - this.pdf.text(r.time || '–', this.margin + 150, y); + // Empfehlungen (fett) + if (recommendedRows && recommendedRows.length) { + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(13); + this.pdf.text('Empfehlungen', this.margin, y); y += 7; + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(12); + this.pdf.text('Wettbewerb', this.margin, y); + this.pdf.text('Datum', this.margin + 110, y); + this.pdf.text('Startzeit', this.margin + 150, y); + y += 7; + for (const r of recommendedRows) { + this.pdf.text(r.name || '', this.margin, y); + this.pdf.text(r.date || '–', this.margin + 110, y); + this.pdf.text(r.time || '–', this.margin + 150, y); + y += 7; + if (y > this.pageHeight) { + this.addNewPage(); + y = this.margin; + } + } + } + + // Weitere spielbare Wettbewerbe (normal) + if (otherRows && otherRows.length) { + y += 5; + if (y > this.pageHeight) { this.addNewPage(); y = this.margin; } + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(13); + this.pdf.text('Ebenfalls spielbar', this.margin, y); + y += 7; + this.pdf.setFont('helvetica', 'normal'); + this.pdf.setFontSize(12); + for (const r of otherRows) { + this.pdf.text(r.name || '', this.margin, y); + this.pdf.text(r.date || '–', this.margin + 110, y); + this.pdf.text(r.time || '–', this.margin + 150, y); + y += 7; + if (y > this.pageHeight) { + this.addNewPage(); + y = this.margin; + } + } + } + // Hinweise-Sektion + const remainingForHints = 60; // Platz für Überschrift + Liste abschätzen + if (y + remainingForHints > this.pageHeight) { + this.addNewPage(); + y = this.margin; + } else { + y += 6; + } + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(13); + this.pdf.text('Hinweise:', this.margin, y); + y += 7; + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(12); + const maxWidth = 210 - this.margin * 2; + const bullets = [ + 'Eine Stunde vor Beginn der Konkurrenz in der Halle sein', + 'Kein weißes Trikot', + 'Sportshorts (oder Sportröckchen), am besten auch nicht weiß', + 'Hallenschuhe (dürfen auf Boden nicht abfärben)', + 'Eine Flasche Wasser dabei haben', + 'Da der Verein die Meldung übernehmen möchte, die Trainer mind. eine Woche vor dem Turnier über die Teilnahme informieren', + ]; + for (const b of bullets) { + const lines = this.pdf.splitTextToSize(`- ${b}`, maxWidth); + for (const line of lines) { + this.pdf.text(line, this.margin, y); + y += 6; + if (y > this.pageHeight) { + this.addNewPage(); + y = this.margin; + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(12); + } + } + } + // Leerzeile vor dem Abschlusssatz + if (y + 6 > this.pageHeight) { + this.addNewPage(); + y = this.margin; + } else { + y += 6; + } + const finalLine = 'Die Trainer probieren bei allen Turnieren anwesend zu sein.'; + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(12); + const finalLines = this.pdf.splitTextToSize(finalLine, maxWidth); + for (const line of finalLines) { + this.pdf.text(line, this.margin, y); + y += 6; if (y > this.pageHeight) { this.addNewPage(); y = this.margin; + this.pdf.setFont('helvetica', 'bold'); + this.pdf.setFontSize(12); } } this.cursorY = y + 10; diff --git a/frontend/src/views/OfficialTournaments.vue b/frontend/src/views/OfficialTournaments.vue index ea2bd42..4969dec 100644 --- a/frontend/src/views/OfficialTournaments.vue +++ b/frontend/src/views/OfficialTournaments.vue @@ -42,12 +42,12 @@ -
+ +
+
- -

Konkurrenzen

@@ -119,11 +119,27 @@ @@ -149,12 +165,24 @@ export default { expanded: {}, selectedMemberIds: [], showMemberDialog: false, + memberRecommendations: {}, + selectedMemberIdForDialog: null, }; }, computed: { ...mapGetters(['currentClub']), activeMembers() { return (this.members || []).filter(m => m.active === true); + }, + selectedMemberInDialog() { + const id = this.selectedMemberIdForDialog; + if (!id) return null; + return (this.members || []).find(m => m.id === id) || null; + }, + selectedMemberCompetitions() { + const m = this.selectedMemberInDialog; + if (!m) return []; + return this.competitionsForMember(m); } }, methods: { @@ -211,15 +239,27 @@ export default { competitionsForMember(member) { const comps = (this.parsed && this.parsed.parsedData && this.parsed.parsedData.competitions) ? this.parsed.parsedData.competitions : []; const rows = []; - for (const c of comps) { + for (let idx = 0; idx < comps.length; idx++) { + const c = comps[idx]; if (this.isEligibleForCompetition(member, c)) { const title = c.ageClassCompetition || c.altersklasseWettbewerb || ''; const st = this.splitDateTime(c.startTime || c.startzeit || ''); - rows.push({ name: title, date: st.date, time: st.time }); + rows.push({ key: String(idx), name: title, date: st.date, time: st.time, raw: c }); } } return rows; }, + isRecommended(memberId, compKey) { + const set = this.memberRecommendations[memberId]; + return !!(set && set.has && set.has(compKey)); + }, + toggleRecommendation(memberId, compKey) { + if (!this.memberRecommendations[memberId]) { + this.$set ? this.$set(this.memberRecommendations, memberId, new Set()) : (this.memberRecommendations[memberId] = new Set()); + } + const set = this.memberRecommendations[memberId]; + if (set.has(compKey)) set.delete(compKey); else set.add(compKey); + }, async generateMembersPdf() { if (!this.selectedMemberIds.length) return; const pdf = new PDFGenerator(); @@ -229,9 +269,12 @@ export default { const m = (this.members || []).find(x => x.id === mid); if (!m) continue; const rows = this.competitionsForMember(m); + const recKeys = this.memberRecommendations[mid] ? Array.from(this.memberRecommendations[mid]) : []; + const recRows = rows.filter(r => recKeys.includes(r.key)); + const otherRows = rows.filter(r => !recKeys.includes(r.key)); if (!first) pdf.addNewPage(); first = false; - pdf.addMemberCompetitions(title, `${m.firstName} ${m.lastName}`, rows); + pdf.addMemberCompetitions(title, `${m.firstName} ${m.lastName}`, recRows, otherRows); } pdf.save('turnier_mitglieder.pdf'); }, @@ -424,6 +467,10 @@ th, td { border-bottom: 1px solid var(--border-color); padding: 0.5rem; text-ali .modal-body { padding: .75rem 1rem; overflow: auto; } .checkbox-column { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .5rem .75rem; } .check-item { display: flex; align-items: center; gap: .35rem; } +.dialog-layout { display: grid; grid-template-columns: 1fr 1.2fr; gap: 1rem; align-items: start; } +.dialog-col h4 { margin: 0 0 .5rem 0; } +.members-col .check-item span.active { font-weight: bold; } +.recommendations-col .check-item { padding: .15rem 0; }