feat: historique des parties et statistiques de réussite par table
This commit is contained in:
1 file changed
+126
-2
@@ -1,14 +1,138 @@
|
||||
package com.livrets.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.livrets.data.HistoryRepository
|
||||
import com.livrets.game.GameResult
|
||||
import com.livrets.game.TableStats
|
||||
import com.livrets.game.tableStats
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Composable
|
||||
fun HistoryScreen(history: HistoryRepository, onBack: () -> Unit) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("Historique") }
|
||||
fun HistoryScreen(
|
||||
history: HistoryRepository,
|
||||
onBack: () -> Unit,
|
||||
) {
|
||||
var tab by remember { mutableIntStateOf(0) }
|
||||
var games by remember { mutableStateOf<List<GameResult>>(emptyList()) }
|
||||
|
||||
LaunchedEffect(Unit) { games = history.load() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.windowInsetsPadding(WindowInsets.safeDrawing)
|
||||
.padding(24.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
TextButton(onClick = onBack) { Text("←") }
|
||||
Text("Historique", style = MaterialTheme.typography.headlineSmall)
|
||||
}
|
||||
|
||||
TabRow(selectedTabIndex = tab) {
|
||||
Tab(selected = tab == 0, onClick = { tab = 0 }, text = { Text("Parties") })
|
||||
Tab(selected = tab == 1, onClick = { tab = 1 }, text = { Text("Tables") })
|
||||
}
|
||||
|
||||
when (tab) {
|
||||
0 -> Box(Modifier.weight(1f)) { GamesTab(games) }
|
||||
else -> Box(Modifier.weight(1f)) { TablesTab(games.tableStats()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDate(date: Instant): String =
|
||||
date.toString().take(16).replace('T', ' ')
|
||||
|
||||
@Composable
|
||||
private fun GamesTab(games: List<GameResult>) {
|
||||
if (games.isEmpty()) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("Aucune partie enregistrée")
|
||||
}
|
||||
return
|
||||
}
|
||||
LazyColumn {
|
||||
items(games.asReversed()) { game ->
|
||||
Column(Modifier.fillMaxWidth().padding(vertical = 8.dp)) {
|
||||
Text(
|
||||
text = "${game.correctCount} / ${game.records.size}",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
Text(
|
||||
text = formatDate(game.date),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Text(
|
||||
text = "${game.config.questionCount} questions · ${game.config.secondsPerQuestion} s par question · jusqu'à la table de ${game.config.maxTable}",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
HorizontalDivider(modifier = Modifier.padding(top = 8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TablesTab(stats: List<TableStats>) {
|
||||
LazyColumn {
|
||||
items(stats) { stat ->
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "Table de ${stat.table}",
|
||||
modifier = Modifier.width(110.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
if (stat.attempts == 0) {
|
||||
LinearProgressIndicator(
|
||||
progress = { 0f },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Text("—", modifier = Modifier.padding(start = 8.dp))
|
||||
} else {
|
||||
LinearProgressIndicator(
|
||||
progress = { stat.successRate.toFloat() },
|
||||
modifier = Modifier.weight(1f),
|
||||
color = if (stat.successRate < 0.6) MaterialTheme.colorScheme.error
|
||||
else MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Text(
|
||||
text = "${(stat.successRate * 100).toInt()} %",
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user