Files
meldestelle/frontend/shells/meldestelle-portal/src/commonMain/kotlin/MainApp.kt
T
stefan 354bd49de6 feat: integrate new desktop shell and extend backend & ADRs
- Added `meldestelle-desktop` module using JVM/Compose Desktop, registered in `settings.gradle.kts`.
- Integrated new screens and desktop navigation into core: `Veranstaltungen`, `TurnierDetail`, etc.
- Expanded backend with `ExposedFunktionaerRepository` in `officials-infrastructure`.
- Completed ADRs for bounded context mapping (`ADR-0014`) and context map (`ADR-0015`).
- Updated and extended project documentation with session logs and architecture decisions.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
2026-03-24 18:22:15 +01:00

164 lines
6.2 KiB
Kotlin

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import at.mocode.frontend.core.auth.data.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.domain.PlatformType
import at.mocode.frontend.core.domain.currentPlatform
import at.mocode.frontend.core.navigation.AppScreen
import at.mocode.ping.feature.presentation.PingScreen
import at.mocode.ping.feature.presentation.PingViewModel
import navigation.StateNavigationPort
import org.koin.compose.koinInject
import org.koin.compose.viewmodel.koinViewModel
import screens.*
@Composable
fun MainApp() {
AppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val navigationPort = koinInject<StateNavigationPort>()
val currentScreen by navigationPort.currentScreen.collectAsState()
val authTokenManager = koinInject<AuthTokenManager>()
val pingViewModel: PingViewModel = koinViewModel()
val loginViewModel: LoginViewModel = koinViewModel()
when (val screen = currentScreen) {
is AppScreen.Landing -> {
if (currentPlatform() == PlatformType.DESKTOP) {
val authState = authTokenManager.authState.collectAsState().value
if (authState.isAuthenticated) {
DashboardScreen(
authTokenManager = authTokenManager,
onLogout = {
authTokenManager.clearToken()
navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Dashboard))
},
onCreateTournament = { navigationPort.navigateToScreen(AppScreen.CreateTournament) }
)
} else {
LaunchedEffect(Unit) {
navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Dashboard))
}
}
} else {
LandingScreen(
onPrimaryCta = { navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Dashboard)) },
onOpenPing = { navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.OrganizerProfile)) }
)
}
}
is AppScreen.Dashboard -> DashboardScreen(
authTokenManager = authTokenManager,
onNennungOeffnen = { navigationPort.navigateToScreen(AppScreen.Nennung) },
onLogout = {
authTokenManager.clearToken()
if (currentPlatform() == PlatformType.DESKTOP) {
navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Dashboard))
} else {
navigationPort.navigateToScreen(AppScreen.Landing)
}
},
onCreateTournament = { navigationPort.navigateToScreen(AppScreen.CreateTournament) }
)
is AppScreen.Home -> DashboardScreen(
authTokenManager = authTokenManager,
onLogout = {
authTokenManager.clearToken()
if (currentPlatform() == PlatformType.DESKTOP) {
navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Dashboard))
} else {
navigationPort.navigateToScreen(AppScreen.Landing)
}
},
onCreateTournament = { navigationPort.navigateToScreen(AppScreen.CreateTournament) }
)
is AppScreen.CreateTournament -> CreateTournamentScreen(
onBack = { navigationPort.navigateToScreen(AppScreen.Dashboard) },
onSave = { navigationPort.navigateToScreen(AppScreen.Dashboard) }
)
is AppScreen.Login -> {
LoginScreen(
viewModel = loginViewModel,
onLoginSuccess = {
val returnTo = screen.returnTo
if (returnTo != null) {
navigationPort.navigateToScreen(returnTo)
} else {
navigationPort.navigateToScreen(AppScreen.Dashboard)
}
},
onBack = {
if (currentPlatform() == PlatformType.DESKTOP) {
// Desktop hat keine Landing Page — bleibt auf Login
} else {
navigationPort.navigateToScreen(AppScreen.Landing)
}
}
)
}
is AppScreen.Ping -> PingScreen(
viewModel = pingViewModel,
onBack = {
if (currentPlatform() == PlatformType.DESKTOP) {
navigationPort.navigateToScreen(AppScreen.Dashboard)
} else {
navigationPort.navigateToScreen(AppScreen.Landing)
}
}
)
is AppScreen.Nennung -> {
// NennungsMaske wird über das nennung-feature eingebunden (jvmMain only)
// Placeholder, bis das Feature vollständig integriert ist
NennungScreenContent()
}
is AppScreen.OrganizerProfile -> OrganizerProfileScreen(
authTokenManager = authTokenManager,
onLogout = {
authTokenManager.clearToken()
navigationPort.navigateToScreen(AppScreen.Landing)
},
onNavigateToDashboard = { navigationPort.navigateToScreen(AppScreen.Dashboard) }
)
is AppScreen.AuthCallback -> { /* OIDC Callback wird vom Auth-Modul verarbeitet */
}
is AppScreen.Profile -> AuthStatusScreen(
authTokenManager = authTokenManager,
onNavigateToLogin = {
navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Profile))
},
onNavigateToPing = { navigationPort.navigateToScreen(AppScreen.Ping) },
onBackToHome = {
if (currentPlatform() == PlatformType.DESKTOP) {
navigationPort.navigateToScreen(AppScreen.Dashboard)
} else {
navigationPort.navigateToScreen(AppScreen.Landing)
}
}
)
else -> {}
}
}
}
}