### feat: verbessere Wizard-Validierung und UI-Feedback

- Integriere Fortschrittsanzeige während der Veranstalter-Suche (`isCheckingStats`).
- Zeige Fehlermeldungen bei Suchfehlern im `EventWizardScreen`.
- Füge `hasSelectedVeranstalter`-Guard und zugehörige Tests hinzu.
- Präzisiere `DemoEventFlow` mit expliziter Guard-Logik.
- Aktualisiere Unit-Tests zur Abdeckung neuer Guard-Szenarien.
This commit is contained in:
2026-04-21 21:26:02 +02:00
parent 3f4ba9eea9
commit 9195cdb14d
6 changed files with 138 additions and 32 deletions
@@ -317,18 +317,31 @@ private fun VeranstalterSelectionStep(
Text("Schritt 2: Veranstalter auswählen", style = MaterialTheme.typography.titleLarge)
Text("Suchen Sie nach dem Verein (Name oder OEPS-Nummer).")
MsTextField(
value = searchQuery,
onValueChange = {
searchQuery = it
if (it.length >= 3) {
viewModel.searchVeranstalterByOepsNr(it)
}
},
label = "Verein suchen (z.B. 6-009)",
placeholder = "OEPS-Nummer eingeben...",
modifier = Modifier.fillMaxWidth()
)
Box(modifier = Modifier.fillMaxWidth()) {
MsTextField(
value = searchQuery,
onValueChange = {
searchQuery = it
if (it.length >= 3) {
viewModel.searchVeranstalterByOepsNr(it)
}
},
label = "Verein suchen (z.B. 6-009)",
placeholder = "OEPS-Nummer eingeben...",
modifier = Modifier.fillMaxWidth(),
enabled = !viewModel.state.isCheckingStats
)
if (viewModel.state.isCheckingStats) {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.CenterEnd).padding(end = 12.dp).size(24.dp),
strokeWidth = 2.dp
)
}
}
if (viewModel.state.error != null) {
Text(viewModel.state.error!!, color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodySmall)
}
if (viewModel.state.veranstalterId != null) {
Card(
@@ -19,6 +19,7 @@ import at.mocode.frontend.core.wizard.runtime.WizardContext
import at.mocode.frontend.core.wizard.runtime.WizardState
import at.mocode.frontend.core.wizard.samples.DemoEventAcc
import at.mocode.frontend.core.wizard.samples.DemoEventFlow
import at.mocode.frontend.core.wizard.samples.DemoEventFlowNextManual
import at.mocode.frontend.core.wizard.samples.DemoEventStep
import at.mocode.frontend.features.turnier.presentation.TurnierWizardViewModel
import at.mocode.frontend.features.veranstalter.domain.VeranstalterRepository
@@ -163,8 +164,10 @@ class EventWizardViewModel(
}
fun searchVeranstalterByOepsNr(oepsNr: String) {
if (oepsNr.isBlank()) return
viewModelScope.launch {
try {
state = state.copy(isCheckingStats = true)
val verein = vereinRepository.findByOepsNr(oepsNr)
if (verein != null) {
// Robustes Parsing für Mock-Daten (z. B. "v1")
@@ -182,13 +185,16 @@ class EventWizardViewModel(
standardOrt = "${verein.plz ?: ""} ${verein.ort ?: ""}".trim(),
logo = null
)
state = state.copy(isCheckingStats = false, znsSearchResults = emptyList())
} else if (oepsNr.length >= 3) {
// Suche in den ZNS-Stammdaten als Fallback
znsImportProvider.searchRemote(oepsNr)
state = state.copy(znsSearchResults = znsImportProvider.state.remoteResults)
state = state.copy(isCheckingStats = false, znsSearchResults = znsImportProvider.state.remoteResults)
} else {
state = state.copy(isCheckingStats = false)
}
} catch (e: Exception) {
state = state.copy(error = "Fehler bei der Veranstalter-Suche: ${e.message}")
state = state.copy(isCheckingStats = false, error = "Fehler bei der Veranstalter-Suche: ${e.message}")
}
}
}
@@ -207,7 +213,7 @@ class EventWizardViewModel(
if (WizardFeatureFlags.WizardRuntimeEnabled && isHandledByRuntime(state.currentStep)) {
val ctx = buildWizardContext()
val currentRuntimeState = ensureWizardStateInitialized()
val next = DemoEventFlow.next(ctx, currentRuntimeState)
val next = DemoEventFlowNextManual(ctx, currentRuntimeState)
wizardState = next
val mapped = mapToWizardStep(next.current)
if (mapped != null) {