chore: implementiere Auth-Status-abhängige Navigation und Icons, deaktiviere Module ohne Initialisierung und passe NavRail sowie Header für besseren UX an

Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
2026-04-20 14:23:44 +02:00
parent a1bf93342e
commit 8806d11e3c
4 changed files with 112 additions and 45 deletions
@@ -8,14 +8,14 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import at.mocode.frontend.shell.desktop.navigation.DesktopNavigationPort
import at.mocode.frontend.shell.desktop.screens.layout.DesktopMainLayout
import at.mocode.frontend.core.auth.data.local.AuthTokenManager
import at.mocode.frontend.core.auth.presentation.LoginScreen
import at.mocode.frontend.core.auth.presentation.LoginViewModel
import at.mocode.frontend.core.designsystem.theme.AppTheme
import at.mocode.frontend.core.navigation.AppScreen
import at.mocode.frontend.features.device.initialization.data.local.DeviceInitializationSettingsManager
import at.mocode.frontend.shell.desktop.navigation.DesktopNavigationPort
import at.mocode.frontend.shell.desktop.screens.layout.DesktopMainLayout
import org.koin.compose.koinInject
import org.koin.compose.viewmodel.koinViewModel
@@ -72,7 +72,7 @@ fun DesktopApp() {
val returnTo = screen.returnTo ?: AppScreen.VeranstaltungVerwaltung
nav.navigateToScreen(returnTo)
},
onBack = { /* Desktop hat keine PortalDashboard-Page */ },
onBack = { nav.navigateBack() },
)
else -> {
@@ -85,6 +85,7 @@ fun DesktopApp() {
authTokenManager.clearToken()
nav.navigateToScreen(AppScreen.Login(returnTo = AppScreen.VeranstaltungVerwaltung))
},
isAuthenticated = authState.isAuthenticated
)
}
}
@@ -5,6 +5,7 @@ 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.automirrored.filled.Login
import androidx.compose.material.icons.automirrored.filled.Logout
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
@@ -85,6 +86,7 @@ fun DesktopMainLayout(
onNavigate: (AppScreen) -> Unit,
onBack: () -> Unit,
onLogout: () -> Unit,
isAuthenticated: Boolean = false
) {
println("[Navigation] Rendering Screen: ${currentScreen::class.simpleName} (Details: $currentScreen)")
// DeviceInitialization-Daten (On-the-fly geladen oder Default)
@@ -106,7 +108,8 @@ fun DesktopMainLayout(
// Navigation Rail (Modernere Seitenleiste)
DesktopNavRail(
currentScreen = currentScreen,
onNavigate = onNavigate
onNavigate = onNavigate,
isConfigured = onboardingSettings.isConfigured
)
Column(modifier = Modifier.fillMaxSize()) {
@@ -115,6 +118,7 @@ fun DesktopMainLayout(
onNavigate = onNavigate,
onBack = onBack,
onLogout = onLogout,
isAuthenticated = isAuthenticated
)
Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
@@ -141,7 +145,8 @@ fun DesktopMainLayout(
@Composable
private fun DesktopNavRail(
currentScreen: AppScreen,
onNavigate: (AppScreen) -> Unit
onNavigate: (AppScreen) -> Unit,
isConfigured: Boolean
) {
Surface(
modifier = Modifier.fillMaxHeight().width(Dimens.NavRailWidth),
@@ -174,14 +179,16 @@ private fun DesktopNavRail(
icon = Icons.Default.Dashboard,
label = "Admin",
selected = currentScreen is AppScreen.VeranstaltungVerwaltung || currentScreen is AppScreen.VeranstaltungDetail,
onClick = { onNavigate(AppScreen.VeranstaltungVerwaltung) }
onClick = { onNavigate(AppScreen.VeranstaltungVerwaltung) },
enabled = isConfigured
)
NavRailItem(
icon = Icons.Default.CloudDownload,
label = "ZNS-Import",
selected = currentScreen is AppScreen.StammdatenImport,
onClick = { onNavigate(AppScreen.StammdatenImport) }
onClick = { onNavigate(AppScreen.StammdatenImport) },
enabled = isConfigured
)
var showStammdatenMenu by remember { mutableStateOf(false) }
@@ -193,11 +200,12 @@ private fun DesktopNavRail(
currentScreen is AppScreen.Reiter || currentScreen is AppScreen.ReiterVerwaltung ||
currentScreen is AppScreen.Pferde || currentScreen is AppScreen.PferdVerwaltung ||
currentScreen is AppScreen.FunktionaerVerwaltung,
onClick = { showStammdatenMenu = true }
onClick = { showStammdatenMenu = true },
enabled = isConfigured
)
DropdownMenu(
expanded = showStammdatenMenu,
expanded = showStammdatenMenu && isConfigured,
onDismissRequest = { showStammdatenMenu = false },
offset = DpOffset(Dimens.NavRailWidth, 0.dp)
) {
@@ -240,14 +248,16 @@ private fun DesktopNavRail(
icon = Icons.Default.Email,
label = "Mails",
selected = currentScreen is AppScreen.NennungsEingang,
onClick = { onNavigate(AppScreen.NennungsEingang) }
onClick = { onNavigate(AppScreen.NennungsEingang) },
enabled = isConfigured
)
NavRailItem(
icon = Icons.Default.WifiTethering,
label = "ConnectivityCheck",
selected = currentScreen is AppScreen.ConnectivityCheck,
onClick = { onNavigate(AppScreen.ConnectivityCheck) }
onClick = { onNavigate(AppScreen.ConnectivityCheck) },
enabled = true // Immer aktiv zur Diagnose
)
Spacer(Modifier.weight(1f))
@@ -256,7 +266,8 @@ private fun DesktopNavRail(
icon = Icons.Default.AppRegistration,
label = "Setup",
selected = currentScreen is AppScreen.DeviceInitialization,
onClick = { onNavigate(AppScreen.DeviceInitialization) }
onClick = { onNavigate(AppScreen.DeviceInitialization) },
enabled = true
)
}
}
@@ -268,9 +279,11 @@ private fun NavRailItem(
icon: ImageVector,
label: String,
selected: Boolean,
onClick: () -> Unit
onClick: () -> Unit,
enabled: Boolean = true
) {
val tint = if (selected) MaterialTheme.colorScheme.primary else AppColors.NavigationContent
val contentAlpha = if (enabled) 1.0f else 0.38f
val tint = if (selected) MaterialTheme.colorScheme.primary else AppColors.NavigationContent.copy(alpha = contentAlpha)
val background = if (selected) MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.5f) else Color.Transparent
TooltipBox(
@@ -287,7 +300,7 @@ private fun NavRailItem(
Surface(
modifier = Modifier
.size(48.dp)
.clickable(onClick = onClick),
.clickable(enabled = enabled, onClick = onClick),
shape = MaterialTheme.shapes.medium,
color = background
) {
@@ -315,6 +328,7 @@ private fun DesktopTopHeader(
onNavigate: (AppScreen) -> Unit,
onBack: () -> Unit,
onLogout: () -> Unit,
isAuthenticated: Boolean
) {
Surface(
modifier = Modifier.fillMaxWidth().height(Dimens.TopBarHeight),
@@ -348,18 +362,34 @@ private fun DesktopTopHeader(
horizontalArrangement = Arrangement.spacedBy(Dimens.SpacingM)
) {
// Profil / Logout Bereich
Text(
text = "Administrator",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
IconButton(onClick = onLogout) {
Icon(
imageVector = Icons.AutoMirrored.Filled.Logout,
contentDescription = "Abmelden",
modifier = Modifier.size(Dimens.IconSizeM),
tint = MaterialTheme.colorScheme.error
if (isAuthenticated) {
Text(
text = "Administrator",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
IconButton(onClick = onLogout) {
Icon(
imageVector = Icons.AutoMirrored.Filled.Logout,
contentDescription = "Abmelden",
modifier = Modifier.size(Dimens.IconSizeM),
tint = MaterialTheme.colorScheme.error
)
}
} else {
Text(
text = "Gast",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
IconButton(onClick = { onNavigate(AppScreen.Login(returnTo = currentScreen)) }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.Login,
contentDescription = "Anmelden",
modifier = Modifier.size(Dimens.IconSizeM),
tint = MaterialTheme.colorScheme.primary
)
}
}
}
}