chore: refaktoriere Veranstaltungs-UI zu Events, implementiere ZNS-Suche und verbessere Navigationslogik
Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
+2
-1
@@ -1,5 +1,6 @@
|
||||
package at.mocode.veranstaltung.feature.di
|
||||
|
||||
import at.mocode.frontend.core.domain.zns.ZnsImportProvider
|
||||
import at.mocode.veranstaltung.feature.presentation.VeranstaltungManagementViewModel
|
||||
import at.mocode.veranstaltung.feature.presentation.VeranstaltungWizardViewModel
|
||||
import org.koin.core.qualifier.named
|
||||
@@ -7,5 +8,5 @@ import org.koin.dsl.module
|
||||
|
||||
val veranstaltungModule = module {
|
||||
factory { VeranstaltungManagementViewModel(get()) }
|
||||
factory { VeranstaltungWizardViewModel(get(named("apiClient")), get(), get(), get()) }
|
||||
factory { VeranstaltungWizardViewModel(get(named("apiClient")), get(), get(), get(), get<ZnsImportProvider>(), get()) }
|
||||
}
|
||||
|
||||
+2
-2
@@ -49,7 +49,7 @@ fun VeranstaltungDetailScreen(
|
||||
val event = veranstaltung
|
||||
if (event == null) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("Veranstaltung #$veranstaltungId nicht gefunden.")
|
||||
Text("Event #$veranstaltungId nicht gefunden.")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -95,7 +95,7 @@ fun VeranstaltungDetailScreen(
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "Turniere in dieser Veranstaltung",
|
||||
text = "Turniere in diesem Event",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
+29
-7
@@ -18,9 +18,7 @@ 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)
|
||||
@@ -37,7 +35,7 @@ fun VeranstaltungWizardScreen(
|
||||
topBar = {
|
||||
Column {
|
||||
TopAppBar(
|
||||
title = { Text("Neue Veranstaltung anlegen") },
|
||||
title = { Text("Neues Event anlegen") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
if (state.currentStep == WizardStep.ZNS_CHECK) onBack()
|
||||
@@ -108,7 +106,7 @@ private fun VorschauCard(state: VeranstaltungWizardState) {
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = state.name.ifBlank { "Neue Veranstaltung" },
|
||||
text = state.name.ifBlank { "Neues Event" },
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -275,7 +273,31 @@ private fun VeranstalterSelectionStep(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (viewModel.state.znsSearchResults.isNotEmpty()) {
|
||||
Text("Gefundene Vereine in den Stammdaten:", style = MaterialTheme.typography.labelMedium)
|
||||
viewModel.state.znsSearchResults.forEach { znsVerein ->
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { viewModel.selectZnsVerein(znsVerein) }
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(Icons.Default.Add, null)
|
||||
Column {
|
||||
Text(znsVerein.name, fontWeight = FontWeight.Medium)
|
||||
Text("OEPS-Nr: ${znsVerein.oepsNummer} | ${znsVerein.ort ?: ""}", style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (viewModel.state.veranstalterId == null && viewModel.state.znsSearchResults.isEmpty()) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@@ -400,13 +422,13 @@ private fun MetaDataStep(viewModel: VeranstaltungWizardViewModel) {
|
||||
@Composable
|
||||
private fun TurnierAnlageStep(viewModel: VeranstaltungWizardViewModel) {
|
||||
val state = viewModel.state
|
||||
val turnierViewModel = viewModel.turnierWizardViewModel
|
||||
var showWizard by remember { mutableStateOf(false) }
|
||||
|
||||
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Text("Schritt 5: Turniere & Ausschreibung", style = MaterialTheme.typography.titleLarge)
|
||||
|
||||
if (showWizard) {
|
||||
val turnierViewModel = koinInject<TurnierWizardViewModel>()
|
||||
Card(modifier = Modifier.fillMaxWidth().height(500.dp)) {
|
||||
TurnierWizard(
|
||||
viewModel = turnierViewModel,
|
||||
@@ -414,7 +436,7 @@ private fun TurnierAnlageStep(viewModel: VeranstaltungWizardViewModel) {
|
||||
onBack = { showWizard = false },
|
||||
onFinish = {
|
||||
showWizard = false
|
||||
viewModel.addTurnier() // Dummy zum Hinzufügen im Haupt-Wizard
|
||||
viewModel.addTurnier(turnierViewModel.state.turnierNr, "")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+47
-25
@@ -9,7 +9,10 @@ import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import at.mocode.frontend.core.auth.data.local.AuthTokenManager
|
||||
import at.mocode.frontend.core.domain.repository.MasterdataRepository
|
||||
import at.mocode.frontend.core.domain.repository.MasterdataStats
|
||||
import at.mocode.frontend.core.domain.zns.ZnsImportProvider
|
||||
import at.mocode.frontend.core.domain.zns.ZnsRemoteVerein
|
||||
import at.mocode.frontend.core.network.NetworkConfig
|
||||
import at.mocode.frontend.features.turnier.presentation.TurnierWizardViewModel
|
||||
import at.mocode.frontend.features.verein.domain.VereinRepository
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.request.*
|
||||
@@ -55,7 +58,8 @@ data class VeranstaltungWizardState(
|
||||
val createdVeranstaltungId: Uuid? = null,
|
||||
val isZnsAvailable: Boolean = false,
|
||||
val stammdatenStats: MasterdataStats? = null,
|
||||
val isCheckingStats: Boolean = false
|
||||
val isCheckingStats: Boolean = false,
|
||||
val znsSearchResults: List<ZnsRemoteVerein> = emptyList()
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
@@ -63,7 +67,9 @@ class VeranstaltungWizardViewModel(
|
||||
private val httpClient: HttpClient,
|
||||
private val authTokenManager: AuthTokenManager,
|
||||
private val vereinRepository: VereinRepository,
|
||||
private val masterdataRepository: MasterdataRepository
|
||||
private val masterdataRepository: MasterdataRepository,
|
||||
private val znsImportProvider: ZnsImportProvider,
|
||||
val turnierWizardViewModel: TurnierWizardViewModel // Injected Child-ViewModel
|
||||
) : ViewModel() {
|
||||
|
||||
var state by mutableStateOf(VeranstaltungWizardState())
|
||||
@@ -98,19 +104,45 @@ class VeranstaltungWizardViewModel(
|
||||
|
||||
fun searchVeranstalterByOepsNr(oepsNr: String) {
|
||||
viewModelScope.launch {
|
||||
val verein = vereinRepository.findByOepsNr(oepsNr)
|
||||
if (verein != null) {
|
||||
setVeranstalter(
|
||||
id = Uuid.parse(verein.id),
|
||||
nummer = verein.oepsNr ?: "",
|
||||
name = verein.name,
|
||||
standardOrt = "${verein.plz ?: ""} ${verein.ort ?: ""}".trim(),
|
||||
logo = null // Hier könnte später ein Logo-Service greifen
|
||||
)
|
||||
try {
|
||||
val verein = vereinRepository.findByOepsNr(oepsNr)
|
||||
if (verein != null) {
|
||||
// Robustes Parsing für Mock-Daten (z. B. "v1")
|
||||
val uuid = try {
|
||||
Uuid.parse(verein.id)
|
||||
} catch (_: Exception) {
|
||||
// Fallback für Mock-IDs während der Entwicklung
|
||||
Uuid.random()
|
||||
}
|
||||
|
||||
setVeranstalter(
|
||||
id = uuid,
|
||||
nummer = verein.oepsNr ?: "",
|
||||
name = verein.name,
|
||||
standardOrt = "${verein.plz ?: ""} ${verein.ort ?: ""}".trim(),
|
||||
logo = null
|
||||
)
|
||||
} else if (oepsNr.length >= 3) {
|
||||
// Suche in den ZNS-Stammdaten als Fallback
|
||||
znsImportProvider.searchRemote(oepsNr)
|
||||
state = state.copy(znsSearchResults = znsImportProvider.state.remoteResults)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
state = state.copy(error = "Fehler bei der Veranstalter-Suche: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectZnsVerein(znsVerein: ZnsRemoteVerein) {
|
||||
setVeranstalter(
|
||||
id = Uuid.random(), // Neuer Veranstalter wird angelegt
|
||||
nummer = znsVerein.oepsNummer,
|
||||
name = znsVerein.name,
|
||||
standardOrt = znsVerein.ort ?: "",
|
||||
logo = null
|
||||
)
|
||||
}
|
||||
|
||||
fun nextStep() {
|
||||
state = state.copy(
|
||||
currentStep = when (state.currentStep) {
|
||||
@@ -155,23 +187,13 @@ class VeranstaltungWizardViewModel(
|
||||
state = state.copy(name = name, ort = ort, startDatum = start, endDatum = end, logoUrl = logo)
|
||||
}
|
||||
|
||||
fun updateTurnier(index: Int, nummer: String, path: String?) {
|
||||
val newList = state.turniere.toMutableList()
|
||||
if (index in newList.indices) {
|
||||
newList[index] = newList[index].copy(nummer = nummer, ausschreibungPath = path)
|
||||
state = state.copy(turniere = newList)
|
||||
}
|
||||
}
|
||||
|
||||
fun addTurnier() {
|
||||
state = state.copy(turniere = state.turniere + TurnierEntry())
|
||||
fun addTurnier(nummer: String = "", pfad: String? = null) {
|
||||
state = state.copy(turniere = state.turniere + TurnierEntry(nummer = nummer, ausschreibungPath = pfad))
|
||||
}
|
||||
|
||||
fun removeTurnier(index: Int) {
|
||||
if (state.turniere.size > 1) {
|
||||
val newList = state.turniere.toMutableList().apply { removeAt(index) }
|
||||
state = state.copy(turniere = newList)
|
||||
}
|
||||
val newList = state.turniere.toMutableList().apply { removeAt(index) }
|
||||
state = state.copy(turniere = newList)
|
||||
}
|
||||
|
||||
fun saveVeranstaltung() {
|
||||
|
||||
+3
-3
@@ -43,12 +43,12 @@ fun VeranstaltungenScreen(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Veranstaltungen - verwalten",
|
||||
text = "Events - verwalten",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
MsButton(
|
||||
text = "Neue Veranstaltung",
|
||||
text = "Neues Event",
|
||||
onClick = onVeranstaltungNeu
|
||||
)
|
||||
}
|
||||
@@ -119,7 +119,7 @@ fun VeranstaltungenScreen(
|
||||
)
|
||||
Spacer(Modifier.height(Dimens.SpacingM))
|
||||
Text(
|
||||
"Keine Veranstaltungen gefunden.",
|
||||
"Keine Events gefunden.",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user