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
+
+
+
+
+
+
+
+ | Mitglied |
+ Konkurrenz |
+ Startzeit |
+ Angemeldet |
+ Teilgenommen |
+ Platzierung |
+
+
+
+
+
+
+ | {{ group.memberName }} |
+ {{ item.competitionName }} |
+ {{ item.start }} |
+ {{ item.registered ? 'Ja' : 'Nein' }} |
+ {{ item.participated ? 'Ja' : 'Nein' }} |
+ {{ item.placement || '–' }} |
+
+
+
+
+ | 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); }