refactor(desktop): V2-Suffixe entfernt und VeranstaltungKomponenten modularisiert

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
Stefan Mogeritsch 2026-04-17 11:40:06 +02:00
parent 0128f98164
commit 3949ab21db
8 changed files with 696 additions and 512 deletions

View File

@ -0,0 +1,49 @@
# Session Journal: 2026-04-17 - Aufräumarbeiten & Konsolidierung
## 🎯 Ziele der Session
1. **V2-Cleanup:** Entfernung aller `V2`-Suffixe aus dem Codebase (Modelle, Stores, Wizards), um eine konsolidierte "
Source of Truth" zu schaffen.
2. **Refactoring:** Zerlegung der massiven `VeranstaltungKonfig`-Komponente in wartbare Teil-Module.
3. **Duplikat-Entfernung:** Zentralisierung von UI-Logik (DatePicker, Validierung) zur Reduzierung von Code-Duplikaten.
## 🛠️ Durchgeführte Änderungen
### 🧹 1. Konsolidierung der Benamung (V2-Entfernung)
* **Änderungen:**
* `VeranstaltungKonfigV2` -> `VeranstaltungKonfig`
* `VeranstaltungV2` -> `Veranstaltung`
* `TurnierV2` -> `Turnier`
* `StoreV2` -> `Store`
* `TurnierStoreV2` -> `TurnierStore`
* `TurnierWizardV2` -> `TurnierWizard`
* **Grund:** Umsetzung der Vereinbarung, nur noch eine "echte" Version zu pflegen und Altlasten aus Migrationsphasen zu
entfernen. Alle Referenzen im gesamten Projekt (`DesktopMainLayout.kt`, `ManagementScreens.kt`, `main.kt`) wurden
erfolgreich aktualisiert.
### 🏗️ 2. Refactoring `VeranstaltungScreens.kt`
* **Extraktion:** Die Wizard-Schritte wurden in eigenständige Composable-Funktionen ausgelagert:
* `Step1Veranstalter`: Auswahl aus ZNS/Lokal-Bestand.
* `Step2Basisdaten`: Titel, Zeitraum, Ort, Disziplinen.
* `Step3Details`: Logo, Sponsoren, Bewerbs-Management.
* **Zentralisierung:**
* Neue Komponente `AppDatePickerDialog` zur Vermeidung von dreifach redundantem Dialog-Code.
* Konsolidierte Validierungslogik für den Veranstaltungszeitraum.
### 🏷️ 3. Fehlerbehebung & Qualitätssicherung
* **Syntax-Fix:** Korrektur von Klammerfehlern, die während des Refactorings in der großen `VeranstaltungScreens.kt`
entstanden sind.
* **Linting:** Erfolgreiche Validierung der Dateien `VeranstaltungScreens.kt`, `Stores.kt` und `DesktopMainLayout.kt`.
## ✅ Ergebnis & Status
* Der Code ist nun wesentlich modularer und besser lesbar.
* Die Benamung ist konsistent ohne verwirrende Versions-Suffixe.
* Redundante Logik-Blöcke (besonders beim Datum-Handling) wurden eliminiert.
---
**🏗️ [Lead Architect]** & **🧹 [Curator]**
Datum: 17. April 2026 | Status: Abgeschlossen

View File

