### feat: füge Validierung und Fehler-Handling zur Veranstalter-Erstellung hinzu
- Implementiere Validierungslogik im `VeranstalterWizardViewModel` (Pflichtfelder, E-Mail-Format). - Zeige Validierungsfehler direkt in der `VeranstalterNeuScreen` an. - Erweiterung der State-Klasse um `errors` für direktes UI-Feedback.
This commit is contained in:
@@ -15,6 +15,7 @@ In dieser Session wurde der Prozess zum Anlegen neuer Veranstalter radikal verei
|
|||||||
- `VeranstalterWizardViewModel` wurde um Such- und Mapping-Logik erweitert.
|
- `VeranstalterWizardViewModel` wurde um Such- und Mapping-Logik erweitert.
|
||||||
- Suche triggert automatisch bei Eingabe (ab 3 Zeichen) gegen den `ZnsImportProvider`.
|
- 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.
|
- 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:**
|
- **Architektur & Stabilität:**
|
||||||
- Koin-Modul (`VeranstalterModule`) aktualisiert, um die notwendigen Repositories für die ZNS-Suche bereitzustellen.
|
- 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`.
|
- 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.
|
- **Workflow:** Die Suche gegen die importierten 1427 Vereine ist nun integraler Bestandteil der Neuanlage.
|
||||||
|
|
||||||
## Nächste Schritte
|
## Nächste Schritte
|
||||||
1. Finalisierung der Validierungs-Regeln für die Veranstalter-Anlage (z.B. E-Mail-Format, Eindeutigkeit der OEBS-Nummer).
|
1. Anbindung der Speichern-Logik an das echte Backend (Upsert-Flow).
|
||||||
2. Anbindung der Speichern-Logik an das echte Backend (Upsert-Flow).
|
2. Integration der Ansprechperson-Suche gegen die Reiter-Stammdaten (Details des Mappings).
|
||||||
3. 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]
|
🏗️ [Lead Architect] | 👷 [Backend Developer] | 🎨 [Frontend Expert] | 🖌️ [UI/UX Designer] | 🧹 [Curator]
|
||||||
|
|||||||
+12
-4
@@ -146,25 +146,33 @@ fun VeranstalterNeuScreen(
|
|||||||
value = state.name,
|
value = state.name,
|
||||||
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateName(it)) },
|
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateName(it)) },
|
||||||
label = "Vereinsname *",
|
label = "Vereinsname *",
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
isError = state.errors.containsKey("name"),
|
||||||
|
errorMessage = state.errors["name"]
|
||||||
)
|
)
|
||||||
MsTextField(
|
MsTextField(
|
||||||
value = state.oepsNummer,
|
value = state.oepsNummer,
|
||||||
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateOeps(it)) },
|
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateOeps(it)) },
|
||||||
label = "OEBS-Nummer *",
|
label = "OEBS-Nummer *",
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
isError = state.errors.containsKey("oeps"),
|
||||||
|
errorMessage = state.errors["oeps"]
|
||||||
)
|
)
|
||||||
MsTextField(
|
MsTextField(
|
||||||
value = state.ansprechpartner,
|
value = state.ansprechpartner,
|
||||||
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateAnsprechpartner(it)) },
|
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateAnsprechpartner(it)) },
|
||||||
label = "Ansprechperson *",
|
label = "Ansprechperson *",
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
isError = state.errors.containsKey("ansprechpartner"),
|
||||||
|
errorMessage = state.errors["ansprechpartner"]
|
||||||
)
|
)
|
||||||
MsTextField(
|
MsTextField(
|
||||||
value = state.email,
|
value = state.email,
|
||||||
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateEmail(it)) },
|
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateEmail(it)) },
|
||||||
label = "E-Mail (für Login-Daten) *",
|
label = "E-Mail (für Login-Daten) *",
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
isError = state.errors.containsKey("email"),
|
||||||
|
errorMessage = state.errors["email"]
|
||||||
)
|
)
|
||||||
MsTextField(
|
MsTextField(
|
||||||
value = state.telefon,
|
value = state.telefon,
|
||||||
|
|||||||
+22
-1
@@ -37,7 +37,8 @@ data class VeranstalterWizardState(
|
|||||||
val reiterSearchQuery: String = "",
|
val reiterSearchQuery: String = "",
|
||||||
val reiterSearchResults: List<ZnsRemoteReiter> = emptyList(),
|
val reiterSearchResults: List<ZnsRemoteReiter> = emptyList(),
|
||||||
val isSearchingVerein: Boolean = false,
|
val isSearchingVerein: Boolean = false,
|
||||||
val isSearchingReiter: Boolean = false
|
val isSearchingReiter: Boolean = false,
|
||||||
|
val errors: Map<String, String> = emptyMap()
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed interface VeranstalterWizardIntent {
|
sealed interface VeranstalterWizardIntent {
|
||||||
@@ -160,7 +161,27 @@ class VeranstalterWizardViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun validate(): Boolean {
|
||||||
|
val errors = mutableMapOf<String, String>()
|
||||||
|
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() {
|
private fun save() {
|
||||||
|
if (!validate()) return
|
||||||
|
|
||||||
val s = _state.value
|
val s = _state.value
|
||||||
_state.value = _state.value.copy(isSaving = true)
|
_state.value = _state.value.copy(isSaving = true)
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
|||||||
Reference in New Issue
Block a user