chore: erweitere Veranstalter-Wizard um Bearbeitungsmodus, füge Kontaktdaten und Step-Logik hinzu
Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
+72
-6
@@ -9,12 +9,78 @@ import at.mocode.frontend.features.veranstalter.domain.VeranstalterRepository
|
||||
*/
|
||||
class FakeVeranstalterRepository : VeranstalterRepository {
|
||||
private val mockData = mutableListOf(
|
||||
Veranstalter(1, "URV Schloss Hof", "1-2345", "Schloßhof", "Aktiv"),
|
||||
Veranstalter(2, "RV Schloß Rosenau", "3-0012", "Rosenau", "Aktiv"),
|
||||
Veranstalter(3, "Reitclub Tulln", "3-1520", "Tulln", "Inaktiv"),
|
||||
Veranstalter(4, "RC St. Pölten", "3-0101", "St. Pölten", "Aktiv"),
|
||||
Veranstalter(5, "Union Reitklub Wien", "9-0001", "Wien", "Aktiv"),
|
||||
Veranstalter(6, "Reitclub Neumarkt", "6-009", "Neumarkt", "Aktiv")
|
||||
Veranstalter(
|
||||
id = 1,
|
||||
name = "URV Schloss Hof",
|
||||
oepsNummer = "1-2345",
|
||||
ort = "Schloßhof",
|
||||
loginStatus = "Aktiv",
|
||||
ansprechpartner = "Max Mustermann",
|
||||
email = "office@schlosshof.at",
|
||||
telefon = "+43 1 234567",
|
||||
adresse = "Schloßstraße 1, 2294 Schloßhof",
|
||||
mitgliedSeit = "01.01.2020"
|
||||
),
|
||||
Veranstalter(
|
||||
id = 2,
|
||||
name = "RV Schloß Rosenau",
|
||||
oepsNummer = "3-0012",
|
||||
ort = "Rosenau",
|
||||
loginStatus = "Aktiv",
|
||||
ansprechpartner = "Erika Muster",
|
||||
email = "erika@rosenau.at",
|
||||
telefon = "+43 2822 1234",
|
||||
adresse = "Schloßplatz 1, 3924 Rosenau",
|
||||
mitgliedSeit = "15.03.2018"
|
||||
),
|
||||
Veranstalter(
|
||||
id = 3,
|
||||
name = "Reitclub Tulln",
|
||||
oepsNummer = "3-1520",
|
||||
ort = "Tulln",
|
||||
loginStatus = "Inaktiv",
|
||||
ansprechpartner = "Hansi Hinterseer",
|
||||
email = "hansi@tulln.at",
|
||||
telefon = "+43 2272 5555",
|
||||
adresse = "Donauweg 10, 3430 Tulln",
|
||||
mitgliedSeit = "10.10.2010"
|
||||
),
|
||||
Veranstalter(
|
||||
id = 4,
|
||||
name = "RC St. Pölten",
|
||||
oepsNummer = "3-0101",
|
||||
ort = "St. Pölten",
|
||||
loginStatus = "Aktiv",
|
||||
ansprechpartner = "Petra Reiter",
|
||||
email = "petra@rc-stpoelten.at",
|
||||
telefon = "+43 2742 9876",
|
||||
adresse = "Pferdegasse 5, 3100 St. Pölten",
|
||||
mitgliedSeit = "20.05.2022"
|
||||
),
|
||||
Veranstalter(
|
||||
id = 5,
|
||||
name = "Union Reitklub Wien",
|
||||
oepsNummer = "9-0001",
|
||||
ort = "Wien",
|
||||
loginStatus = "Aktiv",
|
||||
ansprechpartner = "Stefan Wiener",
|
||||
email = "stefan@urkw.at",
|
||||
telefon = "+43 1 90001",
|
||||
adresse = "Hauptstraße 100, 1010 Wien",
|
||||
mitgliedSeit = "12.12.2012"
|
||||
),
|
||||
Veranstalter(
|
||||
id = 6,
|
||||
name = "Reitclub Neumarkt",
|
||||
oepsNummer = "6-009",
|
||||
ort = "Neumarkt",
|
||||
loginStatus = "Aktiv",
|
||||
ansprechpartner = "Karl Neumarkter",
|
||||
email = "karl@rc-neumarkt.at",
|
||||
telefon = "+43 6216 1234",
|
||||
adresse = "Mühlweg 1, 5202 Neumarkt am Wallersee",
|
||||
mitgliedSeit = "01.04.2024"
|
||||
)
|
||||
)
|
||||
|
||||
override suspend fun list(): Result<List<Veranstalter>> = Result.success(mockData)
|
||||
|
||||
+5
@@ -6,6 +6,11 @@ data class Veranstalter(
|
||||
val oepsNummer: String,
|
||||
val ort: String,
|
||||
val loginStatus: String,
|
||||
val ansprechpartner: String = "",
|
||||
val email: String = "",
|
||||
val telefon: String = "",
|
||||
val adresse: String = "",
|
||||
val mitgliedSeit: String = "",
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
+2
@@ -4,10 +4,12 @@ import at.mocode.frontend.features.veranstalter.data.remote.FakeVeranstalterRepo
|
||||
import at.mocode.frontend.features.veranstalter.domain.VeranstalterRepository
|
||||
import at.mocode.frontend.features.veranstalter.presentation.VeranstalterDetailViewModel
|
||||
import at.mocode.frontend.features.veranstalter.presentation.VeranstalterViewModel
|
||||
import at.mocode.frontend.features.veranstalter.presentation.VeranstalterWizardViewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val veranstalterModule = module {
|
||||
single<VeranstalterRepository> { FakeVeranstalterRepository() }
|
||||
factory { VeranstalterViewModel(get()) }
|
||||
factory { VeranstalterDetailViewModel(get()) }
|
||||
factory { VeranstalterWizardViewModel(get()) }
|
||||
}
|
||||
|
||||
+2
-1
@@ -51,6 +51,7 @@ fun VeranstalterDetailScreen(
|
||||
onZurueck: () -> Unit,
|
||||
onVeranstaltungOeffnen: (Long) -> Unit,
|
||||
onVeranstaltungNeu: () -> Unit,
|
||||
onEditVeranstalter: (Long) -> Unit,
|
||||
) {
|
||||
val state by viewModel.state.collectAsState()
|
||||
|
||||
@@ -134,7 +135,7 @@ fun VeranstalterDetailScreen(
|
||||
}
|
||||
// Profil bearbeiten
|
||||
OutlinedButton(
|
||||
onClick = { /* Navigation zu Vereinen */ },
|
||||
onClick = { onEditVeranstalter(veranstalter.id) },
|
||||
border = BorderStroke(1.dp, Color(0xFFD1D5DB)),
|
||||
) {
|
||||
Icon(Icons.Default.Settings, contentDescription = null, modifier = Modifier.size(14.dp))
|
||||
|
||||
+47
-59
@@ -1,14 +1,14 @@
|
||||
package at.mocode.frontend.features.veranstalter.presentation
|
||||
|
||||
import at.mocode.frontend.core.designsystem.models.LoginStatus
|
||||
import at.mocode.frontend.core.designsystem.models.VeranstaltungStatus
|
||||
import at.mocode.frontend.features.veranstalter.domain.VeranstalterRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
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.core.designsystem.models.VeranstaltungStatus
|
||||
import at.mocode.frontend.core.designsystem.models.LoginStatus
|
||||
|
||||
data class VeranstalterDetailState(
|
||||
val isLoading: Boolean = false,
|
||||
@@ -53,64 +53,52 @@ class VeranstalterDetailViewModel(
|
||||
private fun load(id: Long) {
|
||||
_state.value = _state.value.copy(isLoading = true)
|
||||
scope.launch {
|
||||
// In einer echten App würden wir hier das Repo abfragen.
|
||||
// Für den Prototyp nutzen wir vorerst die Logik aus dem Screen, aber im VM gekapselt.
|
||||
val result = repo.getById(id)
|
||||
result.onSuccess { v ->
|
||||
val uiModel = VeranstalterDetailUiModel(
|
||||
id = v.id,
|
||||
name = v.name,
|
||||
oepsNummer = v.oepsNummer,
|
||||
ansprechpartner = v.ansprechpartner,
|
||||
email = v.email,
|
||||
telefon = v.telefon,
|
||||
adresse = v.adresse,
|
||||
loginStatus = when(v.loginStatus) {
|
||||
"Aktiv" -> LoginStatus.AKTIV
|
||||
else -> LoginStatus.AUSSTEHEND
|
||||
},
|
||||
mitgliedSeit = v.mitgliedSeit
|
||||
)
|
||||
|
||||
val mockVeranstalter = VeranstalterDetailUiModel(
|
||||
id = id,
|
||||
name = "Reit- und Fahrverein Wels",
|
||||
oepsNummer = "V-OOE-1234",
|
||||
ansprechpartner = "Maria Huber",
|
||||
email = "office@rfv-wels.at",
|
||||
telefon = "+43 7242 12345",
|
||||
adresse = "Reitweg 15\n4600 Wels",
|
||||
loginStatus = LoginStatus.AKTIV,
|
||||
mitgliedSeit = "15.1.2023",
|
||||
)
|
||||
// In einer realen App würden wir hier auch die Events vom Repo laden
|
||||
// Für den Prototyp behalten wir vorerst die Mock-Events, filtern sie aber ggf.
|
||||
val mockVeranstaltungen = listOf(
|
||||
VeranstaltungListUiModel(
|
||||
id = 1L,
|
||||
name = "Union Reit- und Fahrverein Neumarkt Frühjahrsturnier 2026",
|
||||
datum = "25.-26. April 2026",
|
||||
ort = "Reitanlage Stroblmair, Neumarkt/M., OO",
|
||||
turnierAnzahl = 2,
|
||||
nennungen = 87,
|
||||
bewerbe = 26,
|
||||
letzteAktivitaet = "22.03.2026 14:30",
|
||||
status = VeranstaltungStatus.VORBEREITUNG,
|
||||
)
|
||||
)
|
||||
|
||||
val mockVeranstaltungen = listOf(
|
||||
VeranstaltungListUiModel(
|
||||
id = 1L,
|
||||
name = "Union Reit- und Fahrverein Neumarkt Frühjahrsturnier 2026",
|
||||
datum = "25.-26. April 2026",
|
||||
ort = "Reitanlage Stroblmair, Neumarkt/M., OO",
|
||||
turnierAnzahl = 2,
|
||||
nennungen = 87,
|
||||
bewerbe = 26,
|
||||
letzteAktivitaet = "22.03.2026 14:30",
|
||||
status = VeranstaltungStatus.VORBEREITUNG,
|
||||
),
|
||||
VeranstaltungListUiModel(
|
||||
id = 2L,
|
||||
name = "AWÖ-Cup Stadl-Paura 2025",
|
||||
datum = "15.-17. Mai 2025",
|
||||
ort = "Bundesgestüt Piber, Stadl-Paura",
|
||||
turnierAnzahl = 2,
|
||||
nennungen = 142,
|
||||
bewerbe = 33,
|
||||
letzteAktivitaet = "17.05.2025 18:45",
|
||||
status = VeranstaltungStatus.ABGESCHLOSSEN,
|
||||
),
|
||||
VeranstaltungListUiModel(
|
||||
id = 3L,
|
||||
name = "Linzer Pferdetage 2026",
|
||||
datum = "12.-14. Juni 2026",
|
||||
ort = "Reitsportzentrum Linz-Ebelsberg",
|
||||
turnierAnzahl = 2,
|
||||
nennungen = 23,
|
||||
bewerbe = 30,
|
||||
letzteAktivitaet = "20.03.2026 09:15",
|
||||
status = VeranstaltungStatus.VORBEREITUNG,
|
||||
),
|
||||
)
|
||||
|
||||
_state.value = _state.value.copy(
|
||||
isLoading = false,
|
||||
veranstalter = mockVeranstalter,
|
||||
veranstaltungen = mockVeranstaltungen,
|
||||
filteredVeranstaltungen = mockVeranstaltungen
|
||||
)
|
||||
applyFilter()
|
||||
_state.value = _state.value.copy(
|
||||
isLoading = false,
|
||||
veranstalter = uiModel,
|
||||
veranstaltungen = mockVeranstaltungen,
|
||||
filteredVeranstaltungen = mockVeranstaltungen
|
||||
)
|
||||
applyFilter()
|
||||
}.onFailure { t ->
|
||||
_state.value = _state.value.copy(
|
||||
isLoading = false,
|
||||
errorMessage = t.message ?: "Fehler beim Laden"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package at.mocode.frontend.features.veranstalter.presentation
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import at.mocode.frontend.features.veranstalter.domain.Veranstalter
|
||||
import at.mocode.frontend.features.veranstalter.domain.VeranstalterRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
data class VeranstalterWizardState(
|
||||
val isLoading: Boolean = false,
|
||||
val isSaving: Boolean = false,
|
||||
val editId: Long? = null,
|
||||
val name: String = "",
|
||||
val oepsNummer: String = "",
|
||||
val ort: String = "",
|
||||
val ansprechpartner: String = "",
|
||||
val email: String = "",
|
||||
val telefon: String = "",
|
||||
val adresse: String = "",
|
||||
val loginStatus: String = "Aktiv",
|
||||
val success: Boolean = false,
|
||||
val errorMessage: String? = null
|
||||
)
|
||||
|
||||
sealed interface VeranstalterWizardIntent {
|
||||
data class Load(val id: Long) : VeranstalterWizardIntent
|
||||
data class UpdateName(val v: String) : VeranstalterWizardIntent
|
||||
data class UpdateOeps(val v: String) : VeranstalterWizardIntent
|
||||
data class UpdateOrt(val v: String) : VeranstalterWizardIntent
|
||||
data class UpdateAnsprechpartner(val v: String) : VeranstalterWizardIntent
|
||||
data class UpdateEmail(val v: String) : VeranstalterWizardIntent
|
||||
data class UpdateTelefon(val v: String) : VeranstalterWizardIntent
|
||||
data class UpdateAdresse(val v: String) : VeranstalterWizardIntent
|
||||
data object Save : VeranstalterWizardIntent
|
||||
}
|
||||
|
||||
class VeranstalterWizardViewModel(
|
||||
private val repo: VeranstalterRepository
|
||||
) : ViewModel() {
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
private val _state = MutableStateFlow(VeranstalterWizardState())
|
||||
val state: StateFlow<VeranstalterWizardState> = _state
|
||||
|
||||
fun send(intent: VeranstalterWizardIntent) {
|
||||
when (intent) {
|
||||
is VeranstalterWizardIntent.Load -> load(intent.id)
|
||||
is VeranstalterWizardIntent.UpdateName -> _state.value = _state.value.copy(name = intent.v)
|
||||
is VeranstalterWizardIntent.UpdateOeps -> _state.value = _state.value.copy(oepsNummer = intent.v)
|
||||
is VeranstalterWizardIntent.UpdateOrt -> _state.value = _state.value.copy(ort = intent.v)
|
||||
is VeranstalterWizardIntent.UpdateAnsprechpartner -> _state.value = _state.value.copy(ansprechpartner = intent.v)
|
||||
is VeranstalterWizardIntent.UpdateEmail -> _state.value = _state.value.copy(email = intent.v)
|
||||
is VeranstalterWizardIntent.UpdateTelefon -> _state.value = _state.value.copy(telefon = intent.v)
|
||||
is VeranstalterWizardIntent.UpdateAdresse -> _state.value = _state.value.copy(adresse = intent.v)
|
||||
is VeranstalterWizardIntent.Save -> save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun load(id: Long) {
|
||||
_state.value = _state.value.copy(isLoading = true, editId = id)
|
||||
scope.launch {
|
||||
repo.getById(id).onSuccess { v ->
|
||||
_state.value = _state.value.copy(
|
||||
isLoading = false,
|
||||
name = v.name,
|
||||
oepsNummer = v.oepsNummer,
|
||||
ort = v.ort,
|
||||
ansprechpartner = v.ansprechpartner,
|
||||
email = v.email,
|
||||
telefon = v.telefon,
|
||||
adresse = v.adresse,
|
||||
loginStatus = v.loginStatus
|
||||
)
|
||||
}.onFailure { t ->
|
||||
_state.value = _state.value.copy(isLoading = false, errorMessage = t.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun save() {
|
||||
val s = _state.value
|
||||
_state.value = _state.value.copy(isSaving = true)
|
||||
scope.launch {
|
||||
val model = Veranstalter(
|
||||
id = s.editId ?: 0L,
|
||||
name = s.name,
|
||||
oepsNummer = s.oepsNummer,
|
||||
ort = s.ort,
|
||||
ansprechpartner = s.ansprechpartner,
|
||||
email = s.email,
|
||||
telefon = s.telefon,
|
||||
adresse = s.adresse,
|
||||
loginStatus = s.loginStatus
|
||||
)
|
||||
val result = if (s.editId != null) {
|
||||
repo.update(s.editId, model)
|
||||
} else {
|
||||
repo.create(model)
|
||||
}
|
||||
|
||||
result.onSuccess {
|
||||
_state.value = _state.value.copy(isSaving = false, success = true)
|
||||
}.onFailure { t ->
|
||||
_state.value = _state.value.copy(isSaving = false, errorMessage = t.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user