diff --git a/android-app/app/src/main/java/de/harheimertc/repositories/CmsRepository.kt b/android-app/app/src/main/java/de/harheimertc/repositories/CmsRepository.kt index c6b06fe..8e69d5b 100755 --- a/android-app/app/src/main/java/de/harheimertc/repositories/CmsRepository.kt +++ b/android-app/app/src/main/java/de/harheimertc/repositories/CmsRepository.kt @@ -125,6 +125,7 @@ class CmsRepository @Inject constructor( spieler = values[7], informationenLink = values[8], letzteAktualisierung = values[9], + spielplanMannschaftId = values.getOrElse(10) { "" }, ) } } @@ -381,6 +382,7 @@ data class CmsMannschaftRow( val spieler: String = "", val informationenLink: String = "", val letzteAktualisierung: String = "", + val spielplanMannschaftId: String = "", ) private fun List.toMannschaftenCsv(): String { @@ -395,6 +397,7 @@ private fun List.toMannschaftenCsv(): String { "Spieler", "Weitere Informationen Link", "Letzte Aktualisierung", + "Spielplan Mannschaft ID", ).toCsvRow() val rows = map { row -> listOf( @@ -408,6 +411,7 @@ private fun List.toMannschaftenCsv(): String { row.spieler, row.informationenLink, row.letzteAktualisierung, + row.spielplanMannschaftId, ).toCsvRow() } return listOf(header).plus(rows).joinToString("\n") diff --git a/android-app/app/src/main/java/de/harheimertc/ui/screens/cms/CmsScreens.kt b/android-app/app/src/main/java/de/harheimertc/ui/screens/cms/CmsScreens.kt index ddb677c..c424441 100755 --- a/android-app/app/src/main/java/de/harheimertc/ui/screens/cms/CmsScreens.kt +++ b/android-app/app/src/main/java/de/harheimertc/ui/screens/cms/CmsScreens.kt @@ -737,6 +737,7 @@ private fun MannschaftEditorCard( ) { DataCard(row.mannschaft.ifBlank { "Mannschaft" }) { OutlinedTextField(value = row.mannschaft, onValueChange = { onChange(row.copy(mannschaft = it)) }, label = { Text("Mannschaft") }, modifier = Modifier.fillMaxWidth()) + OutlinedTextField(value = row.spielplanMannschaftId, onValueChange = { onChange(row.copy(spielplanMannschaftId = it)) }, label = { Text("Spielplan-Team-ID") }, supportingText = { Text("Eindeutige myTischtennis-Team-ID") }, modifier = Modifier.fillMaxWidth()) OutlinedTextField(value = row.liga, onValueChange = { onChange(row.copy(liga = it)) }, label = { Text("Liga") }, modifier = Modifier.fillMaxWidth()) OutlinedTextField(value = row.staffelleiter, onValueChange = { onChange(row.copy(staffelleiter = it)) }, label = { Text("Staffelleiter") }, modifier = Modifier.fillMaxWidth()) OutlinedTextField(value = row.telefon, onValueChange = { onChange(row.copy(telefon = it)) }, label = { Text("Telefon") }, modifier = Modifier.fillMaxWidth()) diff --git a/components/cms/CmsMannschaften.vue b/components/cms/CmsMannschaften.vue index aa292df..7d92120 100755 --- a/components/cms/CmsMannschaften.vue +++ b/components/cms/CmsMannschaften.vue @@ -202,6 +202,17 @@ :disabled="isSaving" > +
+ + +

Verknüpft diese Mannschaft eindeutig mit dem importierten Spielplan.

+
{ for (let i = 0; i < line.length; i++) { const char = line[i]; if (char === '"') { inQuotes = !inQuotes } else if (char === ',' && !inQuotes) { values.push(current.trim()); current = '' } else { current += char } } values.push(current.trim()) if (values.length < 10) return null - return { mannschaft: values[0]?.trim() || '', liga: values[1]?.trim() || '', staffelleiter: values[2]?.trim() || '', telefon: values[3]?.trim() || '', heimspieltag: values[4]?.trim() || '', spielsystem: values[5]?.trim() || '', mannschaftsfuehrer: values[6]?.trim() || '', spieler: values[7]?.trim() || '', weitere_informationen_link: values[8]?.trim() || '', letzte_aktualisierung: values[9]?.trim() || '' } + return { mannschaft: values[0]?.trim() || '', liga: values[1]?.trim() || '', staffelleiter: values[2]?.trim() || '', telefon: values[3]?.trim() || '', heimspieltag: values[4]?.trim() || '', spielsystem: values[5]?.trim() || '', mannschaftsfuehrer: values[6]?.trim() || '', spieler: values[7]?.trim() || '', weitere_informationen_link: values[8]?.trim() || '', letzte_aktualisierung: values[9]?.trim() || '', spielplan_mannschaft_id: values[10]?.trim() || '' } }).filter(m => m !== null && m.mannschaft !== '') } catch (error) { console.error('Fehler beim Laden:', error); errorMessage.value = 'Fehler beim Laden der Mannschaften'; throw error } finally { isLoading.value = false } } @@ -508,10 +519,10 @@ const onSeasonChange = async () => { } const getSpielerListe = (m) => { if (!m.spieler) return []; return m.spieler.split(';').map(s => s.trim()).filter(s => s !== '') } -const openAddModal = () => { formData.value = { mannschaft: '', liga: '', staffelleiter: '', telefon: '', heimspieltag: '', spielsystem: '', mannschaftsfuehrer: '', spielerListe: [], weitere_informationen_link: '', letzte_aktualisierung: nowIsoDate() }; showModal.value = true; errorMessage.value = ''; isEditing.value = false; editingIndex.value = -1; resetSpielerDraftState() } +const openAddModal = () => { formData.value = { mannschaft: '', spielplan_mannschaft_id: '', liga: '', staffelleiter: '', telefon: '', heimspieltag: '', spielsystem: '', mannschaftsfuehrer: '', spielerListe: [], weitere_informationen_link: '', letzte_aktualisierung: nowIsoDate() }; showModal.value = true; errorMessage.value = ''; isEditing.value = false; editingIndex.value = -1; resetSpielerDraftState() } const closeModal = () => { showModal.value = false; errorMessage.value = ''; isEditing.value = false; editingIndex.value = -1; resetSpielerDraftState() } const openEditModal = (mannschaft, index) => { - formData.value = { mannschaft: mannschaft.mannschaft || '', liga: mannschaft.liga || '', staffelleiter: mannschaft.staffelleiter || '', telefon: mannschaft.telefon || '', heimspieltag: mannschaft.heimspieltag || '', spielsystem: mannschaft.spielsystem || '', mannschaftsfuehrer: mannschaft.mannschaftsfuehrer || '', spielerListe: parseSpielerString(mannschaft.spieler || ''), weitere_informationen_link: mannschaft.weitere_informationen_link || '', letzte_aktualisierung: mannschaft.letzte_aktualisierung || nowIsoDate() } + formData.value = { mannschaft: mannschaft.mannschaft || '', spielplan_mannschaft_id: mannschaft.spielplan_mannschaft_id || '', liga: mannschaft.liga || '', staffelleiter: mannschaft.staffelleiter || '', telefon: mannschaft.telefon || '', heimspieltag: mannschaft.heimspieltag || '', spielsystem: mannschaft.spielsystem || '', mannschaftsfuehrer: mannschaft.mannschaftsfuehrer || '', spielerListe: parseSpielerString(mannschaft.spieler || ''), weitere_informationen_link: mannschaft.weitere_informationen_link || '', letzte_aktualisierung: mannschaft.letzte_aktualisierung || nowIsoDate() } isEditing.value = true; editingIndex.value = index; showModal.value = true; errorMessage.value = ''; resetSpielerDraftState() const currentTeam = (formData.value.mannschaft || '').trim() for (const s of formData.value.spielerListe) { @@ -569,7 +580,7 @@ const saveMannschaft = async () => { try { if (!applySelectedSpielerTransfers()) return const spielerString = serializeSpielerList(formData.value.spielerListe) - const updated = { mannschaft: formData.value.mannschaft || '', liga: formData.value.liga || '', staffelleiter: formData.value.staffelleiter || '', telefon: formData.value.telefon || '', heimspieltag: formData.value.heimspieltag || '', spielsystem: formData.value.spielsystem || '', mannschaftsfuehrer: formData.value.mannschaftsfuehrer || '', spieler: spielerString, weitere_informationen_link: formData.value.weitere_informationen_link || '', letzte_aktualisierung: formData.value.letzte_aktualisierung || nowIsoDate() } + const updated = { mannschaft: formData.value.mannschaft || '', spielplan_mannschaft_id: formData.value.spielplan_mannschaft_id || '', liga: formData.value.liga || '', staffelleiter: formData.value.staffelleiter || '', telefon: formData.value.telefon || '', heimspieltag: formData.value.heimspieltag || '', spielsystem: formData.value.spielsystem || '', mannschaftsfuehrer: formData.value.mannschaftsfuehrer || '', spieler: spielerString, weitere_informationen_link: formData.value.weitere_informationen_link || '', letzte_aktualisierung: formData.value.letzte_aktualisierung || nowIsoDate() } if (isEditing.value && editingIndex.value >= 0) { mannschaften.value[editingIndex.value] = { ...updated } } else { mannschaften.value.push({ ...updated }) } const touchedTeamIndexes = Object.keys(pendingSpielerNamesByTeamIndex.value) if (touchedTeamIndexes.length > 0) { const ts = nowIsoDate(); for (const idxStr of touchedTeamIndexes) { const idx = Number(idxStr); if (!Number.isFinite(idx)) continue; const existing = mannschaften.value[idx]; if (!existing) continue; mannschaften.value[idx] = { ...existing, spieler: serializeSpielerNames(pendingSpielerNamesByTeamIndex.value[idx]), letzte_aktualisierung: ts } } } @@ -579,10 +590,10 @@ const saveMannschaft = async () => { } const saveCSV = async () => { - const header = 'Mannschaft,Liga,Staffelleiter,Telefon,Heimspieltag,Spielsystem,Mannschaftsführer,Spieler,Weitere Informationen Link,Letzte Aktualisierung' + const header = 'Mannschaft,Liga,Staffelleiter,Telefon,Heimspieltag,Spielsystem,Mannschaftsführer,Spieler,Weitere Informationen Link,Letzte Aktualisierung,Spielplan Mannschaft ID' const rows = mannschaften.value.map(m => { const esc = (v) => { if (!v) return ''; const s = String(v); if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s.replace(/"/g, '""')}"`; return s } - return [esc(m.mannschaft), esc(m.liga), esc(m.staffelleiter), esc(m.telefon), esc(m.heimspieltag), esc(m.spielsystem), esc(m.mannschaftsfuehrer), esc(m.spieler), esc(m.weitere_informationen_link), esc(m.letzte_aktualisierung)].join(',') + return [esc(m.mannschaft), esc(m.liga), esc(m.staffelleiter), esc(m.telefon), esc(m.heimspieltag), esc(m.spielsystem), esc(m.mannschaftsfuehrer), esc(m.spieler), esc(m.weitere_informationen_link), esc(m.letzte_aktualisierung), esc(m.spielplan_mannschaft_id)].join(',') }) await $fetch('/api/cms/save-csv', { method: 'POST', @@ -639,10 +650,10 @@ const createNextSeason = async () => { isCreatingSeason.value = true errorMessage.value = '' try { - const header = 'Mannschaft,Liga,Staffelleiter,Telefon,Heimspieltag,Spielsystem,Mannschaftsführer,Spieler,Weitere Informationen Link,Letzte Aktualisierung' + const header = 'Mannschaft,Liga,Staffelleiter,Telefon,Heimspieltag,Spielsystem,Mannschaftsführer,Spieler,Weitere Informationen Link,Letzte Aktualisierung,Spielplan Mannschaft ID' const rows = mannschaften.value.map(m => { const esc = (v) => { if (!v) return ''; const s = String(v); if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s.replace(/"/g, '""')}"`; return s } - return [esc(m.mannschaft), esc(m.liga), esc(m.staffelleiter), esc(m.telefon), esc(m.heimspieltag), esc(m.spielsystem), esc(m.mannschaftsfuehrer), esc(m.spieler), esc(m.weitere_informationen_link), esc(m.letzte_aktualisierung || nowIsoDate())].join(',') + return [esc(m.mannschaft), esc(m.liga), esc(m.staffelleiter), esc(m.telefon), esc(m.heimspieltag), esc(m.spielsystem), esc(m.mannschaftsfuehrer), esc(m.spieler), esc(m.weitere_informationen_link), esc(m.letzte_aktualisierung || nowIsoDate()), esc(m.spielplan_mannschaft_id)].join(',') }) await $fetch('/api/cms/save-csv', { diff --git a/package.json b/package.json index b14621e..09a469e 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harheimertc-website", - "version": "1.8.2", + "version": "1.8.3", "description": "Moderne Webseite für den Harheimer Tischtennis Club", "private": true, "type": "module", diff --git a/pages/mannschaften/[slug].vue b/pages/mannschaften/[slug].vue index 1a3ae6a..7a2949a 100755 --- a/pages/mannschaften/[slug].vue +++ b/pages/mannschaften/[slug].vue @@ -473,6 +473,7 @@ const loadMannschaften = async () => { spieler: values[7].trim(), weitere_informationen_link: values[8].trim(), letzte_aktualisierung: values[9].trim(), + spielplan_mannschaft_id: values[10] ? values[10].trim() : '', slug: values[0].trim().toLowerCase().replace(/\s+/g, '-') } }).filter(mannschaft => mannschaft !== null) @@ -557,6 +558,12 @@ const isExactHarheimTeam = (teamName, variant) => { } const isSpielForMannschaft = (row, cmsMannschaft) => { + const configuredTeamId = String(mannschaft.value?.spielplan_mannschaft_id || '').trim() + if (configuredTeamId) { + return String(row.HeimMannschaftId || '').trim() === configuredTeamId || + String(row.GastMannschaftId || '').trim() === configuredTeamId + } + const variants = getTeamVariants(cmsMannschaft) if (!variants.length) return false diff --git a/server/utils/spielplan-data.js b/server/utils/spielplan-data.js index c680ba8..8c99b83 100755 --- a/server/utils/spielplan-data.js +++ b/server/utils/spielplan-data.js @@ -25,12 +25,14 @@ const SPIELPLAN_HEADERS = [ 'HeimVereinName', 'HeimMannschaftAltersklasse', 'HeimMannschaftNr', + 'HeimMannschaftId', 'HeimMannschaft', 'GastVereinVerband', 'GastVereinNr', 'GastVereinName', 'GastMannschaftAltersklasse', 'GastMannschaftNr', + 'GastMannschaftId', 'GastMannschaft', 'SpieleHeim', 'SpieleGast' @@ -250,12 +252,14 @@ export function convertImportedSpielplanToJson(imported) { HeimVereinName: homeIsHarheim ? CLUB_NAME : '', HeimMannschaftAltersklasse: inferAgeClass(match, match.teamHome), HeimMannschaftNr: inferTeamNumber(match.teamHome), + HeimMannschaftId: match.teamHomeId || '', HeimMannschaft: match.teamHome || '', GastVereinVerband: match.leagueOrgShortName || imported.source?.association || '', GastVereinNr: match.teamAwayClubId || '', GastVereinName: awayIsHarheim ? CLUB_NAME : '', GastMannschaftAltersklasse: inferAgeClass(match, match.teamAway), GastMannschaftNr: inferTeamNumber(match.teamAway), + GastMannschaftId: match.teamAwayId || '', GastMannschaft: match.teamAway || '', SpieleHeim: result.home, SpieleGast: result.away