refactor(ui): update app title, footer text, and layout spacing
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Successful in 7m39s
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Successful in 7m21s
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Successful in 3m15s
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Successful in 1m45s

- Changed the app title to "Equest-Events Master Desktop" in `main.kt`.
- Updated footer text with new phrasing: "© Mocode-Software · Developed with love for equestrian sports".
- Improved code formatting for consistent alignment and indentation across files.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-03-19 12:10:17 +01:00
parent 32295bdea2
commit 931fe7badb
4 changed files with 280 additions and 70 deletions
@@ -46,7 +46,6 @@ fun MainApp() {
is AppScreen.Dashboard -> DashboardScreen(
authTokenManager = authTokenManager,
onOpenPing = { navigationPort.navigateToScreen(AppScreen.Ping) },
onCreateTournament = { navigationPort.navigateToScreen(AppScreen.CreateTournament) },
onLogout = {
authTokenManager.clearToken()
navigationPort.navigateToScreen(AppScreen.Landing)
@@ -56,7 +55,6 @@ fun MainApp() {
is AppScreen.Home -> DashboardScreen( // Route /home to Dashboard for now
authTokenManager = authTokenManager,
onOpenPing = { navigationPort.navigateToScreen(AppScreen.Ping) },
onCreateTournament = { navigationPort.navigateToScreen(AppScreen.CreateTournament) },
onLogout = {
authTokenManager.clearToken()
navigationPort.navigateToScreen(AppScreen.Landing)
@@ -358,7 +356,6 @@ private fun FeatureCard(number: String, title: String, body: String) {
private fun DashboardScreen(
authTokenManager: AuthTokenManager,
onOpenPing: () -> Unit,
onCreateTournament: () -> Unit,
onLogout: () -> Unit
) {
val authState by authTokenManager.authState.collectAsState()
@@ -447,11 +444,42 @@ private fun DashboardScreen(
}
}
OutlinedButton(
onClick = onCreateTournament,
modifier = Modifier.fillMaxWidth().padding(top = 16.dp)
// DEIN NEUES KONZEPT: Download Desktop App statt "Neues Turnier anlegen" im Web
Card(
modifier = Modifier.fillMaxWidth().padding(top = 16.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer)
) {
Text("+ Neues Turnier anlegen")
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
"Master-Meldestelle Desktop App",
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Text(
"Für die Turnieranlage, den ZNS-Import und vollen Offline-Betrieb (USB-Sync) laden Sie bitte die Desktop-Anwendung herunter.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(top = 8.dp)
) {
Button(onClick = { /* TODO: Download Trigger */ }) {
Text("Download für Windows (.exe)")
}
// Status Anzeige
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
Surface(
modifier = Modifier.size(12.dp),
shape = MaterialTheme.shapes.small,
color = MaterialTheme.colorScheme.error
) {}
Text("Desktop App derzeit Offline", style = MaterialTheme.typography.labelMedium)
}
}
}
}
}
@@ -470,13 +498,6 @@ private fun DashboardScreen(
) {
Text("Ping-Service (System Status)")
}
OutlinedButton(
onClick = { /* TODO: ZNS Import */ },
modifier = Modifier.fillMaxWidth()
) {
Text("ZNS-Daten Importieren")
}
}
}
}
@@ -509,7 +530,7 @@ fun CreateTournamentScreen(
Text("← Zurück")
}
Text(
text = "Neues Turnier anlegen",
text = "Neues Turnier anlegen (Desktop Client)",
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
@@ -523,20 +544,22 @@ fun CreateTournamentScreen(
modifier = Modifier.padding(16.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
StepIndicator(step = 1, title = "Stammdaten", isActive = currentStep == 1, isCompleted = currentStep > 1)
StepIndicator(step = 2, title = "Konfiguration", isActive = currentStep == 2, isCompleted = currentStep > 2)
StepIndicator(step = 3, title = "Funktionäre", isActive = currentStep == 3, isCompleted = currentStep > 3)
StepIndicator(step = 4, title = "Bewerbe", isActive = currentStep == 4, isCompleted = currentStep > 4)
StepIndicator(step = 1, title = "Transfer", isActive = currentStep == 1, isCompleted = currentStep > 1)
StepIndicator(step = 2, title = "Stammdaten", isActive = currentStep == 2, isCompleted = currentStep > 2)
StepIndicator(step = 3, title = "Konfiguration", isActive = currentStep == 3, isCompleted = currentStep > 3)
StepIndicator(step = 4, title = "Funktionäre", isActive = currentStep == 4, isCompleted = currentStep > 4)
StepIndicator(step = 5, title = "Bewerbe", isActive = currentStep == 5, isCompleted = currentStep > 5)
}
}
// Wizard Content Area
Box(modifier = Modifier.weight(1f).padding(24.dp)) {
when (currentStep) {
1 -> TournamentStepStammdaten()
2 -> TournamentStepKonfiguration()
3 -> TournamentStepFunktionaere()
4 -> TournamentStepBewerbe()
1 -> TournamentStepTransfer()
2 -> TournamentStepStammdaten()
3 -> TournamentStepKonfiguration()
4 -> TournamentStepFunktionaere()
5 -> TournamentStepBewerbe()
}
}
@@ -552,7 +575,7 @@ fun CreateTournamentScreen(
Spacer(modifier = Modifier.width(1.dp)) // Empty space to keep "Weiter" on the right
}
if (currentStep < 4) {
if (currentStep < 5) {
Button(onClick = { currentStep++ }) { Text("Weiter") }
} else {
Button(onClick = onSave) { Text("Turnier speichern") }
@@ -586,56 +609,167 @@ fun StepIndicator(step: Int, title: String, isActive: Boolean, isCompleted: Bool
}
@Composable
fun TournamentStepStammdaten() {
Column(verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth(0.6f)) {
Text("Schritt 1: Turnier-Stammdaten", style = MaterialTheme.typography.headlineSmall)
OutlinedTextField(
value = "",
onValueChange = {},
label = { Text("Turniernummer OEPS (z.B. 26128)") },
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = "",
onValueChange = {},
label = { Text("Turniername (z.B. CSN-C NEU Neumarkt)") },
modifier = Modifier.fillMaxWidth()
fun TournamentStepTransfer() {
Column(verticalArrangement = Arrangement.spacedBy(24.dp), modifier = Modifier.fillMaxWidth(0.8f)) {
Text("Schritt 1: Transfer & Initialisierung", style = MaterialTheme.typography.headlineSmall)
Text(
"In diesem Schritt erschaffen wir eine separate Datenbank für dieses spezifische Turnier. " +
"Diese Datenbank kann für den komplett isolierten Offline-Betrieb (z.B. am USB-Stick) auf andere Laptops übertragen werden.",
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Row(horizontalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth()) {
OutlinedTextField(
value = "",
onValueChange = {},
label = { Text("Turniernummer OEPS (z.B. 26128)") },
modifier = Modifier.weight(1f)
)
Button(onClick = { /*TODO*/ }, modifier = Modifier.align(Alignment.CenterVertically)) {
Text("Turnierdatenbank Initialisieren")
}
}
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
Text("Datenaustausch (OEPS / Externe Systeme)", style = MaterialTheme.typography.titleMedium)
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
Card(modifier = Modifier.weight(1f)) {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text("ZNS / Stammdaten Import", fontWeight = FontWeight.Bold)
Text(
"Aktualisieren Sie die Reiter, Pferde und Funktionäre aus dem zentralen System.",
style = MaterialTheme.typography.bodyMedium
)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = "",
onValueChange = {},
label = { Text("Pfad zur ZNS.zip") },
modifier = Modifier.weight(1f),
readOnly = true
)
Button(onClick = { /* Öffnet File Picker */ }) { Text("...") }
}
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = "",
onValueChange = {},
label = { Text("Pfad zur AWÖ/Zucht-Datei") },
modifier = Modifier.weight(1f),
readOnly = true
)
Button(onClick = { /* Öffnet File Picker */ }) { Text("...") }
}
}
}
Card(modifier = Modifier.weight(1f)) {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text("OEPS Export", fontWeight = FontWeight.Bold)
Text(
"Erzeugt die xxxxx.erg Datei für die offizielle Ergebnismeldung nach dem Turnier.",
style = MaterialTheme.typography.bodyMedium
)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = "",
onValueChange = {},
label = { Text("Ziel-Ordner für .erg Export") },
modifier = Modifier.weight(1f),
readOnly = true
)
Button(onClick = { /* Öffnet Directory Picker */ }) { Text("...") }
}
Button(
onClick = { /*TODO*/ },
modifier = Modifier.fillMaxWidth(),
enabled = false
) { Text("Ergebnis-Export (.erg)") }
}
}
}
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
Text("Offline-Sync (USB-Stick / Lokales Netzwerk)", style = MaterialTheme.typography.titleMedium)
Text(
"Übertragen Sie den kompletten Turnierstand zwischen Master-Meldestelle und Richterturm-Laptops ohne Internet.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
// Export (Speichern auf Stick)
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = "",
onValueChange = {},
label = { Text("Ziel-Pfad (z.B. D:/Export)") },
modifier = Modifier.weight(1f),
readOnly = true
)
Button(onClick = { /* Öffnet Directory Picker */ }) { Text("...") }
}
OutlinedButton(onClick = { /*TODO*/ }, modifier = Modifier.fillMaxWidth()) { Text("↑ Turnier Exportieren") }
}
// Import (Lesen vom Stick)
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = "",
onValueChange = {},
label = { Text("Quell-Datei (z.B. D:/turnier.db)") },
modifier = Modifier.weight(1f),
readOnly = true
)
Button(onClick = { /* Öffnet File Picker */ }) { Text("...") }
}
OutlinedButton(onClick = { /*TODO*/ }, modifier = Modifier.fillMaxWidth()) { Text("↓ Turnier Importieren") }
}
}
}
}
@Composable
fun TournamentStepStammdaten() {
Column(verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth(0.6f)) {
Text("Schritt 2: Turnier-Stammdaten", style = MaterialTheme.typography.headlineSmall)
OutlinedTextField(
value = "CSN-C NEU Neumarkt", // Dummy pre-fill
onValueChange = {},
label = { Text("Turniername") },
modifier = Modifier.fillMaxWidth()
)
Row(horizontalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth()) {
OutlinedTextField(
value = "25.04.2026",
onValueChange = {},
label = { Text("Datum von") },
modifier = Modifier.weight(1f)
)
OutlinedTextField(
value = "",
value = "25.04.2026",
onValueChange = {},
label = { Text("Datum bis") },
modifier = Modifier.weight(1f)
)
}
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text("ZNS Import", fontWeight = FontWeight.Bold)
Text(
"Hier laden wir später das ZNS.zip für dieses Turnier hoch, um Starter und Lizenzen zu importieren.",
style = MaterialTheme.typography.bodyMedium
)
Button(onClick = { /*TODO*/ }) { Text("ZNS.zip auswählen...") }
}
}
}
}
@Composable
fun TournamentStepKonfiguration() {
Column(verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth(0.6f)) {
Text("Schritt 2: Konfiguration", style = MaterialTheme.typography.headlineSmall)
Text("Schritt 3: Konfiguration", style = MaterialTheme.typography.headlineSmall)
Text("Austragungsplätze und Preisliste")
// Placeholder for Austragungsplätze
@@ -643,8 +777,8 @@ fun TournamentStepKonfiguration() {
Column(modifier = Modifier.padding(16.dp)) {
Text("Austragungsplätze", fontWeight = FontWeight.Bold)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(top = 8.dp)) {
FilterChip(selected = true, onClick = {}, label = { Text("Platz 1 (Sand)") })
FilterChip(selected = false, onClick = {}, label = { Text("Halle") })
FilterChip(selected = true, onClick = {}, label = { Text("Platz 1 (Sand/Vlies 45x65m)") })
FilterChip(selected = false, onClick = {}, label = { Text("Halle (Sand/Vlies 20x40m)") })
FilterChip(selected = false, onClick = {}, label = { Text("+ Hinzufügen") })
}
}
@@ -655,29 +789,36 @@ fun TournamentStepKonfiguration() {
@Composable
fun TournamentStepFunktionaere() {
Column(verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth(0.6f)) {
Text("Schritt 3: Team & Funktionäre", style = MaterialTheme.typography.headlineSmall)
Text("Schritt 4: Team & Funktionäre", style = MaterialTheme.typography.headlineSmall)
Text("Zuweisung von Richtern und Parcoursbauern (aus ZNS)")
OutlinedTextField(
value = "",
value = "Rudi Kreupl",
onValueChange = {},
label = { Text("Turnierbeauftragter (Suche nach Name oder ID)") },
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = "",
value = "Helmut Riedler",
onValueChange = {},
label = { Text("Richter (Suche nach Name oder ID)") },
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = "Kurt Reitetschlägerr",
onValueChange = {},
label = { Text("Parcoursbauchef") },
modifier = Modifier.fillMaxWidth()
)
}
}
@Composable
fun TournamentStepBewerbe() {
Column(verticalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxSize()) {
Text("Schritt 4: Bewerbe anlegen", style = MaterialTheme.typography.headlineSmall)
Text("Schritt 5: Bewerbe anlegen", style = MaterialTheme.typography.headlineSmall)
Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.spacedBy(16.dp)) {
// Left: List of Bewerbe
@@ -690,6 +831,8 @@ fun TournamentStepBewerbe() {
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
Text("1: Pony Stilspringprüfung (60cm)", modifier = Modifier.padding(vertical = 4.dp))
Text("2: Einlaufspringprüfung (60cm)", modifier = Modifier.padding(vertical = 4.dp))
Text("3: Pony Stilspringprüfung (70cm)", modifier = Modifier.padding(vertical = 4.dp))
Text("4: Einlaufspringprüfung (70cm)", modifier = Modifier.padding(vertical = 4.dp))
Text("...", modifier = Modifier.padding(vertical = 4.dp), color = MaterialTheme.colorScheme.onSurfaceVariant)
}
}