Integrate Ktor HTTP clients and repositories for Veranstalter and Turnier features:

- Add `ApiRoutes` for central backend routing configuration.
- Implement `DefaultVeranstalterRepository` and `DefaultTurnierRepository` with Ktor clients.
- Add domain models (`Turnier`, `Bewerb`, `Abteilung`, `Veranstalter`) and respective repository interfaces.
- Replace fake VeranstalterRepository with real implementation.
- Update DI with `veranstalterModule` and HTTP client injection.
- Simplify TokenProvider and update HttpClient setup (timeouts, retries, logging).
- Mark roadmap tasks B-2 as partially complete.
This commit is contained in:
2026-04-03 01:09:30 +02:00
parent a5c1fb5bae
commit f82dbd64a5
17 changed files with 314 additions and 67 deletions
@@ -0,0 +1,20 @@
package at.mocode.frontend.features.veranstalter.data.mapper
import at.mocode.frontend.features.veranstalter.data.remote.dto.VeranstalterDto
import at.mocode.frontend.features.veranstalter.domain.Veranstalter
fun VeranstalterDto.toDomain(): Veranstalter = Veranstalter(
id = id,
name = name,
oepsNummer = oepsNummer,
ort = ort,
loginStatus = loginStatus,
)
fun Veranstalter.toDto(): VeranstalterDto = VeranstalterDto(
id = id,
name = name,
oepsNummer = oepsNummer,
ort = ort,
loginStatus = loginStatus,
)
@@ -0,0 +1,12 @@
package at.mocode.frontend.features.veranstalter.data.remote.dto
import kotlinx.serialization.Serializable
@Serializable
data class VeranstalterDto(
val id: Long,
val name: String,
val oepsNummer: String,
val ort: String,
val loginStatus: String,
)
@@ -0,0 +1,23 @@
package at.mocode.frontend.features.veranstalter.domain
/**
* Domänenmodell für Veranstalter (V3-Minimum für Listenansicht).
*/
data class Veranstalter(
val id: Long,
val name: String,
val oepsNummer: String,
val ort: String,
val loginStatus: String,
)
/**
* Repository-Vertrag (commonMain) austauschbar zwischen Mock und Real.
*/
interface VeranstalterRepository {
suspend fun list(): Result<List<Veranstalter>>
suspend fun getById(id: Long): Result<Veranstalter>
suspend fun create(model: Veranstalter): Result<Veranstalter>
suspend fun update(id: Long, model: Veranstalter): Result<Veranstalter>
suspend fun delete(id: Long): Result<Unit>
}
@@ -6,6 +6,8 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import at.mocode.frontend.features.veranstalter.domain.VeranstalterRepository
import at.mocode.frontend.features.veranstalter.domain.Veranstalter as DomainVeranstalter
// UDF: State beschreibt die gesamte UI in einem Snapshot
data class VeranstalterState(
@@ -34,11 +36,6 @@ data class VeranstalterListItem(
val loginStatus: String,
)
// Repository-Vertrag (später gegen echte Backend-Repositories austauschbar)
interface VeranstalterRepository {
suspend fun list(): List<VeranstalterListItem>
}
class VeranstalterViewModel(
private val repo: VeranstalterRepository,
) {
@@ -64,14 +61,14 @@ class VeranstalterViewModel(
private fun load() {
reduce { it.copy(isLoading = true, errorMessage = null) }
scope.launch {
try {
val items = repo.list()
// Nach dem Laden auch initial filtern
val result = repo.list()
result.onSuccess { domainList ->
val items = domainList.map { it.toListItem() }
reduce { cur ->
val filtered = filterList(items, cur.searchQuery)
cur.copy(isLoading = false, list = items, filtered = filtered)
}
} catch (t: Throwable) {
}.onFailure { t ->
reduce { it.copy(isLoading = false, errorMessage = t.message ?: "Unbekannter Fehler beim Laden") }
}
}
@@ -97,3 +94,11 @@ class VeranstalterViewModel(
_state.value = block(_state.value)
}
}
private fun DomainVeranstalter.toListItem() = VeranstalterListItem(
id = id,
name = name,
oepsNummer = oepsNummer,
ort = ort,
loginStatus = loginStatus,
)