chore: implementiere Suche nach Veranstalter via OEPS-Nummer, verbessere UI-Flow im Veranstaltungs-Wizard und erweitere VereinRepository um OEPS-Abfrage
Desktop CI — Headless Tests & Build / Compose Desktop — Tests (headless) & Build (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Has been cancelled
Desktop CI — Headless Tests & Build / Compose Desktop — Tests (headless) & Build (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Has been cancelled
Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
+1
-1
@@ -245,7 +245,7 @@ actual fun DeviceInitializationConfig(
|
||||
Text("Client hinzufügen")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (settings.networkRole != NetworkRole.MASTER) {
|
||||
HorizontalDivider(Modifier, DividerDefaults.Thickness, DividerDefaults.color)
|
||||
Text("🔍 Verfügbare Master im Netzwerk", style = MaterialTheme.typography.titleSmall)
|
||||
|
||||
|
||||
+1
-1
@@ -211,7 +211,7 @@ fun VeranstalterAuswahlScreen(
|
||||
colors = ButtonDefaults.buttonColors(containerColor = PrimaryBlue),
|
||||
shape = MaterialTheme.shapes.medium
|
||||
) {
|
||||
Text("Weiter zur Turnier-Konfiguration")
|
||||
Text("Veranstalter auswählen & Weiter")
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowForward, null, modifier = Modifier.size(16.dp))
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ kotlin {
|
||||
implementation(projects.frontend.core.domain)
|
||||
implementation(projects.core.coreDomain)
|
||||
implementation(projects.frontend.core.auth)
|
||||
implementation(projects.frontend.features.vereinFeature)
|
||||
implementation(projects.frontend.features.deviceInitialization)
|
||||
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.runtime)
|
||||
|
||||
+50
-17
@@ -8,7 +8,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -187,30 +187,63 @@ private fun ZnsCheckStep(viewModel: VeranstaltungWizardViewModel) {
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalUuidApi::class)
|
||||
@Composable
|
||||
private fun VeranstalterSelectionStep(viewModel: VeranstaltungWizardViewModel) {
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Text("Schritt 2: Veranstalter auswählen", style = MaterialTheme.typography.titleLarge)
|
||||
Text("Suchen Sie nach dem Verein (Name oder OEPS-Nummer).")
|
||||
|
||||
// Mock Suche
|
||||
OutlinedTextField(
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
label = { Text("Verein suchen...") },
|
||||
MsTextField(
|
||||
value = searchQuery,
|
||||
onValueChange = {
|
||||
searchQuery = it
|
||||
if (it.length >= 3) {
|
||||
viewModel.searchVeranstalterByOepsNr(it)
|
||||
}
|
||||
},
|
||||
label = "Verein suchen (z.B. 6-009)",
|
||||
placeholder = "OEPS-Nummer eingeben...",
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Button(onClick = {
|
||||
// Mock Selection
|
||||
viewModel.setVeranstalter(
|
||||
id = kotlin.uuid.Uuid.random(),
|
||||
nummer = "6-009",
|
||||
name = "Union Reit- u. Fahrverein Neumarkt/M.",
|
||||
standardOrt = "4212 Neumarkt, Reitanlage Stroblmair",
|
||||
logo = null
|
||||
if (viewModel.state.veranstalterId != null) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Icon(Icons.Default.CheckCircle, null, tint = MaterialTheme.colorScheme.primary)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
viewModel.state.veranstalterName,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Text("OEPS-Nr: ${viewModel.state.veranstalterVereinsNummer}")
|
||||
}
|
||||
Button(onClick = { viewModel.nextStep() }) {
|
||||
Text("Auswählen & Weiter")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Information für den User
|
||||
Text(
|
||||
"Geben Sie mindestens 3 Zeichen der OEPS-Nummer ein, um die Stammdaten zu durchsuchen.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
viewModel.nextStep()
|
||||
}) {
|
||||
Text("Union Reit- u. Fahrverein Neumarkt/M. (6-009) wählen")
|
||||
|
||||
// Fallback/Demo Button beibehalten für 6-009
|
||||
OutlinedButton(
|
||||
onClick = { viewModel.searchVeranstalterByOepsNr("6-009") },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("Beispiel: Union Reit- u. Fahrverein Neumarkt/M. (6-009) suchen")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-4
@@ -7,8 +7,8 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.mocode.core.domain.serialization.UuidSerializer
|
||||
import at.mocode.frontend.core.auth.data.local.AuthTokenManager
|
||||
import at.mocode.frontend.core.domain.zns.ZnsImportProvider
|
||||
import at.mocode.frontend.core.network.NetworkConfig
|
||||
import at.mocode.frontend.features.verein.domain.VereinRepository
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.http.*
|
||||
@@ -58,7 +58,7 @@ data class VeranstaltungWizardState(
|
||||
class VeranstaltungWizardViewModel(
|
||||
private val httpClient: HttpClient,
|
||||
private val authTokenManager: AuthTokenManager,
|
||||
val znsViewModel: ZnsImportProvider
|
||||
private val vereinRepository: VereinRepository
|
||||
) : ViewModel() {
|
||||
|
||||
var state by mutableStateOf(VeranstaltungWizardState())
|
||||
@@ -71,13 +71,28 @@ class VeranstaltungWizardViewModel(
|
||||
}
|
||||
|
||||
fun checkZnsAvailability() {
|
||||
// Hier prüfen wir, ob Stammdaten vorhanden sind (Simuliert)
|
||||
viewModelScope.launch {
|
||||
val hasData = true // Simulation: Stammdaten sind da
|
||||
val vereineResult = vereinRepository.getVereine()
|
||||
val hasData = vereineResult.getOrNull()?.isNotEmpty() ?: false
|
||||
state = state.copy(isZnsAvailable = hasData)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun nextStep() {
|
||||
state = state.copy(
|
||||
currentStep = when (state.currentStep) {
|
||||
@@ -149,6 +164,9 @@ class VeranstaltungWizardViewModel(
|
||||
viewModelScope.launch {
|
||||
state = state.copy(isSaving = true, error = null)
|
||||
try {
|
||||
// PDF-Kopiervorgang (lokal) entfernt wegen Import-Problemen in dieser Umgebung
|
||||
// TODO: File-Copy Logik in ein Platform-Service auslagern
|
||||
|
||||
val token = authTokenManager.authState.value.token
|
||||
val response = httpClient.post("${NetworkConfig.baseUrl}/api/events") {
|
||||
if (token != null) header(HttpHeaders.Authorization, "Bearer $token")
|
||||
|
||||
+4
@@ -26,6 +26,10 @@ class FakeVereinRepository : VereinRepository {
|
||||
|
||||
override suspend fun getVereine(): Result<List<Verein>> = Result.success(vereine.toList())
|
||||
|
||||
override suspend fun findByOepsNr(oepsNr: String): Verein? {
|
||||
return vereine.find { it.oepsNr == oepsNr }
|
||||
}
|
||||
|
||||
override suspend fun saveVerein(verein: Verein): Result<Verein> {
|
||||
val index = vereine.indexOfFirst { it.id == verein.id }
|
||||
if (index >= 0) {
|
||||
|
||||
+11
@@ -41,6 +41,17 @@ class KtorVereinRepository(
|
||||
} else emptyList()
|
||||
}
|
||||
|
||||
override suspend fun findByOepsNr(oepsNr: String): Verein? {
|
||||
return runCatching {
|
||||
val response = client.get("${ApiRoutes.Masterdata.VEREINE}/search") {
|
||||
parameter("oepsNr", oepsNr)
|
||||
}
|
||||
if (response.status.isSuccess()) {
|
||||
response.body<VereinDto>().toDomain()
|
||||
} else null
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
override suspend fun saveVerein(verein: Verein): Result<Verein> = runCatching {
|
||||
if (verein.id.isBlank() || verein.id.startsWith("new_")) {
|
||||
val request = VereinCreateRequest(
|
||||
|
||||
Reference in New Issue
Block a user