Refactor Veranstalter and Veranstaltung flows: add VeranstalterProfil UI, event creation callback, profile enhancements, and save-enable matrix logic. Extend ZNS import and branding workflows.
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
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:
+117
-8
@@ -5,10 +5,17 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Chat
|
||||
import androidx.compose.material.icons.filled.Devices
|
||||
import androidx.compose.material.icons.filled.Wifi
|
||||
import androidx.compose.material.icons.filled.WifiOff
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -57,11 +64,14 @@ fun DesktopMainLayout(
|
||||
onNavigate = onNavigate,
|
||||
onLogout = onLogout,
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
DesktopContentArea(
|
||||
currentScreen = currentScreen,
|
||||
onNavigate = onNavigate,
|
||||
)
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
|
||||
DesktopContentArea(
|
||||
currentScreen = currentScreen,
|
||||
onNavigate = onNavigate,
|
||||
)
|
||||
}
|
||||
DesktopFooterBar()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,6 +269,20 @@ private fun DesktopTopBar(
|
||||
}
|
||||
}
|
||||
|
||||
// Hilfsfunktion: OEPS-Bundeslandcode → Abkürzung
|
||||
private fun mapOepsToBundesland(code: String): String = when (code.uppercase()) {
|
||||
"OOE" -> "OÖ"
|
||||
"NOE" -> "NÖ"
|
||||
"ST" -> "Stmk."
|
||||
"W" -> "Wien"
|
||||
"BGLD", "B" -> "Bgld."
|
||||
"K" -> "Ktn."
|
||||
"S" -> "Sbg."
|
||||
"T" -> "Tirol"
|
||||
"V" -> "Vbg."
|
||||
else -> code
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BreadcrumbSeparator() {
|
||||
Text(
|
||||
@@ -344,7 +368,8 @@ private fun DesktopContentArea(
|
||||
if (vId == 0L) onNavigate(AppScreen.Veranstaltungen)
|
||||
else onNavigate(AppScreen.VeranstalterDetail(vId))
|
||||
},
|
||||
onSaved = { evtId, finalVId -> onNavigate(AppScreen.VeranstaltungUebersicht(finalVId, evtId)) }
|
||||
onSaved = { evtId, finalVId -> onNavigate(AppScreen.VeranstaltungUebersicht(finalVId, evtId)) },
|
||||
onVeranstalterCreated = { newVId -> onNavigate(AppScreen.VeranstalterDetail(newVId)) }
|
||||
)
|
||||
}
|
||||
is AppScreen.VeranstaltungUebersicht -> {
|
||||
@@ -365,7 +390,20 @@ private fun DesktopContentArea(
|
||||
veranstalterId = vId,
|
||||
veranstaltungId = evtId,
|
||||
onBack = { onNavigate(AppScreen.VeranstalterDetail(vId)) },
|
||||
onTurnierNeu = { onNavigate(AppScreen.TurnierNeu(evtId)) },
|
||||
onTurnierNeu = {
|
||||
val veranstaltung = at.mocode.desktop.v2.StoreV2.eventsFor(vId).firstOrNull { it.id == evtId }
|
||||
val list = at.mocode.desktop.v2.TurnierStoreV2.list(evtId)
|
||||
val newId = (list.maxOfOrNull { it.id } ?: 0L) + 1L
|
||||
val draft = at.mocode.desktop.v2.TurnierV2(
|
||||
id = newId,
|
||||
veranstaltungId = evtId,
|
||||
turnierNr = 0,
|
||||
datumVon = veranstaltung?.datumVon ?: "",
|
||||
datumBis = veranstaltung?.datumBis,
|
||||
)
|
||||
at.mocode.desktop.v2.TurnierStoreV2.add(evtId, draft)
|
||||
onNavigate(AppScreen.TurnierDetail(evtId, newId))
|
||||
},
|
||||
onTurnierOpen = { tId -> onNavigate(AppScreen.TurnierDetail(evtId, tId)) },
|
||||
)
|
||||
}
|
||||
@@ -375,7 +413,23 @@ private fun DesktopContentArea(
|
||||
is AppScreen.VeranstaltungDetail -> VeranstaltungDetailScreen(
|
||||
veranstaltungId = currentScreen.id,
|
||||
onBack = { onNavigate(AppScreen.Veranstaltungen) },
|
||||
onTurnierNeu = { onNavigate(AppScreen.TurnierNeu(currentScreen.id)) },
|
||||
onTurnierNeu = {
|
||||
val v = at.mocode.desktop.v2.StoreV2.vereine.firstOrNull { vv ->
|
||||
at.mocode.desktop.v2.StoreV2.eventsFor(vv.id).any { it.id == currentScreen.id }
|
||||
}
|
||||
val veranstaltung = v?.let { at.mocode.desktop.v2.StoreV2.eventsFor(it.id).firstOrNull { e -> e.id == currentScreen.id } }
|
||||
val list = at.mocode.desktop.v2.TurnierStoreV2.list(currentScreen.id)
|
||||
val newId = (list.maxOfOrNull { it.id } ?: 0L) + 1L
|
||||
val draft = at.mocode.desktop.v2.TurnierV2(
|
||||
id = newId,
|
||||
veranstaltungId = currentScreen.id,
|
||||
turnierNr = 0,
|
||||
datumVon = veranstaltung?.datumVon ?: "",
|
||||
datumBis = veranstaltung?.datumBis,
|
||||
)
|
||||
at.mocode.desktop.v2.TurnierStoreV2.add(currentScreen.id, draft)
|
||||
onNavigate(AppScreen.TurnierDetail(currentScreen.id, newId))
|
||||
},
|
||||
onTurnierOeffnen = { tid -> onNavigate(AppScreen.TurnierDetail(currentScreen.id, tid)) },
|
||||
)
|
||||
is AppScreen.VeranstaltungNeu -> VeranstaltungNeuScreen(
|
||||
@@ -395,10 +449,20 @@ private fun DesktopContentArea(
|
||||
onBack = { onNavigate(AppScreen.Veranstaltungen) }
|
||||
)
|
||||
} else {
|
||||
val veranstaltung = at.mocode.desktop.v2.StoreV2.eventsFor(parent.id).firstOrNull { it.id == evtId }
|
||||
val blCode = parent.oepsNummer.split("-").getOrNull(1) ?: ""
|
||||
val bundesland = mapOepsToBundesland(blCode)
|
||||
TurnierDetailScreen(
|
||||
veranstaltungId = evtId,
|
||||
turnierId = currentScreen.turnierId,
|
||||
onBack = { onNavigate(AppScreen.VeranstaltungUebersicht(parent.id, evtId)) },
|
||||
eventVon = veranstaltung?.datumVon,
|
||||
eventBis = veranstaltung?.datumBis,
|
||||
eventOrt = veranstaltung?.ort,
|
||||
veranstalterName = parent.name,
|
||||
veranstalterOrt = parent.ort,
|
||||
veranstalterBundesland = bundesland,
|
||||
veranstalterLogoUrl = veranstaltung?.logoUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -455,3 +519,48 @@ private fun DesktopContentArea(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DesktopFooterBar() {
|
||||
// Stub-Status für MVP
|
||||
val online = remember { mutableStateOf(true) }
|
||||
val deviceConnected = remember { mutableStateOf(true) }
|
||||
val deviceName = "Richter-Turm"
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(36.dp)
|
||||
.background(Color(0xFFF3F4F6))
|
||||
.padding(horizontal = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = if (online.value) Icons.Filled.Wifi else Icons.Filled.WifiOff,
|
||||
contentDescription = null,
|
||||
tint = if (online.value) Color(0xFF059669) else Color(0xFFDC2626)
|
||||
)
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text(if (online.value) "Online" else "Offline", color = Color(0xFF374151), fontSize = 12.sp)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Icon(Icons.Filled.Devices, contentDescription = null, tint = if (deviceConnected.value) Color(0xFF2563EB) else Color(0xFF9CA3AF))
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text(
|
||||
if (deviceConnected.value) "Verbunden: $deviceName" else "Kein Gerät verbunden",
|
||||
color = Color(0xFF374151),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (deviceConnected.value) {
|
||||
OutlinedButton(onClick = { /* öffne Chat-Panel */ }, contentPadding = PaddingValues(horizontal = 10.dp, vertical = 4.dp)) {
|
||||
Icon(Icons.Filled.Chat, contentDescription = null, tint = Color(0xFF2563EB))
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text("Chat", color = Color(0xFF2563EB), fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+51
@@ -125,6 +125,57 @@ fun VeranstalterDetailV2(
|
||||
Button(onClick = onNeuVeranstaltung) { Text("+ Neue Veranstaltung") }
|
||||
}
|
||||
|
||||
// Profil-Bereich (Logo URL, Ansprechpartner, Kontakt, Adresse)
|
||||
val verein = remember(veranstalterId) { StoreV2.vereine.firstOrNull { it.id == veranstalterId } }
|
||||
if (verein != null) {
|
||||
Card {
|
||||
Column(Modifier.fillMaxWidth().padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text("Veranstalter‑Profil", style = MaterialTheme.typography.titleMedium)
|
||||
OutlinedTextField(
|
||||
value = verein.logoUrl ?: "",
|
||||
onValueChange = { verein.logoUrl = it.ifBlank { null } },
|
||||
label = { Text("Logo‑URL (optional)") },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
OutlinedTextField(
|
||||
value = verein.ansprechpartner ?: "",
|
||||
onValueChange = { verein.ansprechpartner = it.ifBlank { null } },
|
||||
label = { Text("Ansprechpartner (optional)") },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = verein.telefon ?: "",
|
||||
onValueChange = { verein.telefon = it.ifBlank { null } },
|
||||
label = { Text("Telefon (optional)") },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
OutlinedTextField(
|
||||
value = verein.email ?: "",
|
||||
onValueChange = { verein.email = it.ifBlank { null } },
|
||||
label = { Text("E‑Mail (optional)") },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = verein.oepsNummer,
|
||||
onValueChange = { verein.oepsNummer = it },
|
||||
label = { Text("OEPS‑Nummer") },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
OutlinedTextField(
|
||||
value = verein.adresse ?: "",
|
||||
onValueChange = { verein.adresse = it.ifBlank { null } },
|
||||
label = { Text("Adresse (optional)") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
minLines = 2
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val events = StoreV2.eventsFor(veranstalterId)
|
||||
if (events.isEmpty()) Text("Noch keine Veranstaltungen angelegt.", color = Color(0xFF6B7280))
|
||||
|
||||
|
||||
+9
-3
@@ -5,9 +5,15 @@ import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
|
||||
data class Verein(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
val oepsNummer: String,
|
||||
val ort: String,
|
||||
var name: String,
|
||||
var oepsNummer: String,
|
||||
var ort: String,
|
||||
// Profil-Felder (minimal laut Abstimmung)
|
||||
var logoUrl: String? = null,
|
||||
var ansprechpartner: String? = null,
|
||||
var email: String? = null,
|
||||
var telefon: String? = null,
|
||||
var adresse: String? = null,
|
||||
)
|
||||
|
||||
data class VeranstaltungV2(
|
||||
|
||||
+3
-2
@@ -297,6 +297,7 @@ fun VeranstaltungKonfigV2(
|
||||
veranstalterId: Long = 0,
|
||||
onBack: () -> Unit,
|
||||
onSaved: (Long, Long) -> Unit, // eventId, veranstalterId
|
||||
onVeranstalterCreated: (Long) -> Unit = {}, // Neuer Flow: nach Vereinsanlage ins Profil
|
||||
) {
|
||||
DesktopThemeV2 {
|
||||
var currentStep by remember { mutableStateOf(if (veranstalterId == 0L) 1 else 2) }
|
||||
@@ -466,9 +467,9 @@ fun VeranstaltungKonfigV2(
|
||||
VeranstalterAnlegenWizard(
|
||||
onCancel = { showVereinNeu = false },
|
||||
onVereinCreated = { newId ->
|
||||
selectedVereinId = newId
|
||||
// Neuer gewünschter Flow: nach Schritt 2 ins Veranstalter‑Profil wechseln
|
||||
showVereinNeu = false
|
||||
currentStep = 2
|
||||
onVeranstalterCreated(newId)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user