--- phase: 05-calendar-strip plan: 01 type: execute wave: 1 depends_on: [] files_modified: - lib/features/home/data/calendar_dao.dart - lib/features/home/data/calendar_dao.g.dart - lib/features/home/domain/calendar_models.dart - lib/features/home/presentation/calendar_providers.dart - lib/core/database/database.dart - lib/core/database/database.g.dart - lib/l10n/app_de.arb - lib/l10n/app_localizations_de.dart - lib/l10n/app_localizations.dart - test/features/home/data/calendar_dao_test.dart autonomous: true requirements: - CAL-02 - CAL-05 must_haves: truths: - "Querying tasks for any arbitrary date returns exactly the tasks whose nextDueDate falls on that day" - "Querying overdue tasks for today returns all tasks whose nextDueDate is strictly before today" - "Querying a future date returns only tasks due that day, no overdue carry-over" - "CalendarState model holds selectedDate, overdue tasks, and day tasks as separate lists" - "Localization strings for calendar UI exist in ARB and generated files" artifacts: - path: "lib/features/home/data/calendar_dao.dart" provides: "Date-parameterized task queries" exports: ["CalendarDao"] - path: "lib/features/home/domain/calendar_models.dart" provides: "CalendarState and reuse of TaskWithRoom" exports: ["CalendarState"] - path: "lib/features/home/presentation/calendar_providers.dart" provides: "Riverpod provider for calendar state" exports: ["calendarProvider", "selectedDateProvider"] - path: "test/features/home/data/calendar_dao_test.dart" provides: "DAO unit tests" min_lines: 50 key_links: - from: "lib/features/home/data/calendar_dao.dart" to: "lib/core/database/database.dart" via: "DAO registered in @DriftDatabase annotation" pattern: "CalendarDao" - from: "lib/features/home/presentation/calendar_providers.dart" to: "lib/features/home/data/calendar_dao.dart" via: "Provider reads CalendarDao from AppDatabase" pattern: "db\\.calendarDao" --- Create the data layer, domain models, Riverpod providers, and localization strings for the calendar strip feature. Purpose: The calendar strip UI (Plan 02) needs a data foundation that can answer "what tasks are due on date X?" and "what tasks are overdue relative to today?" without the old overdue/today/tomorrow bucketing. This plan builds that foundation and tests it. Output: CalendarDao with date-parameterized queries, CalendarState model, Riverpod providers (selectedDateProvider + calendarProvider), new l10n strings, DAO unit tests. @/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md @/home/jlmak/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/05-calendar-strip/5-CONTEXT.md From lib/core/database/database.dart: ```dart // Tables: Rooms, Tasks, TaskCompletions // Existing DAOs: RoomsDao, TasksDao, DailyPlanDao // CalendarDao must be added to the @DriftDatabase annotation daos list // and imported at the top of database.dart @DriftDatabase( tables: [Rooms, Tasks, TaskCompletions], daos: [RoomsDao, TasksDao, DailyPlanDao], // ADD CalendarDao here ) class AppDatabase extends _$AppDatabase { ... } ``` From lib/features/home/domain/daily_plan_models.dart: ```dart class TaskWithRoom { final Task task; final String roomName; final int roomId; const TaskWithRoom({required this.task, required this.roomName, required this.roomId}); } ``` From lib/features/home/presentation/daily_plan_providers.dart: ```dart // Pattern to follow: StreamProvider.autoDispose, manual (not @riverpod) // because of drift's generated Task type final dailyPlanProvider = StreamProvider.autoDispose((ref) { final db = ref.watch(appDatabaseProvider); ... }); ``` From lib/features/home/data/daily_plan_dao.dart: ```dart // Pattern: @DriftAccessor with tables, extends DatabaseAccessor // Uses query.watch() for reactive streams @DriftAccessor(tables: [Tasks, Rooms, TaskCompletions]) class DailyPlanDao extends DatabaseAccessor with _$DailyPlanDaoMixin { ... } ``` From lib/core/providers/database_provider.dart: ```dart // appDatabaseProvider gives access to the database singleton ``` Task 1: Create CalendarDao with date-parameterized queries and tests lib/features/home/data/calendar_dao.dart, lib/core/database/database.dart, test/features/home/data/calendar_dao_test.dart - watchTasksForDate(date): returns tasks whose nextDueDate falls on the given calendar day (same year/month/day), joined with room name, sorted by task name alphabetically - watchOverdueTasks(referenceDate): returns tasks whose nextDueDate is strictly before referenceDate (start of day), joined with room name, sorted by nextDueDate ascending - watchTasksForDate for a date with no tasks returns empty list - watchOverdueTasks returns empty when no tasks are overdue - watchOverdueTasks does NOT include tasks due on the referenceDate itself - watchTasksForDate for a past date returns only tasks originally due that day (does NOT include overdue carry-over) 1. Create `lib/features/home/data/calendar_dao.dart`: - Class `CalendarDao` extends `DatabaseAccessor` with `_$CalendarDaoMixin` - Annotated `@DriftAccessor(tables: [Tasks, Rooms, TaskCompletions])` - `part 'calendar_dao.g.dart';` - Method `Stream> watchTasksForDate(DateTime date)`: Compute startOfDay and endOfDay (startOfDay + 1 day). Join tasks with rooms. Filter `tasks.nextDueDate >= startOfDay AND tasks.nextDueDate < endOfDay`. Order by `tasks.name` ascending. Map to `TaskWithRoom`. - Method `Stream> watchOverdueTasks(DateTime referenceDate)`: Compute startOfReferenceDay. Join tasks with rooms. Filter `tasks.nextDueDate < startOfReferenceDay`. Order by `tasks.nextDueDate` ascending. Map to `TaskWithRoom`. - Import `daily_plan_models.dart` for `TaskWithRoom` (reuse, don't duplicate). 2. Register CalendarDao in `lib/core/database/database.dart`: - Add import: `import '../../features/home/data/calendar_dao.dart';` - Add `CalendarDao` to the `daos:` list in `@DriftDatabase` 3. Run `dart run build_runner build --delete-conflicting-outputs` to generate `calendar_dao.g.dart` and updated `database.g.dart`. 4. Write `test/features/home/data/calendar_dao_test.dart` following the pattern in `test/features/home/data/daily_plan_dao_test.dart`: - Use in-memory database: `AppDatabase(NativeDatabase.memory())` - Create test rooms in setUp - Test group for watchTasksForDate: - Empty when no tasks - Returns only tasks due on the queried date (not before, not after) - Returns tasks from multiple rooms - Sorted alphabetically by name - Test group for watchOverdueTasks: - Empty when no overdue tasks - Returns tasks due before reference date - Does NOT include tasks due ON the reference date - Sorted by nextDueDate ascending cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/home/data/calendar_dao_test.dart CalendarDao registered in AppDatabase, both query methods return correct results for arbitrary dates, all DAO tests pass Task 2: Create CalendarState model, Riverpod providers, and localization strings lib/features/home/domain/calendar_models.dart, lib/features/home/presentation/calendar_providers.dart, lib/l10n/app_de.arb 1. Create `lib/features/home/domain/calendar_models.dart`: ```dart import 'package:household_keeper/features/home/domain/daily_plan_models.dart'; /// State for the calendar day view: tasks for the selected date + overdue tasks. class CalendarDayState { final DateTime selectedDate; final List dayTasks; final List overdueTasks; const CalendarDayState({ required this.selectedDate, required this.dayTasks, required this.overdueTasks, }); /// True when viewing today and all tasks (day + overdue) have been completed /// (lists are empty but completions exist). Determined by the UI layer. bool get isEmpty => dayTasks.isEmpty && overdueTasks.isEmpty; } ``` 2. Create `lib/features/home/presentation/calendar_providers.dart`: - Import Riverpod, database_provider, calendar_dao, calendar_models, daily_plan_models - `final selectedDateProvider = StateProvider((ref) { final now = DateTime.now(); return DateTime(now.year, now.month, now.day); });` This is NOT autoDispose -- the selected date persists as long as the app is alive (resets on restart naturally). - `final calendarDayProvider = StreamProvider.autoDispose((ref) { ... });` Manual definition (not @riverpod) following dailyPlanProvider pattern. Reads `selectedDateProvider` to get the current date. Reads `appDatabaseProvider` to get the DB. Determines if selectedDate is today: `isToday = selectedDate == DateTime(now.year, now.month, now.day)`. Determines if selectedDate is in the future: `isFuture = selectedDate.isAfter(today)`. Watches `db.calendarDao.watchTasksForDate(selectedDate)`. For overdue: if `isToday`, also watch `db.calendarDao.watchOverdueTasks(selectedDate)`. If viewing a past date or future date, overdueTasks = empty. Per user decision: "When viewing past days: show what was due that day. When viewing future days: show only tasks due that day, no overdue carry-over." Combine both streams using `Rx.combineLatest2` or simply use `asyncMap` on the day tasks stream and fetch overdue as a secondary query. Implementation approach: Use the dayTasks stream as the primary, and inside asyncMap call the overdue stream's `.first` when isToday. This keeps it simple and follows the existing `dailyPlanProvider` pattern of `stream.asyncMap()`. 3. Add new l10n strings to `lib/l10n/app_de.arb` (add before the closing `}`): - `"calendarNoTasks": "Keine Aufgaben"` — shown when a day has no tasks at all - `"calendarAllDone": "Alles erledigt!"` — celebration when all tasks for a day are done - `"calendarOverdueSection": "Uberfaellig"` — No, reuse existing `dailyPlanSectionOverdue` ("Uberfaellig") for the overdue section header - `"calendarTodayButton": "Heute"` — floating today button label Actually, we can reuse `dailyPlanSectionOverdue` for the overdue header, `dailyPlanNoTasks` for no-tasks-at-all, and `dailyPlanAllClearTitle`/`dailyPlanAllClearMessage` for celebration. The only truly new string needed is for the Today button: - Add `"calendarTodayButton": "Heute"` to the ARB file 4. Run `flutter gen-l10n` to regenerate localization files. cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter analyze --no-fatal-infos CalendarDayState model exists with selectedDate/dayTasks/overdueTasks fields. selectedDateProvider and calendarDayProvider are defined. calendarDayProvider returns overdue tasks only when viewing today. New l10n string "calendarTodayButton" exists. No analysis errors. - `flutter test test/features/home/data/calendar_dao_test.dart` — all DAO tests pass - `flutter analyze --no-fatal-infos` — no errors in new or modified files - `flutter test` — full test suite still passes (existing tests not broken by database.dart changes) - CalendarDao is registered in AppDatabase and has two working query methods - CalendarDayState model correctly separates day tasks from overdue tasks - calendarDayProvider returns overdue only for today, not for past/future dates - All existing tests still pass after database.dart modification - New DAO tests cover core query behaviors After completion, create `.planning/phases/05-calendar-strip/05-01-SUMMARY.md`