From 1dc84023d05ca14ed9a9004becb4f4752ed9ee3f Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Fri, 17 Jul 2026 09:29:34 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20f=C3=BCge=20Unterst=C3=BCtzung=20f?= =?UTF-8?q?=C3=BCr=20importierte=20Spielplan-Teams=20hinzu=20und=20verbess?= =?UTF-8?q?ere=20Teamfilter-Logik?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../harheimertc/ui/screens/cms/CmsScreens.kt | 53 +++++++++++++++++++ pages/mannschaften/[slug].vue | 41 +------------- utils/spielplan-filter.js | 45 ++++++++++++++++ 3 files changed, 100 insertions(+), 39 deletions(-) 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 fbbdc2b..08bde2e 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 @@ -458,6 +458,9 @@ fun CmsSportbetriebScreen(navController: NavController, showBackNavigation: Bool "mannschaften" to "Mannschaften", "spielplaene" to "Spielpläne", ) + val spielplanTeamOptions = remember(state.sportSpielplanHeaders, state.sportSpielplanRows) { + importedSpielplanTeams(state.sportSpielplanHeaders, state.sportSpielplanRows) + } val terminKategorien = listOf("Training", "Punktspiel", "Turnier", "Veranstaltung", "Sonstiges") LaunchedEffect(Unit) { @@ -606,6 +609,7 @@ fun CmsSportbetriebScreen(navController: NavController, showBackNavigation: Bool items(mannschaften.size) { index -> MannschaftEditorCard( row = mannschaften[index], + spielplanTeams = spielplanTeamOptions, onChange = { updated -> mannschaften[index] = updated }, onRemove = { mannschaften.removeAt(index) }, ) @@ -742,12 +746,32 @@ fun CmsSportbetriebScreen(navController: NavController, showBackNavigation: Bool @Composable private fun MannschaftEditorCard( row: CmsMannschaftRow, + spielplanTeams: List, onChange: (CmsMannschaftRow) -> Unit, onRemove: () -> Unit, ) { + var teamPickerOpen by remember(row.mannschaft, spielplanTeams) { mutableStateOf(false) } 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()) + if (spielplanTeams.isNotEmpty()) { + Box(modifier = Modifier.fillMaxWidth()) { + OutlinedButton(onClick = { teamPickerOpen = true }, modifier = Modifier.fillMaxWidth()) { + Text("Aus importierten Teams auswählen") + } + DropdownMenu(expanded = teamPickerOpen, onDismissRequest = { teamPickerOpen = false }) { + spielplanTeams.forEach { team -> + DropdownMenuItem( + text = { Text(team.label) }, + onClick = { + onChange(row.copy(spielplanMannschaftId = team.id)) + teamPickerOpen = false + }, + ) + } + } + } + } 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()) @@ -763,6 +787,35 @@ private fun MannschaftEditorCard( } } +private data class ImportedSpielplanTeam(val id: String, val label: String) + +private fun importedSpielplanTeams(headers: List, rows: List>): List { + fun index(name: String) = headers.indexOf(name) + fun value(row: List, column: String): String { + val columnIndex = index(column) + return row.getOrElse(columnIndex) { "" }.trim() + } + + val teams = linkedMapOf() + listOf("Heim", "Gast").forEach { side -> + rows.forEach { row -> + val isHarheimer = value(row, "${side}VereinNr") == "43030" || value(row, "${side}VereinName") == "Harheimer TC" + val id = value(row, "${side}MannschaftId") + if (!isHarheimer || id.isBlank()) return@forEach + + val teamName = value(row, "${side}Mannschaft") + val ageClass = value(row, "${side}MannschaftAltersklasse") + val teamNumber = value(row, "${side}MannschaftNr") + val label = listOf(teamName, ageClass, teamNumber.takeIf { it.isNotBlank() }?.let { "Nr. $it" }) + .filterNotNull() + .filter { it.isNotBlank() } + .joinToString(" · ") + teams.putIfAbsent(id, ImportedSpielplanTeam(id, "$label [$id]")) + } + } + return teams.values.sortedBy { it.label } +} + private fun sportSpielplanCsvText(headers: List, rows: List>): String { if (headers.isEmpty()) return "" return listOf(headers).plus(rows).joinToString("\n") { row -> row.joinToString(";") { it.csvCell(";") } } diff --git a/pages/mannschaften/[slug].vue b/pages/mannschaften/[slug].vue index 7955601..370df43 100755 --- a/pages/mannschaften/[slug].vue +++ b/pages/mannschaften/[slug].vue @@ -362,6 +362,7 @@