diff --git a/docs/99_Journal/2026-04-21_Veranstalter-Neu-Overhaul.md b/docs/99_Journal/2026-04-21_Veranstalter-Neu-Overhaul.md index dfed4290..cfebf20e 100644 --- a/docs/99_Journal/2026-04-21_Veranstalter-Neu-Overhaul.md +++ b/docs/99_Journal/2026-04-21_Veranstalter-Neu-Overhaul.md @@ -15,6 +15,7 @@ In dieser Session wurde der Prozess zum Anlegen neuer Veranstalter radikal verei - `VeranstalterWizardViewModel` wurde um Such- und Mapping-Logik erweitert. - Suche triggert automatisch bei Eingabe (ab 3 Zeichen) gegen den `ZnsImportProvider`. - Bei Auswahl eines Suchergebnisses werden alle relevanten Felder (Name, OEBS-Nr, Ort, Ansprechperson) sofort im Formular vorbefüllt. + - **Neu:** Implementierung einer robusten Validierungs-Logik (Pflichtfelder & E-Mail-Format) mit direktem UI-Feedback. - **Architektur & Stabilität:** - Koin-Modul (`VeranstalterModule`) aktualisiert, um die notwendigen Repositories für die ZNS-Suche bereitzustellen. - Bereinigung von obsoleten multi-step Wizard-Aufrufen in der `ContentArea.kt`. @@ -25,8 +26,8 @@ In dieser Session wurde der Prozess zum Anlegen neuer Veranstalter radikal verei - **Workflow:** Die Suche gegen die importierten 1427 Vereine ist nun integraler Bestandteil der Neuanlage. ## Nächste Schritte -1. Finalisierung der Validierungs-Regeln für die Veranstalter-Anlage (z.B. E-Mail-Format, Eindeutigkeit der OEBS-Nummer). -2. Anbindung der Speichern-Logik an das echte Backend (Upsert-Flow). -3. Integration der Ansprechperson-Suche gegen die Reiter-Stammdaten (Details des Mappings). +1. Anbindung der Speichern-Logik an das echte Backend (Upsert-Flow). +2. Integration der Ansprechperson-Suche gegen die Reiter-Stammdaten (Details des Mappings). +3. Finalisierung der Berechtigungs-Prüfung für den ZNS-Zugriff im Desktop-Client. 🏗️ [Lead Architect] | 👷 [Backend Developer] | 🎨 [Frontend Expert] | 🖌️ [UI/UX Designer] | 🧹 [Curator] diff --git a/frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/presentation/VeranstalterNeuScreen.kt b/frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/presentation/VeranstalterNeuScreen.kt index edea234b..bac97ec7 100644 --- a/frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/presentation/VeranstalterNeuScreen.kt +++ b/frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/presentation/VeranstalterNeuScreen.kt @@ -146,25 +146,33 @@ fun VeranstalterNeuScreen( value = state.name, onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateName(it)) }, label = "Vereinsname *", - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), + isError = state.errors.containsKey("name"), + errorMessage = state.errors["name"] ) MsTextField( value = state.oepsNummer, onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateOeps(it)) }, label = "OEBS-Nummer *", - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), + isError = state.errors.containsKey("oeps"), + errorMessage = state.errors["oeps"] ) MsTextField( value = state.ansprechpartner, onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateAnsprechpartner(it)) }, label = "Ansprechperson *", - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), + isError = state.errors.containsKey("ansprechpartner"), + errorMessage = state.errors["ansprechpartner"] ) MsTextField( value = state.email, onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateEmail(it)) }, label = "E-Mail (für Login-Daten) *", - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), + isError = state.errors.containsKey("email"), + errorMessage = state.errors["email"] ) MsTextField( value = state.telefon, diff --git a/frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/presentation/VeranstalterWizardViewModel.kt b/frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/presentation/VeranstalterWizardViewModel.kt index d599abc3..f955793c 100644 --- a/frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/presentation/VeranstalterWizardViewModel.kt +++ b/frontend/features/veranstalter-feature/src/jvmMain/kotlin/at/mocode/frontend/features/veranstalter/presentation/VeranstalterWizardViewModel.kt @@ -37,7 +37,8 @@ data class VeranstalterWizardState( val reiterSearchQuery: String = "", val reiterSearchResults: List = emptyList(), val isSearchingVerein: Boolean = false, - val isSearchingReiter: Boolean = false + val isSearchingReiter: Boolean = false, + val errors: Map = emptyMap() ) sealed interface VeranstalterWizardIntent { @@ -160,7 +161,27 @@ class VeranstalterWizardViewModel( } } + private fun validate(): Boolean { + val errors = mutableMapOf() + val s = _state.value + + if (s.name.isBlank()) errors["name"] = "Vereinsname ist erforderlich" + if (s.oepsNummer.isBlank()) errors["oeps"] = "OEBS-Nummer ist erforderlich" + if (s.ansprechpartner.isBlank()) errors["ansprechpartner"] = "Ansprechperson ist erforderlich" + + if (s.email.isBlank()) { + errors["email"] = "E-Mail ist erforderlich" + } else if (!s.email.contains("@") || !s.email.contains(".")) { + errors["email"] = "Ungültiges E-Mail Format" + } + + _state.value = _state.value.copy(errors = errors) + return errors.isEmpty() + } + private fun save() { + if (!validate()) return + val s = _state.value _state.value = _state.value.copy(isSaving = true) scope.launch {