feat(Diary): implement quick create functionality for training days and enhance localization
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s

- Added a new button for quick creation of training days in the DiaryView, improving user experience.
- Implemented logic to find the next available training slot across groups and create a training day entry.
- Enhanced localization by adding new keys for quick create messages in multiple languages, ensuring better accessibility for users.
- Updated the DiaryManager to handle quick create operations and clear errors effectively.
This commit is contained in:
Torsten Schulz (local)
2026-05-14 22:35:29 +02:00
parent 95a3e9438a
commit 83294406a4
29 changed files with 1976 additions and 46 deletions

View File

@@ -43,6 +43,11 @@ kotlin {
iosMain.dependencies {
implementation(libs.ktor.client.darwin)
}
androidUnitTest.dependencies {
implementation(libs.junit)
implementation(kotlin("test-junit"))
implementation(libs.kotlinx.serialization.json)
}
}
}

View File

@@ -0,0 +1,43 @@
package de.tsschulz.tt_tagebuch.shared.api.models
import kotlinx.serialization.json.Json
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
/**
* Phase 14: Regression gegen API-Inkonsistenzen (Boolean als 0/1/String) siehe [FlexibleNullableBooleanSerializer].
*/
class InternalTournamentSerializationTest {
private val json = Json {
ignoreUnknownKeys = true
isLenient = true
}
@Test
fun internalSummary_deserializesNumericBooleans() {
val raw = """{"id":1,"name":"X","allowsExternal":1,"isDoublesTournament":0}"""
val dto = json.decodeFromString(InternalTournamentSummaryDto.serializer(), raw)
assertEquals(1, dto.id)
assertEquals(true, dto.allowsExternal)
assertEquals(false, dto.isDoublesTournament)
}
@Test
fun internalSummary_deserializesStringBooleans() {
val raw = """{"id":2,"allowsExternal":"true","isDoublesTournament":"no"}"""
val dto = json.decodeFromString(InternalTournamentSummaryDto.serializer(), raw)
assertTrue(dto.allowsExternal == true)
assertTrue(dto.isDoublesTournament == false)
}
@Test
fun internalDetail_deserializesMixedBooleanForms() {
val raw = """{"id":3,"allowsExternal":0,"isDoublesTournament":"1"}"""
val dto = json.decodeFromString(InternalTournamentDetailDto.serializer(), raw)
assertFalse(dto.allowsExternal == true)
assertTrue(dto.isDoublesTournament == true)
}
}

View File

@@ -219,6 +219,10 @@ class DiaryManager(
participantsApi.updateParticipantGroup(diaryDateId, memberId, groupId)
}
fun clearError() {
_state.value = _state.value.copy(error = null)
}
suspend fun loadDates(clubId: Int) {
_state.value = _state.value.copy(isLoading = true, error = null)
try {
@@ -229,13 +233,16 @@ class DiaryManager(
}
}
suspend fun createDate(clubId: Int, date: String, trainingStart: String?, trainingEnd: String?) {
/** @return neue `diaryDateId` bei Erfolg, sonst `null` */
suspend fun createDate(clubId: Int, date: String, trainingStart: String?, trainingEnd: String?): Int? {
_state.value = _state.value.copy(isLoading = true, error = null)
try {
diaryApi.createDate(clubId, date, trainingStart, trainingEnd)
return try {
val created = diaryApi.createDate(clubId, date, trainingStart, trainingEnd)
loadDates(clubId)
created.id
} catch (t: Throwable) {
_state.value = _state.value.copy(isLoading = false, error = t.toUserMessage("Eintrag konnte nicht erstellt werden"))
null
}
}