feat: Add friendly match management features including API integration and UI updates
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
- Implemented API methods for listing, creating, updating, and deleting friendly matches. - Enhanced the ScheduleManager to handle friendly matches, including loading and state management. - Updated UI components to support editing and displaying friendly match results. - Modified localization files to reflect changes in terminology for match sets.
This commit is contained in:
@@ -337,7 +337,7 @@
|
||||
"statusFinished": "Fertig",
|
||||
"dataNotRecorded": "No nid erfasst",
|
||||
"resultsRanking": "Rangliste",
|
||||
"newSetPlaceholder": "Neue Satz, z. B. 11:7",
|
||||
"newSetPlaceholder": "Satz",
|
||||
"finishMatch": "Abschliessen",
|
||||
"correctMatch": "Korrigiere",
|
||||
"markMatchLive": "Als laufend markiere",
|
||||
|
||||
@@ -983,7 +983,7 @@
|
||||
"noPlayerDataAvailable": "Keine Spielerdaten verfügbar",
|
||||
"dataNotRecorded": "Noch nicht erfasst",
|
||||
"resultsRanking": "Rangliste",
|
||||
"newSetPlaceholder": "Neuen Satz, z. B. 11:7",
|
||||
"newSetPlaceholder": "Satz",
|
||||
"finishMatch": "Abschließen",
|
||||
"correctMatch": "Korrigieren",
|
||||
"markMatchLive": "Als laufend markieren",
|
||||
|
||||
@@ -1049,7 +1049,7 @@
|
||||
"noPlayerDataAvailable": "Keine Spielerdaten verfügbar",
|
||||
"dataNotRecorded": "Noch nicht erfasst",
|
||||
"resultsRanking": "Rangliste",
|
||||
"newSetPlaceholder": "Neuen Satz, z. B. 11:7",
|
||||
"newSetPlaceholder": "Satz",
|
||||
"finishMatch": "Abschließen",
|
||||
"correctMatch": "Korrigieren",
|
||||
"markMatchLive": "Als laufend markieren",
|
||||
|
||||
@@ -1033,7 +1033,7 @@
|
||||
"noPlayerDataAvailable": "No player data available",
|
||||
"dataNotRecorded": "Not yet recorded",
|
||||
"resultsRanking": "Ranking",
|
||||
"newSetPlaceholder": "New set, e.g. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Finish",
|
||||
"correctMatch": "Correct",
|
||||
"markMatchLive": "Mark as live",
|
||||
|
||||
@@ -1034,7 +1034,7 @@
|
||||
"noPlayerDataAvailable": "No player data available",
|
||||
"dataNotRecorded": "Not yet recorded",
|
||||
"resultsRanking": "Ranking",
|
||||
"newSetPlaceholder": "New set, e.g. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Finish",
|
||||
"correctMatch": "Correct",
|
||||
"markMatchLive": "Mark as live",
|
||||
|
||||
@@ -1034,7 +1034,7 @@
|
||||
"noPlayerDataAvailable": "No player data available",
|
||||
"dataNotRecorded": "Not yet recorded",
|
||||
"resultsRanking": "Ranking",
|
||||
"newSetPlaceholder": "New set, e.g. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Finish",
|
||||
"correctMatch": "Correct",
|
||||
"markMatchLive": "Mark as live",
|
||||
|
||||
@@ -1034,7 +1034,7 @@
|
||||
"noPlayerDataAvailable": "No hay datos de jugadores disponibles",
|
||||
"dataNotRecorded": "Aún no registrado",
|
||||
"resultsRanking": "Clasificación",
|
||||
"newSetPlaceholder": "Nuevo set, p. ej. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Finalizar",
|
||||
"correctMatch": "Corregir",
|
||||
"markMatchLive": "Marcar como en vivo",
|
||||
|
||||
@@ -1035,7 +1035,7 @@
|
||||
"noPlayerDataAvailable": "Walang available na data ng player",
|
||||
"dataNotRecorded": "Hindi pa naitala",
|
||||
"resultsRanking": "Ranggo",
|
||||
"newSetPlaceholder": "Bagong set, hal. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Tapusin",
|
||||
"correctMatch": "Itama",
|
||||
"markMatchLive": "Markahan bilang live",
|
||||
|
||||
@@ -1033,7 +1033,7 @@
|
||||
"noPlayerDataAvailable": "Aucune donnée de joueur disponible",
|
||||
"dataNotRecorded": "Pas encore enregistré",
|
||||
"resultsRanking": "Classement",
|
||||
"newSetPlaceholder": "Nouveau set, p. ex. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Terminer",
|
||||
"correctMatch": "Corriger",
|
||||
"markMatchLive": "Marquer en direct",
|
||||
|
||||
@@ -1035,7 +1035,7 @@
|
||||
"noPlayerDataAvailable": "Nessun dato del giocatore disponibile",
|
||||
"dataNotRecorded": "Non ancora registrato",
|
||||
"resultsRanking": "Classifica",
|
||||
"newSetPlaceholder": "Nuovo set, es. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Concludi",
|
||||
"correctMatch": "Correggi",
|
||||
"markMatchLive": "Segna come live",
|
||||
|
||||
@@ -1035,7 +1035,7 @@
|
||||
"noPlayerDataAvailable": "利用可能な選手データがありません",
|
||||
"dataNotRecorded": "未登録",
|
||||
"resultsRanking": "順位",
|
||||
"newSetPlaceholder": "新しいセット 例: 11:7",
|
||||
"newSetPlaceholder": "セット",
|
||||
"finishMatch": "終了",
|
||||
"correctMatch": "修正",
|
||||
"markMatchLive": "進行中としてマーク",
|
||||
|
||||
@@ -1031,7 +1031,7 @@
|
||||
"noPlayerDataAvailable": "Brak danych gracza",
|
||||
"dataNotRecorded": "Jeszcze nie wprowadzono",
|
||||
"resultsRanking": "Zaszeregowanie",
|
||||
"newSetPlaceholder": "Nowy set, np. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Zakończ",
|
||||
"correctMatch": "Popraw",
|
||||
"markMatchLive": "Oznacz jako na żywo",
|
||||
|
||||
@@ -1034,7 +1034,7 @@
|
||||
"noPlayerDataAvailable": "ไม่มีข้อมูลผู้เล่น",
|
||||
"dataNotRecorded": "ยังไม่ได้บันทึก",
|
||||
"resultsRanking": "อันดับ",
|
||||
"newSetPlaceholder": "เซตใหม่ เช่น 11:7",
|
||||
"newSetPlaceholder": "เซต",
|
||||
"finishMatch": "จบการแข่งขัน",
|
||||
"correctMatch": "แก้ไข",
|
||||
"markMatchLive": "ทำเครื่องหมายว่ากำลังแข่ง",
|
||||
|
||||
@@ -1035,7 +1035,7 @@
|
||||
"noPlayerDataAvailable": "Walang available na data ng player",
|
||||
"dataNotRecorded": "Hindi pa naitala",
|
||||
"resultsRanking": "Ranggo",
|
||||
"newSetPlaceholder": "Bagong set, hal. 11:7",
|
||||
"newSetPlaceholder": "Set",
|
||||
"finishMatch": "Tapusin",
|
||||
"correctMatch": "Itama",
|
||||
"markMatchLive": "Markahan bilang live",
|
||||
|
||||
@@ -1035,7 +1035,7 @@
|
||||
"noPlayerDataAvailable": "没有可用的球员数据",
|
||||
"dataNotRecorded": "尚未录入",
|
||||
"resultsRanking": "排名",
|
||||
"newSetPlaceholder": "新一局,例如 11:7",
|
||||
"newSetPlaceholder": "局",
|
||||
"finishMatch": "结束比赛",
|
||||
"correctMatch": "更正",
|
||||
"markMatchLive": "标记为进行中",
|
||||
|
||||
@@ -22,6 +22,10 @@ import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.filled.Stop
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
@@ -543,6 +547,8 @@ internal fun TournamentEditorMatchesTab(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
var confirmDelete by remember { mutableStateOf<Pair<Int, Int>?>(null) }
|
||||
var editingSet by remember { mutableStateOf<Pair<Int, Int>?>(null) }
|
||||
var editInput by remember { mutableStateOf("") }
|
||||
val hasKO = matches.any { (it.round ?: "").lowercase() != "group" && (it.round ?: "").isNotBlank() }
|
||||
var showDistributedDialog by remember { mutableStateOf(false) }
|
||||
var distributedMatches by remember { mutableStateOf<List<TournamentMatchDto>>(emptyList()) }
|
||||
@@ -720,7 +726,7 @@ internal fun TournamentEditorMatchesTab(
|
||||
resultInput = it
|
||||
resultError = null
|
||||
},
|
||||
placeholder = { Text(tr("tournaments.newSetPlaceholder", "11:7")) },
|
||||
placeholder = { Text(tr("tournaments.setShort", "Satz")) },
|
||||
singleLine = true,
|
||||
isError = resultError != null,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
@@ -746,10 +752,29 @@ internal fun TournamentEditorMatchesTab(
|
||||
val results = m.tournamentResults
|
||||
if (!results.isNullOrEmpty()) {
|
||||
Text(formatMatchSets(m))
|
||||
results.sortedBy { it.set }.forEach { r ->
|
||||
results.sortedBy { it.set }.forEach { r ->
|
||||
val a = r.pointsPlayer1?.let { kotlin.math.abs(it).toString() } ?: "-"
|
||||
val b = r.pointsPlayer2?.let { kotlin.math.abs(it).toString() } ?: "-"
|
||||
Text("${r.set}: $a:$b", style = MaterialTheme.typography.caption)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("${r.set}: $a:$b", style = MaterialTheme.typography.caption)
|
||||
if (m.isFinished != true) {
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
IconButton(onClick = { confirmDelete = Pair(m.id, r.set) }, modifier = Modifier.size(28.dp)) {
|
||||
Icon(
|
||||
Icons.Filled.Close,
|
||||
contentDescription = "Löschen",
|
||||
tint = MaterialTheme.colors.error,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = {
|
||||
editingSet = Pair(m.id, r.set)
|
||||
editInput = "$a:$b"
|
||||
}, modifier = Modifier.size(28.dp)) {
|
||||
Icon(Icons.Filled.Edit, contentDescription = "Bearbeiten", modifier = Modifier.size(18.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text(m.tournamentResults?.size?.toString() ?: "0")
|
||||
@@ -785,7 +810,7 @@ internal fun TournamentEditorMatchesTab(
|
||||
Column(modifier = Modifier.width(120.dp)) {
|
||||
if (m.isFinished != true) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
TextButton(onClick = {
|
||||
IconButton(onClick = {
|
||||
// finish
|
||||
scope.launch {
|
||||
runCatching {
|
||||
@@ -795,18 +820,31 @@ internal fun TournamentEditorMatchesTab(
|
||||
}.onFailure { onError(it.message) }
|
||||
onReload()
|
||||
}
|
||||
}) { Text(tr("tournaments.finishMatchShort", "Fertig")) }
|
||||
// start/stop match
|
||||
TextButton(onClick = {
|
||||
scope.launch {
|
||||
runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
api.setMatchActive(clubId, tournamentId, m.id, TournamentMatchActiveBody(isActive = true))
|
||||
}
|
||||
}.onFailure { onError(it.message) }
|
||||
onReload()
|
||||
}
|
||||
}) { Text(tr("tournaments.startMatch", "Starten")) }
|
||||
}) { Icon(Icons.Filled.Check, contentDescription = tr("tournaments.finishMatchShort", "Fertig")) }
|
||||
|
||||
if (m.isActive == true) {
|
||||
IconButton(onClick = {
|
||||
scope.launch {
|
||||
runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
api.setMatchActive(clubId, tournamentId, m.id, TournamentMatchActiveBody(isActive = false))
|
||||
}
|
||||
}.onFailure { onError(it.message) }
|
||||
onReload()
|
||||
}
|
||||
}) { Icon(Icons.Filled.Stop, contentDescription = tr("tournaments.stopMatch", "Stoppen")) }
|
||||
} else {
|
||||
IconButton(onClick = {
|
||||
scope.launch {
|
||||
runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
api.setMatchActive(clubId, tournamentId, m.id, TournamentMatchActiveBody(isActive = true))
|
||||
}
|
||||
}.onFailure { onError(it.message) }
|
||||
onReload()
|
||||
}
|
||||
}) { Icon(Icons.Filled.PlayArrow, contentDescription = tr("tournaments.startMatch", "Starten")) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TextButton(onClick = {
|
||||
@@ -820,19 +858,7 @@ internal fun TournamentEditorMatchesTab(
|
||||
}
|
||||
}) { Text(tr("tournaments.correct", "Korrigieren")) }
|
||||
}
|
||||
// Stop button shown regardless (to allow stopping active matches)
|
||||
if (m.isActive == true) {
|
||||
TextButton(onClick = {
|
||||
scope.launch {
|
||||
runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
api.setMatchActive(clubId, tournamentId, m.id, TournamentMatchActiveBody(isActive = false))
|
||||
}
|
||||
}.onFailure { onError(it.message) }
|
||||
onReload()
|
||||
}
|
||||
}) { Text(tr("tournaments.stopMatch", "Stoppen")) }
|
||||
}
|
||||
// no duplicate stop button
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
@@ -868,6 +894,73 @@ internal fun TournamentEditorMatchesTab(
|
||||
}
|
||||
)
|
||||
}
|
||||
// Edit set dialog
|
||||
if (editingSet != null) {
|
||||
val (matchIdToEdit, setToEdit) = editingSet!!
|
||||
AlertDialog(
|
||||
onDismissRequest = { editingSet = null },
|
||||
title = { Text(tr("tournaments.editSetTitle", "Satz bearbeiten")) },
|
||||
text = {
|
||||
Column {
|
||||
OutlinedTextField(
|
||||
value = editInput,
|
||||
onValueChange = { editInput = it },
|
||||
placeholder = { Text("11:7") }
|
||||
)
|
||||
Text(tr("tournaments.editSetHint", "Gib ein neues Ergebnis ein, z.B. 11:7"), style = MaterialTheme.typography.caption)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
val normalized = normalizeResult(editInput)
|
||||
if (normalized == null) {
|
||||
onError(tr("tournaments.invalidResultInput", "Ungültiges Ergebnis"))
|
||||
return@TextButton
|
||||
}
|
||||
scope.launch {
|
||||
runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
// Find current match results snapshot
|
||||
val matchObj = matches.firstOrNull { it.id == matchIdToEdit }
|
||||
val results = matchObj?.tournamentResults?.sortedBy { it.set } ?: emptyList()
|
||||
// Tail: sets after the edited one
|
||||
val tail = results.filter { it.set > setToEdit }.map { Pair(it.pointsPlayer1, it.pointsPlayer2) }
|
||||
// Delete tail from highest to lowest
|
||||
for (i in results.size downTo setToEdit + 1) {
|
||||
api.deleteMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentDeleteMatchResultBody(
|
||||
clubId, tournamentId, matchIdToEdit, i
|
||||
))
|
||||
}
|
||||
// Delete the set to edit
|
||||
api.deleteMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentDeleteMatchResultBody(
|
||||
clubId, tournamentId, matchIdToEdit, setToEdit
|
||||
))
|
||||
// Add the edited set
|
||||
api.addMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentAddMatchResultBody(
|
||||
clubId, tournamentId, matchIdToEdit, setToEdit, normalized
|
||||
))
|
||||
// Re-add tail sets in order
|
||||
var cur = setToEdit + 1
|
||||
for (t in tail) {
|
||||
val r = "${kotlin.math.abs(t.first ?: 0)}:${kotlin.math.abs(t.second ?: 0)}"
|
||||
api.addMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentAddMatchResultBody(
|
||||
clubId, tournamentId, matchIdToEdit, cur, r
|
||||
))
|
||||
cur++
|
||||
}
|
||||
}
|
||||
}.onFailure { onError(it.message) }
|
||||
editingSet = null
|
||||
editInput = ""
|
||||
onReload()
|
||||
}
|
||||
}) { Text(tr("common.save", "Speichern")) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { editingSet = null }) { Text(tr("common.cancel", "Abbrechen")) }
|
||||
}
|
||||
)
|
||||
}
|
||||
if (showDistributedDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDistributedDialog = false },
|
||||
@@ -948,7 +1041,7 @@ private fun MatchResultRow(
|
||||
value = resultInput,
|
||||
onValueChange = { resultInput = it },
|
||||
modifier = Modifier.weight(1f),
|
||||
placeholder = { Text(tr("tournaments.newSetPlaceholder", "Neuen Satz, z.B. 11:7")) },
|
||||
placeholder = { Text(tr("tournaments.setShort", "Satz")) },
|
||||
singleLine = true,
|
||||
)
|
||||
var tableExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.OutlinedButton
|
||||
import androidx.compose.material.OutlinedTextField
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -51,6 +52,9 @@ import de.tsschulz.tt_tagebuch.app.stats.TrainingStatsDerived
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchScope
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleViewMode
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyMatchSaveBody
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyParticipantDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyResultRowDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.canReadSchedule
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.canWriteSchedule
|
||||
import de.tsschulz.tt_tagebuch.shared.i18n.MobileStrings
|
||||
|
||||
@@ -3,12 +3,16 @@ package de.tsschulz.tt_tagebuch.shared.api
|
||||
import de.tsschulz.tt_tagebuch.shared.api.http.AuthedHttpClient
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.LeaguePlayerStatDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.LeagueTableRowDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyMatchSaveBody
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateMatchPlayersBody
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.delete
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.parameter
|
||||
import io.ktor.client.request.patch
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.put
|
||||
import io.ktor.client.request.setBody
|
||||
|
||||
class MatchesApi(
|
||||
@@ -41,4 +45,24 @@ class MatchesApi(
|
||||
setBody(body)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun listFriendlyMatches(clubId: Int): List<ScheduleMatchDto> {
|
||||
return client.http.get("/api/friendly-matches/$clubId").body()
|
||||
}
|
||||
|
||||
suspend fun createFriendlyMatch(clubId: Int, body: FriendlyMatchSaveBody): ScheduleMatchDto {
|
||||
return client.http.post("/api/friendly-matches/$clubId") {
|
||||
setBody(body)
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun updateFriendlyMatch(clubId: Int, matchId: Int, body: FriendlyMatchSaveBody): ScheduleMatchDto {
|
||||
return client.http.put("/api/friendly-matches/$clubId/$matchId") {
|
||||
setBody(body)
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun deleteFriendlyMatch(clubId: Int, matchId: Int) {
|
||||
client.http.delete("/api/friendly-matches/$clubId/$matchId")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,8 @@ data class ScheduleLeagueDetailsDto(
|
||||
@Serializable
|
||||
data class ScheduleMatchDto(
|
||||
val id: Int,
|
||||
val friendlyMatchId: Int? = null,
|
||||
val isFriendly: Boolean = false,
|
||||
val date: String? = null,
|
||||
val time: String? = null,
|
||||
val homeTeamId: Int? = null,
|
||||
@@ -95,6 +97,13 @@ data class ScheduleMatchDto(
|
||||
val guestMatchPoints: Int = 0,
|
||||
val isCompleted: Boolean = false,
|
||||
val pdfUrl: String? = null,
|
||||
val matchSystem: String? = null,
|
||||
val singlesCount: Int = 12,
|
||||
val doublesCount: Int = 4,
|
||||
val winningSets: Int = 3,
|
||||
val homeParticipants: List<FriendlyParticipantDto> = emptyList(),
|
||||
val guestParticipants: List<FriendlyParticipantDto> = emptyList(),
|
||||
val resultDetails: List<FriendlyResultRowDto> = emptyList(),
|
||||
@Serializable(with = LenientIntListSerializer::class)
|
||||
val playersReady: List<Int> = emptyList(),
|
||||
@Serializable(with = LenientIntListSerializer::class)
|
||||
@@ -107,6 +116,40 @@ data class ScheduleMatchDto(
|
||||
val leagueDetails: ScheduleLeagueDetailsDto? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FriendlyParticipantDto(
|
||||
val type: String = "manual",
|
||||
val memberId: Int? = null,
|
||||
val firstName: String = "",
|
||||
val lastName: String = "",
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FriendlyResultRowDto(
|
||||
val id: String = "",
|
||||
val type: String = "single",
|
||||
val homeName: String = "",
|
||||
val guestName: String = "",
|
||||
val sets: List<String> = emptyList(),
|
||||
val completed: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FriendlyMatchSaveBody(
|
||||
val date: String,
|
||||
val time: String? = null,
|
||||
val homeTeamName: String,
|
||||
val guestTeamName: String,
|
||||
val matchSystem: String = "Braunschweiger System",
|
||||
val winningSets: Int = 3,
|
||||
val homeParticipants: List<FriendlyParticipantDto> = emptyList(),
|
||||
val guestParticipants: List<FriendlyParticipantDto> = emptyList(),
|
||||
val homeMatchPoints: Int = 0,
|
||||
val guestMatchPoints: Int = 0,
|
||||
val isCompleted: Boolean = false,
|
||||
val resultDetails: List<FriendlyResultRowDto> = emptyList(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LeagueTableRowDto(
|
||||
val teamId: Int,
|
||||
@@ -137,4 +180,5 @@ enum class ScheduleViewMode {
|
||||
Team,
|
||||
Overall,
|
||||
Adult,
|
||||
Friendly,
|
||||
}
|
||||
|
||||
@@ -2209,7 +2209,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Fehlendi Date – Top 3 Minimeisterschaft",
|
||||
"tournaments.name" to "Name",
|
||||
"tournaments.newMiniChampionship" to "Neue Minimeisterschaft",
|
||||
"tournaments.newSetPlaceholder" to "Neue Satz, z. B. 11:7",
|
||||
"tournaments.setShort" to "Satz",
|
||||
"tournaments.newTournament" to "Neues Turnier",
|
||||
"tournaments.noAssignableMatches" to "Kei Spiel verfüegbar, wo beidi Spieler frei sind.",
|
||||
"tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.",
|
||||
@@ -4725,7 +4725,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Fehlende Daten – Top 3 Minimeisterschaft",
|
||||
"tournaments.name" to "Name",
|
||||
"tournaments.newMiniChampionship" to "Neue Minimeisterschaft",
|
||||
"tournaments.newSetPlaceholder" to "Neuen Satz, z. B. 11:7",
|
||||
"tournaments.setShort" to "Satz",
|
||||
"tournaments.newTournament" to "Neues Turnier",
|
||||
"tournaments.noAssignableMatches" to "Keine Spiele verfügbar, bei denen beide Spieler frei sind.",
|
||||
"tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.",
|
||||
@@ -7236,7 +7236,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Fehlende Daten – Top 3 Minimeisterschaft",
|
||||
"tournaments.name" to "Name",
|
||||
"tournaments.newMiniChampionship" to "Neue Minimeisterschaft",
|
||||
"tournaments.newSetPlaceholder" to "Neuen Satz, z. B. 11:7",
|
||||
"tournaments.setShort" to "Satz",
|
||||
"tournaments.newTournament" to "Neues Turnier",
|
||||
"tournaments.noAssignableMatches" to "Keine Spiele verfügbar, bei denen beide Spieler frei sind.",
|
||||
"tournaments.noClassesYet" to "Noch keine Klassen vorhanden. Fügen Sie eine neue Klasse hinzu.",
|
||||
@@ -9743,7 +9743,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Missing data – Top 3 Mini championship",
|
||||
"tournaments.name" to "name",
|
||||
"tournaments.newMiniChampionship" to "New Mini Championship",
|
||||
"tournaments.newSetPlaceholder" to "New set, e.g. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Add New Tournament",
|
||||
"tournaments.noAssignableMatches" to "No matches available where both players are free.",
|
||||
"tournaments.noClassesYet" to "No classes available yet. Add a new class.",
|
||||
@@ -12250,7 +12250,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Missing data – Top 3 Mini championship",
|
||||
"tournaments.name" to "name",
|
||||
"tournaments.newMiniChampionship" to "New Mini Championship",
|
||||
"tournaments.newSetPlaceholder" to "New set, e.g. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Add New Tournament",
|
||||
"tournaments.noAssignableMatches" to "No matches available where both players are free.",
|
||||
"tournaments.noClassesYet" to "No classes available yet. Add a new class.",
|
||||
@@ -14757,7 +14757,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Missing data – Top 3 Mini championship",
|
||||
"tournaments.name" to "name",
|
||||
"tournaments.newMiniChampionship" to "New Mini Championship",
|
||||
"tournaments.newSetPlaceholder" to "New set, e.g. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Add New Tournament",
|
||||
"tournaments.noAssignableMatches" to "No matches available where both players are free.",
|
||||
"tournaments.noClassesYet" to "No classes available yet. Add a new class.",
|
||||
@@ -17264,7 +17264,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Datos faltantes – Top 3 Mini campeonato",
|
||||
"tournaments.name" to "nombre",
|
||||
"tournaments.newMiniChampionship" to "Nuevo Mini Campeonato",
|
||||
"tournaments.newSetPlaceholder" to "Nuevo set, p. ej. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Agregar nuevo torneo",
|
||||
"tournaments.noAssignableMatches" to "No hay partidos disponibles en los que ambos jugadores estén libres.",
|
||||
"tournaments.noClassesYet" to "Aún no hay clases disponibles. Añade una nueva clase.",
|
||||
@@ -19771,7 +19771,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Nawawalang datos – Top 3 Mini championship",
|
||||
"tournaments.name" to "pangalan",
|
||||
"tournaments.newMiniChampionship" to "Bagong Mini Championship",
|
||||
"tournaments.newSetPlaceholder" to "Bagong set, hal. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Magdagdag ng Bagong Tournament",
|
||||
"tournaments.noAssignableMatches" to "Walang laban kung saan pareho ang mga manlalaro ay bakante.",
|
||||
"tournaments.noClassesYet" to "Wala pang klase. Magdagdag ng bagong klase.",
|
||||
@@ -22278,7 +22278,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Données manquantes – Top 3 Mini-championnat",
|
||||
"tournaments.name" to "nom",
|
||||
"tournaments.newMiniChampionship" to "Nouveau mini-championnat",
|
||||
"tournaments.newSetPlaceholder" to "Nouveau set, p. ex. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Ajouter un nouveau tournoi",
|
||||
"tournaments.noAssignableMatches" to "Aucun match disponible où les deux joueurs sont libres.",
|
||||
"tournaments.noClassesYet" to "Aucun cours disponible pour l'instant. Ajoutez une nouvelle classe.",
|
||||
@@ -24785,7 +24785,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Dati mancanti – Top 3 Mini campionato",
|
||||
"tournaments.name" to "nome",
|
||||
"tournaments.newMiniChampionship" to "Nuovo Mini Campionato",
|
||||
"tournaments.newSetPlaceholder" to "Nuovo set, es. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Aggiungi nuovo torneo",
|
||||
"tournaments.noAssignableMatches" to "Nessuna partita disponibile in cui entrambi i giocatori sono liberi.",
|
||||
"tournaments.noClassesYet" to "Nessuna lezione ancora disponibile. Aggiungi una nuova classe.",
|
||||
@@ -27292,7 +27292,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "不足データ – トップ3 ミニ選手権",
|
||||
"tournaments.name" to "名前",
|
||||
"tournaments.newMiniChampionship" to "新しいミニチャンピオンシップ",
|
||||
"tournaments.newSetPlaceholder" to "新しいセット 例: 11:7",
|
||||
"tournaments.setShort" to "セット",
|
||||
"tournaments.newTournament" to "新しいトーナメントを追加",
|
||||
"tournaments.noAssignableMatches" to "両選手が空いている試合がありません。",
|
||||
"tournaments.noClassesYet" to "まだ利用可能なクラスはありません。新しいクラスを追加します。",
|
||||
@@ -29799,7 +29799,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Brakujące dane – Top 3 Mini mistrzostwa",
|
||||
"tournaments.name" to "nazwa",
|
||||
"tournaments.newMiniChampionship" to "Nowe Mini Mistrzostwa",
|
||||
"tournaments.newSetPlaceholder" to "Nowy set, np. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Dodaj nowy turniej",
|
||||
"tournaments.noAssignableMatches" to "Brak meczów, w których obaj gracze są wolni.",
|
||||
"tournaments.noClassesYet" to "Nie ma jeszcze dostępnych zajęć. Dodaj nową klasę.",
|
||||
@@ -32306,7 +32306,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "ข้อมูลที่ขาดหาย – 3 อันดับแรก มินิแชมเปี้ยนชิพ",
|
||||
"tournaments.name" to "ชื่อ",
|
||||
"tournaments.newMiniChampionship" to "ใหม่ มินิ แชมเปี้ยนชิพ",
|
||||
"tournaments.newSetPlaceholder" to "เซตใหม่ เช่น 11:7",
|
||||
"tournaments.setShort" to "เซต",
|
||||
"tournaments.newTournament" to "เพิ่มทัวร์นาเมนต์ใหม่",
|
||||
"tournaments.noAssignableMatches" to "ไม่มีแมตช์ที่ผู้เล่นทั้งสองว่าง",
|
||||
"tournaments.noClassesYet" to "ยังไม่มีชั้นเรียนว่าง เพิ่มชั้นเรียนใหม่",
|
||||
@@ -34813,7 +34813,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "Nawawalang datos – Top 3 Mini championship",
|
||||
"tournaments.name" to "pangalan",
|
||||
"tournaments.newMiniChampionship" to "Bagong Mini Championship",
|
||||
"tournaments.newSetPlaceholder" to "Bagong set, hal. 11:7",
|
||||
"tournaments.setShort" to "Set",
|
||||
"tournaments.newTournament" to "Magdagdag ng Bagong Tournament",
|
||||
"tournaments.noAssignableMatches" to "Walang laban kung saan pareho ang mga manlalaro ay bakante.",
|
||||
"tournaments.noClassesYet" to "Wala pang klase. Magdagdag ng bagong klase.",
|
||||
@@ -37320,7 +37320,7 @@ object MobileStrings {
|
||||
"tournaments.missingDataPDFTitleTop3" to "缺失数据 – 前3名迷你锦标赛",
|
||||
"tournaments.name" to "姓名",
|
||||
"tournaments.newMiniChampionship" to "新迷你锦标赛",
|
||||
"tournaments.newSetPlaceholder" to "新一局,例如 11:7",
|
||||
"tournaments.setShort" to "局",
|
||||
"tournaments.newTournament" to "添加新锦标赛",
|
||||
"tournaments.noAssignableMatches" to "没有两位选手都空闲的比赛。",
|
||||
"tournaments.noClassesYet" to "还没有可用的课程。添加一个新类。",
|
||||
|
||||
@@ -4,6 +4,7 @@ import de.tsschulz.tt_tagebuch.shared.api.ClubTeamsApi
|
||||
import de.tsschulz.tt_tagebuch.shared.api.MatchesApi
|
||||
import de.tsschulz.tt_tagebuch.shared.api.ScheduleLogic
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.ClubTeamDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyMatchSaveBody
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.LeagueTableRowDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchScope
|
||||
@@ -21,6 +22,7 @@ data class ScheduleState(
|
||||
val ownMatches: List<ScheduleMatchDto> = emptyList(),
|
||||
val allMatches: List<ScheduleMatchDto> = emptyList(),
|
||||
val overallMatches: List<ScheduleMatchDto> = emptyList(),
|
||||
val friendlyMatches: List<ScheduleMatchDto> = emptyList(),
|
||||
val leagueTable: List<LeagueTableRowDto> = emptyList(),
|
||||
val matchScope: ScheduleMatchScope = ScheduleMatchScope.Own,
|
||||
val otherTeamName: String = "",
|
||||
@@ -36,6 +38,7 @@ data class ScheduleState(
|
||||
ScheduleViewMode.Overall -> ScheduleLogic.sortMatches(overallMatches)
|
||||
ScheduleViewMode.Adult ->
|
||||
ScheduleLogic.sortMatches(ScheduleLogic.filterAdultLeagues(overallMatches))
|
||||
ScheduleViewMode.Friendly -> ScheduleLogic.sortMatches(friendlyMatches)
|
||||
ScheduleViewMode.Team -> {
|
||||
val t = selectedTeam ?: return emptyList()
|
||||
ScheduleLogic.applyTeamMatchScope(
|
||||
@@ -75,6 +78,7 @@ class ScheduleManager(
|
||||
}
|
||||
ScheduleViewMode.Overall -> loadOverallSchedule(clubId)
|
||||
ScheduleViewMode.Adult -> loadAdultSchedule(clubId)
|
||||
ScheduleViewMode.Friendly -> loadFriendlyMatches(clubId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +232,35 @@ class ScheduleManager(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadFriendlyMatches(clubId: Int) {
|
||||
_state.update {
|
||||
it.copy(
|
||||
isLoading = true,
|
||||
error = null,
|
||||
viewMode = ScheduleViewMode.Friendly,
|
||||
matchScope = ScheduleMatchScope.Own,
|
||||
otherTeamName = "",
|
||||
selectedTeamId = null,
|
||||
ownMatches = emptyList(),
|
||||
allMatches = emptyList(),
|
||||
overallMatches = emptyList(),
|
||||
leagueTable = emptyList(),
|
||||
)
|
||||
}
|
||||
try {
|
||||
val matches = matchesApi.listFriendlyMatches(clubId)
|
||||
_state.update { it.copy(friendlyMatches = matches, isLoading = false, error = null) }
|
||||
} catch (t: Throwable) {
|
||||
_state.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
friendlyMatches = emptyList(),
|
||||
error = t.toUserMessage("Freundschaftsspiele konnten nicht geladen werden"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setMatchScope(scope: ScheduleMatchScope) {
|
||||
_state.update { it.copy(matchScope = scope) }
|
||||
if (scope == ScheduleMatchScope.Other) {
|
||||
@@ -255,4 +288,25 @@ class ScheduleManager(
|
||||
)
|
||||
refresh(clubId)
|
||||
}
|
||||
|
||||
suspend fun createFriendlyMatch(clubId: Int, body: FriendlyMatchSaveBody) {
|
||||
val saved = matchesApi.createFriendlyMatch(clubId, body)
|
||||
_state.update { it.copy(friendlyMatches = ScheduleLogic.sortMatches(it.friendlyMatches + saved)) }
|
||||
}
|
||||
|
||||
suspend fun updateFriendlyMatch(clubId: Int, matchId: Int, body: FriendlyMatchSaveBody) {
|
||||
val saved = matchesApi.updateFriendlyMatch(clubId, matchId, body)
|
||||
_state.update {
|
||||
it.copy(
|
||||
friendlyMatches = ScheduleLogic.sortMatches(
|
||||
it.friendlyMatches.filterNot { match -> match.id == matchId } + saved,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteFriendlyMatch(clubId: Int, matchId: Int) {
|
||||
matchesApi.deleteFriendlyMatch(clubId, matchId)
|
||||
_state.update { it.copy(friendlyMatches = it.friendlyMatches.filterNot { match -> match.id == matchId }) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user