chore: erweitere Veranstalter-Wizard um Bearbeitungsmodus, füge Kontaktdaten und Step-Logik hinzu

Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
2026-04-21 14:37:41 +02:00
parent 18e619abfc
commit 544fbf792c
12 changed files with 389 additions and 90 deletions
@@ -178,6 +178,17 @@ fun DesktopContentArea(
onBack = onBack,
onZurVeranstaltung = { evtId: Long -> onNavigate(AppScreen.EventProfil(currentScreen.id, evtId)) },
onNeuVeranstaltung = { onNavigate(AppScreen.EventNeu) },
onEditVeranstalter = { id ->
onNavigate(AppScreen.VeranstalterProfilEdit(id))
}
)
is AppScreen.VeranstalterProfilEdit -> VeranstalterAnlegenWizard(
editId = currentScreen.id,
onCancel = onBack,
onVereinCreated = { id ->
onNavigate(AppScreen.VeranstalterProfil(id))
}
)
// Neuer Flow: Veranstalter auswählen → Event-Wizard
@@ -188,6 +199,7 @@ fun DesktopContentArea(
)
is AppScreen.VeranstalterNeu -> VeranstalterAnlegenWizard(
editId = null,
onCancel = onBack,
onVereinCreated = { newId: Long -> onNavigate(AppScreen.VeranstalterProfil(newId)) }
)
@@ -199,6 +211,7 @@ fun DesktopContentArea(
onBack = onBack,
onZurVeranstaltung = { evtId -> onNavigate(AppScreen.EventProfil(vId, evtId)) },
onNeuVeranstaltung = { onNavigate(AppScreen.EventKonfig(vId)) },
onEditVeranstalter = { id -> onNavigate(AppScreen.VeranstalterProfilEdit(id)) }
)
}
@@ -30,12 +30,14 @@ fun VeranstalterDetail(
onBack: () -> Unit,
onZurVeranstaltung: (Long) -> Unit,
onNeuVeranstaltung: () -> Unit,
onEditVeranstalter: (Long) -> Unit,
) {
VeranstalterDetailScreen(
veranstalterId = veranstalterId,
viewModel = koinInject(),
onZurueck = onBack,
onVeranstaltungOeffnen = onZurVeranstaltung,
onVeranstaltungNeu = onNeuVeranstaltung
onVeranstaltungNeu = onNeuVeranstaltung,
onEditVeranstalter = onEditVeranstalter
)
}
@@ -82,6 +82,7 @@ fun PreviewVeranstalterDetailScreen() {
onZurueck = {},
onVeranstaltungOeffnen = {},
onVeranstaltungNeu = {},
onEditVeranstalter = {},
)
}
}
@@ -4,37 +4,61 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import at.mocode.frontend.shell.desktop.data.Store
import at.mocode.frontend.core.domain.zns.ZnsImportProvider
import at.mocode.frontend.core.domain.zns.ZnsImportState
import at.mocode.frontend.features.veranstalter.presentation.VeranstalterWizardIntent
import at.mocode.frontend.features.veranstalter.presentation.VeranstalterWizardViewModel
import at.mocode.frontend.shell.desktop.data.Store
import at.mocode.frontend.shell.desktop.screens.veranstaltung.components.pickZnsFile
import org.koin.compose.koinInject
import androidx.compose.ui.draw.clip
import androidx.compose.foundation.shape.RoundedCornerShape
import org.koin.compose.viewmodel.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun VeranstalterAnlegenWizard(
editId: Long? = null,
onCancel: () -> Unit,
onVereinCreated: (Long) -> Unit
) {
var step by remember { mutableStateOf(1) }
var selectedVereinId by remember { mutableLongStateOf(0L) }
val viewModel = koinViewModel<VeranstalterWizardViewModel>()
val state by viewModel.state.collectAsState()
var step by remember { mutableIntStateOf(1) }
LaunchedEffect(editId) {
if (editId != null) {
viewModel.send(VeranstalterWizardIntent.Load(editId))
step = 2 // Direkt zu den Details beim Editieren
}
}
LaunchedEffect(state.success) {
if (state.success) {
onVereinCreated(state.editId ?: 0L)
}
}
val znsImporter = koinInject<ZnsImportProvider>()
val znsState = znsImporter.state
Column(Modifier.fillMaxSize().padding(24.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onCancel) { Icon(Icons.Default.Close, null) }
Text("Veranstalter registrieren", style = MaterialTheme.typography.headlineSmall)
Text(
if (editId == null) "Veranstalter registrieren" else "Veranstalter-Profil bearbeiten",
style = MaterialTheme.typography.headlineSmall
)
}
LinearProgressIndicator(
@@ -47,14 +71,21 @@ fun VeranstalterAnlegenWizard(
1 -> Step1Veranstalter(
znsState = znsState,
znsImporter = znsImporter,
selectedVereinId = selectedVereinId,
onVereinSelected = { selectedVereinId = it },
selectedVereinId = state.editId ?: 0L,
onVereinSelected = { id ->
// Mock: Wir laden die Daten des Vereins aus dem Store in das VM
at.mocode.frontend.shell.desktop.data.Store.vereine.find { it.id == id }?.let { v ->
viewModel.send(VeranstalterWizardIntent.UpdateName(v.name))
viewModel.send(VeranstalterWizardIntent.UpdateOeps(v.oepsNummer))
viewModel.send(VeranstalterWizardIntent.UpdateOrt(v.ort ?: ""))
}
},
onVeranstalterCreated = {
selectedVereinId = it
onVereinCreated(it)
// Nicht mehr direkt navigieren, sondern zu Step 2
step = 2
}
)
2 -> { /* Optional: Weitere Details für den Veranstalter */ }
2 -> Step2VeranstalterDetails(viewModel)
}
}
@@ -63,16 +94,91 @@ fun VeranstalterAnlegenWizard(
Spacer(Modifier.width(8.dp))
if (step == 1) {
Button(
onClick = { onVereinCreated(selectedVereinId) },
enabled = selectedVereinId != 0L
onClick = { step = 2 },
enabled = state.name.isNotBlank()
) {
Text("Fertigstellen")
Text("Weiter zu den Details")
}
} else {
Button(
onClick = { viewModel.send(VeranstalterWizardIntent.Save) },
enabled = !state.isSaving && state.name.isNotBlank()
) {
if (state.isSaving) {
CircularProgressIndicator(Modifier.size(18.dp), strokeWidth = 2.dp, color = Color.White)
} else {
Text(if (editId == null) "Registrierung abschließen" else "Änderungen speichern")
}
}
}
}
}
}
@Composable
fun Step2VeranstalterDetails(viewModel: VeranstalterWizardViewModel) {
val state by viewModel.state.collectAsState()
Column(verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.verticalScroll(rememberScrollState())) {
Text("Ergänzen Sie die Kontaktdaten für diesen Veranstalter.", style = MaterialTheme.typography.bodyMedium)
OutlinedTextField(
value = state.name,
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateName(it)) },
label = { Text("Name des Veranstalters / Vereins") },
modifier = Modifier.fillMaxWidth()
)
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp)) {
OutlinedTextField(
value = state.oepsNummer,
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateOeps(it)) },
label = { Text("OEBS-Nr") },
modifier = Modifier.weight(1f)
)
OutlinedTextField(
value = state.ort,
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateOrt(it)) },
label = { Text("Ort") },
modifier = Modifier.weight(2f)
)
}
HorizontalDivider()
Text("Ansprechpartner & Kontakt", style = MaterialTheme.typography.titleSmall)
OutlinedTextField(
value = state.ansprechpartner,
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateAnsprechpartner(it)) },
label = { Text("Name Ansprechpartner") },
modifier = Modifier.fillMaxWidth()
)
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp)) {
OutlinedTextField(
value = state.email,
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateEmail(it)) },
label = { Text("E-Mail Adresse") },
modifier = Modifier.weight(1f)
)
OutlinedTextField(
value = state.telefon,
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateTelefon(it)) },
label = { Text("Telefonnummer") },
modifier = Modifier.weight(1f)
)
}
OutlinedTextField(
value = state.adresse,
onValueChange = { viewModel.send(VeranstalterWizardIntent.UpdateAdresse(it)) },
label = { Text("Postanschrift") },
modifier = Modifier.fillMaxWidth(),
minLines = 2
)
}
}
@Composable
fun Step1Veranstalter(
znsState: ZnsImportState,