chore: enhance Stammdaten-Verwaltung and refine desktop UX across multiple features, fix typo in settings.json, enable WASM builds, and add Master-Detail layout for Funktionäre
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

This commit is contained in:
2026-04-20 02:49:30 +02:00
parent d4aeba4666
commit 345c329350
14 changed files with 748 additions and 123 deletions
@@ -1,6 +1,6 @@
{
"deviceName": "Meldestelle",
"sharedKey": "Paassword",
"sharedKey": "Password",
"backupPath": "/mocode/meldestelle/docs/temp",
"networkRole": "MASTER",
"expectedClients": [
@@ -12,6 +12,7 @@ import at.mocode.frontend.core.network.networkModule
import at.mocode.frontend.core.sync.di.syncModule
import at.mocode.frontend.features.billing.di.billingModule
import at.mocode.frontend.features.device.initialization.di.deviceInitializationModule
import at.mocode.frontend.features.funktionaer.di.funktionaerModule
import at.mocode.frontend.features.nennung.di.nennungFeatureModule
import at.mocode.frontend.features.pferde.di.pferdeModule
import at.mocode.frontend.features.profile.di.profileModule
@@ -42,6 +43,7 @@ fun main() = application {
billingModule,
pferdeModule,
reiterModule,
funktionaerModule,
vereinFeatureModule,
turnierFeatureModule,
deviceInitializationModule,
@@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import at.mocode.frontend.core.auth.data.local.AuthTokenManager
@@ -29,6 +30,9 @@ import at.mocode.frontend.features.device.initialization.data.local.DeviceInitia
import at.mocode.frontend.features.device.initialization.domain.model.DeviceInitializationSettings
import at.mocode.frontend.features.device.initialization.presentation.DeviceInitializationScreen
import at.mocode.frontend.features.device.initialization.presentation.DeviceInitializationViewModel
import at.mocode.frontend.features.funktionaer.presentation.FunktionaerIntent
import at.mocode.frontend.features.funktionaer.presentation.FunktionaerScreen
import at.mocode.frontend.features.funktionaer.presentation.FunktionaerViewModel
import at.mocode.frontend.features.nennung.presentation.NennungManagementScreen
import at.mocode.frontend.features.nennung.presentation.NennungViewModel
import at.mocode.frontend.features.pferde.presentation.PferdeScreen
@@ -176,12 +180,64 @@ private fun DesktopNavRail(
)
NavRailItem(
icon = Icons.Default.People,
label = "Vereine",
selected = currentScreen is AppScreen.Vereine || currentScreen is AppScreen.VereinVerwaltung,
onClick = { onNavigate(AppScreen.Vereine) }
icon = Icons.Default.CloudDownload,
label = "ZNS-Import",
selected = currentScreen is AppScreen.StammdatenImport,
onClick = { onNavigate(AppScreen.StammdatenImport) }
)
var showStammdatenMenu by remember { mutableStateOf(false) }
Box {
NavRailItem(
icon = Icons.Default.Storage,
label = "Stammdaten",
selected = currentScreen is AppScreen.Vereine || currentScreen is AppScreen.VereinVerwaltung ||
currentScreen is AppScreen.Reiter || currentScreen is AppScreen.ReiterVerwaltung ||
currentScreen is AppScreen.Pferde || currentScreen is AppScreen.PferdVerwaltung ||
currentScreen is AppScreen.FunktionaerVerwaltung,
onClick = { showStammdatenMenu = true }
)
DropdownMenu(
expanded = showStammdatenMenu,
onDismissRequest = { showStammdatenMenu = false },
offset = DpOffset(Dimens.NavRailWidth, 0.dp)
) {
DropdownMenuItem(
text = { Text("Vereine") },
onClick = {
showStammdatenMenu = false
onNavigate(AppScreen.Vereine)
},
leadingIcon = { Icon(Icons.Default.People, contentDescription = null) }
)
DropdownMenuItem(
text = { Text("Reiter") },
onClick = {
showStammdatenMenu = false
onNavigate(AppScreen.Reiter)
},
leadingIcon = { Icon(Icons.Default.Person, contentDescription = null) }
)
DropdownMenuItem(
text = { Text("Pferde") },
onClick = {
showStammdatenMenu = false
onNavigate(AppScreen.Pferde)
},
leadingIcon = { Icon(Icons.Default.Pets, contentDescription = null) }
)
DropdownMenuItem(
text = { Text("Richter") },
onClick = {
showStammdatenMenu = false
onNavigate(AppScreen.FunktionaerVerwaltung)
},
leadingIcon = { Icon(Icons.Default.Gavel, contentDescription = null) }
)
}
}
NavRailItem(
icon = Icons.Default.Email,
label = "Mails",
@@ -573,7 +629,7 @@ private fun DesktopContentArea(
}
// --- Pferde-Verwaltung & Profil ---
is AppScreen.PferdVerwaltung -> {
is AppScreen.Pferde, is AppScreen.PferdVerwaltung -> {
val viewModel = koinViewModel<PferdeViewModel>()
PferdeScreen(viewModel = viewModel)
}
@@ -591,7 +647,7 @@ private fun DesktopContentArea(
}
// --- Reiter-Verwaltung & Profil ---
is AppScreen.ReiterVerwaltung -> {
is AppScreen.Reiter, is AppScreen.ReiterVerwaltung -> {
val viewModel = koinViewModel<ReiterViewModel>()
ReiterScreen(viewModel = viewModel)
}
@@ -607,7 +663,7 @@ private fun DesktopContentArea(
}
// --- Verein-Verwaltung & Profil ---
is AppScreen.VereinVerwaltung -> {
is AppScreen.Vereine, is AppScreen.VereinVerwaltung -> {
println("[Screen] Rendering VereinVerwaltung (VereinScreen)")
val vereinViewModel: VereinViewModel = koinViewModel()
VereinScreen(viewModel = vereinViewModel)
@@ -621,15 +677,20 @@ private fun DesktopContentArea(
}
// --- Funktionaer-Verwaltung & Profil ---
is AppScreen.FunktionaerVerwaltung -> FunktionaerVerwaltungScreen(
onBack = onBack,
onEdit = { onNavigate(AppScreen.FunktionaerProfil(it)) }
)
is AppScreen.FunktionaerVerwaltung -> {
val viewModel = koinViewModel<FunktionaerViewModel>()
FunktionaerScreen(viewModel = viewModel)
}
is AppScreen.FunktionaerProfil -> FunktionaerProfil(
id = currentScreen.id,
onBack = onBack,
)
is AppScreen.FunktionaerProfil -> {
val viewModel = koinViewModel<FunktionaerViewModel>()
LaunchedEffect(currentScreen.id) {
viewModel.state.value.list.find { it.id == currentScreen.id }?.let {
viewModel.send(FunktionaerIntent.Select(it))
}
}
FunktionaerScreen(viewModel = viewModel)
}
// --- Veranstalter-Verwaltung & Profil ---
is AppScreen.VeranstalterVerwaltung -> VeranstalterVerwaltungScreen(