--- phase: 03-daily-plan-and-cleanliness plan: 01 subsystem: database tags: [drift, riverpod, join-query, stream-provider, localization, arb] # Dependency graph requires: - phase: 02-rooms-and-tasks provides: Tasks, Rooms, TaskCompletions tables; TasksDao with completeTask(); appDatabaseProvider provides: - DailyPlanDao with cross-room join query (watchAllTasksWithRoomName) - DailyPlanDao completion count stream (watchCompletionsToday) - TaskWithRoom and DailyPlanState model classes - dailyPlanProvider with overdue/today/tomorrow categorization and stable progress tracking - 10 German localization keys for daily plan UI affects: [03-02-daily-plan-ui, 03-03-phase-verification] # Tech tracking tech-stack: added: [] patterns: - "Drift innerJoin for cross-table queries with readTable() mapping" - "customSelect with readsFrom for aggregate stream invalidation" - "Stable progress denominator: remaining + completedTodayCount" key-files: created: - lib/features/home/data/daily_plan_dao.dart - lib/features/home/data/daily_plan_dao.g.dart - lib/features/home/domain/daily_plan_models.dart - lib/features/home/presentation/daily_plan_providers.dart - test/features/home/data/daily_plan_dao_test.dart modified: - lib/core/database/database.dart - lib/core/database/database.g.dart - lib/l10n/app_de.arb - lib/l10n/app_localizations.dart - lib/l10n/app_localizations_de.dart key-decisions: - "DailyPlanDao uses innerJoin (not leftOuterJoin) since tasks always have a room" - "watchCompletionsToday uses customSelect with readsFrom for proper stream invalidation on TaskCompletions table" - "dailyPlanProvider uses manual StreamProvider.autoDispose (not @riverpod) due to drift Task type issue" - "Progress total = remaining overdue + remaining today + completedTodayCount for stable denominator" patterns-established: - "Drift innerJoin with readTable() for cross-table data: used in DailyPlanDao.watchAllTasksWithRoomName()" - "customSelect with epoch-second variables for date-range aggregation" - "Manual StreamProvider.autoDispose with asyncMap for combining DAO streams" requirements-completed: [PLAN-01, PLAN-02, PLAN-03, PLAN-05] # Metrics duration: 5min completed: 2026-03-16 --- # Phase 3 Plan 01: Daily Plan Data Layer Summary **Drift DailyPlanDao with cross-room join query, completion count stream, Riverpod provider with overdue/today/tomorrow categorization, and 10 German localization keys** ## Performance - **Duration:** 5 min - **Started:** 2026-03-16T11:26:02Z - **Completed:** 2026-03-16T11:31:13Z - **Tasks:** 2 - **Files modified:** 10 ## Accomplishments - DailyPlanDao with `watchAllTasksWithRoomName()` returning tasks joined with room names, sorted by due date - `watchCompletionsToday()` using customSelect with readsFrom for proper reactive stream invalidation - `dailyPlanProvider` categorizing tasks into overdue/today/tomorrow with stable progress denominator - TaskWithRoom and DailyPlanState model classes providing the data contract for Plan 02's UI - 7 unit tests covering all DAO behaviors (empty state, join correctness, sort order, cross-room pairing, completion counts, date boundaries) - 10 new German localization keys for daily plan sections, progress text, empty states ## Task Commits Each task was committed atomically: 1. **Task 1 RED: Failing tests for DailyPlanDao** - `74b3bd5` (test) 2. **Task 1 GREEN: DailyPlanDao implementation** - `ad70eb7` (feat) 3. **Task 2: Daily plan provider and localization keys** - `1c09a43` (feat) _TDD task had RED and GREEN commits. No REFACTOR needed -- code was clean._ ## Files Created/Modified - `lib/features/home/data/daily_plan_dao.dart` - DailyPlanDao with cross-room join query and completion count stream - `lib/features/home/data/daily_plan_dao.g.dart` - Generated Drift mixin for DailyPlanDao - `lib/features/home/domain/daily_plan_models.dart` - TaskWithRoom and DailyPlanState data classes - `lib/features/home/presentation/daily_plan_providers.dart` - dailyPlanProvider with date categorization and progress tracking - `test/features/home/data/daily_plan_dao_test.dart` - 7 unit tests for DailyPlanDao behaviors - `lib/core/database/database.dart` - Added DailyPlanDao import and registration - `lib/core/database/database.g.dart` - Regenerated with DailyPlanDao accessor - `lib/l10n/app_de.arb` - 10 new daily plan localization keys - `lib/l10n/app_localizations.dart` - Regenerated with new key accessors - `lib/l10n/app_localizations_de.dart` - Regenerated with German translations ## Decisions Made - Used `innerJoin` (not `leftOuterJoin`) since every task always belongs to a room -- no orphaned tasks possible with foreign key constraint - `watchCompletionsToday` uses `customSelect` with raw SQL COUNT(*) and `readsFrom: {taskCompletions}` to ensure Drift knows which table to watch for stream invalidation. The selectOnly approach would also work but customSelect is more explicit about the reactive dependency. - `dailyPlanProvider` defined as manual `StreamProvider.autoDispose` (same pattern as `tasksInRoomProvider`) because riverpod_generator has `InvalidTypeException` with drift's generated `Task` type - Progress denominator formula: `overdue.length + todayList.length + completedTodayCount` keeps the total stable as tasks are completed and move to future due dates ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Data layer complete: DailyPlanDao, models, and provider ready for Plan 02 UI consumption - Plan 02 can directly `ref.watch(dailyPlanProvider)` to get categorized task data - All localization keys for daily plan UI are available via AppLocalizations - 66/66 tests passing with no regressions ## Self-Check: PASSED All 5 created files verified present on disk. All 3 commit hashes verified in git log. --- *Phase: 03-daily-plan-and-cleanliness* *Completed: 2026-03-16*