### feat: aktualisiere Netzwerk-URLs und vereinfachte Online-Nennung
- **PlatformConfig:** Passe Standard-URLs für lokale Dienste an (`8092` statt `8083/8085`). - **OnlineNennungFormular:** Entferne zusätzliche Felder und vereinfachere Validierungslogik. - **OnlineNennungViewModel:** Ersetze HttpClient-Logik durch Repository-Injektion. - **DI:** Aktualisiere Dependencies für `NennungRemoteRepository`.
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@ actual object PlatformConfig {
|
|||||||
actual fun resolveMailServiceUrl(): String {
|
actual fun resolveMailServiceUrl(): String {
|
||||||
val env = System.getenv("MAIL_SERVICE_URL")?.trim().orEmpty()
|
val env = System.getenv("MAIL_SERVICE_URL")?.trim().orEmpty()
|
||||||
if (env.isNotEmpty()) return env.removeSuffix("/")
|
if (env.isNotEmpty()) return env.removeSuffix("/")
|
||||||
return "http://localhost:8083"
|
return "http://localhost:8092"
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun resolveKeycloakUrl(): String {
|
actual fun resolveKeycloakUrl(): String {
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ actual object PlatformConfig {
|
|||||||
actual fun resolveMailServiceUrl(): String {
|
actual fun resolveMailServiceUrl(): String {
|
||||||
val fromGlobal = getGlobalMailServiceUrl()
|
val fromGlobal = getGlobalMailServiceUrl()
|
||||||
if (fromGlobal.isNotEmpty()) return fromGlobal.removeSuffix("/")
|
if (fromGlobal.isNotEmpty()) return fromGlobal.removeSuffix("/")
|
||||||
return "http://localhost:8085"
|
return "http://localhost:8092"
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun resolveKeycloakUrl(): String {
|
actual fun resolveKeycloakUrl(): String {
|
||||||
|
|||||||
+1
-1
@@ -11,5 +11,5 @@ import org.koin.dsl.module
|
|||||||
val nennungFeatureModule = module {
|
val nennungFeatureModule = module {
|
||||||
single<NennungRemoteRepository> { NennungRemoteRepository(get<HttpClient>(named("apiClient"))) }
|
single<NennungRemoteRepository> { NennungRemoteRepository(get<HttpClient>(named("apiClient"))) }
|
||||||
viewModel { NennungViewModel() }
|
viewModel { NennungViewModel() }
|
||||||
viewModel { OnlineNennungViewModel(get(named("apiClient"))) }
|
viewModel { OnlineNennungViewModel(get()) }
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-258
@@ -1,12 +1,8 @@
|
|||||||
package at.mocode.frontend.features.nennung.presentation.web
|
package at.mocode.frontend.features.nennung.presentation.web
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Info
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -17,7 +13,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import at.mocode.frontend.core.designsystem.theme.AppColors
|
import at.mocode.frontend.core.designsystem.theme.AppColors
|
||||||
import at.mocode.frontend.features.nennung.domain.Bewerb
|
import at.mocode.frontend.features.nennung.domain.Bewerb
|
||||||
import at.mocode.frontend.features.nennung.domain.NennungMockData
|
import at.mocode.frontend.features.nennung.domain.Sparte
|
||||||
|
|
||||||
data class NennungPayload(
|
data class NennungPayload(
|
||||||
val vorname: String,
|
val vorname: String,
|
||||||
@@ -39,271 +35,92 @@ fun OnlineNennungFormular(
|
|||||||
) {
|
) {
|
||||||
var vorname by remember { mutableStateOf("") }
|
var vorname by remember { mutableStateOf("") }
|
||||||
var nachname by remember { mutableStateOf("") }
|
var nachname by remember { mutableStateOf("") }
|
||||||
var lizenz by remember { mutableStateOf("Lizenzfrei") }
|
|
||||||
var pferdName by remember { mutableStateOf("") }
|
|
||||||
var pferdAlter by remember { mutableStateOf("2020") }
|
|
||||||
var email by remember { mutableStateOf("") }
|
var email by remember { mutableStateOf("") }
|
||||||
var telefon by remember { mutableStateOf("") }
|
|
||||||
var bemerkungen by remember { mutableStateOf("") }
|
|
||||||
var dsgvoAkzeptiert by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
val ausgewaehlteBewerbe = remember { mutableStateListOf<Bewerb>() }
|
|
||||||
|
|
||||||
val lizenzen = listOf("Lizenzfrei", "R1", "R2", "R3", "R4", "RS1", "RS2")
|
|
||||||
val jahre = (2000..2022).map { it.toString() }.reversed()
|
|
||||||
|
|
||||||
val isEmailValid = email.contains("@") && email.contains(".")
|
val isEmailValid = email.contains("@") && email.contains(".")
|
||||||
val canSubmit = vorname.isNotBlank() &&
|
val canSubmit = vorname.isNotBlank() && nachname.isNotBlank() && isEmailValid
|
||||||
nachname.isNotBlank() &&
|
|
||||||
pferdName.isNotBlank() &&
|
|
||||||
isEmailValid &&
|
|
||||||
ausgewaehlteBewerbe.isNotEmpty() &&
|
|
||||||
dsgvoAkzeptiert
|
|
||||||
|
|
||||||
// Clean-White Layout: Hintergrund hellgrau, Formular in weißen Cards
|
Box(
|
||||||
Box(modifier = Modifier.fillMaxSize().background(Color(0xFFF8F9FA))) {
|
modifier = Modifier.fillMaxSize().background(Color(0xFFF8F9FA)),
|
||||||
LazyColumn(
|
contentAlignment = Alignment.Center
|
||||||
modifier = Modifier.fillMaxSize().padding(horizontal = 16.dp),
|
) {
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
Card(
|
||||||
|
modifier = Modifier.width(400.dp).padding(16.dp),
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = Color.White),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
|
||||||
) {
|
) {
|
||||||
item {
|
Column(
|
||||||
Spacer(Modifier.height(32.dp))
|
modifier = Modifier.padding(24.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Turnier Online-Nennung",
|
text = "Hallo Du! 👋",
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
fontWeight = FontWeight.ExtraBold,
|
fontWeight = FontWeight.ExtraBold,
|
||||||
color = Color(0xFF2D3436)
|
color = AppColors.Primary
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Turnier-Nr: $turnierNr",
|
text = "Lass uns Plan-B testen. Turnier: $turnierNr",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = Color.Gray,
|
|
||||||
modifier = Modifier.padding(bottom = 24.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- REITER CARD ---
|
|
||||||
item {
|
|
||||||
FormCard("Persönliche Daten (Reiter)") {
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
|
||||||
ModernTextField(vorname, { vorname = it }, "Vorname *", Modifier.weight(1f))
|
|
||||||
ModernTextField(nachname, { nachname = it }, "Nachname *", Modifier.weight(1f))
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("Lizenzklasse", style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.Bold)
|
|
||||||
DropdownSelector(lizenz, lizenzen) { lizenz = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- PFERD CARD ---
|
|
||||||
item {
|
|
||||||
FormCard("Pferdedaten") {
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
|
||||||
ModernTextField(pferdName, { pferdName = it }, "Name oder Kopfnummer *")
|
|
||||||
|
|
||||||
Text("Geburtsjahr", style = MaterialTheme.typography.labelLarge, fontWeight = FontWeight.Bold)
|
|
||||||
DropdownSelector(pferdAlter, jahre) { pferdAlter = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- KONTAKT CARD ---
|
|
||||||
item {
|
|
||||||
FormCard("Kontakt für Rückfragen") {
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
|
||||||
ModernTextField(
|
|
||||||
value = email,
|
|
||||||
onValueChange = { email = it },
|
|
||||||
label = "E-Mail Adresse *",
|
|
||||||
isError = email.isNotBlank() && !isEmailValid
|
|
||||||
)
|
|
||||||
ModernTextField(telefon, { telefon = it }, "Telefonnummer (optional)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- BEWERBE CARD ---
|
|
||||||
item {
|
|
||||||
FormCard("Bewerbe & Prüfungen") {
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
|
||||||
NennungMockData.bewerbe.forEach { bewerb ->
|
|
||||||
val isSelected = ausgewaehlteBewerbe.any { it.nr == bewerb.nr }
|
|
||||||
BewerbRow(bewerb, isSelected) {
|
|
||||||
if (isSelected) {
|
|
||||||
val item = ausgewaehlteBewerbe.find { it.nr == bewerb.nr }
|
|
||||||
if (item != null) ausgewaehlteBewerbe.remove(item)
|
|
||||||
} else {
|
|
||||||
ausgewaehlteBewerbe.add(bewerb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- WÜNSCHE CARD ---
|
|
||||||
item {
|
|
||||||
FormCard("Anmerkungen") {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = bemerkungen,
|
|
||||||
onValueChange = { bemerkungen = it },
|
|
||||||
placeholder = { Text("Besondere Wünsche, Stallplaketten, etc.") },
|
|
||||||
modifier = Modifier.fillMaxWidth().height(120.dp),
|
|
||||||
shape = RoundedCornerShape(12.dp),
|
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
|
||||||
focusedBorderColor = AppColors.Primary,
|
|
||||||
unfocusedBorderColor = Color(0xFFE0E0E0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- DSGVO & ABSCHLUSS ---
|
|
||||||
item {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxWidth().padding(vertical = 24.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier.clickable { dsgvoAkzeptiert = !dsgvoAkzeptiert }.padding(8.dp)
|
|
||||||
) {
|
|
||||||
Checkbox(checked = dsgvoAkzeptiert, onCheckedChange = { dsgvoAkzeptiert = it })
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
Text(
|
|
||||||
"Ich akzeptiere die Datenschutzbestimmungen.",
|
|
||||||
style = MaterialTheme.typography.bodyMedium
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
onNennenAbgeschickt(
|
|
||||||
NennungPayload(
|
|
||||||
vorname, nachname, lizenz, pferdName, pferdAlter,
|
|
||||||
email, telefon, ausgewaehlteBewerbe.toList(), bemerkungen
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
enabled = canSubmit,
|
|
||||||
modifier = Modifier.fillMaxWidth().height(56.dp),
|
|
||||||
shape = RoundedCornerShape(16.dp),
|
|
||||||
colors = ButtonDefaults.buttonColors(
|
|
||||||
containerColor = if (canSubmit) Color(0xFF2ECC71) else Color(0xFFBDC3C7)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text("JETZT NENNEN", fontWeight = FontWeight.Bold, fontSize = 16.sp)
|
|
||||||
}
|
|
||||||
|
|
||||||
TextButton(onClick = onBack, modifier = Modifier.padding(top = 8.dp)) {
|
|
||||||
Text("Abbrechen", color = Color.Gray)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(Modifier.height(48.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun FormCard(title: String, content: @Composable () -> Unit) {
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
|
|
||||||
shape = RoundedCornerShape(20.dp),
|
|
||||||
colors = CardDefaults.cardColors(containerColor = Color.White),
|
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
|
|
||||||
) {
|
|
||||||
Column(modifier = Modifier.padding(20.dp)) {
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = AppColors.Primary,
|
|
||||||
modifier = Modifier.padding(bottom = 16.dp)
|
|
||||||
)
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ModernTextField(
|
|
||||||
value: String,
|
|
||||||
onValueChange: (String) -> Unit,
|
|
||||||
label: String,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
isError: Boolean = false
|
|
||||||
) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = value,
|
|
||||||
onValueChange = onValueChange,
|
|
||||||
label = { Text(label) },
|
|
||||||
modifier = modifier.fillMaxWidth(),
|
|
||||||
shape = RoundedCornerShape(12.dp),
|
|
||||||
isError = isError,
|
|
||||||
singleLine = true,
|
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
|
||||||
focusedBorderColor = AppColors.Primary,
|
|
||||||
unfocusedBorderColor = Color(0xFFE0E0E0),
|
|
||||||
errorBorderColor = Color.Red
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DropdownSelector(current: String, options: List<String>, onSelect: (String) -> Unit) {
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
Box {
|
|
||||||
OutlinedButton(
|
|
||||||
onClick = { expanded = true },
|
|
||||||
modifier = Modifier.fillMaxWidth().height(56.dp),
|
|
||||||
shape = RoundedCornerShape(12.dp),
|
|
||||||
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.Black),
|
|
||||||
border = ButtonDefaults.outlinedButtonBorder(enabled = true).copy(brush = androidx.compose.ui.graphics.SolidColor(Color(0xFFE0E0E0)))
|
|
||||||
) {
|
|
||||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
Text(current)
|
|
||||||
Icon(Icons.Default.Info, null, modifier = Modifier.size(18.dp), tint = Color.LightGray)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
|
||||||
options.forEach { opt ->
|
|
||||||
DropdownMenuItem(text = { Text(opt) }, onClick = { onSelect(opt); expanded = false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun BewerbRow(bewerb: Bewerb, isSelected: Boolean, onClick: () -> Unit) {
|
|
||||||
Surface(
|
|
||||||
onClick = onClick,
|
|
||||||
shape = RoundedCornerShape(12.dp),
|
|
||||||
color = if (isSelected) Color(0xFFE8F5E9) else Color(0xFFF5F5F5),
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier.padding(12.dp)
|
|
||||||
) {
|
|
||||||
Checkbox(checked = isSelected, onCheckedChange = null)
|
|
||||||
Spacer(Modifier.width(12.dp))
|
|
||||||
Column {
|
|
||||||
Text(
|
|
||||||
"Bewerb ${bewerb.nr}: ${bewerb.name}",
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
fontSize = 14.sp
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
bewerb.tag,
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = Color.Gray
|
color = Color.Gray
|
||||||
)
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = vorname,
|
||||||
|
onValueChange = { vorname = it },
|
||||||
|
label = { Text("Vorname") },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = nachname,
|
||||||
|
onValueChange = { nachname = it },
|
||||||
|
label = { Text("Nachname") },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = email,
|
||||||
|
onValueChange = { email = it },
|
||||||
|
label = { Text("E-Mail Adresse") },
|
||||||
|
singleLine = true,
|
||||||
|
isError = email.isNotEmpty() && !isEmailValid,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
// Wir füllen den Rest mit Dummy-Daten für den Test
|
||||||
|
val payload = NennungPayload(
|
||||||
|
vorname = vorname,
|
||||||
|
nachname = nachname,
|
||||||
|
lizenz = "Lizenzfrei",
|
||||||
|
pferdName = "Test-Pferd (Plan-B)",
|
||||||
|
pferdAlter = "2020",
|
||||||
|
email = email,
|
||||||
|
telefon = "0123456789",
|
||||||
|
bewerbe = listOf(Bewerb(1, "Tag 1", 1, "08:00", "Test-Bewerb", Sparte.SPRINGEN, "A")),
|
||||||
|
bemerkungen = "Dies ist ein automatischer Test für Plan-B."
|
||||||
|
)
|
||||||
|
onNennenAbgeschickt(payload)
|
||||||
|
},
|
||||||
|
enabled = canSubmit,
|
||||||
|
modifier = Modifier.fillMaxWidth().height(50.dp),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = AppColors.Primary)
|
||||||
|
) {
|
||||||
|
Text("Jetzt schicken!", fontWeight = FontWeight.Bold, fontSize = 16.sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(onClick = onBack) {
|
||||||
|
Text("Zurück", color = Color.Gray)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-45
@@ -2,31 +2,12 @@ package at.mocode.frontend.features.nennung.presentation.web
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import io.ktor.client.*
|
import at.mocode.frontend.features.nennung.domain.NennungRemoteRepository
|
||||||
import io.ktor.client.request.*
|
|
||||||
import io.ktor.http.*
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class NennungDto(
|
|
||||||
val id: String? = null,
|
|
||||||
val turnierNr: String,
|
|
||||||
val status: String = "NEU",
|
|
||||||
val vorname: String,
|
|
||||||
val nachname: String,
|
|
||||||
val lizenz: String,
|
|
||||||
val pferdName: String,
|
|
||||||
val pferdAlter: String,
|
|
||||||
val email: String,
|
|
||||||
val telefon: String?,
|
|
||||||
val bewerbe: String, // Als JSON-String oder Komma-separiert
|
|
||||||
val bemerkungen: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class OnlineNennungUiState(
|
data class OnlineNennungUiState(
|
||||||
val isLoading: Boolean = false,
|
val isLoading: Boolean = false,
|
||||||
@@ -35,7 +16,7 @@ data class OnlineNennungUiState(
|
|||||||
)
|
)
|
||||||
|
|
||||||
class OnlineNennungViewModel(
|
class OnlineNennungViewModel(
|
||||||
private val httpClient: HttpClient
|
private val nennungRepository: NennungRemoteRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(OnlineNennungUiState())
|
private val _uiState = MutableStateFlow(OnlineNennungUiState())
|
||||||
@@ -44,31 +25,11 @@ class OnlineNennungViewModel(
|
|||||||
fun sendeNennung(turnierNr: String, payload: NennungPayload) {
|
fun sendeNennung(turnierNr: String, payload: NennungPayload) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_uiState.update { it.copy(isLoading = true, error = null) }
|
_uiState.update { it.copy(isLoading = true, error = null) }
|
||||||
try {
|
val result = nennungRepository.sendeNennung(turnierNr, payload)
|
||||||
val dto = NennungDto(
|
if (result.isSuccess) {
|
||||||
turnierNr = turnierNr,
|
|
||||||
vorname = payload.vorname,
|
|
||||||
nachname = payload.nachname,
|
|
||||||
lizenz = payload.lizenz,
|
|
||||||
pferdName = payload.pferdName,
|
|
||||||
pferdAlter = payload.pferdAlter,
|
|
||||||
email = payload.email,
|
|
||||||
telefon = payload.telefon,
|
|
||||||
bewerbe = payload.bewerbe.joinToString(",") { it.nr.toString() },
|
|
||||||
bemerkungen = payload.bemerkungen
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wir nutzen den httpClient, der via Koin injiziert wird.
|
|
||||||
// Da im Web-Frontend evtl. kein API-Gateway davor ist (oder ein anderes),
|
|
||||||
// konfigurieren wir den Pfad hier explizit.
|
|
||||||
httpClient.post("/api/mail/nennungen") {
|
|
||||||
contentType(ContentType.Application.Json)
|
|
||||||
setBody(dto)
|
|
||||||
}
|
|
||||||
|
|
||||||
_uiState.update { it.copy(isLoading = false, isSuccess = true) }
|
_uiState.update { it.copy(isLoading = false, isSuccess = true) }
|
||||||
} catch (e: Exception) {
|
} else {
|
||||||
_uiState.update { it.copy(isLoading = false, error = "Fehler beim Senden: ${e.message}") }
|
_uiState.update { it.copy(isLoading = false, error = "Fehler beim Senden: ${result.exceptionOrNull()?.message}") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user