@ -50,11 +50,22 @@
* Anzeige der letzten Sync-Version (z.B. `ZNS: V12` oder `ZNS: Kein Sync`). * Anzeige der letzten Sync-Version (z.B. `ZNS: V12` oder `ZNS: Kein Sync`).
* Farbliche Kennzeichnung (Grün/Gelb/Rot) je nach Synchronisationsstand. * Farbliche Kennzeichnung (Grün/Gelb/Rot) je nach Synchronisationsstand.
### 🏗️ 5. Fachlich: Disziplinen & Bewerbe (Schritt 2 & 3)
* **Datei:** `frontend/shells/meldestelle-desktop/src/jvmMain/kotlin/at/mocode/desktop/v2/VeranstaltungScreens.kt`
* **Änderung:**
* **Schritt 2:** Felder für PLZ und Disziplin-Auswahl (Springen, Dressur, etc.) hinzugefügt.
* **Schritt 3:** Komplettes Bewerbs-Management implementiert. User können Prüfungsnummern, Klassen und Bezeichnungen
erfassen.
* **Validierung:** Der Wizard lässt sich erst finalisieren, wenn mindestens ein Bewerb angelegt wurde.
* **Grund:** Vorbereitung der Datenstruktur für den OEPS-Export und Verbesserung der fachlichen Abdeckung im Wizard.
## ✅ Ergebnis & Status ## ✅ Ergebnis & Status
* Das Consul-Dashboard sollte nun einen stabilen "Grün"-Status für den `masterdata-service` anzeigen. * Das Consul-Dashboard sollte nun einen stabilen "Grün"-Status für den `masterdata-service` anzeigen.
* Der Desktop-Wizard leitet den User fachlich korrekt durch die Turnier-Anlage. * Der Desktop-Wizard leitet den User fachlich korrekt durch die Turnier-Anlage.
* Der User hat jederzeit volle Transparenz über den Stand seiner lokalen ZNS-Daten. * Der User hat jederzeit volle Transparenz über den Stand seiner lokalen ZNS-Daten.
* Die Erfassung von Bewerben legt den Grundstein für die spätere Nennungs- und Ergebnisverwaltung.
--- ---
**🏗️ [Lead Architect]** & **🧹 [Curator]** **🏗️ [Lead Architect]** & **🧹 [Curator]**

View File

@ -12,13 +12,13 @@ import at.mocode.frontend.core.localdb.localDbModule
import at.mocode.frontend.core.network.networkModule import at.mocode.frontend.core.network.networkModule
import at.mocode.frontend.core.sync.di.syncModule import at.mocode.frontend.core.sync.di.syncModule
import at.mocode.frontend.features.billing.di.billingModule import at.mocode.frontend.features.billing.di.billingModule
import at.mocode.frontend.features.profile.di.profileModule
import at.mocode.frontend.features.verein.di.vereinFeatureModule
import at.mocode.frontend.features.nennung.di.nennungFeatureModule import at.mocode.frontend.features.nennung.di.nennungFeatureModule
import at.mocode.frontend.features.pferde.di.pferdeModule import at.mocode.frontend.features.pferde.di.pferdeModule
import at.mocode.frontend.features.profile.di.profileModule
import at.mocode.frontend.features.reiter.di.reiterModule import at.mocode.frontend.features.reiter.di.reiterModule
import at.mocode.turnier.feature.di.turnierFeatureModule import at.mocode.frontend.features.verein.di.vereinFeatureModule
import at.mocode.ping.feature.di.pingFeatureModule import at.mocode.ping.feature.di.pingFeatureModule
import at.mocode.turnier.feature.di.turnierFeatureModule
import at.mocode.zns.feature.di.znsImportModule import at.mocode.zns.feature.di.znsImportModule
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.koin.core.context.GlobalContext import org.koin.core.context.GlobalContext
@ -48,7 +48,7 @@ fun main() = application {
} }
println("[DesktopApp] KOIN initialisiert") println("[DesktopApp] KOIN initialisiert")
// Testdaten für Prototyp laden // Testdaten für Prototyp laden
at.mocode.desktop.v2.StoreV2.seed() at.mocode.desktop.v2.Store.seed()
} catch (e: Exception) { } catch (e: Exception) {
println("[DesktopApp] Koin-Warnung: ${e.message}") println("[DesktopApp] Koin-Warnung: ${e.message}")
} }

View File

