Add registration tab to LoginScreen and implement OrganizerProfile screen

This commit is contained in:
2026-03-20 23:57:21 +01:00
parent 7f224f0d03
commit 499b93e100
6 changed files with 598 additions and 160 deletions
@@ -9,6 +9,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import at.mocode.frontend.core.auth.data.AuthTokenManager
import at.mocode.frontend.core.auth.presentation.LoginScreen
import at.mocode.frontend.core.auth.presentation.LoginViewModel
@@ -65,7 +67,7 @@ fun MainApp() {
} else {
LandingScreen(
onPrimaryCta = { navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Dashboard)) }, // Takes you to Meldestelle login
onOpenPing = { navigationPort.navigateToScreen(AppScreen.Profile) } // Open the Ping Overview / Status page
onOpenPing = { navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.OrganizerProfile)) } // Nach Login zum OrganizerProfile
)
}
}
@@ -124,9 +126,25 @@ fun MainApp() {
is AppScreen.Ping -> PingScreen(
viewModel = pingViewModel,
onBack = { navigationPort.navigateToScreen(AppScreen.Profile) } // Always go back to overview
onBack = { navigationPort.navigateToScreen(AppScreen.Profile) } // Zurück zum Profil/Übersicht
)
is AppScreen.OrganizerProfile -> OrganizerProfileScreen(
authTokenManager = authTokenManager,
onLogout = {
authTokenManager.clearToken()
navigationPort.navigateToScreen(AppScreen.Landing)
},
onNavigateToDashboard = { navigationPort.navigateToScreen(AppScreen.Dashboard) }
)
is AppScreen.AuthCallback -> {
// OIDC-Callback: Nach erfolgreichem OAuth-Redirect zum Dashboard navigieren
LaunchedEffect(Unit) {
navigationPort.navigateToScreen(AppScreen.Dashboard)
}
}
is AppScreen.Profile -> AuthStatusScreen(
authTokenManager = authTokenManager,
onNavigateToLogin = {
@@ -144,7 +162,7 @@ fun MainApp() {
}
)
else -> {}
else -> { navigationPort.navigateToScreen(AppScreen.Landing) }
}
}
}
@@ -194,7 +212,7 @@ private fun LandingScreen(
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Die moderne Turniermeldestelle",
text = "Die moderne Meldestelle",
style = MaterialTheme.typography.displayMedium,
fontWeight = FontWeight.Bold
)
@@ -1188,6 +1206,327 @@ fun TournamentStepBewerbe() {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun OrganizerProfileScreen(
authTokenManager: AuthTokenManager,
onLogout: () -> Unit,
onNavigateToDashboard: () -> Unit
) {
val authState by authTokenManager.authState.collectAsState()
val scrollState = rememberScrollState()
// Formular-Felder
var vereinsname by remember { mutableStateOf("URFV Neumarkt") }
var vereinskuerzel by remember { mutableStateOf("URFV") }
var adresse by remember { mutableStateOf("") }
var plz by remember { mutableStateOf("") }
var ort by remember { mutableStateOf("") }
var land by remember { mutableStateOf("Österreich") }
var mapsLink by remember { mutableStateOf("") }
// Ansprechpersonen
var kontakt1Name by remember { mutableStateOf("") }
var kontakt1Email by remember { mutableStateOf("") }
var kontakt1Telefon by remember { mutableStateOf("") }
var kontakt2Name by remember { mutableStateOf("") }
var kontakt2Email by remember { mutableStateOf("") }
var kontakt2Telefon by remember { mutableStateOf("") }
// Social / Links
var webseite by remember { mutableStateOf("") }
var facebook by remember { mutableStateOf("") }
var instagram by remember { mutableStateOf("") }
var youtube by remember { mutableStateOf("") }
// Weitere Infos
var vereinsbeschreibung by remember { mutableStateOf("") }
var bankverbindung by remember { mutableStateOf("") }
var uid by remember { mutableStateOf("") }
var saveSuccess by remember { mutableStateOf(false) }
Scaffold(
topBar = {
TopAppBar(
title = { Text("Veranstalter Profil") },
navigationIcon = {
IconButton(onClick = onNavigateToDashboard) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Zurück")
}
},
actions = {
Text(
text = authState.username ?: "",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = androidx.compose.ui.Modifier.padding(end = 8.dp)
)
TextButton(onClick = onLogout) { Text("Abmelden") }
}
)
}
) { paddingValues ->
Column(
modifier = androidx.compose.ui.Modifier
.fillMaxSize()
.padding(paddingValues)
.verticalScroll(scrollState)
.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
// --- Logo & Vereinsname ---
Card(modifier = androidx.compose.ui.Modifier.fillMaxWidth()) {
Column(modifier = androidx.compose.ui.Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text("Verein / Veranstalter", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
// Logo Placeholder
Surface(
modifier = androidx.compose.ui.Modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.colorScheme.surfaceVariant,
border = androidx.compose.foundation.BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant)
) {
Column(
modifier = androidx.compose.ui.Modifier.padding(24.dp).fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("🏆", style = MaterialTheme.typography.displayMedium)
Text("Vereins-/Veranstaltungslogo", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
OutlinedButton(onClick = { /* TODO: File Picker */ }) {
Text("Logo hochladen")
}
}
}
OutlinedTextField(
value = vereinsname,
onValueChange = { vereinsname = it },
label = { Text("Vereinsname / Veranstalter") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = vereinskuerzel,
onValueChange = { vereinskuerzel = it },
label = { Text("Kürzel") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = vereinsbeschreibung,
onValueChange = { vereinsbeschreibung = it },
label = { Text("Kurzbeschreibung / Über uns") },
minLines = 3,
maxLines = 6,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
}
}
// --- Adresse ---
Card(modifier = androidx.compose.ui.Modifier.fillMaxWidth()) {
Column(modifier = androidx.compose.ui.Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text("Adresse", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
OutlinedTextField(
value = adresse,
onValueChange = { adresse = it },
label = { Text("Straße & Hausnummer") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
OutlinedTextField(
value = plz,
onValueChange = { plz = it },
label = { Text("PLZ") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.width(100.dp)
)
OutlinedTextField(
value = ort,
onValueChange = { ort = it },
label = { Text("Ort") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.weight(1f)
)
}
OutlinedTextField(
value = land,
onValueChange = { land = it },
label = { Text("Land") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = mapsLink,
onValueChange = { mapsLink = it },
label = { Text("Google Maps / OpenStreetMap Link") },
placeholder = { Text("https://maps.google.com/...") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
}
}
// --- Ansprechpersonen ---
Card(modifier = androidx.compose.ui.Modifier.fillMaxWidth()) {
Column(modifier = androidx.compose.ui.Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text("Ansprechpersonen", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
Text("Hauptkontakt", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
OutlinedTextField(
value = kontakt1Name,
onValueChange = { kontakt1Name = it },
label = { Text("Name") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = kontakt1Email,
onValueChange = { kontakt1Email = it },
label = { Text("E-Mail") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = kontakt1Telefon,
onValueChange = { kontakt1Telefon = it },
label = { Text("Telefon / Mobil") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
HorizontalDivider()
Text("Weiterer Kontakt (optional)", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.secondary)
OutlinedTextField(
value = kontakt2Name,
onValueChange = { kontakt2Name = it },
label = { Text("Name") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = kontakt2Email,
onValueChange = { kontakt2Email = it },
label = { Text("E-Mail") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = kontakt2Telefon,
onValueChange = { kontakt2Telefon = it },
label = { Text("Telefon / Mobil") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
}
}
// --- Social Media & Links ---
Card(modifier = androidx.compose.ui.Modifier.fillMaxWidth()) {
Column(modifier = androidx.compose.ui.Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text("Links & Social Media", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
OutlinedTextField(
value = webseite,
onValueChange = { webseite = it },
label = { Text("Webseite") },
placeholder = { Text("https://www.meinverein.at") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = facebook,
onValueChange = { facebook = it },
label = { Text("Facebook") },
placeholder = { Text("https://facebook.com/...") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = instagram,
onValueChange = { instagram = it },
label = { Text("Instagram") },
placeholder = { Text("https://instagram.com/...") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = youtube,
onValueChange = { youtube = it },
label = { Text("YouTube") },
placeholder = { Text("https://youtube.com/...") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
}
}
// --- Weitere Vereinsdaten ---
Card(modifier = androidx.compose.ui.Modifier.fillMaxWidth()) {
Column(modifier = androidx.compose.ui.Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text("Weitere Informationen", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
OutlinedTextField(
value = bankverbindung,
onValueChange = { bankverbindung = it },
label = { Text("IBAN / Bankverbindung") },
placeholder = { Text("AT12 3456 7890 1234 5678") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
OutlinedTextField(
value = uid,
onValueChange = { uid = it },
label = { Text("UID-Nummer / ZVR-Zahl") },
singleLine = true,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
)
}
}
// --- Speichern ---
if (saveSuccess) {
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer),
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
) {
Text(
"✓ Profil erfolgreich gespeichert!",
modifier = androidx.compose.ui.Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.bodyLarge
)
}
}
Button(
onClick = {
// TODO: Backend-Anbindung (PUT /api/organizer/profile)
saveSuccess = true
},
modifier = androidx.compose.ui.Modifier.fillMaxWidth().height(52.dp)
) {
Text("Profil speichern", style = MaterialTheme.typography.titleMedium)
}
OutlinedButton(
onClick = onNavigateToDashboard,
modifier = androidx.compose.ui.Modifier.fillMaxWidth()
) {
Text("Zum Dashboard")
}
Spacer(modifier = androidx.compose.ui.Modifier.height(24.dp))
}
}
}
@Composable
private fun AuthStatusScreen(
authTokenManager: AuthTokenManager,