chore: consolidate redundant controllers in mail-service, improve backend stability, refine desktop UX, and enhance Vereinsverwaltung functionality

This commit is contained in:
2026-04-20 00:21:16 +02:00
parent bcabb86841
commit dfaa2e8545
14 changed files with 519 additions and 138 deletions
@@ -1,6 +1,12 @@
{
"deviceName": "Meldestelle",
"deviceName": "Meldestelle\n",
"sharedKey": "Password",
"backupPath": "/mocode/meldestelle/docs/temp",
"networkRole": "MASTER"
"networkRole": "MASTER",
"expectedClients": [
{
"name": "Richter-Turm",
"role": "RICHTER"
}
]
}
@@ -16,20 +16,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import at.mocode.frontend.shell.desktop.data.Store
import at.mocode.frontend.shell.desktop.data.Turnier
import at.mocode.frontend.shell.desktop.data.TurnierStore
import at.mocode.frontend.shell.desktop.screens.management.FunktionaerVerwaltungScreen
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterAuswahl
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterDetail
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterVerwaltungScreen
import at.mocode.frontend.shell.desktop.screens.nennung.NennungsEingangScreen
import at.mocode.frontend.shell.desktop.screens.profile.FunktionaerProfil
import at.mocode.frontend.shell.desktop.screens.veranstaltung.VeranstaltungVerwaltung
import at.mocode.frontend.shell.desktop.screens.veranstaltung.details.VeranstaltungKonfig
import at.mocode.frontend.shell.desktop.screens.veranstaltung.details.VeranstaltungProfilScreen
import at.mocode.frontend.shell.desktop.screens.veranstaltung.wizards.TurnierWizard
import at.mocode.frontend.shell.desktop.screens.veranstaltung.wizards.VeranstalterAnlegenWizard
import at.mocode.frontend.core.auth.data.local.AuthTokenManager
import at.mocode.frontend.core.designsystem.theme.AppColors
import at.mocode.frontend.core.designsystem.theme.Dimens
@@ -47,20 +33,34 @@ import at.mocode.frontend.features.nennung.presentation.NennungManagementScreen
import at.mocode.frontend.features.nennung.presentation.NennungViewModel
import at.mocode.frontend.features.pferde.presentation.PferdeScreen
import at.mocode.frontend.features.pferde.presentation.PferdeViewModel
import at.mocode.frontend.features.ping.presentation.PingScreen
import at.mocode.frontend.features.ping.presentation.PingViewModel
import at.mocode.frontend.features.profile.presentation.ProfileScreen
import at.mocode.frontend.features.profile.presentation.ProfileViewModel
import at.mocode.frontend.features.reiter.presentation.ReiterScreen
import at.mocode.frontend.features.reiter.presentation.ReiterViewModel
import at.mocode.frontend.features.verein.presentation.VereinScreen
import at.mocode.frontend.features.verein.presentation.VereinViewModel
import at.mocode.frontend.features.ping.presentation.PingScreen
import at.mocode.frontend.features.ping.presentation.PingViewModel
import at.mocode.frontend.features.turnier.presentation.SeriesScreen
import at.mocode.frontend.features.turnier.presentation.TurnierDetailScreen
import at.mocode.frontend.features.veranstalter.presentation.VeranstaltungKonfigScreen
import at.mocode.frontend.features.verein.presentation.VereinScreen
import at.mocode.frontend.features.verein.presentation.VereinViewModel
import at.mocode.frontend.features.zns.import.presentation.StammdatenImportScreen
import at.mocode.frontend.shell.desktop.data.Store
import at.mocode.frontend.shell.desktop.data.Turnier
import at.mocode.frontend.shell.desktop.data.TurnierStore
import at.mocode.frontend.shell.desktop.screens.management.FunktionaerVerwaltungScreen
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterAuswahl
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterDetail
import at.mocode.frontend.shell.desktop.screens.management.VeranstalterVerwaltungScreen
import at.mocode.frontend.shell.desktop.screens.nennung.NennungsEingangScreen
import at.mocode.frontend.shell.desktop.screens.profile.FunktionaerProfil
import at.mocode.frontend.shell.desktop.screens.veranstaltung.VeranstaltungVerwaltung
import at.mocode.frontend.shell.desktop.screens.veranstaltung.details.VeranstaltungProfilScreen
import at.mocode.frontend.shell.desktop.screens.veranstaltung.wizards.TurnierWizard
import at.mocode.frontend.shell.desktop.screens.veranstaltung.wizards.VeranstalterAnlegenWizard
import at.mocode.veranstaltung.feature.presentation.AdminUebersichtScreen
import at.mocode.veranstaltung.feature.presentation.VeranstaltungDetailScreen
import at.mocode.veranstaltung.feature.presentation.VeranstaltungNeuScreen
import at.mocode.frontend.features.zns.import.presentation.StammdatenImportScreen
import kotlinx.coroutines.delay
import org.koin.compose.koinInject
import org.koin.compose.viewmodel.koinViewModel
@@ -671,12 +671,24 @@ private fun DesktopContentArea(
is AppScreen.VeranstaltungKonfig -> {
val vId = currentScreen.veranstalterId
// Falls vId == 0, kommen wir aus der Gesamtübersicht und wählen erst im Wizard
VeranstaltungKonfig(
VeranstaltungKonfigScreen(
veranstalterId = vId,
onBack = onBack,
onSaved = { evtId: Long, finalVId: Long -> onNavigate(AppScreen.VeranstaltungProfil(finalVId, evtId)) },
onVeranstalterCreated = { newVId: Long -> onNavigate(AppScreen.VeranstalterDetail(newVId)) }
onAbbrechen = onBack,
onSpeichern = { titel, datumVon, datumBis ->
// In-Memory Store Simulation
val allEvents = Store.allEvents()
val newId = (allEvents.maxOfOrNull { it.id } ?: 0L) + 1L
val newEvent = at.mocode.frontend.shell.desktop.data.Veranstaltung(
id = newId,
veranstalterId = vId,
titel = titel,
datumVon = datumVon,
datumBis = datumBis,
status = "NEU"
)
Store.addEventFirst(vId, newEvent)
onNavigate(AppScreen.VeranstaltungProfil(vId, newId))
}
)
}
@@ -83,13 +83,15 @@ fun NennungsEingangScreen(onBack: () -> Unit) {
}
val filteredMails = remember(mails, searchQuery) {
if (searchQuery.isBlank()) mails
val base = if (searchQuery.isBlank()) mails
else mails.filter {
it.vorname.contains(searchQuery, ignoreCase = true) ||
it.nachname.contains(searchQuery, ignoreCase = true) ||
it.pferd.contains(searchQuery, ignoreCase = true) ||
it.turnierNr.contains(searchQuery, ignoreCase = true)
}
// Standard-Sortierung: Neueste zuerst (Status NEU oben, dann nach TurnierNr)
base.sortedWith(compareBy({ it.status != "NEU" }, { it.turnierNr }, { it.datum }))
}
// Initiales Laden
@@ -108,6 +110,21 @@ fun NennungsEingangScreen(onBack: () -> Unit) {
mails = updated
selectedMail = null
}
},
onSendReply = {
scope.launch {
repository.sendeAntwort(
email = selectedMail!!.sender,
turnierNr = selectedMail!!.turnierNr,
vorname = selectedMail!!.vorname,
nachname = selectedMail!!.nachname
)
// Nach Antwort automatisch als gelesen markieren
repository.markiereAlsGelesen(selectedMail!!.id)
val updated = mails.map { if (it.id == selectedMail!!.id) it.copy(status = "GELESEN") else it }
mails = updated
selectedMail = null
}
}
)
}
@@ -212,7 +229,12 @@ fun NennungsEingangScreen(onBack: () -> Unit) {
}
@Composable
fun NennungDetailDialog(mail: OnlineNennungMail, onDismiss: () -> Unit, onMarkProcessed: () -> Unit) {
fun NennungDetailDialog(
mail: OnlineNennungMail,
onDismiss: () -> Unit,
onMarkProcessed: () -> Unit,
onSendReply: () -> Unit
) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("Details zur Online-EntryManagement") },
@@ -235,7 +257,19 @@ fun NennungDetailDialog(mail: OnlineNennungMail, onDismiss: () -> Unit, onMarkPr
}
},
confirmButton = {
Button(onClick = onMarkProcessed) { Text("Als gelesen markieren") }
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
if (mail.status == "NEU") {
Button(
onClick = onSendReply,
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF2E7D32))
) {
Icon(Icons.Default.Email, null, modifier = Modifier.size(18.dp))
Spacer(Modifier.width(8.dp))
Text("Antwort & Übernahme")
}
}
Button(onClick = onMarkProcessed) { Text("Als gelesen markieren") }
}
},
dismissButton = {
TextButton(onClick = onDismiss) { Text("Schließen") }