@ -660,7 +660,7 @@ private fun DesktopContentArea(
is AppScreen.VeranstaltungKonfig -> { is AppScreen.VeranstaltungKonfig -> {
val vId = currentScreen.veranstalterId val vId = currentScreen.veranstalterId
// Falls vId == 0, kommen wir aus der Gesamtübersicht und wählen erst im Wizard // Falls vId == 0, kommen wir aus der Gesamtübersicht und wählen erst im Wizard
at.mocode.desktop.v2.VeranstaltungKonfigV2( at.mocode.desktop.v2.VeranstaltungKonfig(
veranstalterId = vId, veranstalterId = vId,
onBack = onBack, onBack = onBack,
onSaved = { evtId, finalVId -> onNavigate(AppScreen.VeranstaltungProfil(finalVId, evtId)) }, onSaved = { evtId, finalVId -> onNavigate(AppScreen.VeranstaltungProfil(finalVId, evtId)) },
@ -671,12 +671,12 @@ private fun DesktopContentArea(
is AppScreen.VeranstaltungProfil -> { is AppScreen.VeranstaltungProfil -> {
val vId = currentScreen.veranstalterId val vId = currentScreen.veranstalterId
val evtId = currentScreen.veranstaltungId val evtId = currentScreen.veranstaltungId
if (at.mocode.desktop.v2.StoreV2.vereine.none { it.id == vId }) { if (at.mocode.desktop.v2.Store.vereine.none { it.id == vId }) {
InvalidContextNotice( InvalidContextNotice(
message = "Veranstalter (ID=$vId) nicht gefunden.", message = "Veranstalter (ID=$vId) nicht gefunden.",
onBack = onBack onBack = onBack
) )
} else if (at.mocode.desktop.v2.StoreV2.eventsFor(vId).none { it.id == evtId }) { } else if (at.mocode.desktop.v2.Store.eventsFor(vId).none { it.id == evtId }) {
InvalidContextNotice( InvalidContextNotice(
message = "Veranstaltung (ID=$evtId) gehört nicht zu Veranstalter #$vId.", message = "Veranstaltung (ID=$evtId) gehört nicht zu Veranstalter #$vId.",
onBack = onBack onBack = onBack
@ -687,17 +687,17 @@ private fun DesktopContentArea(
veranstaltungId = evtId, veranstaltungId = evtId,
onBack = onBack, onBack = onBack,
onTurnierNeu = { onTurnierNeu = {
val veranstaltung = at.mocode.desktop.v2.StoreV2.eventsFor(vId).firstOrNull { it.id == evtId } val veranstaltung = at.mocode.desktop.v2.Store.eventsFor(vId).firstOrNull { it.id == evtId }
val list = at.mocode.desktop.v2.TurnierStoreV2.list(evtId) val list = at.mocode.desktop.v2.TurnierStore.list(evtId)
val newId = (list.maxOfOrNull { it.id } ?: 0L) + 1L val newId = (list.maxOfOrNull { it.id } ?: 0L) + 1L
val draft = at.mocode.desktop.v2.TurnierV2( val draft = at.mocode.desktop.v2.Turnier(
id = newId, id = newId,
veranstaltungId = evtId, veranstaltungId = evtId,
turnierNr = 0, turnierNr = 0,
datumVon = veranstaltung?.datumVon ?: "", datumVon = veranstaltung?.datumVon ?: "",
datumBis = veranstaltung?.datumBis, datumBis = veranstaltung?.datumBis,
) )
at.mocode.desktop.v2.TurnierStoreV2.add(evtId, draft) at.mocode.desktop.v2.TurnierStore.add(evtId, draft)
onNavigate(AppScreen.TurnierDetail(evtId, newId)) onNavigate(AppScreen.TurnierDetail(evtId, newId))
}, },
onTurnierOpen = { tId -> onNavigate(AppScreen.TurnierDetail(evtId, tId)) }, onTurnierOpen = { tId -> onNavigate(AppScreen.TurnierDetail(evtId, tId)) },
@ -711,21 +711,21 @@ private fun DesktopContentArea(
veranstaltungId = currentScreen.id, veranstaltungId = currentScreen.id,
onBack = onBack, onBack = onBack,
onTurnierNeu = { onTurnierNeu = {
val v = at.mocode.desktop.v2.StoreV2.vereine.firstOrNull { vv -> val v = at.mocode.desktop.v2.Store.vereine.firstOrNull { vv ->
at.mocode.desktop.v2.StoreV2.eventsFor(vv.id).any { it.id == currentScreen.id } at.mocode.desktop.v2.Store.eventsFor(vv.id).any { it.id == currentScreen.id }
} }
val veranstaltung = val veranstaltung =
v?.let { at.mocode.desktop.v2.StoreV2.eventsFor(it.id).firstOrNull { e -> e.id == currentScreen.id } } v?.let { at.mocode.desktop.v2.Store.eventsFor(it.id).firstOrNull { e -> e.id == currentScreen.id } }
val list = at.mocode.desktop.v2.TurnierStoreV2.list(currentScreen.id) val list = at.mocode.desktop.v2.TurnierStore.list(currentScreen.id)
val newId = (list.maxOfOrNull { it.id } ?: 0L) + 1L val newId = (list.maxOfOrNull { it.id } ?: 0L) + 1L
val draft = at.mocode.desktop.v2.TurnierV2( val draft = at.mocode.desktop.v2.Turnier(
id = newId, id = newId,
veranstaltungId = currentScreen.id, veranstaltungId = currentScreen.id,
turnierNr = 0, turnierNr = 0,
datumVon = veranstaltung?.datumVon ?: "", datumVon = veranstaltung?.datumVon ?: "",
datumBis = veranstaltung?.datumBis, datumBis = veranstaltung?.datumBis,
) )
at.mocode.desktop.v2.TurnierStoreV2.add(currentScreen.id, draft) at.mocode.desktop.v2.TurnierStore.add(currentScreen.id, draft)
onNavigate(AppScreen.TurnierDetail(currentScreen.id, newId)) onNavigate(AppScreen.TurnierDetail(currentScreen.id, newId))
}, },
onTurnierOeffnen = { tid -> onNavigate(AppScreen.TurnierDetail(currentScreen.id, tid)) }, onTurnierOeffnen = { tid -> onNavigate(AppScreen.TurnierDetail(currentScreen.id, tid)) },
@ -739,8 +739,8 @@ private fun DesktopContentArea(
// Turnier-Screens // Turnier-Screens
is AppScreen.TurnierDetail -> { is AppScreen.TurnierDetail -> {
val evtId = currentScreen.veranstaltungId val evtId = currentScreen.veranstaltungId
val parent = at.mocode.desktop.v2.StoreV2.vereine.firstOrNull { v -> val parent = at.mocode.desktop.v2.Store.vereine.firstOrNull { v ->
at.mocode.desktop.v2.StoreV2.eventsFor(v.id).any { it.id == evtId } at.mocode.desktop.v2.Store.eventsFor(v.id).any { it.id == evtId }
} }
if (parent == null) { if (parent == null) {
InvalidContextNotice( InvalidContextNotice(
@ -748,7 +748,7 @@ private fun DesktopContentArea(
onBack = onBack onBack = onBack
) )
} else { } else {
val veranstaltung = at.mocode.desktop.v2.StoreV2.eventsFor(parent.id).firstOrNull { it.id == evtId } val veranstaltung = at.mocode.desktop.v2.Store.eventsFor(parent.id).firstOrNull { it.id == evtId }
val blCode = parent.oepsNummer.split("-").getOrNull(1) ?: "" val blCode = parent.oepsNummer.split("-").getOrNull(1) ?: ""
val bundesland = mapOepsToBundesland(blCode) val bundesland = mapOepsToBundesland(blCode)
TurnierDetailScreen( TurnierDetailScreen(
@ -769,8 +769,8 @@ private fun DesktopContentArea(
is AppScreen.TurnierNeu -> { is AppScreen.TurnierNeu -> {
val evtId = currentScreen.veranstaltungId val evtId = currentScreen.veranstaltungId
// V2: Wir erlauben Turnier-Nr nur, wenn die Veranstaltung im V2-Store existiert // V2: Wir erlauben Turnier-Nr nur, wenn die Veranstaltung im V2-Store existiert
val parent = at.mocode.desktop.v2.StoreV2.vereine.firstOrNull { v -> val parent = at.mocode.desktop.v2.Store.vereine.firstOrNull { v ->
at.mocode.desktop.v2.StoreV2.eventsFor(v.id).any { it.id == evtId } at.mocode.desktop.v2.Store.eventsFor(v.id).any { it.id == evtId }
} }
if (parent == null) { if (parent == null) {
InvalidContextNotice( InvalidContextNotice(
@ -778,7 +778,7 @@ private fun DesktopContentArea(
onBack = onBack onBack = onBack
) )
} else { } else {
at.mocode.desktop.v2.TurnierWizardV2( at.mocode.desktop.v2.TurnierWizard(
veranstalterId = parent.id, veranstalterId = parent.id,
veranstaltungId = evtId, veranstaltungId = evtId,
onBack = onBack, onBack = onBack,

View File

@ -6,9 +6,11 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -140,7 +142,7 @@ data class TableColumn<T>(
@Composable @Composable
fun PferdeVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) { fun PferdeVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
val pferde = StoreV2.pferde val pferde = Store.pferde
var filter by remember { mutableStateOf("") } var filter by remember { mutableStateOf("") }
val filteredItems = if (filter.isEmpty()) pferde else pferde.filter { val filteredItems = if (filter.isEmpty()) pferde else pferde.filter {
it.name.contains(filter, ignoreCase = true) || it.feiId?.contains(filter, ignoreCase = true) == true it.name.contains(filter, ignoreCase = true) || it.feiId?.contains(filter, ignoreCase = true) == true
@ -162,14 +164,14 @@ fun PferdeVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
onBack = onBack, onBack = onBack,
onNew = { /* CRUD Logik */ }, onNew = { /* CRUD Logik */ },
onEdit = { onEdit(it.id) }, onEdit = { onEdit(it.id) },
onDelete = { StoreV2.pferde.remove(it) }, onDelete = { Store.pferde.remove(it) },
onSearch = { filter = it } onSearch = { filter = it }
) )
} }
@Composable @Composable
fun ReiterVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) { fun ReiterVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
val reiter = StoreV2.reiter val reiter = Store.reiter
var filter by remember { mutableStateOf("") } var filter by remember { mutableStateOf("") }
val filteredItems = if (filter.isEmpty()) reiter else reiter.filter { val filteredItems = if (filter.isEmpty()) reiter else reiter.filter {
it.vorname.contains(filter, ignoreCase = true) || it.nachname.contains( it.vorname.contains(filter, ignoreCase = true) || it.nachname.contains(
@ -192,14 +194,14 @@ fun ReiterVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
onBack = onBack, onBack = onBack,
onNew = { }, onNew = { },
onEdit = { onEdit(it.id) }, onEdit = { onEdit(it.id) },
onDelete = { StoreV2.reiter.remove(it) }, onDelete = { Store.reiter.remove(it) },
onSearch = { filter = it } onSearch = { filter = it }
) )
} }
@Composable @Composable
fun VereinVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) { fun VereinVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
val vereine = StoreV2.vereine val vereine = Store.vereine
var filter by remember { mutableStateOf("") } var filter by remember { mutableStateOf("") }
val filteredItems = if (filter.isEmpty()) vereine else vereine.filter { val filteredItems = if (filter.isEmpty()) vereine else vereine.filter {
it.name.contains(filter, ignoreCase = true) || it.oepsNummer.contains(filter, ignoreCase = true) it.name.contains(filter, ignoreCase = true) || it.oepsNummer.contains(filter, ignoreCase = true)
@ -218,14 +220,14 @@ fun VereinVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
onBack = onBack, onBack = onBack,
onNew = { }, onNew = { },
onEdit = { onEdit(it.id) }, onEdit = { onEdit(it.id) },
onDelete = { StoreV2.vereine.remove(it) }, onDelete = { Store.vereine.remove(it) },
onSearch = { filter = it } onSearch = { filter = it }
) )
} }
@Composable @Composable
fun FunktionaerVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) { fun FunktionaerVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
val funktionaere = StoreV2.funktionaere val funktionaere = Store.funktionaere
var filter by remember { mutableStateOf("") } var filter by remember { mutableStateOf("") }
val filteredItems = if (filter.isEmpty()) funktionaere else funktionaere.filter { val filteredItems = if (filter.isEmpty()) funktionaere else funktionaere.filter {
it.vorname.contains(filter, ignoreCase = true) || it.nachname.contains(filter, ignoreCase = true) it.vorname.contains(filter, ignoreCase = true) || it.nachname.contains(filter, ignoreCase = true)
@ -244,7 +246,7 @@ fun FunktionaerVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
onBack = onBack, onBack = onBack,
onNew = { }, onNew = { },
onEdit = { onEdit(it.id) }, onEdit = { onEdit(it.id) },
onDelete = { StoreV2.funktionaere.remove(it) }, onDelete = { Store.funktionaere.remove(it) },
onSearch = { filter = it } onSearch = { filter = it }
) )
} }
@ -253,7 +255,7 @@ fun FunktionaerVerwaltungScreen(onBack: () -> Unit, onEdit: (Long) -> Unit) {
fun VeranstalterVerwaltungScreen(onBack: () -> Unit, onNew: () -> Unit, onEdit: (Long) -> Unit) { fun VeranstalterVerwaltungScreen(onBack: () -> Unit, onNew: () -> Unit, onEdit: (Long) -> Unit) {
// Veranstalter sind in unserem System eigentlich Vereine, die Veranstaltungen ausrichten // Veranstalter sind in unserem System eigentlich Vereine, die Veranstaltungen ausrichten
// Wir nutzen hier die 'vereine' Liste aus dem Store. // Wir nutzen hier die 'vereine' Liste aus dem Store.
val vereine = StoreV2.vereine val vereine = Store.vereine
var filter by remember { mutableStateOf("") } var filter by remember { mutableStateOf("") }
val filteredItems = if (filter.isEmpty()) vereine else vereine.filter { val filteredItems = if (filter.isEmpty()) vereine else vereine.filter {
it.name.contains(filter, ignoreCase = true) || it.oepsNummer.contains(filter, ignoreCase = true) it.name.contains(filter, ignoreCase = true) || it.oepsNummer.contains(filter, ignoreCase = true)

View File

@ -431,7 +431,7 @@ fun OnboardingScreenPreview() {
@Composable @Composable
fun PferdProfilV2(id: Long, onBack: () -> Unit) { fun PferdProfilV2(id: Long, onBack: () -> Unit) {
DesktopThemeV2 { DesktopThemeV2 {
val pferd = remember(id) { StoreV2.pferde.firstOrNull { it.id == id } } val pferd = remember(id) { Store.pferde.firstOrNull { it.id == id } }
if (pferd == null) { if (pferd == null) {
Text("Pferd nicht gefunden"); return@DesktopThemeV2 Text("Pferd nicht gefunden"); return@DesktopThemeV2
} }
@ -506,7 +506,7 @@ fun PferdProfilV2(id: Long, onBack: () -> Unit) {
@Composable @Composable
fun ReiterProfilV2(id: Long, onBack: () -> Unit) { fun ReiterProfilV2(id: Long, onBack: () -> Unit) {
DesktopThemeV2 { DesktopThemeV2 {
val r = remember(id) { StoreV2.reiter.firstOrNull { it.id == id } } val r = remember(id) { Store.reiter.firstOrNull { it.id == id } }
if (r == null) { if (r == null) {
Text("Reiter nicht gefunden"); return@DesktopThemeV2 Text("Reiter nicht gefunden"); return@DesktopThemeV2
} }
@ -590,7 +590,7 @@ fun ReiterProfilV2(id: Long, onBack: () -> Unit) {
@Composable @Composable
fun VereinProfilV2(id: Long, onBack: () -> Unit) { fun VereinProfilV2(id: Long, onBack: () -> Unit) {
DesktopThemeV2 { DesktopThemeV2 {
val v = remember(id) { StoreV2.vereine.firstOrNull { it.id == id } } val v = remember(id) { Store.vereine.firstOrNull { it.id == id } }
if (v == null) { if (v == null) {
Text("Verein nicht gefunden"); return@DesktopThemeV2 Text("Verein nicht gefunden"); return@DesktopThemeV2
} }
@ -681,7 +681,7 @@ fun VereinProfilV2(id: Long, onBack: () -> Unit) {
@Composable @Composable
fun FunktionaerProfilV2(id: Long, onBack: () -> Unit) { fun FunktionaerProfilV2(id: Long, onBack: () -> Unit) {
DesktopThemeV2 { DesktopThemeV2 {
val f = remember(id) { StoreV2.funktionaere.firstOrNull { it.id == id } } val f = remember(id) { Store.funktionaere.firstOrNull { it.id == id } }
if (f == null) { if (f == null) {
Text("Funktionär nicht gefunden"); return@DesktopThemeV2 Text("Funktionär nicht gefunden"); return@DesktopThemeV2
} }
@ -782,7 +782,7 @@ fun VeranstalterAuswahlV2(
var selectedId by remember { mutableStateOf<Long?>(null) } var selectedId by remember { mutableStateOf<Long?>(null) }
LazyColumn(Modifier.fillMaxSize()) { LazyColumn(Modifier.fillMaxSize()) {
items(StoreV2.vereine) { v -> items(Store.vereine) { v ->
val sel = selectedId == v.id val sel = selectedId == v.id
Card( Card(
modifier = Modifier modifier = Modifier
@ -822,14 +822,14 @@ fun VeranstalterDetailV2(
Icons.AutoMirrored.Filled.ArrowBack, Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Zurück", contentDescription = "Zurück",
modifier = Modifier.clickable { onBack() }) modifier = Modifier.clickable { onBack() })
val verein = StoreV2.vereine.firstOrNull { it.id == veranstalterId } val verein = Store.vereine.firstOrNull { it.id == veranstalterId }
Text(verein?.name ?: "Veranstalter", style = MaterialTheme.typography.titleLarge) Text(verein?.name ?: "Veranstalter", style = MaterialTheme.typography.titleLarge)
Spacer(Modifier.weight(1f)) Spacer(Modifier.weight(1f))
Button(onClick = onNeuVeranstaltung) { Text("+ Neue Veranstaltung") } Button(onClick = onNeuVeranstaltung) { Text("+ Neue Veranstaltung") }
} }
// Veranstalter Vorschau-Karte mit Bearbeiten-Dialog // Veranstalter Vorschau-Karte mit Bearbeiten-Dialog
val verein = remember(veranstalterId) { StoreV2.vereine.firstOrNull { it.id == veranstalterId } } val verein = remember(veranstalterId) { Store.vereine.firstOrNull { it.id == veranstalterId } }
if (verein != null) { if (verein != null) {
var editOpen by remember { mutableStateOf(false) } var editOpen by remember { mutableStateOf(false) }
Card(Modifier.fillMaxWidth()) { Card(Modifier.fillMaxWidth()) {
@ -953,7 +953,7 @@ fun VeranstalterDetailV2(
} }
} }
val events = StoreV2.eventsFor(veranstalterId) val events = Store.eventsFor(veranstalterId)
// Filter-/Suchmaske // Filter-/Suchmaske
var search by remember { mutableStateOf("") } var search by remember { mutableStateOf("") }
OutlinedTextField( OutlinedTextField(
@ -992,7 +992,7 @@ fun VeranstalterDetailV2(
onDismissRequest = { confirm = false }, onDismissRequest = { confirm = false },
confirmButton = { confirmButton = {
TextButton(onClick = { TextButton(onClick = {
StoreV2.removeEvent(veranstalterId, evt.id) Store.removeEvent(veranstalterId, evt.id)
confirm = false confirm = false
}) { Text("Löschen") } }) { Text("Löschen") }
}, },

View File

@ -71,7 +71,7 @@ data class Funktionaer(
var istAktiv: Boolean = true, var istAktiv: Boolean = true,
) )
data class VeranstaltungV2( data class Veranstaltung(
val id: Long, val id: Long,
var veranstalterId: Long, var veranstalterId: Long,
var titel: String, var titel: String,
@ -85,7 +85,7 @@ data class VeranstaltungV2(
var sponsoren: SnapshotStateList<String> = mutableStateListOf(), var sponsoren: SnapshotStateList<String> = mutableStateListOf(),
) )
object StoreV2 { object Store {
val pferde: SnapshotStateList<Pferd> = mutableStateListOf( val pferde: SnapshotStateList<Pferd> = mutableStateListOf(
Pferd( Pferd(
id = 1, id = 1,
@ -268,7 +268,7 @@ object StoreV2 {
return id return id
} }
private val veranstaltungen: MutableMap<Long, SnapshotStateList<VeranstaltungV2>> = mutableMapOf() private val veranstaltungen: MutableMap<Long, SnapshotStateList<Veranstaltung>> = mutableMapOf()
fun seed() { fun seed() {
// Falls bereits Daten da sind (außer den statischen Vereinen), nichts tun // Falls bereits Daten da sind (außer den statischen Vereinen), nichts tun
@ -277,7 +277,7 @@ object StoreV2 {
// 1. Neumarkt April 2026 (ID 100) // 1. Neumarkt April 2026 (ID 100)
val neumarktId = 100L val neumarktId = 100L
addEventFirst( addEventFirst(
1, VeranstaltungV2( 1, Veranstaltung(
id = neumarktId, id = neumarktId,
veranstalterId = 1, veranstalterId = 1,
titel = "CSN-B* Neumarkt am Wallersee", titel = "CSN-B* Neumarkt am Wallersee",
@ -289,17 +289,17 @@ object StoreV2 {
) )
) )
TurnierStoreV2.add( TurnierStore.add(
neumarktId, neumarktId,
TurnierV2(101, neumarktId, 26128, datumVon = "2026-04-24", datumBis = "2026-04-26", znsDataLoaded = true).apply { Turnier(101, neumarktId, 26128, datumVon = "2026-04-24", datumBis = "2026-04-26", znsDataLoaded = true).apply {
titel = "Springturnier Neumarkt" titel = "Springturnier Neumarkt"
kategorie.add("CSN-B*") kategorie.add("CSN-B*")
kategorie.add("CSNP-B") kategorie.add("CSNP-B")
} }
) )
TurnierStoreV2.add( TurnierStore.add(
neumarktId, neumarktId,
TurnierV2(102, neumarktId, 26129, datumVon = "2026-04-24", datumBis = "2026-04-26", znsDataLoaded = true).apply { Turnier(102, neumarktId, 26129, datumVon = "2026-04-24", datumBis = "2026-04-26", znsDataLoaded = true).apply {
titel = "Dressurturnier Neumarkt" titel = "Dressurturnier Neumarkt"
kategorie.add("CDN-B") kategorie.add("CDN-B")
kategorie.add("CDNP-B") kategorie.add("CDNP-B")
@ -309,7 +309,7 @@ object StoreV2 {
// 2. Linz 2026 (ID 200) // 2. Linz 2026 (ID 200)
val linzId = 200L val linzId = 200L
addEventFirst( addEventFirst(
2, VeranstaltungV2( 2, Veranstaltung(
id = linzId, id = linzId,
veranstalterId = 2, veranstalterId = 2,
titel = "Linzer Pferdefestival", titel = "Linzer Pferdefestival",
@ -319,15 +319,15 @@ object StoreV2 {
beschreibung = "Große Reitsport-Veranstaltung am Ebelsberger Schlosspark." beschreibung = "Große Reitsport-Veranstaltung am Ebelsberger Schlosspark."
) )
) )
TurnierStoreV2.add( TurnierStore.add(
linzId, linzId,
TurnierV2(201, linzId, 26500, datumVon = "2026-05-20", datumBis = "2026-05-24", znsDataLoaded = true).apply { Turnier(201, linzId, 26500, datumVon = "2026-05-20", datumBis = "2026-05-24", znsDataLoaded = true).apply {
kategorie.add("CSN-B*") kategorie.add("CSN-B*")
}) })
// 3. Ein historisches Event (ID 300) // 3. Ein historisches Event (ID 300)
addEventFirst( addEventFirst(
1, VeranstaltungV2( 1, Veranstaltung(
id = 300L, id = 300L,
veranstalterId = 1, veranstalterId = 1,
titel = "Herbst-Turnier 2025", titel = "Herbst-Turnier 2025",
@ -338,10 +338,10 @@ object StoreV2 {
) )
} }
fun eventsFor(vereinId: Long): SnapshotStateList<VeranstaltungV2> = fun eventsFor(vereinId: Long): SnapshotStateList<Veranstaltung> =
veranstaltungen.getOrPut(vereinId) { mutableStateListOf() } veranstaltungen.getOrPut(vereinId) { mutableStateListOf() }
fun addEventFirst(vereinId: Long, v: VeranstaltungV2) { fun addEventFirst(vereinId: Long, v: Veranstaltung) {
eventsFor(vereinId).add(0, v) eventsFor(vereinId).add(0, v)
} }
@ -351,5 +351,5 @@ object StoreV2 {
if (idx >= 0) list.removeAt(idx) if (idx >= 0) list.removeAt(idx)
} }
fun allEvents(): List<VeranstaltungV2> = veranstaltungen.values.flatten() fun allEvents(): List<Veranstaltung> = veranstaltungen.values.flatten()
} }