Android client updated
Some checks failed
Code Analysis and Production Deploy / analyze (push) Failing after 5m45s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been skipped

This commit is contained in:
Torsten Schulz (local)
2026-06-05 08:34:49 +02:00
parent e517720b03
commit 7aa7970f2e
16 changed files with 102 additions and 69 deletions

View File

@@ -13,6 +13,7 @@ val localApiBaseUrl = providers.gradleProperty("LOCAL_API_BASE_URL")
val productionApiBaseUrl = providers.gradleProperty("PRODUCTION_API_BASE_URL") val productionApiBaseUrl = providers.gradleProperty("PRODUCTION_API_BASE_URL")
.orElse("https://harheimertc.de/") .orElse("https://harheimertc.de/")
.get() .get()
val expectedProductionApiBaseUrl = "https://harheimertc.de/"
val sentryDsn = providers.gradleProperty("SENTRY_DSN") val sentryDsn = providers.gradleProperty("SENTRY_DSN")
.orElse("") .orElse("")
.get() .get()
@@ -61,6 +62,16 @@ val ensureReleaseSigning = tasks.register("ensureReleaseSigning") {
} }
} }
val ensureProductionApiBaseUrl = tasks.register("ensureProductionApiBaseUrl") {
doFirst {
if (productionApiBaseUrl != expectedProductionApiBaseUrl) {
throw GradleException(
"Production Play Store builds must use $expectedProductionApiBaseUrl, but PRODUCTION_API_BASE_URL is $productionApiBaseUrl."
)
}
}
}
android { android {
namespace = "de.harheimertc" namespace = "de.harheimertc"
compileSdk = 35 compileSdk = 35
@@ -163,6 +174,7 @@ val collectPlayStoreArtifacts = tasks.register("collectPlayStoreArtifacts") {
group = "distribution" group = "distribution"
description = "Builds production release artifacts and collects AAB, mapping, and native symbols for Play Console upload." description = "Builds production release artifacts and collects AAB, mapping, and native symbols for Play Console upload."
dependsOn(ensureReleaseSigning) dependsOn(ensureReleaseSigning)
dependsOn(ensureProductionApiBaseUrl)
dependsOn(":app:bundleProductionRelease") dependsOn(":app:bundleProductionRelease")
dependsOn(packageNativeDebugSymbolsForProductionRelease) dependsOn(packageNativeDebugSymbolsForProductionRelease)
@@ -193,6 +205,7 @@ tasks.matching {
it.name in setOf("bundleProductionRelease", "assembleProductionRelease") it.name in setOf("bundleProductionRelease", "assembleProductionRelease")
}.configureEach { }.configureEach {
dependsOn(ensureReleaseSigning) dependsOn(ensureReleaseSigning)
dependsOn(ensureProductionApiBaseUrl)
} }
kotlin { kotlin {

View File

@@ -90,7 +90,7 @@ private fun CompactNavigation(
BrandRow( BrandRow(
loggedIn = navigationState.loggedIn, loggedIn = navigationState.loggedIn,
onLogin = { onNavigate(Destinations.Login.route) }, onLogin = { onNavigate(Destinations.Login.route) },
onOpenCms = { sectionOverride.value = MenuSection.CMS }, onLogout = onLogout,
) )
ScrollableMenuRow(scrollState = mainScroll) { ScrollableMenuRow(scrollState = mainScroll) {
@@ -106,6 +106,9 @@ private fun CompactNavigation(
} }
if (navigationState.loggedIn) { if (navigationState.loggedIn) {
CompactSectionLink("Intern", MenuSection.INTERN, section) { sectionOverride.value = MenuSection.INTERN } CompactSectionLink("Intern", MenuSection.INTERN, section) { sectionOverride.value = MenuSection.INTERN }
if (navigationState.canAccessCms) {
CompactSectionLink("CMS", MenuSection.CMS, section) { sectionOverride.value = MenuSection.CMS }
}
} else { } else {
CompactLink("Login", Destinations.Login.route, selectedRoute, onNavigate) { sectionOverride.value = null } CompactLink("Login", Destinations.Login.route, selectedRoute, onNavigate) { sectionOverride.value = null }
} }
@@ -202,6 +205,12 @@ private fun WebTabletNavigation(
MainLink("Intern", section == MenuSection.INTERN, onClick = { MainLink("Intern", section == MenuSection.INTERN, onClick = {
sectionOverride.value = MenuSection.INTERN sectionOverride.value = MenuSection.INTERN
}) })
if (navigationState.canAccessCms) {
MainLink("CMS", section == MenuSection.CMS, onClick = {
sectionOverride.value = MenuSection.CMS
onNavigate(Destinations.Cms.route)
})
}
} }
MainLink("Kontakt", selectedRoute == Destinations.Contact.route, primary = true, onClick = { MainLink("Kontakt", selectedRoute == Destinations.Contact.route, primary = true, onClick = {
sectionOverride.value = null sectionOverride.value = null
@@ -210,7 +219,7 @@ private fun WebTabletNavigation(
} }
Spacer(Modifier.width(12.dp)) Spacer(Modifier.width(12.dp))
if (navigationState.loggedIn) { if (navigationState.loggedIn) {
TextButton(onClick = { sectionOverride.value = MenuSection.CMS }) { Text("CMS", color = Color.White) } TextButton(onClick = onLogout) { Text("Logout", color = Color.White) }
} else { } else {
TextButton(onClick = { TextButton(onClick = {
sectionOverride.value = null sectionOverride.value = null
@@ -237,13 +246,13 @@ private fun WebTabletNavigation(
private fun BrandRow( private fun BrandRow(
loggedIn: Boolean, loggedIn: Boolean,
onLogin: () -> Unit, onLogin: () -> Unit,
onOpenCms: () -> Unit, onLogout: () -> Unit,
) { ) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Brand() Brand()
Spacer(Modifier.weight(1f)) Spacer(Modifier.weight(1f))
if (loggedIn) { if (loggedIn) {
TextButton(onClick = onOpenCms) { Text("CMS", color = Color.White) } TextButton(onClick = onLogout) { Text("Logout", color = Color.White) }
} else { } else {
TextButton(onClick = onLogin) { Text("Login", color = Color.White) } TextButton(onClick = onLogin) { Text("Login", color = Color.White) }
} }
@@ -465,23 +474,23 @@ private fun submenu(section: MenuSection?, state: NavigationUiState): List<MenuT
add(MenuTarget("News", Destinations.MemberNews.route)) add(MenuTarget("News", Destinations.MemberNews.route))
add(MenuTarget("Mein Profil", Destinations.Profile.route)) add(MenuTarget("Mein Profil", Destinations.Profile.route))
add(MenuTarget("API-Dokumentation", Destinations.MemberApi.route)) add(MenuTarget("API-Dokumentation", Destinations.MemberApi.route))
}
MenuSection.CMS -> buildList {
if (state.canAccessFullCms) {
add(MenuTarget("Übersicht", Destinations.Cms.route))
add(MenuTarget("Startseite", Destinations.CmsStartseite.route))
add(MenuTarget("Inhalte", Destinations.CmsInhalte.route))
add(MenuTarget("Vereinsmeisterschaften", Destinations.CmsVereinsmeisterschaften.route))
add(MenuTarget("News", Destinations.MemberNews.route))
add(MenuTarget("Sportbetrieb", Destinations.CmsSportbetrieb.route))
add(MenuTarget("Mitgliederverwaltung", Destinations.CmsMitgliederverwaltung.route))
add(MenuTarget("Einstellungen", Destinations.CmsEinstellungen.route))
add(MenuTarget("Benutzerverwaltung", Destinations.CmsBenutzer.route))
}
if (state.canAccessNewsletter) add(MenuTarget("Newsletter", Destinations.CmsNewsletter.route)) if (state.canAccessNewsletter) add(MenuTarget("Newsletter", Destinations.CmsNewsletter.route))
if (state.canAccessContactRequests) add(MenuTarget("Kontaktanfragen", Destinations.CmsContactRequests.route)) if (state.canAccessContactRequests) add(MenuTarget("Kontaktanfragen", Destinations.CmsContactRequests.route))
} }
MenuSection.CMS -> buildList {
add(MenuTarget("Übersicht", Destinations.Cms.route))
add(MenuTarget("Startseite", Destinations.CmsStartseite.route))
add(MenuTarget("Inhalte", Destinations.CmsInhalte.route))
add(MenuTarget("Vereinsmeisterschaften", Destinations.CmsVereinsmeisterschaften.route))
add(MenuTarget("News", Destinations.MemberNews.route))
add(MenuTarget("Sportbetrieb", Destinations.CmsSportbetrieb.route))
add(MenuTarget("Mitgliederverwaltung", Destinations.CmsMitgliederverwaltung.route))
add(MenuTarget("Kontaktanfragen", Destinations.CmsContactRequests.route))
add(MenuTarget("Einstellungen", Destinations.CmsEinstellungen.route))
add(MenuTarget("Benutzerverwaltung", Destinations.CmsBenutzer.route))
add(MenuTarget("Ausloggen", LOGOUT_ROUTE))
}
null -> emptyList() null -> emptyList()
} }

View File

@@ -0,0 +1,24 @@
package de.harheimertc.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import de.harheimertc.ui.theme.Accent500
import de.harheimertc.ui.theme.Primary600
@Composable
internal fun LoadingState(message: String = "Daten werden geladen...") {
Column(
modifier = Modifier.fillMaxWidth().padding(28.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
CircularProgressIndicator(color = Primary600)
Text(message, color = Accent500, modifier = Modifier.padding(top = 12.dp))
}
}

View File

@@ -23,8 +23,10 @@ data class NavigationUiState(
val connectionNote: String? = null, val connectionNote: String? = null,
) { ) {
val isAdmin: Boolean get() = "admin" in roles val isAdmin: Boolean get() = "admin" in roles
val canAccessFullCms: Boolean get() = roles.any { it in setOf("admin", "vorstand") }
val canAccessNewsletter: Boolean get() = roles.any { it in setOf("admin", "vorstand", "newsletter") } val canAccessNewsletter: Boolean get() = roles.any { it in setOf("admin", "vorstand", "newsletter") }
val canAccessContactRequests: Boolean get() = roles.any { it in setOf("admin", "vorstand", "trainer") } val canAccessContactRequests: Boolean get() = roles.any { it in setOf("admin", "vorstand", "trainer") }
val canAccessCms: Boolean get() = canAccessFullCms || canAccessNewsletter || canAccessContactRequests
val showGallery: Boolean get() = hasGalleryImages || canAccessNewsletter val showGallery: Boolean get() = hasGalleryImages || canAccessNewsletter
} }

View File

@@ -12,7 +12,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -36,6 +35,7 @@ import androidx.compose.ui.platform.LocalContext
import de.harheimertc.data.NewsDto import de.harheimertc.data.NewsDto
import de.harheimertc.data.NewsSaveRequest import de.harheimertc.data.NewsSaveRequest
import de.harheimertc.ui.components.FormMessages import de.harheimertc.ui.components.FormMessages
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.components.NativeRichTextEditor import de.harheimertc.ui.components.NativeRichTextEditor
import de.harheimertc.ui.navigation.Destinations import de.harheimertc.ui.navigation.Destinations
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -112,7 +112,7 @@ fun CmsNewsScreen(navController: NavController, showBackNavigation: Boolean, vie
} }
CmsPage(navController, showBackNavigation, "News", "Interne und öffentliche News") { CmsPage(navController, showBackNavigation, "News", "Interne und öffentliche News") {
if (state.loading) item { CircularProgressIndicator() } if (state.loading) item { LoadingState("News werden geladen...") }
item { item {
Button(onClick = { viewModel.load(); /* ensure latest */ }, modifier = Modifier.fillMaxWidth()) { Text("Neu laden") } Button(onClick = { viewModel.load(); /* ensure latest */ }, modifier = Modifier.fillMaxWidth()) { Text("Neu laden") }

View File

@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -50,6 +49,7 @@ import de.harheimertc.data.PasswordResetMatchingUserDto
import de.harheimertc.data.PasswordResetAttemptDto import de.harheimertc.data.PasswordResetAttemptDto
import de.harheimertc.data.PasswordResetStepDto import de.harheimertc.data.PasswordResetStepDto
import de.harheimertc.ui.components.FormMessages import de.harheimertc.ui.components.FormMessages
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.components.NativeRichTextEditor import de.harheimertc.ui.components.NativeRichTextEditor
import de.harheimertc.ui.navigation.Destinations import de.harheimertc.ui.navigation.Destinations
import de.harheimertc.repositories.MeisterschaftResult import de.harheimertc.repositories.MeisterschaftResult
@@ -67,7 +67,7 @@ import java.util.Locale
fun CmsDashboardScreen(navController: NavController, showBackNavigation: Boolean, viewModel: CmsViewModel = hiltViewModel()) { fun CmsDashboardScreen(navController: NavController, showBackNavigation: Boolean, viewModel: CmsViewModel = hiltViewModel()) {
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
CmsPage(navController, showBackNavigation, "CMS", "Verwaltung und interne Werkzeuge") { CmsPage(navController, showBackNavigation, "CMS", "Verwaltung und interne Werkzeuge") {
if (state.loading) item { CircularProgressIndicator(color = Primary600) } if (state.loading) item { LoadingState("CMS-Daten werden geladen...") }
item { CmsSummaryGrid(navController, state) } item { CmsSummaryGrid(navController, state) }
} }
} }
@@ -85,7 +85,7 @@ fun CmsStartseiteScreen(navController: NavController, showBackNavigation: Boolea
CmsPage(navController, showBackNavigation, "Startseite konfigurieren", "Legen Sie die Reihenfolge der Elemente auf der Startseite fest.") { CmsPage(navController, showBackNavigation, "Startseite konfigurieren", "Legen Sie die Reihenfolge der Elemente auf der Startseite fest.") {
when { when {
state.loading || config == null -> item { CircularProgressIndicator(color = Primary600) } state.loading || config == null -> item { LoadingState("Startseitenkonfiguration wird geladen...") }
else -> { else -> {
item { item {
Button( Button(
@@ -171,7 +171,7 @@ fun CmsInhalteScreen(navController: NavController, showBackNavigation: Boolean,
CmsPage(navController, showBackNavigation, "Inhalte", "Vereinsseiten und strukturierte Inhalte") { CmsPage(navController, showBackNavigation, "Inhalte", "Vereinsseiten und strukturierte Inhalte") {
when { when {
state.loading || config == null -> item { CircularProgressIndicator(color = Primary600) } state.loading || config == null -> item { LoadingState("Inhalte werden geladen...") }
else -> { else -> {
item { item {
Surface(color = Color.White, shape = RoundedCornerShape(14.dp), shadowElevation = 3.dp) { Surface(color = Color.White, shape = RoundedCornerShape(14.dp), shadowElevation = 3.dp) {
@@ -262,7 +262,7 @@ fun CmsVereinsmeisterschaftenScreen(navController: NavController, showBackNaviga
CmsPage(navController, showBackNavigation, "Vereinsmeisterschaften", "Ergebnisse und Hinweise als CSV-Inhalt bearbeiten") { CmsPage(navController, showBackNavigation, "Vereinsmeisterschaften", "Ergebnisse und Hinweise als CSV-Inhalt bearbeiten") {
when { when {
state.loading -> item { CircularProgressIndicator(color = Primary600) } state.loading -> item { LoadingState("Vereinsmeisterschaften werden geladen...") }
else -> { else -> {
item { item {
Row(horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth()) {
@@ -456,7 +456,7 @@ fun CmsSportbetriebScreen(navController: NavController, showBackNavigation: Bool
CmsPage(navController, showBackNavigation, "Sportbetrieb", "Trainingszeiten, Trainingsort und Trainer pflegen") { CmsPage(navController, showBackNavigation, "Sportbetrieb", "Trainingszeiten, Trainingsort und Trainer pflegen") {
when { when {
state.loading || config == null -> item { CircularProgressIndicator(color = Primary600) } state.loading || config == null -> item { LoadingState("Sportbetriebsdaten werden geladen...") }
else -> { else -> {
item { item {
Button( Button(
@@ -555,7 +555,7 @@ fun CmsMitgliederverwaltungScreen(navController: NavController, showBackNavigati
fun CmsContactRequestsScreen(navController: NavController, showBackNavigation: Boolean, viewModel: CmsViewModel = hiltViewModel()) { fun CmsContactRequestsScreen(navController: NavController, showBackNavigation: Boolean, viewModel: CmsViewModel = hiltViewModel()) {
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
CmsPage(navController, showBackNavigation, "Kontaktanfragen", "Eingegangene Nachrichten") { CmsPage(navController, showBackNavigation, "Kontaktanfragen", "Eingegangene Nachrichten") {
if (state.loading) item { CircularProgressIndicator(color = Primary600) } if (state.loading) item { LoadingState("Kontaktanfragen werden geladen...") }
if (!state.loading && state.contactRequests.isEmpty()) item { EmptyCard("Keine Kontaktanfragen gefunden.") } if (!state.loading && state.contactRequests.isEmpty()) item { EmptyCard("Keine Kontaktanfragen gefunden.") }
items(state.contactRequests.size) { index -> ContactRequestCard(state.contactRequests[index], viewModel) } items(state.contactRequests.size) { index -> ContactRequestCard(state.contactRequests[index], viewModel) }
} }
@@ -592,7 +592,7 @@ fun CmsNewsletterScreen(
var grpTargetGroup by remember { mutableStateOf("") } var grpTargetGroup by remember { mutableStateOf("") }
var grpSendToExternal by remember { mutableStateOf(true) } var grpSendToExternal by remember { mutableStateOf(true) }
CmsPage(navController, showBackNavigation, "Newsletter", "Newsletter und Gruppen") { CmsPage(navController, showBackNavigation, "Newsletter", "Newsletter und Gruppen") {
if (state.loading) item { CircularProgressIndicator(color = Primary600) } if (state.loading) item { LoadingState("Newsletter-Daten werden geladen...") }
item { item {
if (canWrite) Button(onClick = { if (canWrite) Button(onClick = {
editingNewsletter = null editingNewsletter = null
@@ -769,7 +769,7 @@ fun CmsEinstellungenScreen(navController: NavController, showBackNavigation: Boo
CmsPage(navController, showBackNavigation, "Einstellungen", "Vereins- und Website-Konfiguration") { CmsPage(navController, showBackNavigation, "Einstellungen", "Vereins- und Website-Konfiguration") {
when { when {
state.loading || config == null -> item { CircularProgressIndicator(color = Primary600) } state.loading || config == null -> item { LoadingState("Einstellungen werden geladen...") }
else -> { else -> {
item { item {
Button( Button(
@@ -883,7 +883,7 @@ fun CmsPasswordResetDiagnosticsScreen(navController: NavController, showBackNavi
} }
if (state.loading) { if (state.loading) {
item { CircularProgressIndicator(color = Primary600) } item { LoadingState("Diagnosedaten werden geladen...") }
} }
if (state.passwordResetSearchTerm.isNotBlank()) { if (state.passwordResetSearchTerm.isNotBlank()) {
@@ -1073,7 +1073,7 @@ private fun CmsConfigPage(
) { ) {
CmsPage(navController, showBackNavigation, title, subtitle) { CmsPage(navController, showBackNavigation, title, subtitle) {
if (config == null) { if (config == null) {
item { CircularProgressIndicator(color = Primary600) } item { LoadingState("Konfiguration wird geladen...") }
} else { } else {
item { item {
Surface(color = Color.White, shape = RoundedCornerShape(14.dp), shadowElevation = 3.dp) { Surface(color = Color.White, shape = RoundedCornerShape(14.dp), shadowElevation = 3.dp) {

View File

@@ -14,7 +14,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
@@ -36,6 +35,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import de.harheimertc.R import de.harheimertc.R
import de.harheimertc.ui.components.FormMessages import de.harheimertc.ui.components.FormMessages
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.components.ImageGrid import de.harheimertc.ui.components.ImageGrid
@Composable @Composable
@@ -131,7 +131,7 @@ fun GalleryScreen(viewModel: GalleryViewModel = hiltViewModel()) {
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
if (loading) { if (loading) {
CircularProgressIndicator() LoadingState("Galerie wird geladen...")
} else if (images.isEmpty()) { } else if (images.isEmpty()) {
Text(text = stringResource(R.string.gallery_empty)) Text(text = stringResource(R.string.gallery_empty))
} else { } else {

View File

@@ -17,7 +17,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -45,6 +44,7 @@ import androidx.navigation.NavController
import de.harheimertc.data.SpielDto import de.harheimertc.data.SpielDto
import de.harheimertc.data.LeagueTableRowDto import de.harheimertc.data.LeagueTableRowDto
import de.harheimertc.repositories.Mannschaft import de.harheimertc.repositories.Mannschaft
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.navigation.Destinations import de.harheimertc.ui.navigation.Destinations
import de.harheimertc.ui.theme.Accent100 import de.harheimertc.ui.theme.Accent100
import de.harheimertc.ui.theme.Accent500 import de.harheimertc.ui.theme.Accent500
@@ -424,9 +424,7 @@ private fun BackLink(navController: NavController, visible: Boolean) {
@Composable @Composable
private fun Loading() { private fun Loading() {
Column(Modifier.fillMaxWidth().padding(30.dp), horizontalAlignment = Alignment.CenterHorizontally) { LoadingState("Mannschaftsdaten werden geladen...")
CircularProgressIndicator(color = Primary600)
}
} }
@Composable @Composable

View File

@@ -14,7 +14,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@@ -39,6 +38,7 @@ import androidx.navigation.NavController
import de.harheimertc.data.MemberDto import de.harheimertc.data.MemberDto
import de.harheimertc.data.NewsDto import de.harheimertc.data.NewsDto
import de.harheimertc.data.QttrRowDto import de.harheimertc.data.QttrRowDto
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.components.RichText import de.harheimertc.ui.components.RichText
import de.harheimertc.ui.theme.Accent100 import de.harheimertc.ui.theme.Accent100
import de.harheimertc.ui.theme.Accent500 import de.harheimertc.ui.theme.Accent500
@@ -163,7 +163,7 @@ fun MembersScreen(
} }
when { when {
state.loading -> item { CircularProgressIndicator(color = Primary600) } state.loading -> item { LoadingState("Mitglieder werden geladen...") }
state.error != null -> item { ErrorCard(state.error.orEmpty(), viewModel::load) } state.error != null -> item { ErrorCard(state.error.orEmpty(), viewModel::load) }
display.isEmpty() -> item { Text("Keine Mitglieder gefunden.", color = Accent700) } display.isEmpty() -> item { Text("Keine Mitglieder gefunden.", color = Accent700) }
else -> if (viewMode == "table") { else -> if (viewMode == "table") {
@@ -200,7 +200,7 @@ fun MemberNewsScreen(
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
MemberAreaPage(navController, showBackNavigation, "News", "Neuigkeiten und Ankündigungen im Mitgliederbereich") { MemberAreaPage(navController, showBackNavigation, "News", "Neuigkeiten und Ankündigungen im Mitgliederbereich") {
when { when {
state.loading -> item { CircularProgressIndicator(color = Primary600) } state.loading -> item { LoadingState("News werden geladen...") }
state.error != null -> item { ErrorCard(state.error.orEmpty(), viewModel::load) } state.error != null -> item { ErrorCard(state.error.orEmpty(), viewModel::load) }
state.news.isEmpty() -> item { Text("Noch keine News vorhanden.", color = Accent700) } state.news.isEmpty() -> item { Text("Noch keine News vorhanden.", color = Accent700) }
else -> items(state.news.size) { index -> NewsCard(state.news[index]) } else -> items(state.news.size) { index -> NewsCard(state.news[index]) }
@@ -269,7 +269,7 @@ fun QttrScreen(
} }
} }
when { when {
state.loading -> item { CircularProgressIndicator(color = Primary600) } state.loading -> item { LoadingState("QTTR-Werte werden geladen...") }
state.error != null -> item { ErrorCard(state.error.orEmpty(), viewModel::load) } state.error != null -> item { ErrorCard(state.error.orEmpty(), viewModel::load) }
state.rows.isEmpty() -> item { Text("Keine QTTR-Werte gefunden.", color = Accent700) } state.rows.isEmpty() -> item { Text("Keine QTTR-Werte gefunden.", color = Accent700) }
else -> items(state.rows.size) { index -> QttrRowCard(state.rows[index], isOwnRow(state.rows[index].playerName, state.currentUserName)) } else -> items(state.rows.size) { index -> QttrRowCard(state.rows[index], isOwnRow(state.rows[index].playerName, state.currentUserName)) }

View File

@@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -28,6 +27,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import de.harheimertc.data.BirthdayDto import de.harheimertc.data.BirthdayDto
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.navigation.Destinations import de.harheimertc.ui.navigation.Destinations
import de.harheimertc.ui.theme.Accent100 import de.harheimertc.ui.theme.Accent100
import de.harheimertc.ui.theme.Accent500 import de.harheimertc.ui.theme.Accent500
@@ -158,8 +158,7 @@ private fun BirthdayCard(
when { when {
loading -> { loading -> {
CircularProgressIndicator(color = Primary600, modifier = Modifier.size(28.dp)) LoadingState("Geburtstage werden geladen...")
Text("Lade...", color = Accent500)
} }
error != null -> { error != null -> {
Text(error, color = MaterialTheme.colorScheme.error) Text(error, color = MaterialTheme.colorScheme.error)

View File

@@ -14,7 +14,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -32,6 +31,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import de.harheimertc.data.NewsletterGroupDto import de.harheimertc.data.NewsletterGroupDto
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.components.ValidatedTextField import de.harheimertc.ui.components.ValidatedTextField
import de.harheimertc.ui.navigation.Destinations import de.harheimertc.ui.navigation.Destinations
import de.harheimertc.ui.theme.Accent100 import de.harheimertc.ui.theme.Accent100
@@ -93,8 +93,7 @@ fun NewsletterConfirmScreen(
NewsletterStatusPage(navController, showBackNavigation, "Newsletter bestätigen") { NewsletterStatusPage(navController, showBackNavigation, "Newsletter bestätigen") {
when { when {
state.loading -> { state.loading -> {
CircularProgressIndicator(color = Primary600) LoadingState("Newsletter-Anmeldung wird bestätigt...")
Text("Newsletter-Anmeldung wird bestätigt...", color = Accent700)
} }
state.error != null -> { state.error != null -> {
Text("Fehler", style = MaterialTheme.typography.titleLarge, color = Accent900) Text("Fehler", style = MaterialTheme.typography.titleLarge, color = Accent900)
@@ -154,7 +153,7 @@ private fun NewsletterFormScreen(
Text("Wählen Sie einen Newsletter und geben Sie Ihre E-Mail-Adresse ein.", color = Accent500, modifier = Modifier.padding(top = 8.dp)) Text("Wählen Sie einen Newsletter und geben Sie Ihre E-Mail-Adresse ein.", color = Accent500, modifier = Modifier.padding(top = 8.dp))
} }
if (state.loading) { if (state.loading) {
item { CircularProgressIndicator(color = Primary600) } item { LoadingState("Newsletter-Daten werden geladen...") }
} else { } else {
item { item {
Surface(color = Color.White, shape = RoundedCornerShape(14.dp), shadowElevation = 3.dp) { Surface(color = Color.White, shape = RoundedCornerShape(14.dp), shadowElevation = 3.dp) {

View File

@@ -38,6 +38,7 @@ import androidx.navigation.NavController
import de.harheimertc.ui.theme.Accent500 import de.harheimertc.ui.theme.Accent500
import de.harheimertc.ui.theme.Accent900 import de.harheimertc.ui.theme.Accent900
import de.harheimertc.ui.components.ValidatedTextField import de.harheimertc.ui.components.ValidatedTextField
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.theme.Primary600 import de.harheimertc.ui.theme.Primary600
@Composable @Composable
@@ -67,9 +68,7 @@ fun ProfileScreen(
if (state.loading) { if (state.loading) {
item { item {
Column(Modifier.fillMaxWidth().padding(28.dp), horizontalAlignment = Alignment.CenterHorizontally) { LoadingState("Profil wird geladen...")
CircularProgressIndicator(color = Primary600)
}
} }
} else { } else {
item { item {

View File

@@ -15,7 +15,6 @@ import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -27,6 +26,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import de.harheimertc.BuildConfig import de.harheimertc.BuildConfig
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.components.RichText import de.harheimertc.ui.components.RichText
import de.harheimertc.ui.theme.Accent500 import de.harheimertc.ui.theme.Accent500
import de.harheimertc.ui.theme.Accent900 import de.harheimertc.ui.theme.Accent900
@@ -68,9 +68,7 @@ internal fun PublicCard(title: String? = null, content: @Composable () -> Unit)
@Composable @Composable
internal fun PublicLoading() { internal fun PublicLoading() {
Column(Modifier.fillMaxWidth().padding(28.dp), horizontalAlignment = Alignment.CenterHorizontally) { LoadingState()
CircularProgressIndicator(color = Primary600)
}
} }
@Composable @Composable

View File

@@ -18,7 +18,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -43,6 +42,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import de.harheimertc.data.SeasonDto import de.harheimertc.data.SeasonDto
import de.harheimertc.data.SpielDto import de.harheimertc.data.SpielDto
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.theme.Accent100 import de.harheimertc.ui.theme.Accent100
import de.harheimertc.ui.theme.Accent200 import de.harheimertc.ui.theme.Accent200
import de.harheimertc.ui.theme.Accent500 import de.harheimertc.ui.theme.Accent500
@@ -228,10 +228,7 @@ private fun MatchRow(game: SpielDto) {
@Composable @Composable
private fun LoadingPlan() { private fun LoadingPlan() {
Column(Modifier.fillMaxWidth().padding(40.dp), horizontalAlignment = Alignment.CenterHorizontally) { LoadingState("Spielpläne werden geladen...")
CircularProgressIndicator(color = Primary600)
Text("Spielpläne werden geladen...", color = Accent500, modifier = Modifier.padding(top = 12.dp))
}
} }
@Composable @Composable

View File

@@ -15,7 +15,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -32,6 +31,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import de.harheimertc.data.TerminDto import de.harheimertc.data.TerminDto
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.theme.Accent500 import de.harheimertc.ui.theme.Accent500
import de.harheimertc.ui.theme.Accent700 import de.harheimertc.ui.theme.Accent700
import de.harheimertc.ui.theme.Accent900 import de.harheimertc.ui.theme.Accent900
@@ -125,10 +125,7 @@ private fun TerminCard(termin: TerminDto) {
@Composable @Composable
private fun LoadingPanel() { private fun LoadingPanel() {
Column(Modifier.fillMaxWidth().padding(vertical = 38.dp), horizontalAlignment = Alignment.CenterHorizontally) { LoadingState("Termine werden geladen...")
CircularProgressIndicator(color = Primary600)
Text("Termine werden geladen...", color = Accent500, modifier = Modifier.padding(top = 12.dp))
}
} }
@Composable @Composable

View File

@@ -13,7 +13,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@@ -32,6 +31,7 @@ import androidx.navigation.NavController
import de.harheimertc.data.TrainingTimeDto import de.harheimertc.data.TrainingTimeDto
import de.harheimertc.data.TrainerDto import de.harheimertc.data.TrainerDto
import de.harheimertc.ui.navigation.Destinations import de.harheimertc.ui.navigation.Destinations
import de.harheimertc.ui.components.LoadingState
import de.harheimertc.ui.theme.Accent500 import de.harheimertc.ui.theme.Accent500
import de.harheimertc.ui.theme.Accent700 import de.harheimertc.ui.theme.Accent700
import de.harheimertc.ui.theme.Accent900 import de.harheimertc.ui.theme.Accent900
@@ -193,9 +193,7 @@ private fun TrainerCard(trainer: TrainerDto) {
@Composable @Composable
private fun Loading() { private fun Loading() {
Column(Modifier.fillMaxWidth().padding(30.dp), horizontalAlignment = Alignment.CenterHorizontally) { LoadingState("Trainingsdaten werden geladen...")
CircularProgressIndicator(color = Primary600)
}
} }
@Composable @Composable