From cf04e5bfe8428c2581dd4e902aa0137d8571e8f4 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Fri, 12 Sep 2025 13:58:04 +0200 Subject: [PATCH] =?UTF-8?q?Erweitert=20die=20Benutzeroberfl=C3=A4che=20in?= =?UTF-8?q?=20OfficialTournaments.vue=20um=20einen=20neuen=20Tab=20f=C3=BC?= =?UTF-8?q?r=20Teilnehmer,=20einschlie=C3=9Flich=20Filteroptionen=20zur=20?= =?UTF-8?q?Anzeige=20von=20Anmeldestatus=20und=20Teilnahme.=20Implementier?= =?UTF-8?q?t=20die=20Logik=20zur=20Gruppierung=20und=20Anzeige=20der=20Tei?= =?UTF-8?q?lnehmerdaten=20in=20einer=20Tabelle.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/OfficialTournaments.vue | 120 ++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/OfficialTournaments.vue b/frontend/src/views/OfficialTournaments.vue index 7895c99..5978564 100644 --- a/frontend/src/views/OfficialTournaments.vue +++ b/frontend/src/views/OfficialTournaments.vue @@ -55,6 +55,7 @@
+
@@ -64,7 +65,7 @@ Altersklasse/Wettbewerb - Startzeit + Startzeit Startgeld @@ -144,6 +145,46 @@
+
+

Teilnehmer

+
+ + +
+ + + + + + + + + + + + + + + + + +
MitgliedKonkurrenzStartzeitAngemeldetTeilgenommenPlatzierung
Keine Einträge für den gewählten Filter.
+

Ergebnisse

@@ -269,6 +310,7 @@ export default { participationMap: {}, // key: `${competitionId}-${memberId}` => { wants, registered, participated, placement } collator: new Intl.Collator('de', { sensitivity: 'base' }), activeTab: 'competitions', + participantsFilter: 'wants_not_registered', topActiveTab: 'events', loadingClubParticipations: false, clubParticipationRowsData: [], @@ -320,6 +362,79 @@ export default { if (m !== 0) return m; return this.collator.compare(a.competitionName, b.competitionName); }); + }, + participantsRows() { + const comps = (this.parsed?.parsedData?.competitions) || []; + const compById = Object.fromEntries(comps.map(c => [String(c.id), c])); + const rows = []; + // Merge Quelle: parsed.participation + aktueller UI-Status aus participationMap + const seen = new Set(); + const merged = []; + if (Array.isArray(this.parsed?.participation)) { + for (const e of this.parsed.participation) { + const competitionId = String(e.competitionId); + const memberId = String(e.memberId); + const key = `${competitionId}-${memberId}`; + seen.add(key); + merged.push({ competitionId, memberId }); + } + } + for (const [key, p] of Object.entries(this.participationMap || {})) { + if (seen.has(key)) continue; + const [competitionId, memberId] = key.split('-'); + merged.push({ competitionId: String(competitionId), memberId: String(memberId) }); + } + for (const e of merged) { + const competitionId = String(e.competitionId); + const memberId = String(e.memberId); + const c = compById[competitionId]; + if (!c) continue; + // Hole aktuellen Status (inkl. UI-Änderungen) aus participationMap + const current = this.getParticipation(competitionId, memberId); + const mname = this.memberNameById(memberId); + const start = String(c.startTime || c.startzeit || '–'); + const base = { + key: `${competitionId}-${memberId}`, + memberName: mname, + competitionName: c.ageClassCompetition || c.altersklasseWettbewerb || '', + start, + registered: !!current.registered, + participated: !!current.participated, + placement: current.placement || null, + wants: !!current.wants, + }; + if (this.participantsFilter === 'wants_not_registered') { + if (base.wants && !base.registered && !base.participated) rows.push(base); + } else if (this.participantsFilter === 'registered') { + if (base.registered && !base.participated) rows.push(base); + } else if (this.participantsFilter === 'participated') { + if (base.participated) rows.push(base); + } + } + return rows.sort((a, b) => { + const m = this.collator.compare(a.memberName, b.memberName); + if (m !== 0) return m; + return this.collator.compare(a.competitionName, b.competitionName); + }); + }, + participantsGroups() { + const groups = []; + const byMember = new Map(); + for (const row of this.participantsRows) { + const key = row.memberName; + if (!byMember.has(key)) byMember.set(key, []); + byMember.get(key).push(row); + } + for (const [memberName, items] of byMember.entries()) { + items.sort((a, b) => this.collator.compare(a.competitionName, b.competitionName)); + groups.push({ + memberName, + memberId: items[0]?.key.split('-')[1] || memberName, + items, + }); + } + groups.sort((a, b) => this.collator.compare(a.memberName, b.memberName)); + return groups; } }, methods: { @@ -780,6 +895,9 @@ th, td { border-bottom: 1px solid var(--border-color); padding: 0.5rem; text-ali .eligible-name { background: var(--background, #f1f1f1); border: 1px solid var(--border-color, #ddd); border-radius: 4px; padding: 2px 6px; } .eligible-table { width: 100%; border-collapse: collapse; margin-top: .25rem; } .eligible-table th, .eligible-table td { border-bottom: 1px solid var(--border-color); padding: .25rem .4rem; text-align: left; } +.indented { padding-left: 1.25rem; } +.member-cell { font-weight: 600; vertical-align: top; } +.empty-first { border-bottom: 1px solid var(--border-color); } .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.35); display: flex; align-items: center; justify-content: center; z-index: 1000; } .modal { background: #fff; border-radius: 8px; width: min(800px, 92vw); max-height: 85vh; display: flex; flex-direction: column; box-shadow: 0 10px 30px rgba(0,0,0,.2); } .modal-header { padding: .75rem 1rem; border-bottom: 1px solid var(--border-color); }