feat(TrainingGroup): add excludeFromQuickDiaryCreate feature and update related logic
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s

- Introduced a new boolean field `excludeFromQuickDiaryCreate` in the TrainingGroup model to control group visibility in quick diary creation.
- Updated the `updateTrainingGroup` service method to handle the new field, allowing for dynamic updates based on user input.
- Enhanced the TrainingTimesTab component to include a checkbox for excluding groups from quick diary creation, improving user interaction.
- Updated localization files to include new strings related to the exclude feature, ensuring clarity in multiple languages.
- Refactored logic in DiaryView and mobile app to consider the new exclusion criteria when suggesting training slots.
This commit is contained in:
Torsten Schulz (local)
2026-05-14 22:47:02 +02:00
parent 83294406a4
commit bf3e1af084
12 changed files with 239 additions and 63 deletions

View File

@@ -58,8 +58,8 @@ android {
applicationId = "de.tsschulz.tt_tagebuch"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 2
versionName = "1.0.1"
versionCode = 3
versionName = "1.1.0"
buildConfigField("String", "BACKEND_BASE_URL", "\"$backendBaseUrl\"")
buildConfigField("String", "SOCKET_BASE_URL", "\"$socketBaseUrl\"")
}

View File

@@ -6573,29 +6573,63 @@ private fun localDateToJsWeekday(d: java.time.LocalDate): Int =
java.time.DayOfWeek.SATURDAY -> 6
}
/** Nächster freier Kalendertag ab heute: erstes Datum ohne Tagebuch-Eintrag, dann erste passende Gruppe (Sortierung) mit Trainingszeit. */
private fun trainingTimeStartToMinutes(start: String): Int? {
val t = start.trim().take(5)
val parts = t.split(':')
if (parts.size < 2) return null
val h = parts[0].toIntOrNull() ?: return null
val m = parts[1].toIntOrNull() ?: return null
return h * 60 + m
}
/** Nächster freier Kalendertag: früheste Startzeit über alle nicht ausgeschlossenen Gruppen an diesem Wochentag (0=So … 6=Sa). */
private fun findNextQuickDiarySlotAcrossGroups(
groups: List<TrainingGroupDto>,
existingDiaryDatesYyyyMmDd: Set<String>,
): NextDiarySlotSuggestion? {
val sortedGroups = groups.sortedWith(compareBy<TrainingGroupDto> { it.sortOrder }.thenBy { it.id })
val eligible = groups.filter { !it.excludeFromQuickDiaryCreate }
val today = java.time.LocalDate.now()
for (offset in 0..366) {
val check = today.plusDays(offset.toLong())
val wd = localDateToJsWeekday(check)
val norm = check.toString().take(10)
if (norm in existingDiaryDatesYyyyMmDd) continue
for (g in sortedGroups) {
val timesForDay = g.trainingTimes
.filter { it.weekday == wd && it.startTime.isNotBlank() }
.sortedWith(compareBy<TrainingTimeDto> { it.startTime }.thenBy { it.id })
val time = timesForDay.firstOrNull() ?: continue
return NextDiarySlotSuggestion(
date = norm,
trainingStart = time.startTime.trim().take(5),
trainingEnd = time.endTime.trim().take(5),
)
data class SlotCand(
val startMin: Int,
val suggestion: NextDiarySlotSuggestion,
val groupOrder: Int,
val groupId: Int,
val timeId: Int,
)
val cands = buildList {
for (g in eligible) {
for (time in g.trainingTimes) {
if (time.weekday != wd || time.startTime.isBlank()) continue
val sm = trainingTimeStartToMinutes(time.startTime) ?: continue
add(
SlotCand(
startMin = sm,
suggestion = NextDiarySlotSuggestion(
date = norm,
trainingStart = time.startTime.trim().take(5),
trainingEnd = time.endTime.trim().take(5),
),
groupOrder = g.sortOrder,
groupId = g.id,
timeId = time.id,
),
)
}
}
}
if (cands.isEmpty()) continue
val best = cands.minWith(
compareBy<SlotCand> { it.startMin }
.thenBy { it.groupOrder }
.thenBy { it.groupId }
.thenBy { it.timeId },
)
return best.suggestion
}
return null
}