diff --git a/app/src/main/java/no/naiv/meddetsamme/ui/TodayScreen.kt b/app/src/main/java/no/naiv/meddetsamme/ui/TodayScreen.kt index a1265e6..a58c9da 100644 --- a/app/src/main/java/no/naiv/meddetsamme/ui/TodayScreen.kt +++ b/app/src/main/java/no/naiv/meddetsamme/ui/TodayScreen.kt @@ -69,6 +69,10 @@ private data class TodayDose( ) { val status: DoseStatus? get() = log?.status val atMillis: Long get() = at.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() + val resolved: Boolean get() = status == DoseStatus.TAKEN || status == DoseStatus.SKIPPED + + /** Due now = unresolved and its scheduled time has passed (what escalation nags about). */ + fun isDueNow(now: LocalDateTime): Boolean = !resolved && at.isBefore(now) } @OptIn(ExperimentalMaterial3Api::class) @@ -211,6 +215,25 @@ fun TodayScreen(nav: Nav) { } } } + // Bulk "take now" for a cluster of overdue doses (e.g. a morning + // handful). Same path as each card's "Ta nå", just looped — take() + // is idempotent so a stale entry can't double-decrement stock. + val dueNow = doses.filter { it.isDueNow(LocalDateTime.now()) } + if (dueNow.size >= 2) { + item { + FilledTonalButton( + onClick = { + scope.launch { + for (d in dueNow) { + val log = actions.ensureLog(d.doseTime.id, d.atMillis) + actions.take(log.id) + } + } + }, + modifier = Modifier.fillMaxWidth(), + ) { Text("Ta alle nå (${dueNow.size})") } + } + } items(doses, key = { "${it.doseTime.id}-${it.atMillis}" }) { dose -> DoseCard( dose = dose, @@ -248,8 +271,8 @@ private fun DoseCard( ) { val time = "%02d:%02d".format(dose.doseTime.minuteOfDay / 60, dose.doseTime.minuteOfDay % 60) val statusText = dose.status?.label - val resolved = dose.status == DoseStatus.TAKEN || dose.status == DoseStatus.SKIPPED - val overdue = !resolved && dose.at.isBefore(LocalDateTime.now()) + val resolved = dose.resolved + val overdue = dose.isDueNow(LocalDateTime.now()) val scheme = MaterialTheme.colorScheme // Visual state: overdue screams a little, resolved fades back, upcoming is calm.