chore: integriere Turnier-Wizard und ZNS-Importer in Veranstaltungsscreen, implementiere Profil-Onboarding und aktualisiere Modulabhängigkeiten

Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
2026-04-21 10:42:43 +02:00
parent 01bf440f21
commit 1a295c18c8
12 changed files with 508 additions and 96 deletions
@@ -17,6 +17,10 @@ import androidx.compose.ui.unit.dp
import at.mocode.frontend.core.designsystem.components.MsFilePicker
import at.mocode.frontend.core.designsystem.components.MsTextField
import at.mocode.frontend.core.designsystem.theme.Dimens
import at.mocode.frontend.features.turnier.presentation.TurnierWizard
import at.mocode.frontend.features.turnier.presentation.TurnierWizardViewModel
import at.mocode.frontend.features.zns.import.presentation.StammdatenImportScreen
import org.koin.compose.koinInject
import kotlin.uuid.ExperimentalUuidApi
@OptIn(ExperimentalMaterial3Api::class, ExperimentalUuidApi::class)
@@ -174,41 +178,39 @@ private fun ZnsCheckStep(viewModel: VeranstaltungWizardViewModel) {
}
if (!state.isZnsAvailable && !state.isCheckingStats) {
Card(colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer)) {
Row(Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Warning, null, tint = MaterialTheme.colorScheme.error)
Spacer(Modifier.width(12.dp))
Column {
Text("🚨 Stammdaten fehlen!", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.titleMedium)
Text("Bitte importieren Sie die aktuelle ZNS.zip über den ZNS-Importer.")
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Card(colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer)) {
Row(Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Warning, null, tint = MaterialTheme.colorScheme.error)
Spacer(Modifier.width(12.dp))
Column {
Text(
"🚨 Stammdaten fehlen!",
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.titleMedium
)
Text("Bitte importieren Sie die aktuelle ZNS.zip, um fortzufahren.")
}
}
}
}
}
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(
onClick = { viewModel.checkStammdatenStatus() },
enabled = !state.isCheckingStats,
modifier = Modifier.weight(1f)
) {
if (state.isCheckingStats) {
CircularProgressIndicator(modifier = Modifier.size(18.dp), strokeWidth = 2.dp, color = MaterialTheme.colorScheme.onPrimary)
} else {
Icon(Icons.Default.Refresh, null)
}
Spacer(Modifier.width(8.dp))
Text("Status prüfen")
}
if (!state.isZnsAvailable) {
OutlinedButton(
onClick = { /* Navigiere zum ZNS Importer */ },
modifier = Modifier.weight(1f)
// Plug-and-Play Integration des Importers
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(max = 500.dp)
.padding(vertical = 8.dp)
) {
Icon(Icons.Default.CloudDownload, null)
StammdatenImportScreen(onBack = {})
}
Button(
onClick = { viewModel.checkStammdatenStatus() },
modifier = Modifier.fillMaxWidth()
) {
Icon(Icons.Default.Refresh, null)
Spacer(Modifier.width(8.dp))
Text("Zum ZNS-Importer")
Text("Import-Status aktualisieren")
}
}
}
@@ -394,52 +396,46 @@ private fun MetaDataStep(viewModel: VeranstaltungWizardViewModel) {
@Composable
private fun TurnierAnlageStep(viewModel: VeranstaltungWizardViewModel) {
val state = viewModel.state
var showWizard by remember { mutableStateOf(false) }
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text("Schritt 5: Turniere & Ausschreibung", style = MaterialTheme.typography.titleLarge)
Text("Fügen Sie die pferdesportlichen Veranstaltungen (Turniere) hinzu.")
state.turniere.forEachIndexed { index, turnier ->
Card(modifier = Modifier.fillMaxWidth()) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Turnier #${index + 1}", fontWeight = FontWeight.Bold)
if (state.turniere.size > 1) {
IconButton(onClick = { viewModel.removeTurnier(index) }) {
Icon(Icons.Default.Delete, contentDescription = "Entfernen", tint = MaterialTheme.colorScheme.error)
}
if (showWizard) {
val turnierViewModel = koinInject<TurnierWizardViewModel>()
Card(modifier = Modifier.fillMaxWidth().height(500.dp)) {
TurnierWizard(
viewModel = turnierViewModel,
veranstaltungId = 0, // In Echt wird hier die ID aus dem State genutzt
onBack = { showWizard = false },
onFinish = {
showWizard = false
viewModel.addTurnier() // Dummy zum Hinzufügen im Haupt-Wizard
}
)
}
} else {
Text("Fügen Sie die pferdesportlichen Veranstaltungen (Turniere) hinzu.")
state.turniere.forEachIndexed { index, turnier ->
ListItem(
headlineContent = { Text("Turnier #${index + 1}: ${turnier.nummer}") },
trailingContent = {
IconButton(onClick = { viewModel.removeTurnier(index) }) {
Icon(Icons.Default.Delete, null, tint = MaterialTheme.colorScheme.error)
}
}
MsTextField(
value = turnier.nummer,
onValueChange = { viewModel.updateTurnier(index, it, turnier.ausschreibungPath) },
label = "Turnier-Nummer (ZNS)",
placeholder = "z.B. 26123",
modifier = Modifier.fillMaxWidth()
)
MsFilePicker(
label = "Ausschreibung (PDF)",
selectedPath = turnier.ausschreibungPath,
onFileSelected = { viewModel.updateTurnier(index, turnier.nummer, it) },
fileExtensions = listOf("pdf"),
modifier = Modifier.fillMaxWidth()
)
}
)
}
}
OutlinedButton(
onClick = { viewModel.addTurnier() },
modifier = Modifier.fillMaxWidth()
) {
Icon(Icons.Default.Add, contentDescription = null)
Spacer(Modifier.width(8.dp))
Text("Weiteres Turnier hinzufügen")
OutlinedButton(
onClick = { showWizard = true },
modifier = Modifier.fillMaxWidth()
) {
Icon(Icons.Default.Add, null)
Spacer(Modifier.width(8.dp))
Text("Neues Turnier mit Wizard anlegen")
}
}
Spacer(Modifier.height(16.dp))
@@ -447,7 +443,7 @@ private fun TurnierAnlageStep(viewModel: VeranstaltungWizardViewModel) {
Button(
onClick = { viewModel.nextStep() },
modifier = Modifier.align(Alignment.End),
enabled = state.turniere.all { it.nummer.isNotBlank() && it.ausschreibungPath != null }
enabled = !showWizard && state.turniere.isNotEmpty()
) {
Text("Weiter zur Zusammenfassung")
}