From 1c09a4399594f1746ecd7fb8ab84faae0449ed49 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 16 Mar 2026 12:30:58 +0100 Subject: [PATCH] feat(03-01): add daily plan provider with date categorization and localization keys - dailyPlanProvider: manual StreamProvider.autoDispose with overdue/today/tomorrow partitioning - Stable progress denominator: remaining overdue + remaining today + completedTodayCount - 10 new German localization keys for daily plan sections, progress, empty states - dart analyze clean, full test suite (66/66) passes Co-Authored-By: Claude Opus 4.6 --- .../presentation/daily_plan_providers.dart | 56 +++++++++++++++++++ lib/l10n/app_de.arb | 22 +++++++- lib/l10n/app_localizations.dart | 54 ++++++++++++++++++ lib/l10n/app_localizations_de.dart | 32 +++++++++++ 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 lib/features/home/presentation/daily_plan_providers.dart diff --git a/lib/features/home/presentation/daily_plan_providers.dart b/lib/features/home/presentation/daily_plan_providers.dart new file mode 100644 index 0000000..3f5457c --- /dev/null +++ b/lib/features/home/presentation/daily_plan_providers.dart @@ -0,0 +1,56 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:household_keeper/core/providers/database_provider.dart'; +import 'package:household_keeper/features/home/domain/daily_plan_models.dart'; + +/// Reactive daily plan data: tasks categorized into overdue/today/tomorrow +/// with progress tracking. +/// +/// Defined manually (not @riverpod) because riverpod_generator has trouble +/// with drift's generated [Task] type. Same pattern as [tasksInRoomProvider]. +final dailyPlanProvider = + StreamProvider.autoDispose((ref) { + final db = ref.watch(appDatabaseProvider); + final taskStream = db.dailyPlanDao.watchAllTasksWithRoomName(); + + return taskStream.asyncMap((allTasks) async { + // Get today's completion count (latest value from stream) + final completedToday = + await db.dailyPlanDao.watchCompletionsToday().first; + + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final tomorrow = today.add(const Duration(days: 1)); + final dayAfterTomorrow = tomorrow.add(const Duration(days: 1)); + + final overdue = []; + final todayList = []; + final tomorrowList = []; + + for (final tw in allTasks) { + final dueDate = DateTime( + tw.task.nextDueDate.year, + tw.task.nextDueDate.month, + tw.task.nextDueDate.day, + ); + if (dueDate.isBefore(today)) { + overdue.add(tw); + } else if (dueDate.isBefore(tomorrow)) { + todayList.add(tw); + } else if (dueDate.isBefore(dayAfterTomorrow)) { + tomorrowList.add(tw); + } + } + + // totalTodayCount includes completedTodayCount so the denominator + // stays stable as tasks are completed (their nextDueDate moves to + // the future, shrinking overdue+today, but completedToday grows). + return DailyPlanState( + overdueTasks: overdue, + todayTasks: todayList, + tomorrowTasks: tomorrowList, + completedTodayCount: completedToday, + totalTodayCount: overdue.length + todayList.length + completedToday, + ); + }); +}); diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index df73a76..60376a6 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -68,5 +68,25 @@ "placeholders": { "count": { "type": "int" } } - } + }, + "dailyPlanProgress": "{completed} von {total} erledigt", + "@dailyPlanProgress": { + "placeholders": { + "completed": { "type": "int" }, + "total": { "type": "int" } + } + }, + "dailyPlanSectionOverdue": "\u00dcberf\u00e4llig", + "dailyPlanSectionToday": "Heute", + "dailyPlanSectionUpcoming": "Demn\u00e4chst", + "dailyPlanUpcomingCount": "Demn\u00e4chst ({count})", + "@dailyPlanUpcomingCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "dailyPlanAllClearTitle": "Alles erledigt! \ud83c\udf1f", + "dailyPlanAllClearMessage": "Keine Aufgaben f\u00fcr heute. Genie\u00dfe den Moment!", + "dailyPlanNoOverdue": "Keine \u00fcberf\u00e4lligen Aufgaben", + "dailyPlanNoTasks": "Noch keine Aufgaben angelegt" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index b545c84..e319ac6 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -417,6 +417,60 @@ abstract class AppLocalizations { /// In de, this message translates to: /// **'{count} ausgewählt'** String templatePickerSelected(int count); + + /// No description provided for @dailyPlanProgress. + /// + /// In de, this message translates to: + /// **'{completed} von {total} erledigt'** + String dailyPlanProgress(int completed, int total); + + /// No description provided for @dailyPlanSectionOverdue. + /// + /// In de, this message translates to: + /// **'Überfällig'** + String get dailyPlanSectionOverdue; + + /// No description provided for @dailyPlanSectionToday. + /// + /// In de, this message translates to: + /// **'Heute'** + String get dailyPlanSectionToday; + + /// No description provided for @dailyPlanSectionUpcoming. + /// + /// In de, this message translates to: + /// **'Demnächst'** + String get dailyPlanSectionUpcoming; + + /// No description provided for @dailyPlanUpcomingCount. + /// + /// In de, this message translates to: + /// **'Demnächst ({count})'** + String dailyPlanUpcomingCount(int count); + + /// No description provided for @dailyPlanAllClearTitle. + /// + /// In de, this message translates to: + /// **'Alles erledigt! 🌟'** + String get dailyPlanAllClearTitle; + + /// No description provided for @dailyPlanAllClearMessage. + /// + /// In de, this message translates to: + /// **'Keine Aufgaben für heute. Genieße den Moment!'** + String get dailyPlanAllClearMessage; + + /// No description provided for @dailyPlanNoOverdue. + /// + /// In de, this message translates to: + /// **'Keine überfälligen Aufgaben'** + String get dailyPlanNoOverdue; + + /// No description provided for @dailyPlanNoTasks. + /// + /// In de, this message translates to: + /// **'Noch keine Aufgaben angelegt'** + String get dailyPlanNoTasks; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 4c46ff5..1a99a3d 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -178,4 +178,36 @@ class AppLocalizationsDe extends AppLocalizations { String templatePickerSelected(int count) { return '$count ausgewählt'; } + + @override + String dailyPlanProgress(int completed, int total) { + return '$completed von $total erledigt'; + } + + @override + String get dailyPlanSectionOverdue => 'Überfällig'; + + @override + String get dailyPlanSectionToday => 'Heute'; + + @override + String get dailyPlanSectionUpcoming => 'Demnächst'; + + @override + String dailyPlanUpcomingCount(int count) { + return 'Demnächst ($count)'; + } + + @override + String get dailyPlanAllClearTitle => 'Alles erledigt! 🌟'; + + @override + String get dailyPlanAllClearMessage => + 'Keine Aufgaben für heute. Genieße den Moment!'; + + @override + String get dailyPlanNoOverdue => 'Keine überfälligen Aufgaben'; + + @override + String get dailyPlanNoTasks => 'Noch keine Aufgaben angelegt'; }