UI
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
package de.drazz.boehmitools.training.ui
|
package de.drazz.boehmitools.training.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -7,9 +9,7 @@ import androidx.compose.material.icons.filled.FitnessCenter
|
|||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
import androidx.compose.material3.NavigationBarDefaults
|
|
||||||
import androidx.compose.material3.NavigationBarItem
|
import androidx.compose.material3.NavigationBarItem
|
||||||
import androidx.compose.material3.NavigationBarItemDefaults
|
import androidx.compose.material3.NavigationBarItemDefaults
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -42,41 +42,40 @@ fun AppRoot(
|
|||||||
) {
|
) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val destinations = listOf(
|
val destinations = listOf(
|
||||||
AppDestination("tracker", "Training") { Icon(Icons.Default.FitnessCenter, null) },
|
AppDestination("tracker", "Tracking") { Icon(Icons.Default.FitnessCenter, null) },
|
||||||
AppDestination("settings", "Mehr") { Icon(Icons.Default.Settings, null) },
|
AppDestination("settings", "Mehr") { Icon(Icons.Default.Settings, null) },
|
||||||
)
|
)
|
||||||
val backStack by navController.currentBackStackEntryAsState()
|
val backStack by navController.currentBackStackEntryAsState()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = androidx.compose.material3.MaterialTheme.colorScheme.background,
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
Surface(
|
Box(Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 10.dp)) {
|
||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
|
Surface(
|
||||||
shape = RoundedCornerShape(24.dp),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
tonalElevation = 8.dp,
|
shape = RoundedCornerShape(24.dp),
|
||||||
shadowElevation = 10.dp,
|
tonalElevation = 10.dp,
|
||||||
) {
|
shadowElevation = 12.dp,
|
||||||
NavigationBar(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
|
||||||
windowInsets = NavigationBarDefaults.windowInsets,
|
|
||||||
) {
|
) {
|
||||||
destinations.forEach { destination ->
|
NavigationBar(containerColor = androidx.compose.material3.MaterialTheme.colorScheme.surface) {
|
||||||
val selected = backStack?.destination?.hierarchy?.any { it.route == destination.route } == true
|
destinations.forEach { destination ->
|
||||||
NavigationBarItem(
|
val selected = backStack?.destination?.hierarchy?.any { it.route == destination.route } == true
|
||||||
selected = selected,
|
NavigationBarItem(
|
||||||
onClick = {
|
selected = selected,
|
||||||
navController.navigate(destination.route) {
|
onClick = {
|
||||||
popUpTo("tracker") { saveState = true }
|
navController.navigate(destination.route) {
|
||||||
launchSingleTop = true
|
popUpTo("tracker") { saveState = true }
|
||||||
restoreState = true
|
launchSingleTop = true
|
||||||
}
|
restoreState = true
|
||||||
},
|
}
|
||||||
icon = destination.icon,
|
},
|
||||||
label = { Text(destination.label) },
|
icon = destination.icon,
|
||||||
colors = NavigationBarItemDefaults.colors(
|
label = { Text(destination.label) },
|
||||||
indicatorColor = MaterialTheme.colorScheme.primaryContainer,
|
colors = NavigationBarItemDefaults.colors(
|
||||||
),
|
indicatorColor = androidx.compose.material3.MaterialTheme.colorScheme.primaryContainer,
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +102,7 @@ private fun WhatsNewDialog(onDismiss: () -> Unit) {
|
|||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
title = { Text("Neu in RipMe ${BuildConfig.VERSION_NAME}") },
|
title = { Text("Neu in RipMe ${BuildConfig.VERSION_NAME}") },
|
||||||
text = { Text("Moderner Card-Look, verbesserte Hierarchie und kompaktere Trainingseingaben. Alle bestehenden Daten- und Synchronisationsfunktionen bleiben erhalten.") },
|
text = { Text("Modern Card Design, klarere Trainingseingaben und eine kompaktere Navigation.") },
|
||||||
confirmButton = { TextButton(onClick = onDismiss) { Text("Verstanden") } },
|
confirmButton = { TextButton(onClick = onDismiss) { Text("Verstanden") } },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-52
@@ -1,8 +1,6 @@
|
|||||||
package de.drazz.boehmitools.training.ui.setup
|
package de.drazz.boehmitools.training.ui.setup
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -13,17 +11,13 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.CloudDone
|
import androidx.compose.material.icons.filled.CloudDone
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material.icons.filled.Lock
|
|
||||||
import androidx.compose.material.icons.filled.Palette
|
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ElevatedCard
|
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -33,17 +27,12 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import de.drazz.boehmitools.training.BuildConfig
|
import de.drazz.boehmitools.training.BuildConfig
|
||||||
import de.drazz.boehmitools.training.data.settings.SettingsRepository
|
import de.drazz.boehmitools.training.data.settings.SettingsRepository
|
||||||
import de.drazz.boehmitools.training.ui.theme.BrandIndigo
|
|
||||||
import de.drazz.boehmitools.training.ui.theme.BrandPurple
|
|
||||||
import de.drazz.boehmitools.training.ui.theme.BrandViolet
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun SettingsScreen(
|
||||||
@@ -59,63 +48,65 @@ fun SettingsScreen(
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(contentPadding)
|
.padding(contentPadding)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(horizontal = 14.dp, vertical = 12.dp),
|
.padding(16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(14.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
Box(
|
Text("RipMe Einstellungen", style = MaterialTheme.typography.headlineMedium)
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.background(Brush.linearGradient(listOf(BrandIndigo, BrandPurple, BrandViolet)), RoundedCornerShape(26.dp))
|
|
||||||
.padding(20.dp),
|
|
||||||
) {
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
||||||
Text("Einstellungen", color = Color.White, style = MaterialTheme.typography.headlineMedium)
|
|
||||||
Text("Verbindung, Darstellung und App-Informationen", color = Color.White.copy(alpha = 0.82f))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
Text("Server & Anmeldung", style = MaterialTheme.typography.titleLarge)
|
||||||
Icon(Icons.Default.Lock, null, tint = MaterialTheme.colorScheme.primary)
|
|
||||||
Text("Server & Anmeldung", style = MaterialTheme.typography.titleLarge)
|
|
||||||
}
|
|
||||||
Text(
|
Text(
|
||||||
"Zugangsdaten werden lokal mit Android-Keystore-Unterstützung geschützt. Release-Builds akzeptieren ausschließlich HTTPS.",
|
"Basic-Auth-Zugangsdaten werden mit einem Android-Keystore-Schlüssel verschlüsselt. Release-Builds akzeptieren ausschließlich HTTPS.",
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
)
|
||||||
OutlinedTextField(state.serverUrl, viewModel::updateServer, Modifier.fillMaxWidth(), label = { Text("Server-URL") }, singleLine = true)
|
|
||||||
OutlinedTextField(state.username, viewModel::updateUsername, Modifier.fillMaxWidth(), label = { Text("Benutzername") }, singleLine = true)
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
state.password,
|
value = state.serverUrl,
|
||||||
viewModel::updatePassword,
|
onValueChange = viewModel::updateServer,
|
||||||
Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
label = { Text("Server-URL") },
|
||||||
|
singleLine = true,
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = state.username,
|
||||||
|
onValueChange = viewModel::updateUsername,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
label = { Text("Benutzername") },
|
||||||
|
singleLine = true,
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = state.password,
|
||||||
|
onValueChange = viewModel::updatePassword,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
label = { Text("Passwort") },
|
label = { Text("Passwort") },
|
||||||
visualTransformation = PasswordVisualTransformation(),
|
visualTransformation = PasswordVisualTransformation(),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
)
|
)
|
||||||
Button(onClick = viewModel::saveAndTest, enabled = !state.loading, modifier = Modifier.fillMaxWidth()) {
|
Button(
|
||||||
if (state.loading) CircularProgressIndicator(modifier = Modifier.height(18.dp), strokeWidth = 2.dp)
|
onClick = viewModel::saveAndTest,
|
||||||
else Icon(Icons.Default.CloudDone, null)
|
enabled = !state.loading,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
if (state.loading) {
|
||||||
|
CircularProgressIndicator(modifier = Modifier.height(18.dp), strokeWidth = 2.dp)
|
||||||
|
} else {
|
||||||
|
Icon(Icons.Default.CloudDone, null)
|
||||||
|
}
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
Text("Speichern & Verbindung testen")
|
Text("Speichern & Verbindung testen")
|
||||||
}
|
}
|
||||||
if (state.message.isNotBlank()) Text(state.message, color = MaterialTheme.colorScheme.tertiary)
|
if (state.message.isNotBlank()) Text(state.message, color = MaterialTheme.colorScheme.primary)
|
||||||
if (state.error.isNotBlank()) Text(state.error, color = MaterialTheme.colorScheme.error)
|
if (state.error.isNotBlank()) Text(state.error, color = MaterialTheme.colorScheme.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ElevatedCard(Modifier.fillMaxWidth(), elevation = CardDefaults.elevatedCardElevation(3.dp)) {
|
Card(Modifier.fillMaxWidth()) {
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
Text("Darstellung", style = MaterialTheme.typography.titleLarge)
|
||||||
Icon(Icons.Default.Palette, null, tint = MaterialTheme.colorScheme.primary)
|
Row(
|
||||||
Text("Darstellung", style = MaterialTheme.typography.titleLarge)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
}
|
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
) {
|
||||||
listOf("system" to "System", "light" to "Hell", "dark" to "Dunkel").forEach { (value, label) ->
|
listOf("system" to "System", "light" to "Hell", "dark" to "Dunkel").forEach { (value, label) ->
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = current.theme == value,
|
selected = current.theme == value,
|
||||||
@@ -128,11 +119,14 @@ fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ElevatedCard(Modifier.fillMaxWidth(), elevation = CardDefaults.elevatedCardElevation(3.dp)) {
|
Card(Modifier.fillMaxWidth()) {
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
Text("Über RipMe", style = MaterialTheme.typography.titleLarge)
|
Text("Über RipMe", style = MaterialTheme.typography.titleLarge)
|
||||||
Text("Version ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
Text("Version ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
||||||
Text("Native Android-App für deinen Trainingstracker.", color = MaterialTheme.colorScheme.onSurfaceVariant)
|
Text(
|
||||||
|
"Native Android-App für deinen Trainingstracker.",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
OutlinedButton(onClick = onShowWhatsNew, modifier = Modifier.fillMaxWidth()) {
|
OutlinedButton(onClick = onShowWhatsNew, modifier = Modifier.fillMaxWidth()) {
|
||||||
Icon(Icons.Default.Info, null)
|
Icon(Icons.Default.Info, null)
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
|
|||||||
@@ -1,75 +1,63 @@
|
|||||||
package de.drazz.boehmitools.training.ui.theme
|
package de.drazz.boehmitools.training.ui.theme
|
||||||
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Shapes
|
|
||||||
import androidx.compose.material3.Typography
|
import androidx.compose.material3.Typography
|
||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
val BrandPurple = Color(0xFF7C4DFF)
|
val ModernPurple = Color(0xFF7C3AED)
|
||||||
val BrandViolet = Color(0xFF9C4DFF)
|
val ModernViolet = Color(0xFF9B5CF6)
|
||||||
val BrandIndigo = Color(0xFF5B5FEF)
|
val ModernIndigo = Color(0xFF4F46E5)
|
||||||
val SuccessGreen = Color(0xFF22C58B)
|
val ModernGreen = Color(0xFF22C55E)
|
||||||
|
|
||||||
private val Light = lightColorScheme(
|
private val Light = lightColorScheme(
|
||||||
primary = Color(0xFF6D3FE8),
|
primary = ModernPurple,
|
||||||
onPrimary = Color.White,
|
onPrimary = Color.White,
|
||||||
primaryContainer = Color(0xFFE9DDFF),
|
primaryContainer = Color(0xFFEDE9FE),
|
||||||
onPrimaryContainer = Color(0xFF22005D),
|
onPrimaryContainer = Color(0xFF32136B),
|
||||||
secondary = Color(0xFF64558A),
|
secondary = ModernIndigo,
|
||||||
onSecondary = Color.White,
|
onSecondary = Color.White,
|
||||||
secondaryContainer = Color(0xFFE9DEFF),
|
secondaryContainer = Color(0xFFE0E7FF),
|
||||||
onSecondaryContainer = Color(0xFF201047),
|
onSecondaryContainer = Color(0xFF1E1B4B),
|
||||||
tertiary = Color(0xFF006C63),
|
tertiary = ModernGreen,
|
||||||
tertiaryContainer = Color(0xFF74F8E8),
|
background = Color(0xFFF6F5FA),
|
||||||
background = Color(0xFFF8F7FC),
|
onBackground = Color(0xFF17151D),
|
||||||
onBackground = Color(0xFF1C1B20),
|
surface = Color(0xFFFFFFFF),
|
||||||
surface = Color(0xFFFFFBFF),
|
onSurface = Color(0xFF17151D),
|
||||||
onSurface = Color(0xFF1C1B20),
|
surfaceVariant = Color(0xFFF0EEF5),
|
||||||
surfaceVariant = Color(0xFFE8E5EE),
|
onSurfaceVariant = Color(0xFF66616F),
|
||||||
outline = Color(0xFF7A757F),
|
outline = Color(0xFFDDD8E7),
|
||||||
outlineVariant = Color(0xFFCBC5D0),
|
outlineVariant = Color(0xFFEAE6F0),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val Dark = darkColorScheme(
|
private val Dark = darkColorScheme(
|
||||||
primary = Color(0xFFCBB5FF),
|
primary = Color(0xFFA78BFA),
|
||||||
onPrimary = Color(0xFF3B008F),
|
onPrimary = Color(0xFF221047),
|
||||||
primaryContainer = Color(0xFF5427B6),
|
primaryContainer = Color(0xFF4C1D95),
|
||||||
onPrimaryContainer = Color(0xFFEADDFF),
|
onPrimaryContainer = Color(0xFFF3E8FF),
|
||||||
secondary = Color(0xFFCDC1E9),
|
secondary = Color(0xFF818CF8),
|
||||||
onSecondary = Color(0xFF352A4F),
|
onSecondary = Color(0xFF11133F),
|
||||||
secondaryContainer = Color(0xFF4C4167),
|
secondaryContainer = Color(0xFF312E81),
|
||||||
onSecondaryContainer = Color(0xFFE9DEFF),
|
onSecondaryContainer = Color(0xFFE0E7FF),
|
||||||
tertiary = Color(0xFF54DBC9),
|
tertiary = Color(0xFF34D399),
|
||||||
tertiaryContainer = Color(0xFF005049),
|
background = Color(0xFF0D111A),
|
||||||
background = Color(0xFF0F1118),
|
onBackground = Color(0xFFF5F3FA),
|
||||||
onBackground = Color(0xFFE8E1EA),
|
surface = Color(0xFF151A25),
|
||||||
surface = Color(0xFF151821),
|
onSurface = Color(0xFFF5F3FA),
|
||||||
onSurface = Color(0xFFE8E1EA),
|
surfaceVariant = Color(0xFF202634),
|
||||||
surfaceVariant = Color(0xFF2A2D38),
|
onSurfaceVariant = Color(0xFFB7B2C3),
|
||||||
outline = Color(0xFF958F9A),
|
outline = Color(0xFF383F50),
|
||||||
outlineVariant = Color(0xFF46434D),
|
outlineVariant = Color(0xFF282E3B),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val AppShapes = Shapes(
|
private val ModernTypography = Typography().copy(
|
||||||
extraSmall = RoundedCornerShape(10.dp),
|
headlineSmall = Typography().headlineSmall.copy(fontWeight = FontWeight.Bold, letterSpacing = (-0.4).sp),
|
||||||
small = RoundedCornerShape(14.dp),
|
titleLarge = Typography().titleLarge.copy(fontWeight = FontWeight.Bold),
|
||||||
medium = RoundedCornerShape(20.dp),
|
|
||||||
large = RoundedCornerShape(26.dp),
|
|
||||||
extraLarge = RoundedCornerShape(32.dp),
|
|
||||||
)
|
|
||||||
|
|
||||||
private val AppTypography = Typography(
|
|
||||||
headlineMedium = Typography().headlineMedium.copy(fontWeight = FontWeight.Bold, letterSpacing = (-0.4).sp),
|
|
||||||
headlineSmall = Typography().headlineSmall.copy(fontWeight = FontWeight.Bold, letterSpacing = (-0.3).sp),
|
|
||||||
titleLarge = Typography().titleLarge.copy(fontWeight = FontWeight.SemiBold),
|
|
||||||
titleMedium = Typography().titleMedium.copy(fontWeight = FontWeight.SemiBold),
|
titleMedium = Typography().titleMedium.copy(fontWeight = FontWeight.SemiBold),
|
||||||
labelLarge = Typography().labelLarge.copy(fontWeight = FontWeight.SemiBold),
|
labelLarge = Typography().labelLarge.copy(fontWeight = FontWeight.SemiBold),
|
||||||
)
|
)
|
||||||
@@ -83,8 +71,7 @@ fun TrainingTheme(theme: String, content: @Composable () -> Unit) {
|
|||||||
}
|
}
|
||||||
MaterialTheme(
|
MaterialTheme(
|
||||||
colorScheme = if (dark) Dark else Light,
|
colorScheme = if (dark) Dark else Light,
|
||||||
typography = AppTypography,
|
typography = ModernTypography,
|
||||||
shapes = AppShapes,
|
|
||||||
content = content,
|
content = content,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+81
-167
@@ -11,11 +11,15 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.CheckCircle
|
import androidx.compose.material.icons.filled.CheckCircle
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
@@ -27,7 +31,6 @@ import androidx.compose.material3.AlertDialog
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.ElevatedCard
|
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
@@ -42,8 +45,7 @@ import androidx.compose.material3.OutlinedButton
|
|||||||
import androidx.compose.material3.OutlinedCard
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.PrimaryTabRow
|
||||||
import androidx.compose.material3.TabRow
|
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@@ -54,15 +56,13 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import de.drazz.boehmitools.training.ui.theme.BrandIndigo
|
import de.drazz.boehmitools.training.ui.theme.ModernIndigo
|
||||||
import de.drazz.boehmitools.training.ui.theme.BrandPurple
|
import de.drazz.boehmitools.training.ui.theme.ModernPurple
|
||||||
import de.drazz.boehmitools.training.ui.theme.BrandViolet
|
import de.drazz.boehmitools.training.ui.theme.ModernViolet
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import de.drazz.boehmitools.training.data.local.PendingMutation
|
import de.drazz.boehmitools.training.data.local.PendingMutation
|
||||||
@@ -103,11 +103,12 @@ fun TrackerScreen(
|
|||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.padding(contentPadding),
|
modifier = Modifier.padding(contentPadding),
|
||||||
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Column {
|
Column {
|
||||||
Text(state.plan?.title ?: "RipMe", maxLines = 1, style = MaterialTheme.typography.titleLarge)
|
Text(state.plan?.title ?: "RipMe", maxLines = 2)
|
||||||
if (state.saveState.isNotBlank()) Text(state.saveState, style = MaterialTheme.typography.labelSmall)
|
if (state.saveState.isNotBlank()) Text(state.saveState, style = MaterialTheme.typography.labelSmall)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -131,11 +132,9 @@ fun TrackerScreen(
|
|||||||
) { inner ->
|
) { inner ->
|
||||||
Column(Modifier.fillMaxSize().padding(inner)) {
|
Column(Modifier.fillMaxSize().padding(inner)) {
|
||||||
val tabs = listOf("session" to "Session", "progress" to "Fortschritt", "plan" to "Plan", "faq" to "FAQ")
|
val tabs = listOf("session" to "Session", "progress" to "Fortschritt", "plan" to "Plan", "faq" to "FAQ")
|
||||||
TabRow(
|
PrimaryTabRow(
|
||||||
selectedTabIndex = tabs.indexOfFirst { it.first == state.tab }.coerceAtLeast(0),
|
selectedTabIndex = tabs.indexOfFirst { it.first == state.tab }.coerceAtLeast(0),
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
contentColor = MaterialTheme.colorScheme.primary,
|
|
||||||
divider = {},
|
|
||||||
) {
|
) {
|
||||||
tabs.forEach { (id, label) ->
|
tabs.forEach { (id, label) ->
|
||||||
Tab(
|
Tab(
|
||||||
@@ -185,7 +184,7 @@ private fun SessionPane(state: TrackerUiState, pending: List<PendingMutation>, v
|
|||||||
val session = trackerSession(state.tracker, de.drazz.boehmitools.training.domain.sessionKey(state.week, state.day), plan)
|
val session = trackerSession(state.tracker, de.drazz.boehmitools.training.domain.sessionKey(state.week, state.day), plan)
|
||||||
Column(
|
Column(
|
||||||
Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 14.dp, vertical = 12.dp),
|
Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 14.dp, vertical = 12.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
) {
|
) {
|
||||||
if (state.error.isNotBlank()) Text(state.error, color = MaterialTheme.colorScheme.error)
|
if (state.error.isNotBlank()) Text(state.error, color = MaterialTheme.colorScheme.error)
|
||||||
if (pending.isNotEmpty()) PendingQueue(pending, vm)
|
if (pending.isNotEmpty()) PendingQueue(pending, vm)
|
||||||
@@ -194,22 +193,16 @@ private fun SessionPane(state: TrackerUiState, pending: List<PendingMutation>, v
|
|||||||
|
|
||||||
SequenceCard("Aufwärmen", "warmup", day.warmup, session, vm)
|
SequenceCard("Aufwärmen", "warmup", day.warmup, session, vm)
|
||||||
day.rotations.forEach { rotation ->
|
day.rotations.forEach { rotation ->
|
||||||
ElevatedCard(
|
Card(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
shape = RoundedCornerShape(22.dp),
|
||||||
) {
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
|
||||||
Column(Modifier.padding(14.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
elevation = CardDefaults.cardElevation(defaultElevation = 5.dp),
|
||||||
|
) {
|
||||||
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(14.dp)) {
|
||||||
Text(rotation.label, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
Text(rotation.label, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||||
rotation.exercises.forEach { exercise ->
|
rotation.exercises.forEach { exercise ->
|
||||||
Surface(
|
ExerciseCard(exercise, session, plan, vm)
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
shape = RoundedCornerShape(18.dp),
|
|
||||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.42f),
|
|
||||||
) {
|
|
||||||
Box(Modifier.padding(14.dp)) {
|
|
||||||
ExerciseCard(exercise, session, plan, vm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,10 +215,7 @@ private fun SessionPane(state: TrackerUiState, pending: List<PendingMutation>, v
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PendingQueue(pending: List<PendingMutation>, vm: TrackerViewModel) {
|
private fun PendingQueue(pending: List<PendingMutation>, vm: TrackerViewModel) {
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Icon(Icons.Default.CloudOff, null)
|
Icon(Icons.Default.CloudOff, null)
|
||||||
@@ -304,69 +294,34 @@ private fun SessionHeader(plan: PlanUi, day: DayUi, session: JsonObject, week: I
|
|||||||
"completed" -> "Abgeschlossen"
|
"completed" -> "Abgeschlossen"
|
||||||
else -> "Geplant"
|
else -> "Geplant"
|
||||||
}
|
}
|
||||||
ElevatedCard(
|
Card(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 6.dp),
|
shape = RoundedCornerShape(24.dp),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.background(Brush.linearGradient(listOf(BrandIndigo, BrandPurple, BrandViolet)))
|
.background(Brush.linearGradient(listOf(ModernPurple, ModernIndigo)))
|
||||||
.padding(18.dp),
|
.padding(20.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
Text("Woche $week · Tag ${day.num}", color = Color.White.copy(alpha = 0.86f), style = MaterialTheme.typography.labelLarge)
|
Text("Woche $week · Tag ${day.num}", color = Color.White.copy(alpha = .8f), style = MaterialTheme.typography.labelLarge)
|
||||||
Text(day.focus, color = Color.White, style = MaterialTheme.typography.headlineSmall)
|
Text(day.focus, color = Color.White, style = MaterialTheme.typography.headlineSmall)
|
||||||
Text("Planrevision R${session.int("plan_revision", plan.publishedRevision)} · Status: $statusLabel", color = Color.White.copy(alpha = 0.78f), style = MaterialTheme.typography.bodySmall)
|
Text("Planrevision R${session.int("plan_revision", plan.publishedRevision)} · $statusLabel", color = Color.White.copy(alpha = .78f), style = MaterialTheme.typography.bodySmall)
|
||||||
when (status) {
|
when (status) {
|
||||||
"in_progress" -> Row(
|
"in_progress" -> Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
Button(onClick = { vm.setSessionStatus("completed") }, modifier = Modifier.weight(1f)) {
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
Icon(Icons.Default.CheckCircle, null); Spacer(Modifier.width(6.dp)); Text("Abschließen", maxLines = 1)
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = { vm.setSessionStatus("completed") },
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
colors = androidx.compose.material3.ButtonDefaults.buttonColors(
|
|
||||||
containerColor = Color.White,
|
|
||||||
contentColor = BrandPurple,
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.CheckCircle, null)
|
|
||||||
Spacer(Modifier.width(6.dp))
|
|
||||||
Text("Abschließen", maxLines = 1)
|
|
||||||
}
|
}
|
||||||
OutlinedButton(
|
OutlinedButton(onClick = { vm.setSessionStatus("stopped") }, modifier = Modifier.weight(1f)) {
|
||||||
onClick = { vm.setSessionStatus("stopped") },
|
Icon(Icons.Default.Stop, null); Spacer(Modifier.width(6.dp)); Text("Stoppen", maxLines = 1)
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.Stop, null)
|
|
||||||
Spacer(Modifier.width(6.dp))
|
|
||||||
Text("Stoppen", maxLines = 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"stopped" -> Button(
|
"stopped" -> Button(onClick = { vm.setSessionStatus("in_progress") }, modifier = Modifier.fillMaxWidth()) { Icon(Icons.Default.PlayArrow, null); Spacer(Modifier.width(6.dp)); Text("Session fortsetzen") }
|
||||||
onClick = { vm.setSessionStatus("in_progress") },
|
"completed" -> OutlinedButton(onClick = { vm.setSessionStatus("in_progress") }, modifier = Modifier.fillMaxWidth()) { Text("Session wieder öffnen") }
|
||||||
modifier = Modifier.fillMaxWidth(),
|
else -> Button(onClick = { vm.setSessionStatus("in_progress") }, modifier = Modifier.fillMaxWidth()) { Icon(Icons.Default.PlayArrow, null); Spacer(Modifier.width(6.dp)); Text("Session starten") }
|
||||||
) {
|
|
||||||
Icon(Icons.Default.PlayArrow, null)
|
|
||||||
Spacer(Modifier.width(6.dp))
|
|
||||||
Text("Session fortsetzen")
|
|
||||||
}
|
|
||||||
"completed" -> OutlinedButton(
|
|
||||||
onClick = { vm.setSessionStatus("in_progress") },
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
) { Text("Session wieder öffnen") }
|
|
||||||
else -> Button(
|
|
||||||
onClick = { vm.setSessionStatus("in_progress") },
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.PlayArrow, null)
|
|
||||||
Spacer(Modifier.width(6.dp))
|
|
||||||
Text("Session starten")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextButton(onClick = vm::resetSession, modifier = Modifier.fillMaxWidth()) {
|
|
||||||
Text("Session zurücksetzen")
|
|
||||||
}
|
}
|
||||||
|
TextButton(onClick = vm::resetSession, modifier = Modifier.fillMaxWidth()) { Text("Session zurücksetzen", color = Color.White.copy(alpha = .82f)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,11 +329,12 @@ private fun SessionHeader(plan: PlanUi, day: DayUi, session: JsonObject, week: I
|
|||||||
@Composable
|
@Composable
|
||||||
private fun SequenceCard(title: String, prefix: String, items: List<String>, session: JsonObject, vm: TrackerViewModel) {
|
private fun SequenceCard(title: String, prefix: String, items: List<String>, session: JsonObject, vm: TrackerViewModel) {
|
||||||
if (items.isEmpty()) return
|
if (items.isEmpty()) return
|
||||||
ElevatedCard(
|
Card(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
|
||||||
) {
|
) {
|
||||||
Column(Modifier.padding(14.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Text(title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
Text(title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||||
items.forEachIndexed { index, label ->
|
items.forEachIndexed { index, label ->
|
||||||
val id = "$prefix-$index"
|
val id = "$prefix-$index"
|
||||||
@@ -402,17 +358,28 @@ private fun ExerciseCard(exercise: ExerciseUi, session: JsonObject, plan: PlanUi
|
|||||||
val data = normalizedResultData(item, schema)
|
val data = normalizedResultData(item, schema)
|
||||||
var progressionMenu by remember(exercise.id, progression) { mutableStateOf(false) }
|
var progressionMenu by remember(exercise.id, progression) { mutableStateOf(false) }
|
||||||
|
|
||||||
Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Card(
|
||||||
Text(exercise.name, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
if (exercise.cue.isNotBlank()) Text(exercise.cue, style = MaterialTheme.typography.bodySmall)
|
shape = RoundedCornerShape(18.dp),
|
||||||
Text("Übungs-ID · ${exercise.id}", color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.labelSmall)
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = .72f)),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
|
||||||
|
) {
|
||||||
|
Column(Modifier.fillMaxWidth().padding(14.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Column(Modifier.weight(1f)) {
|
||||||
|
Text(exercise.name, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold)
|
||||||
|
if (exercise.cue.isNotBlank()) Text(exercise.cue, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
|
}
|
||||||
|
Box(Modifier.size(32.dp).clip(RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.primaryContainer))
|
||||||
|
}
|
||||||
|
Text("ID ${exercise.id}", style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Text("Status", style = MaterialTheme.typography.labelMedium)
|
Text("Status", style = MaterialTheme.typography.labelMedium)
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
) {
|
) {
|
||||||
listOf("planned" to "Offen", "completed" to "Fertig", "partial" to "Teilw.", "skipped" to "Skip").forEach { (value, label) ->
|
listOf("planned" to "Offen", "completed" to "Erledigt", "partial" to "Teilweise", "skipped" to "Übersprungen").forEach { (value, label) ->
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = status == value,
|
selected = status == value,
|
||||||
onClick = { vm.setItemStatus(exercise.id, value) },
|
onClick = { vm.setItemStatus(exercise.id, value) },
|
||||||
@@ -469,6 +436,7 @@ private fun ExerciseCard(exercise: ExerciseUi, session: JsonObject, plan: PlanUi
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -502,10 +470,7 @@ private fun ManualResultFormatEditor(
|
|||||||
return JsonObject(values)
|
return JsonObject(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
Column(Modifier.weight(1f)) {
|
Column(Modifier.weight(1f)) {
|
||||||
@@ -551,10 +516,7 @@ private fun ChoiceChips(label: String, choices: List<String>, selected: String,
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ResultEditor(schema: ResultSchemaUi, data: JsonObject, onChange: (JsonObject) -> Unit) {
|
private fun ResultEditor(schema: ResultSchemaUi, data: JsonObject, onChange: (JsonObject) -> Unit) {
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Text("Ergebnis · ${schema.source}", style = MaterialTheme.typography.labelLarge)
|
Text("Ergebnis · ${schema.source}", style = MaterialTheme.typography.labelLarge)
|
||||||
Text("${schema.mode}, ${schema.laterality}, Gewicht ${schema.weightMode}", style = MaterialTheme.typography.bodySmall)
|
Text("${schema.mode}, ${schema.laterality}, Gewicht ${schema.weightMode}", style = MaterialTheme.typography.bodySmall)
|
||||||
@@ -651,15 +613,12 @@ private fun ProgressPane(state: TrackerUiState, pending: List<PendingMutation>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 14.dp, vertical = 12.dp),
|
Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(12.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
Text("Fortschritt", style = MaterialTheme.typography.headlineSmall)
|
Text("Fortschritt", style = MaterialTheme.typography.headlineSmall)
|
||||||
|
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Text("Lokale Übersicht", style = MaterialTheme.typography.titleMedium)
|
Text("Lokale Übersicht", style = MaterialTheme.typography.titleMedium)
|
||||||
Text("$completed abgeschlossene Sessions · $stopped gestoppte Sessions · ${sessions.size} angelegte Sessions")
|
Text("$completed abgeschlossene Sessions · $stopped gestoppte Sessions · ${sessions.size} angelegte Sessions")
|
||||||
@@ -669,10 +628,7 @@ private fun ProgressPane(state: TrackerUiState, pending: List<PendingMutation>,
|
|||||||
|
|
||||||
if (pending.isNotEmpty()) PendingQueue(pending, vm)
|
if (pending.isNotEmpty()) PendingQueue(pending, vm)
|
||||||
|
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
Text("Auswertung auswählen", style = MaterialTheme.typography.titleMedium)
|
Text("Auswertung auswählen", style = MaterialTheme.typography.titleMedium)
|
||||||
Row(
|
Row(
|
||||||
@@ -700,10 +656,7 @@ private fun ProgressPane(state: TrackerUiState, pending: List<PendingMutation>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state.analysisScope == "week") {
|
if (state.analysisScope == "week") {
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
Text("Wochenstatus", style = MaterialTheme.typography.titleMedium)
|
Text("Wochenstatus", style = MaterialTheme.typography.titleMedium)
|
||||||
val closed = state.tracker.obj("week_statuses").obj(state.week.toString()).string("status") == "closed"
|
val closed = state.tracker.obj("week_statuses").obj(state.week.toString()).string("status") == "closed"
|
||||||
@@ -721,10 +674,7 @@ private fun ProgressPane(state: TrackerUiState, pending: List<PendingMutation>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
Text("Manuelle Analyse", style = MaterialTheme.typography.titleMedium)
|
Text("Manuelle Analyse", style = MaterialTheme.typography.titleMedium)
|
||||||
Button(
|
Button(
|
||||||
@@ -758,10 +708,7 @@ private fun ProgressPane(state: TrackerUiState, pending: List<PendingMutation>,
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AnalysisCard(title: String, record: JsonObject?) {
|
private fun AnalysisCard(title: String, record: JsonObject?) {
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
Text(title, style = MaterialTheme.typography.titleMedium)
|
Text(title, style = MaterialTheme.typography.titleMedium)
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
@@ -791,10 +738,7 @@ private fun AnalysisCard(title: String, record: JsonObject?) {
|
|||||||
|
|
||||||
val dataQuality = result.string("data_quality", result.obj("overview").string("data_quality"))
|
val dataQuality = result.string("data_quality", result.obj("overview").string("data_quality"))
|
||||||
if (dataQuality.isNotBlank()) {
|
if (dataQuality.isNotBlank()) {
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
Text("Datenbasis", fontWeight = FontWeight.Bold)
|
Text("Datenbasis", fontWeight = FontWeight.Bold)
|
||||||
Text(dataQuality)
|
Text(dataQuality)
|
||||||
@@ -806,10 +750,7 @@ private fun AnalysisCard(title: String, record: JsonObject?) {
|
|||||||
if (metrics.isNotEmpty()) {
|
if (metrics.isNotEmpty()) {
|
||||||
Text("Kennzahlen", style = MaterialTheme.typography.titleMedium)
|
Text("Kennzahlen", style = MaterialTheme.typography.titleMedium)
|
||||||
metrics.forEach { metric ->
|
metrics.forEach { metric ->
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(3.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(3.dp)) {
|
||||||
Text(metric.string("label"), style = MaterialTheme.typography.labelMedium)
|
Text(metric.string("label"), style = MaterialTheme.typography.labelMedium)
|
||||||
Text(metric.string("value"), style = MaterialTheme.typography.titleMedium)
|
Text(metric.string("value"), style = MaterialTheme.typography.titleMedium)
|
||||||
@@ -827,10 +768,7 @@ private fun AnalysisCard(title: String, record: JsonObject?) {
|
|||||||
if (exercises.isNotEmpty()) {
|
if (exercises.isNotEmpty()) {
|
||||||
Text("Übungsstand", style = MaterialTheme.typography.titleMedium)
|
Text("Übungsstand", style = MaterialTheme.typography.titleMedium)
|
||||||
exercises.forEach { update ->
|
exercises.forEach { update ->
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
||||||
Text(update.string("name", "Übung"), fontWeight = FontWeight.Bold)
|
Text(update.string("name", "Übung"), fontWeight = FontWeight.Bold)
|
||||||
val trend = when (update.string("trend")) {
|
val trend = when (update.string("trend")) {
|
||||||
@@ -858,10 +796,7 @@ private fun AnalysisCard(title: String, record: JsonObject?) {
|
|||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
)
|
)
|
||||||
adjustments.forEach { item ->
|
adjustments.forEach { item ->
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
||||||
Text(item.string("target", "Vorschlag"), fontWeight = FontWeight.Bold)
|
Text(item.string("target", "Vorschlag"), fontWeight = FontWeight.Bold)
|
||||||
if (item.string("suggested_change").isNotBlank()) Text(item.string("suggested_change"))
|
if (item.string("suggested_change").isNotBlank()) Text(item.string("suggested_change"))
|
||||||
@@ -875,10 +810,7 @@ private fun AnalysisCard(title: String, record: JsonObject?) {
|
|||||||
|
|
||||||
val warnings = result.array("warnings")
|
val warnings = result.array("warnings")
|
||||||
if (warnings.isNotEmpty()) {
|
if (warnings.isNotEmpty()) {
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
||||||
Text("Hinweise", fontWeight = FontWeight.Bold)
|
Text("Hinweise", fontWeight = FontWeight.Bold)
|
||||||
warnings.forEach { warning ->
|
warnings.forEach { warning ->
|
||||||
@@ -904,10 +836,7 @@ private fun AnalysisVisuals(visuals: JsonObject) {
|
|||||||
|
|
||||||
Text("Visualisierte Erkenntnisse", style = MaterialTheme.typography.titleMedium)
|
Text("Visualisierte Erkenntnisse", style = MaterialTheme.typography.titleMedium)
|
||||||
charts.forEach { chart ->
|
charts.forEach { chart ->
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
Text(chart.string("title", "Verlauf"), fontWeight = FontWeight.Bold)
|
Text(chart.string("title", "Verlauf"), fontWeight = FontWeight.Bold)
|
||||||
val series = chart.array("series").mapNotNull { it as? JsonObject }
|
val series = chart.array("series").mapNotNull { it as? JsonObject }
|
||||||
@@ -920,10 +849,7 @@ private fun AnalysisVisuals(visuals: JsonObject) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
flowcharts.forEach { flow ->
|
flowcharts.forEach { flow ->
|
||||||
OutlinedCard(
|
OutlinedCard(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
Text(flow.string("title", "Progressionspfad"), fontWeight = FontWeight.Bold)
|
Text(flow.string("title", "Progressionspfad"), fontWeight = FontWeight.Bold)
|
||||||
val nodes = flow.array("nodes").mapNotNull { it as? JsonObject }
|
val nodes = flow.array("nodes").mapNotNull { it as? JsonObject }
|
||||||
@@ -938,15 +864,12 @@ private fun AnalysisVisuals(visuals: JsonObject) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PlanPane(plan: PlanUi) {
|
private fun PlanPane(plan: PlanUi) {
|
||||||
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 14.dp, vertical = 12.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
Text(plan.title, style = MaterialTheme.typography.headlineSmall)
|
Text(plan.title, style = MaterialTheme.typography.headlineSmall)
|
||||||
Text(plan.subtitle)
|
Text(plan.subtitle)
|
||||||
Text("${plan.weeks} Wochen · veröffentlichte Revision ${plan.publishedRevision}")
|
Text("${plan.weeks} Wochen · veröffentlichte Revision ${plan.publishedRevision}")
|
||||||
plan.days.forEach { day ->
|
plan.days.forEach { day ->
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(14.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(Modifier.padding(14.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Text("Tag ${day.num}: ${day.focus}", style = MaterialTheme.typography.titleMedium)
|
Text("Tag ${day.num}: ${day.focus}", style = MaterialTheme.typography.titleMedium)
|
||||||
day.rotations.forEach { rotation ->
|
day.rotations.forEach { rotation ->
|
||||||
@@ -963,12 +886,9 @@ private fun PlanPane(plan: PlanUi) {
|
|||||||
private fun FaqPane(state: TrackerUiState) {
|
private fun FaqPane(state: TrackerUiState) {
|
||||||
val plan = state.plan ?: return
|
val plan = state.plan ?: return
|
||||||
val guide = state.response.obj("equivalence_guide")
|
val guide = state.response.obj("equivalence_guide")
|
||||||
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 14.dp, vertical = 12.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
Text("FAQ & Variantencluster", style = MaterialTheme.typography.headlineSmall)
|
Text("FAQ & Variantencluster", style = MaterialTheme.typography.headlineSmall)
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Text("Trainingsformat", style = MaterialTheme.typography.titleMedium)
|
Text("Trainingsformat", style = MaterialTheme.typography.titleMedium)
|
||||||
Text("Modus: ${plan.trainingFormat.string("mode", "sets_reps")}")
|
Text("Modus: ${plan.trainingFormat.string("mode", "sets_reps")}")
|
||||||
@@ -978,10 +898,7 @@ private fun FaqPane(state: TrackerUiState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Text("Planbezogene Übungsbibliothek", style = MaterialTheme.typography.titleMedium)
|
Text("Planbezogene Übungsbibliothek", style = MaterialTheme.typography.titleMedium)
|
||||||
plan.exerciseCatalog.values.mapNotNull { it as? JsonObject }.take(100).forEach { entry ->
|
plan.exerciseCatalog.values.mapNotNull { it as? JsonObject }.take(100).forEach { entry ->
|
||||||
@@ -990,10 +907,7 @@ private fun FaqPane(state: TrackerUiState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (guide.isNotEmpty()) {
|
if (guide.isNotEmpty()) {
|
||||||
ElevatedCard(
|
Card(Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp),
|
|
||||||
) {
|
|
||||||
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Text("Fallbackregeln des Servers", style = MaterialTheme.typography.titleMedium)
|
Text("Fallbackregeln des Servers", style = MaterialTheme.typography.titleMedium)
|
||||||
Text(guide.toString(), style = MaterialTheme.typography.bodySmall)
|
Text(guide.toString(), style = MaterialTheme.typography.bodySmall)
|
||||||
|
|||||||
@@ -2,4 +2,3 @@ org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8
|
|||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
kapt.use.k2=false
|
|
||||||
|
|||||||
Reference in New Issue
Block a user