feat(masterdata): add ÖTO seed data, regulation validation tests, and profile module integration

- Introduced ÖTO 2026-compliant seed data (`V008__Seed_OETO_2026_Data.sql`) for tournament classes, license matrix, and age groups.
- Added `RegulationSeedVerificationTest` to validate repository queries and domain eligibility logic.
- Implemented a new `profile-feature` module covering user profile management and ZNS linking.
- Integrated the `profile-feature` into the desktop shell and frontend with Koin DI configuration.
- Extended CHANGELOG, ROADMAP, and architecture documentation to reflect related changes.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-03-30 16:14:08 +02:00
parent 2262826603
commit c5c1e96d25
13 changed files with 682 additions and 10 deletions
@@ -34,6 +34,7 @@ kotlin {
implementation(projects.frontend.features.veranstalterFeature)
implementation(projects.frontend.features.veranstaltungFeature)
implementation(projects.frontend.features.turnierFeature)
implementation(project(":frontend:features:profile-feature"))
// Compose Desktop
implementation(compose.desktop.currentOs)
@@ -11,6 +11,7 @@ import at.mocode.frontend.core.localdb.DatabaseProvider
import at.mocode.frontend.core.localdb.localDbModule
import at.mocode.frontend.core.network.networkModule
import at.mocode.frontend.core.sync.di.syncModule
import at.mocode.frontend.features.profile.di.profileModule
import at.mocode.nennung.feature.di.nennungFeatureModule
import at.mocode.ping.feature.di.pingFeatureModule
import at.mocode.zns.feature.di.znsImportModule
@@ -31,10 +32,11 @@ fun main() = application {
pingFeatureModule,
nennungFeatureModule,
znsImportModule,
profileModule,
desktopModule,
)
}
println("[DesktopApp] Koin initialisiert")
println("[DesktopApp] KOIN initialisiert")
} catch (e: Exception) {
println("[DesktopApp] Koin-Warnung: ${e.message}")
}
@@ -3,11 +3,10 @@ package at.mocode.desktop.screens.layout
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Logout
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
@@ -19,19 +18,17 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import at.mocode.frontend.core.navigation.AppScreen
import at.mocode.frontend.features.profile.presentation.ProfileScreen
import at.mocode.frontend.features.profile.presentation.ProfileViewModel
import at.mocode.ping.feature.presentation.PingScreen
import at.mocode.ping.feature.presentation.PingViewModel
import at.mocode.turnier.feature.presentation.TurnierDetailScreen
import at.mocode.turnier.feature.presentation.TurnierNeuScreen
import at.mocode.veranstalter.feature.presentation.VeranstalterAuswahlScreen
import at.mocode.veranstalter.feature.presentation.VeranstalterDetailScreen
import at.mocode.veranstalter.feature.presentation.VeranstalterNeuScreen
import at.mocode.veranstalter.feature.presentation.FakeVeranstalterStore
import at.mocode.veranstalter.feature.presentation.FakeVeranstaltungStore
import at.mocode.veranstalter.feature.presentation.VeranstalterNeuScreen
import at.mocode.veranstaltung.feature.presentation.AdminUebersichtScreen
import at.mocode.veranstaltung.feature.presentation.VeranstaltungDetailScreen
import at.mocode.veranstaltung.feature.presentation.VeranstaltungNeuScreen
import at.mocode.veranstaltung.feature.presentation.VeranstaltungUebersichtScreen
import org.koin.compose.koinInject
// Primärfarbe der TopBar (kann später ins Theme ausgelagert werden)
@@ -300,7 +297,7 @@ private fun DesktopContentArea(
}
}
// Root-Screen: Leitet in V2-Fluss
// Root-Screen: Leitet in V2-Fluss ab
is AppScreen.Veranstaltungen -> {
// Direkt zur Veranstalter-Auswahl V2
at.mocode.desktop.v2.VeranstalterAuswahlV2(
@@ -407,7 +404,7 @@ private fun DesktopContentArea(
}
is AppScreen.TurnierNeu -> {
val evtId = currentScreen.veranstaltungId
// V2: wir erlauben Turnier-Nr nur, wenn die Veranstaltung im V2-Store existiert
// V2: Wir erlauben Turnier-Nr nur, wenn die Veranstaltung im V2-Store existiert
val parent = at.mocode.desktop.v2.StoreV2.vereine.firstOrNull { v ->
at.mocode.desktop.v2.StoreV2.eventsFor(v.id).any { it.id == evtId }
}
@@ -435,6 +432,14 @@ private fun DesktopContentArea(
)
}
// Profil-Screen
is AppScreen.Profile -> {
val profileViewModel: ProfileViewModel = koinInject()
ProfileScreen(
viewModel = profileViewModel,
)
}
// Fallback → Root
else -> AdminUebersichtScreen(
onVeranstalterAuswahl = { onNavigate(AppScreen.VeranstalterAuswahl) },