Add Reiter and Pferd edit dialogs, extend Masterdata repository with save and fetch methods, and integrate editors into Nennungen tab UI. Fix DI configuration and update previews.
This commit is contained in:
+6
@@ -37,7 +37,13 @@ data class Verein(
|
||||
|
||||
interface MasterdataRepository {
|
||||
suspend fun searchReiter(query: String): Result<List<Reiter>>
|
||||
suspend fun getReiter(id: String): Result<Reiter>
|
||||
suspend fun saveReiter(reiter: Reiter): Result<Reiter>
|
||||
|
||||
suspend fun searchPferde(query: String): Result<List<Pferd>>
|
||||
suspend fun getPferd(id: String): Result<Pferd>
|
||||
suspend fun savePferd(pferd: Pferd): Result<Pferd>
|
||||
|
||||
suspend fun searchFunktionaere(query: String): Result<List<Funktionaer>>
|
||||
suspend fun listVereine(): Result<List<Verein>>
|
||||
suspend fun getVereinById(id: String): Result<Verein>
|
||||
|
||||
+48
-2
@@ -14,6 +14,9 @@ data class NennungenState(
|
||||
val nennungen: List<Nennung> = emptyList(),
|
||||
val searchResultsReiter: List<Reiter> = emptyList(),
|
||||
val searchResultsPferde: List<Pferd> = emptyList(),
|
||||
val searchResultsFunktionaere: List<Funktionaer> = emptyList(),
|
||||
val selectedReiter: Reiter? = null,
|
||||
val selectedPferd: Pferd? = null,
|
||||
val errorMessage: String? = null
|
||||
)
|
||||
|
||||
@@ -43,7 +46,10 @@ class NennungViewModel(
|
||||
}
|
||||
|
||||
fun searchReiter(query: String) {
|
||||
if (query.length < 2) return
|
||||
if (query.length < 2) {
|
||||
_state.value = _state.value.copy(searchResultsReiter = emptyList())
|
||||
return
|
||||
}
|
||||
scope.launch {
|
||||
masterdataRepo.searchReiter(query).onSuccess { list ->
|
||||
_state.value = _state.value.copy(searchResultsReiter = list)
|
||||
@@ -51,8 +57,24 @@ class NennungViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun selectReiter(reiter: Reiter?) {
|
||||
_state.value = _state.value.copy(selectedReiter = reiter)
|
||||
}
|
||||
|
||||
fun saveReiter(reiter: Reiter) {
|
||||
scope.launch {
|
||||
masterdataRepo.saveReiter(reiter).onSuccess {
|
||||
_state.value = _state.value.copy(selectedReiter = null)
|
||||
// Evtl. Suchen/Listen aktualisieren
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun searchPferde(query: String) {
|
||||
if (query.length < 2) return
|
||||
if (query.length < 2) {
|
||||
_state.value = _state.value.copy(searchResultsPferde = emptyList())
|
||||
return
|
||||
}
|
||||
scope.launch {
|
||||
masterdataRepo.searchPferde(query).onSuccess { list ->
|
||||
_state.value = _state.value.copy(searchResultsPferde = list)
|
||||
@@ -60,6 +82,30 @@ class NennungViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun selectPferd(pferd: Pferd?) {
|
||||
_state.value = _state.value.copy(selectedPferd = pferd)
|
||||
}
|
||||
|
||||
fun savePferd(pferd: Pferd) {
|
||||
scope.launch {
|
||||
masterdataRepo.savePferd(pferd).onSuccess {
|
||||
_state.value = _state.value.copy(selectedPferd = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun searchFunktionaere(query: String) {
|
||||
if (query.length < 2) {
|
||||
_state.value = _state.value.copy(searchResultsFunktionaere = emptyList())
|
||||
return
|
||||
}
|
||||
scope.launch {
|
||||
masterdataRepo.searchFunktionaere(query).onSuccess { list ->
|
||||
_state.value = _state.value.copy(searchResultsFunktionaere = list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun einreichen(bewerbId: String, abteilungId: String, reiterId: String, pferdId: String) {
|
||||
_state.value = _state.value.copy(isLoading = true)
|
||||
scope.launch {
|
||||
|
||||
+48
@@ -22,6 +22,30 @@ class DefaultMasterdataRepository(
|
||||
} else emptyList()
|
||||
}
|
||||
|
||||
override suspend fun getReiter(id: String): Result<Reiter> = runCatching {
|
||||
val response = client.get("${ApiRoutes.Masterdata.REITER}/$id")
|
||||
if (response.status.isSuccess()) {
|
||||
response.body<ReiterApiDto>().toDomain()
|
||||
} else throw Exception("Reiter nicht gefunden")
|
||||
}
|
||||
|
||||
override suspend fun saveReiter(reiter: Reiter): Result<Reiter> = runCatching {
|
||||
val response = client.put("${ApiRoutes.Masterdata.REITER}/${reiter.id}") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(ReiterApiDto(
|
||||
reiterId = reiter.id,
|
||||
vorname = reiter.vorname,
|
||||
nachname = reiter.nachname,
|
||||
satznummer = reiter.satznummer,
|
||||
vereinsName = reiter.verein,
|
||||
feiId = reiter.feiId
|
||||
))
|
||||
}
|
||||
if (response.status.isSuccess()) {
|
||||
response.body<ReiterApiDto>().toDomain()
|
||||
} else throw Exception("Fehler beim Speichern des Reiters")
|
||||
}
|
||||
|
||||
override suspend fun searchPferde(query: String): Result<List<Pferd>> = runCatching {
|
||||
val response = client.get("${ApiRoutes.Masterdata.PFERDE}/search") {
|
||||
parameter("q", query)
|
||||
@@ -31,6 +55,30 @@ class DefaultMasterdataRepository(
|
||||
} else emptyList()
|
||||
}
|
||||
|
||||
override suspend fun getPferd(id: String): Result<Pferd> = runCatching {
|
||||
val response = client.get("${ApiRoutes.Masterdata.PFERDE}/$id")
|
||||
if (response.status.isSuccess()) {
|
||||
response.body<HorseApiDto>().toDomain()
|
||||
} else throw Exception("Pferd nicht gefunden")
|
||||
}
|
||||
|
||||
override suspend fun savePferd(pferd: Pferd): Result<Pferd> = runCatching {
|
||||
val response = client.put("${ApiRoutes.Masterdata.PFERDE}/${pferd.id}") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(HorseApiDto(
|
||||
pferdId = pferd.id,
|
||||
pferdeName = pferd.name,
|
||||
lebensnummer = pferd.lebensnummer,
|
||||
geschlecht = "UNBEKANNT", // Fallback
|
||||
geburtsjahr = pferd.geburtsjahr,
|
||||
satznummer = pferd.oepsNummer
|
||||
))
|
||||
}
|
||||
if (response.status.isSuccess()) {
|
||||
response.body<HorseApiDto>().toDomain()
|
||||
} else throw Exception("Fehler beim Speichern des Pferdes")
|
||||
}
|
||||
|
||||
override suspend fun searchFunktionaere(query: String): Result<List<Funktionaer>> = runCatching {
|
||||
val response = client.get("${ApiRoutes.Masterdata.FUNKTIONAERE}/search") {
|
||||
parameter("q", query)
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package at.mocode.turnier.feature.presentation
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.unit.dp
|
||||
import at.mocode.turnier.feature.domain.Pferd
|
||||
import at.mocode.turnier.feature.domain.Reiter
|
||||
|
||||
@Composable
|
||||
fun ReiterEditDialog(
|
||||
reiter: Reiter,
|
||||
onDismiss: () -> Unit,
|
||||
onSave: (Reiter) -> Unit
|
||||
) {
|
||||
var vorname by remember { mutableStateOf(reiter.vorname) }
|
||||
var nachname by remember { mutableStateOf(reiter.nachname) }
|
||||
var oepsNummer by remember { mutableStateOf(reiter.oepsNummer ?: "") }
|
||||
var verein by remember { mutableStateOf(reiter.verein ?: "") }
|
||||
var feiId by remember { mutableStateOf(reiter.feiId ?: "") }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text("Reiter bearbeiten") },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
OutlinedTextField(value = vorname, onValueChange = { vorname = it }, label = { Text("Vorname") })
|
||||
OutlinedTextField(value = nachname, onValueChange = { nachname = it }, label = { Text("Nachname") })
|
||||
OutlinedTextField(value = oepsNummer, onValueChange = { oepsNummer = it }, label = { Text("OEPS-Nr.") })
|
||||
OutlinedTextField(value = verein, onValueChange = { verein = it }, label = { Text("Verein") })
|
||||
OutlinedTextField(value = feiId, onValueChange = { feiId = it }, label = { Text("FEI-ID") })
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Button(onClick = {
|
||||
onSave(reiter.copy(
|
||||
vorname = vorname,
|
||||
nachname = nachname,
|
||||
oepsNummer = oepsNummer,
|
||||
satznummer = oepsNummer,
|
||||
verein = verein,
|
||||
feiId = feiId
|
||||
))
|
||||
}) {
|
||||
Text("Speichern")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("Abbrechen")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PferdEditDialog(
|
||||
pferd: Pferd,
|
||||
onDismiss: () -> Unit,
|
||||
onSave: (Pferd) -> Unit
|
||||
) {
|
||||
var name by remember { mutableStateOf(pferd.name) }
|
||||
var lebensnummer by remember { mutableStateOf(pferd.lebensnummer) }
|
||||
var oepsNummer by remember { mutableStateOf(pferd.oepsNummer ?: "") }
|
||||
var geburtsjahr by remember { mutableStateOf(pferd.geburtsjahr?.toString() ?: "") }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text("Pferd bearbeiten") },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
OutlinedTextField(value = name, onValueChange = { name = it }, label = { Text("Name") })
|
||||
OutlinedTextField(value = lebensnummer, onValueChange = { lebensnummer = it }, label = { Text("Lebensnummer") })
|
||||
OutlinedTextField(value = oepsNummer, onValueChange = { oepsNummer = it }, label = { Text("OEPS-Nr.") })
|
||||
OutlinedTextField(value = geburtsjahr, onValueChange = { geburtsjahr = it }, label = { Text("Geburtsjahr") })
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Button(onClick = {
|
||||
onSave(pferd.copy(
|
||||
name = name,
|
||||
lebensnummer = lebensnummer,
|
||||
oepsNummer = oepsNummer,
|
||||
geburtsjahr = geburtsjahr.toIntOrNull()
|
||||
))
|
||||
}) {
|
||||
Text("Speichern")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("Abbrechen")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
+16
@@ -36,6 +36,22 @@ fun NennungenTabContent(
|
||||
) {
|
||||
val state by viewModel.state.collectAsState()
|
||||
|
||||
// --- Editoren ---
|
||||
state.selectedReiter?.let { reiter ->
|
||||
ReiterEditDialog(
|
||||
reiter = reiter,
|
||||
onDismiss = { viewModel.selectReiter(null) },
|
||||
onSave = { viewModel.saveReiter(it) }
|
||||
)
|
||||
}
|
||||
state.selectedPferd?.let { pferd ->
|
||||
PferdEditDialog(
|
||||
pferd = pferd,
|
||||
onDismiss = { viewModel.selectPferd(null) },
|
||||
onSave = { viewModel.savePferd(it) }
|
||||
)
|
||||
}
|
||||
|
||||
Row(modifier = Modifier.fillMaxSize()) {
|
||||
// ── Linke Spalte: Suche + Tabelle ─────────────────────────────────────
|
||||
Column(modifier = Modifier.weight(1f).fillMaxHeight()) {
|
||||
|
||||
@@ -44,6 +44,10 @@ fun DesktopApp() {
|
||||
&& currentScreen !is AppScreen.VeranstalterDetail && currentScreen !is AppScreen.VeranstaltungKonfig
|
||||
&& currentScreen !is AppScreen.VeranstaltungProfil && currentScreen !is AppScreen.TurnierDetail
|
||||
&& currentScreen !is AppScreen.TurnierNeu
|
||||
&& currentScreen !is AppScreen.ReiterVerwaltung
|
||||
&& currentScreen !is AppScreen.PferdVerwaltung
|
||||
&& currentScreen !is AppScreen.VereinVerwaltung
|
||||
&& currentScreen !is AppScreen.StammdatenImport
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
// Standard: Start im Onboarding
|
||||
|
||||
@@ -17,6 +17,7 @@ import at.mocode.frontend.features.verein.di.vereinFeatureModule
|
||||
import at.mocode.frontend.features.nennung.di.nennungFeatureModule
|
||||
import at.mocode.frontend.features.pferde.di.pferdeModule
|
||||
import at.mocode.frontend.features.reiter.di.reiterModule
|
||||
import at.mocode.turnier.feature.di.turnierFeatureModule
|
||||
import at.mocode.ping.feature.di.pingFeatureModule
|
||||
import at.mocode.zns.feature.di.znsImportModule
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -41,6 +42,7 @@ fun main() = application {
|
||||
pferdeModule,
|
||||
reiterModule,
|
||||
vereinFeatureModule,
|
||||
turnierFeatureModule,
|
||||
desktopModule,
|
||||
)
|
||||
}
|
||||
|
||||
+8
@@ -117,6 +117,10 @@ fun PreviewTurnierOrganisationTab() {
|
||||
val mockMasterdataRepo = object : MasterdataRepository {
|
||||
override suspend fun searchReiter(query: String): Result<List<Reiter>> = Result.success(emptyList())
|
||||
override suspend fun searchPferde(query: String): Result<List<Pferd>> = Result.success(emptyList())
|
||||
override suspend fun getReiter(id: String): Result<Reiter> = Result.failure(NotImplementedError())
|
||||
override suspend fun saveReiter(reiter: Reiter): Result<Reiter> = Result.success(reiter)
|
||||
override suspend fun getPferd(id: String): Result<Pferd> = Result.failure(NotImplementedError())
|
||||
override suspend fun savePferd(pferd: Pferd): Result<Pferd> = Result.success(pferd)
|
||||
override suspend fun searchFunktionaere(query: String): Result<List<Funktionaer>> = Result.success(emptyList())
|
||||
override suspend fun listVereine(): Result<List<Verein>> = Result.success(emptyList())
|
||||
override suspend fun getVereinById(id: String): Result<Verein> = Result.failure(NotImplementedError())
|
||||
@@ -185,6 +189,10 @@ fun PreviewTurnierNennungenTab() {
|
||||
val mockMasterdataRepo = object : MasterdataRepository {
|
||||
override suspend fun searchReiter(query: String): Result<List<Reiter>> = Result.success(emptyList())
|
||||
override suspend fun searchPferde(query: String): Result<List<Pferd>> = Result.success(emptyList())
|
||||
override suspend fun getReiter(id: String): Result<Reiter> = Result.failure(NotImplementedError())
|
||||
override suspend fun saveReiter(reiter: Reiter): Result<Reiter> = Result.success(reiter)
|
||||
override suspend fun getPferd(id: String): Result<Pferd> = Result.failure(NotImplementedError())
|
||||
override suspend fun savePferd(pferd: Pferd): Result<Pferd> = Result.success(pferd)
|
||||
override suspend fun searchFunktionaere(query: String): Result<List<Funktionaer>> = Result.success(emptyList())
|
||||
override suspend fun listVereine(): Result<List<Verein>> = Result.success(emptyList())
|
||||
override suspend fun getVereinById(id: String): Result<Verein> = Result.failure(NotImplementedError())
|
||||
|
||||
Reference in New Issue
Block a user