docs(02): create phase plan — 5 plans in 4 waves covering rooms, tasks, templates, and verification

This commit is contained in:
2026-03-15 21:40:40 +01:00
parent 988a8fdade
commit 515304b432
6 changed files with 1260 additions and 2 deletions

View File

@@ -45,7 +45,13 @@ Plans:
4. User can mark a task done (tap or swipe), which records the completion and sets the next due date correctly based on the interval
5. Overdue tasks are visually highlighted with a distinct color or badge on room cards and in task lists; tasks within a room are sorted by due date by default
6. Each room card shows its name, icon, count of due tasks, and cleanliness indicator
**Plans**: TBD
**Plans**: 5 plans
Plans:
- [ ] 02-01-PLAN.md — Data layer: Drift tables, migration v1->v2, DAOs, scheduling utility, domain models, templates, tests
- [ ] 02-02-PLAN.md — Room CRUD UI: 2-column card grid, room form, icon picker, drag-and-drop reorder, providers
- [ ] 02-03-PLAN.md — Task CRUD UI: task list, task row with completion, task form, overdue highlighting, providers
- [ ] 02-04-PLAN.md — Template selection: template picker bottom sheet, room type detection, integration with room creation
- [ ] 02-05-PLAN.md — Visual and functional verification checkpoint
### Phase 3: Daily Plan and Cleanliness
**Goal**: Users can open the app and immediately see what needs doing today, act on tasks directly from the plan view, and see a room-level health indicator
@@ -81,6 +87,6 @@ Note: Phase 4 depends on Phase 2 (needs scheduling data) but can be developed in
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Foundation | 2/2 | Complete | 2026-03-15 |
| 2. Rooms and Tasks | 0/TBD | Not started | - |
| 2. Rooms and Tasks | 0/5 | Planning complete | - |
| 3. Daily Plan and Cleanliness | 0/TBD | Not started | - |
| 4. Notifications | 0/TBD | Not started | - |

View File

@@ -0,0 +1,336 @@
---
phase: 02-rooms-and-tasks
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- lib/core/database/database.dart
- lib/features/rooms/data/rooms_dao.dart
- lib/features/tasks/data/tasks_dao.dart
- lib/features/tasks/domain/scheduling.dart
- lib/features/tasks/domain/frequency.dart
- lib/features/tasks/domain/effort_level.dart
- lib/features/tasks/domain/relative_date.dart
- lib/features/rooms/domain/room_icons.dart
- lib/features/templates/data/task_templates.dart
- test/features/rooms/data/rooms_dao_test.dart
- test/features/tasks/data/tasks_dao_test.dart
- test/features/tasks/domain/scheduling_test.dart
- test/features/templates/task_templates_test.dart
autonomous: true
requirements:
- ROOM-01
- ROOM-02
- ROOM-03
- ROOM-04
- ROOM-05
- TASK-01
- TASK-02
- TASK-03
- TASK-04
- TASK-05
- TASK-06
- TASK-07
- TASK-08
- TMPL-01
- TMPL-02
must_haves:
truths:
- "Room CRUD operations (insert, update, delete with cascade, reorder) work correctly at the database layer"
- "Task CRUD operations (insert, update, delete, sorted by due date) work correctly at the database layer"
- "Task completion records a timestamp and auto-calculates next due date using the scheduling utility"
- "All 11 preset frequency intervals and custom intervals produce correct next due dates"
- "Calendar-anchored intervals clamp to last day of month with anchor memory"
- "Catch-up logic advances past-due dates to the next future occurrence"
- "All 14 room types have bundled German-language task templates"
- "Overdue detection correctly identifies tasks with nextDueDate before today"
artifacts:
- path: "lib/core/database/database.dart"
provides: "Rooms, Tasks, TaskCompletions table definitions, schema v2 migration, foreign keys PRAGMA"
contains: "schemaVersion => 2"
- path: "lib/features/rooms/data/rooms_dao.dart"
provides: "Room CRUD + stream queries + reorder + cascade delete"
exports: ["RoomsDao"]
- path: "lib/features/tasks/data/tasks_dao.dart"
provides: "Task CRUD + stream queries + completion transaction"
exports: ["TasksDao"]
- path: "lib/features/tasks/domain/scheduling.dart"
provides: "calculateNextDueDate, catchUpToPresent pure functions"
exports: ["calculateNextDueDate", "catchUpToPresent"]
- path: "lib/features/tasks/domain/frequency.dart"
provides: "IntervalType enum, FrequencyInterval model"
exports: ["IntervalType", "FrequencyInterval"]
- path: "lib/features/tasks/domain/effort_level.dart"
provides: "EffortLevel enum (low, medium, high)"
exports: ["EffortLevel"]
- path: "lib/features/tasks/domain/relative_date.dart"
provides: "formatRelativeDate German formatter"
exports: ["formatRelativeDate"]
- path: "lib/features/rooms/domain/room_icons.dart"
provides: "Curated list of ~25 household Material Icons with name+IconData pairs"
exports: ["curatedRoomIcons", "mapIconName"]
- path: "lib/features/templates/data/task_templates.dart"
provides: "Static Dart constant template data for all 14 room types"
exports: ["TaskTemplate", "roomTemplates", "detectRoomType"]
key_links:
- from: "lib/features/tasks/data/tasks_dao.dart"
to: "lib/features/tasks/domain/scheduling.dart"
via: "completeTask calls calculateNextDueDate + catchUpToPresent"
pattern: "calculateNextDueDate|catchUpToPresent"
- from: "lib/core/database/database.dart"
to: "lib/features/rooms/data/rooms_dao.dart"
via: "database registers RoomsDao"
pattern: "daos.*RoomsDao"
- from: "lib/core/database/database.dart"
to: "lib/features/tasks/data/tasks_dao.dart"
via: "database registers TasksDao"
pattern: "daos.*TasksDao"
- from: "lib/features/tasks/domain/frequency.dart"
to: "lib/core/database/database.dart"
via: "IntervalType enum used as intEnum column in Tasks table"
pattern: "intEnum.*IntervalType"
---
<objective>
Build the complete data layer for rooms and tasks: Drift tables with schema v2 migration, DAOs with stream queries and cascade operations, pure scheduling utility, domain enums/models, template data, and comprehensive unit tests.
Purpose: This is the foundation everything else in Phase 2 builds on. All UI plans (rooms, tasks, templates) depend on having working DAOs, domain models, and tested scheduling logic.
Output: Database schema v2 with Rooms/Tasks/TaskCompletions tables, two DAOs, scheduling utility, template data, and passing unit test suite.
</objective>
<execution_context>
@/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md
@/home/jlmak/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-rooms-and-tasks/2-CONTEXT.md
@.planning/phases/02-rooms-and-tasks/02-RESEARCH.md
@.planning/phases/02-rooms-and-tasks/02-VALIDATION.md
<interfaces>
<!-- Existing code contracts the executor needs -->
From lib/core/database/database.dart:
```dart
@DriftDatabase(tables: [])
class AppDatabase extends _$AppDatabase {
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
@override
int get schemaVersion => 1;
}
```
From lib/core/providers/database_provider.dart:
```dart
@Riverpod(keepAlive: true)
AppDatabase appDatabase(Ref ref) {
final db = AppDatabase();
ref.onDispose(db.close);
return db;
}
```
From lib/core/theme/theme_provider.dart (pattern reference for AsyncNotifier):
```dart
@riverpod
class ThemeNotifier extends _$ThemeNotifier {
@override
ThemeMode build() { ... }
}
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Define Drift tables, migration, DAOs, and domain models</name>
<files>
lib/features/tasks/domain/frequency.dart,
lib/features/tasks/domain/effort_level.dart,
lib/features/tasks/domain/scheduling.dart,
lib/features/tasks/domain/relative_date.dart,
lib/features/rooms/domain/room_icons.dart,
lib/core/database/database.dart,
lib/features/rooms/data/rooms_dao.dart,
lib/features/tasks/data/tasks_dao.dart,
test/features/rooms/data/rooms_dao_test.dart,
test/features/tasks/data/tasks_dao_test.dart,
test/features/tasks/domain/scheduling_test.dart
</files>
<behavior>
--- Scheduling (scheduling_test.dart) ---
- calculateNextDueDate: daily adds 1 day
- calculateNextDueDate: everyNDays(3) adds 3 days
- calculateNextDueDate: weekly adds 7 days
- calculateNextDueDate: biweekly adds 14 days
- calculateNextDueDate: monthly from Jan 15 gives Feb 15
- calculateNextDueDate: monthly from Jan 31 gives Feb 28 (clamping)
- calculateNextDueDate: monthly from Feb 28 (anchor 31) gives Mar 31 (anchor memory)
- calculateNextDueDate: quarterly from Jan 15 gives Apr 15
- calculateNextDueDate: yearly from 2026-03-15 gives 2027-03-15
- calculateNextDueDate: everyNMonths(2) adds 2 months
- catchUpToPresent: skips past occurrences to reach today or future
- catchUpToPresent: returns date unchanged if already in future
- formatRelativeDate: today returns "Heute"
- formatRelativeDate: tomorrow returns "Morgen"
- formatRelativeDate: 3 days from now returns "in 3 Tagen"
- formatRelativeDate: 1 day overdue returns "Uberfaellig seit 1 Tag"
- formatRelativeDate: 5 days overdue returns "Uberfaellig seit 5 Tagen"
--- RoomsDao (rooms_dao_test.dart) ---
- insertRoom returns a valid id
- watchAllRooms emits rooms ordered by sortOrder
- updateRoom changes name and iconName
- deleteRoom cascades to associated tasks and completions
- reorderRooms updates sortOrder for all rooms
- watchRoomWithStats emits room with due task count and cleanliness ratio
--- TasksDao (tasks_dao_test.dart) ---
- insertTask returns a valid id
- watchTasksInRoom emits tasks sorted by nextDueDate ascending
- updateTask changes name, description, interval, effort
- deleteTask removes the task
- completeTask records completion and updates nextDueDate
- completeTask with overdue task catches up to present
- tasks with nextDueDate before today are detected as overdue
</behavior>
<action>
1. Create domain models FIRST (these are the contracts):
**lib/features/tasks/domain/frequency.dart**: Define `IntervalType` enum with values in this EXACT order (for intEnum stability): `daily, everyNDays, weekly, biweekly, monthly, everyNMonths, quarterly, yearly`. Add index comments. Create `FrequencyInterval` class with `intervalType` and `days` fields, plus a `label()` method returning German display strings ("Taeglich", "Alle 2 Tage", "Woechentlich", "Alle 2 Wochen", "Monatlich", "Alle 2 Monate", "Vierteljaehrlich", "Halbjaehrlich", "Jaehrlich", "Alle N Tage/Wochen/Monate" for custom). Include the full preset list as a static const list per TASK-04: daily, every 2 days, every 3 days, weekly, biweekly, monthly, every 2 months, quarterly, every 6 months, yearly.
**lib/features/tasks/domain/effort_level.dart**: Define `EffortLevel` enum: `low, medium, high` (this exact order, with index comments). Add `label()` extension returning German: "Gering", "Mittel", "Hoch".
**lib/features/tasks/domain/scheduling.dart**: Implement `calculateNextDueDate()` and `catchUpToPresent()` as top-level pure functions per the RESEARCH.md Pattern 5. Accept `DateTime today` parameter (not `DateTime.now()`) for testability. Day-count intervals use `Duration(days: N)`. Calendar-anchored intervals use `_addMonths()` helper with anchor day clamping. Catch-up uses a while loop advancing until `nextDue >= today`.
**lib/features/tasks/domain/relative_date.dart**: Implement `formatRelativeDate(DateTime dueDate, DateTime today)` returning German strings: "Heute", "Morgen", "in X Tagen", "Uberfaellig seit 1 Tag", "Uberfaellig seit X Tagen". Accept `today` parameter for testability.
**lib/features/rooms/domain/room_icons.dart**: Define `curatedRoomIcons` as a const list of records `({String name, IconData icon})` with ~25 household Material Icons (kitchen, bathtub, bed, living, weekend, door_front_door, desk, garage, balcony, local_laundry_service, stairs, child_care, single_bed, dining, yard, grass, home, inventory_2, window, cleaning_services, iron, microwave, shower, chair, door_sliding). Add `IconData mapIconName(String name)` function that maps stored string names back to IconData.
2. Update database with tables and migration:
**lib/core/database/database.dart**: Add three Drift table classes (`Rooms`, `Tasks`, `TaskCompletions`) above the database class. Import `IntervalType` and `EffortLevel` for `intEnum` columns. Register tables and DAOs in `@DriftDatabase` annotation. Increment `schemaVersion` to 2. Add `MigrationStrategy` with `onCreate` calling `m.createAll()`, `onUpgrade` that creates all three tables when upgrading from v1, and `beforeOpen` that runs `PRAGMA foreign_keys = ON`.
Table columns per RESEARCH.md Pattern 1:
- Rooms: id (autoIncrement), name (text 1-100), iconName (text), sortOrder (int, default 0), createdAt (dateTime clientDefault)
- Tasks: id (autoIncrement), roomId (int, references Rooms#id), name (text 1-200), description (text nullable), intervalType (intEnum<IntervalType>), intervalDays (int, default 1), anchorDay (int nullable), effortLevel (intEnum<EffortLevel>), nextDueDate (dateTime), createdAt (dateTime clientDefault)
- TaskCompletions: id (autoIncrement), taskId (int, references Tasks#id), completedAt (dateTime)
3. Create DAOs:
**lib/features/rooms/data/rooms_dao.dart**: `@DriftAccessor(tables: [Rooms, Tasks, TaskCompletions])` with:
- `watchAllRooms()` -> `Stream<List<Room>>` ordered by sortOrder
- `watchRoomWithStats()` -> `Stream<List<RoomWithStats>>` joining rooms with task counts and cleanliness ratio. Create a `RoomWithStats` class (room, totalTasks, dueTasks, overdueCount, cleanlinessRatio). The cleanliness ratio = (totalTasks - overdueCount) / totalTasks (1.0 when no overdue, 0.0 when all overdue). Use `customSelect` or join query to compute these counts.
- `insertRoom(RoomsCompanion)` -> `Future<int>`
- `updateRoom(Room)` -> `Future<bool>`
- `deleteRoom(int roomId)` -> `Future<void>` with transaction: delete completions for room's tasks, delete tasks, delete room
- `reorderRooms(List<int> roomIds)` -> `Future<void>` with transaction updating sortOrder
- `getRoomById(int id)` -> `Future<Room>`
**lib/features/tasks/data/tasks_dao.dart**: `@DriftAccessor(tables: [Tasks, TaskCompletions])` with:
- `watchTasksInRoom(int roomId)` -> `Stream<List<Task>>` ordered by nextDueDate ASC
- `insertTask(TasksCompanion)` -> `Future<int>`
- `updateTask(Task)` -> `Future<bool>`
- `deleteTask(int taskId)` -> `Future<void>` (delete completions first, then task)
- `completeTask(int taskId, {DateTime? now})` -> `Future<void>` with transaction: get task, insert completion, calculate next due via `calculateNextDueDate`, catch up via `catchUpToPresent`, update task's nextDueDate. Accept optional `now` for testability (defaults to `DateTime.now()`).
- `getOverdueTaskCount(int roomId, {DateTime? today})` -> `Future<int>`
4. Run `dart run build_runner build --delete-conflicting-outputs` to generate all .g.dart files.
5. Run `dart run drift_dev make-migrations` to capture drift_schema_v2.json.
6. Write unit tests (RED phase first, then make GREEN):
**test/features/tasks/domain/scheduling_test.dart**: Test all behaviors listed above for calculateNextDueDate and catchUpToPresent and formatRelativeDate. Use fixed dates (no DateTime.now()).
**test/features/rooms/data/rooms_dao_test.dart**: Use `AppDatabase(NativeDatabase.memory())` pattern from Phase 1. Test insert, watchAll, update, delete with cascade, reorder, watchRoomWithStats.
**test/features/tasks/data/tasks_dao_test.dart**: Same in-memory DB pattern. Test insert, watchTasksInRoom (verify sort order), update, delete, completeTask (verify completion record + nextDueDate update), overdue detection.
7. Run `flutter test` to verify all tests pass.
</action>
<verify>
<automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/tasks/domain/scheduling_test.dart test/features/rooms/data/rooms_dao_test.dart test/features/tasks/data/tasks_dao_test.dart</automated>
</verify>
<done>
All domain models defined with correct enum ordering. Drift database at schema v2 with Rooms, Tasks, TaskCompletions tables. Foreign keys enabled via PRAGMA. RoomsDao and TasksDao with full CRUD + stream queries + cascade delete + completion transaction. Scheduling utility correctly handles all 8 interval types, anchor memory, and catch-up logic. All unit tests passing.
</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Create task template data and template tests</name>
<files>
lib/features/templates/data/task_templates.dart,
test/features/templates/task_templates_test.dart
</files>
<behavior>
- roomTemplates map contains exactly 14 room type keys
- Each room type key maps to a non-empty list of TaskTemplate objects
- Every TaskTemplate has a non-empty German name
- Every TaskTemplate has a valid IntervalType
- Every TaskTemplate has a valid EffortLevel
- detectRoomType("Kueche") returns "kueche"
- detectRoomType("Mein Badezimmer") returns "badezimmer"
- detectRoomType("Bad") returns "badezimmer" (alias)
- detectRoomType("Random Name") returns null
- detectRoomType is case-insensitive
- All 14 room types from TMPL-02 are present: kueche, badezimmer, schlafzimmer, wohnzimmer, flur, buero, garage, balkon, waschkueche, keller, kinderzimmer, gaestezimmer, esszimmer, garten
</behavior>
<action>
**lib/features/templates/data/task_templates.dart**: Create `TaskTemplate` class with const constructor (name, description nullable, intervalType, intervalDays defaults to 1, effortLevel). Create `roomTemplates` as `const Map<String, List<TaskTemplate>>` with 4-6 templates per room type for all 14 types. Use realistic German household task names with appropriate frequencies and effort levels.
Room types and sample tasks:
- kueche: Abspuelen (daily/low), Herd reinigen (weekly/medium), Kuehlschrank reinigen (monthly/medium), Backofen reinigen (monthly/high), Muell rausbringen (everyNDays 2/low)
- badezimmer: Toilette putzen (weekly/medium), Spiegel reinigen (weekly/low), Dusche reinigen (weekly/medium), Waschbecken reinigen (weekly/low), Badezimmerboden wischen (weekly/medium)
- schlafzimmer: Bettwasche wechseln (biweekly/medium), Staubsaugen (weekly/medium), Staub wischen (weekly/low), Fenster putzen (monthly/medium)
- wohnzimmer: Staubsaugen (weekly/medium), Staub wischen (weekly/low), Kissen aufschuetteln (daily/low), Fenster putzen (monthly/medium)
- flur: Boden wischen (weekly/medium), Schuhe aufraumen (weekly/low), Garderobe aufraeumen (monthly/low)
- buero: Schreibtisch aufraeumen (weekly/low), Staubsaugen (weekly/medium), Bildschirm reinigen (monthly/low), Papierkorb leeren (weekly/low)
- garage: Boden fegen (monthly/medium), Aufraeumen (quarterly/high), Werkzeug sortieren (quarterly/medium)
- balkon: Boden fegen (weekly/low), Pflanzen giessen (everyNDays 2/low), Gelaender reinigen (monthly/medium), Moebel reinigen (monthly/medium)
- waschkueche: Waschmaschine reinigen (monthly/medium), Trockner reinigen (monthly/medium), Boden wischen (weekly/medium), Waschmittel auffuellen (monthly/low)
- keller: Staubsaugen (monthly/medium), Aufraeumen (quarterly/high), Luftentfeuchter pruefen (monthly/low)
- kinderzimmer: Spielzeug aufraeumen (daily/low), Staubsaugen (weekly/medium), Bettwasche wechseln (biweekly/medium), Staub wischen (weekly/low)
- gaestezimmer: Staubsaugen (biweekly/medium), Bettwasche wechseln (monthly/medium), Staub wischen (biweekly/low), Lueften (weekly/low)
- esszimmer: Tisch abwischen (daily/low), Staubsaugen (weekly/medium), Stuehle reinigen (monthly/medium)
- garten: Rasen maehen (weekly/high), Unkraut jaeten (weekly/medium), Hecke schneiden (quarterly/high), Laub harken (weekly/medium in autumn context), Pflanzen giessen (everyNDays 2/low)
Create `detectRoomType(String roomName)` function: lowercase + trim the input, check if it contains any room type key or its aliases. Aliases map: badezimmer -> [bad, wc, toilette], buero -> [arbeitszimmer, office], waschkueche -> [waschraum], garten -> [terrasse, aussenbereich, hof], gaestezimmer -> [gaeste], kinderzimmer -> [kinder], schlafzimmer -> [schlafraum], kueche -> [kitchen], esszimmer -> [essbereich].
**test/features/templates/task_templates_test.dart**: Test all behaviors listed above. Verify exact room type count (14), non-empty template lists, valid fields on every template, detectRoomType matching and null cases.
</action>
<verify>
<automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/templates/task_templates_test.dart</automated>
</verify>
<done>
All 14 room types have 3-6 German-language task templates with valid names, intervals, and effort levels. detectRoomType correctly identifies room types from names and aliases. Template test suite passing.
</done>
</task>
</tasks>
<verification>
```bash
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/rooms/ test/features/tasks/ test/features/templates/ && flutter test
```
All Phase 2 data layer tests pass. Full existing test suite (Phase 1 + Phase 2) passes. `dart analyze` shows no new errors.
</verification>
<success_criteria>
- Database schema v2 with Rooms, Tasks, TaskCompletions tables
- RoomsDao: CRUD + watchAll + watchWithStats + cascade delete + reorder
- TasksDao: CRUD + watchInRoom (sorted by due date) + completeTask transaction + overdue detection
- Scheduling utility: all 8 interval types, anchor memory, catch-up logic
- Domain models: IntervalType, EffortLevel, FrequencyInterval, curatedRoomIcons, formatRelativeDate
- Template data: 14 room types with German task templates, detectRoomType
- All unit tests green
</success_criteria>
<output>
After completion, create `.planning/phases/02-rooms-and-tasks/02-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,284 @@
---
phase: 02-rooms-and-tasks
plan: 02
type: execute
wave: 2
depends_on: ["02-01"]
files_modified:
- lib/features/rooms/presentation/rooms_screen.dart
- lib/features/rooms/presentation/room_card.dart
- lib/features/rooms/presentation/room_form_screen.dart
- lib/features/rooms/presentation/icon_picker_sheet.dart
- lib/features/rooms/presentation/room_providers.dart
- lib/core/router/router.dart
- lib/l10n/app_de.arb
- pubspec.yaml
autonomous: true
requirements:
- ROOM-01
- ROOM-02
- ROOM-03
- ROOM-04
- ROOM-05
must_haves:
truths:
- "User can create a room by entering a name and selecting an icon from a curated bottom sheet picker"
- "User can edit a room's name and icon from the room detail screen"
- "User can delete a room with a confirmation dialog warning about cascade deletion"
- "User can reorder rooms via drag-and-drop on the 2-column grid"
- "Rooms screen shows a 2-column card grid with room icon, name, due task count, and thin cleanliness progress bar"
- "Rooms screen shows empty state when no rooms exist, with a create button"
artifacts:
- path: "lib/features/rooms/presentation/rooms_screen.dart"
provides: "2-column reorderable room card grid with FAB for room creation"
min_lines: 50
- path: "lib/features/rooms/presentation/room_card.dart"
provides: "Room card widget with icon, name, due count, cleanliness bar"
min_lines: 40
- path: "lib/features/rooms/presentation/room_form_screen.dart"
provides: "Full-screen form for room creation and editing"
min_lines: 50
- path: "lib/features/rooms/presentation/icon_picker_sheet.dart"
provides: "Bottom sheet with curated Material Icons grid"
min_lines: 30
- path: "lib/features/rooms/presentation/room_providers.dart"
provides: "Riverpod providers wrapping RoomsDao stream queries and mutations"
exports: ["roomListProvider", "roomWithStatsProvider", "roomActionsProvider"]
- path: "lib/core/router/router.dart"
provides: "Nested routes under /rooms for room detail and forms"
contains: "rooms/new"
key_links:
- from: "lib/features/rooms/presentation/room_providers.dart"
to: "lib/features/rooms/data/rooms_dao.dart"
via: "providers watch appDatabaseProvider then access roomsDao"
pattern: "ref\\.watch\\(appDatabaseProvider\\)"
- from: "lib/features/rooms/presentation/rooms_screen.dart"
to: "lib/features/rooms/presentation/room_providers.dart"
via: "ConsumerWidget watches roomWithStatsProvider"
pattern: "ref\\.watch\\(roomWithStats"
- from: "lib/features/rooms/presentation/room_card.dart"
to: "lib/core/router/router.dart"
via: "InkWell onTap navigates to /rooms/:roomId"
pattern: "context\\.go.*rooms/"
---
<objective>
Build the complete room management UI: 2-column reorderable card grid, room creation/edit form with icon picker, delete with confirmation, and Riverpod providers connecting to the data layer.
Purpose: Delivers ROOM-01 through ROOM-05 as a working user-facing feature. After this plan, users can create, view, edit, reorder, and delete rooms.
Output: Rooms screen with card grid, room form, icon picker, providers, and router updates.
</objective>
<execution_context>
@/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md
@/home/jlmak/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-rooms-and-tasks/2-CONTEXT.md
@.planning/phases/02-rooms-and-tasks/02-RESEARCH.md
@.planning/phases/02-rooms-and-tasks/02-01-SUMMARY.md
<interfaces>
<!-- From Plan 01 (data layer) - the executor needs these contracts -->
From lib/features/rooms/data/rooms_dao.dart (created in Plan 01):
```dart
class RoomWithStats {
final Room room;
final int totalTasks;
final int dueTasks;
final int overdueCount;
final double cleanlinessRatio; // 0.0 to 1.0
}
@DriftAccessor(tables: [Rooms, Tasks, TaskCompletions])
class RoomsDao extends DatabaseAccessor<AppDatabase> with _$RoomsDaoMixin {
Stream<List<Room>> watchAllRooms();
Stream<List<RoomWithStats>> watchRoomWithStats();
Future<int> insertRoom(RoomsCompanion room);
Future<bool> updateRoom(Room room);
Future<void> deleteRoom(int roomId);
Future<void> reorderRooms(List<int> roomIds);
Future<Room> getRoomById(int id);
}
```
From lib/features/rooms/domain/room_icons.dart (created in Plan 01):
```dart
const List<({String name, IconData icon})> curatedRoomIcons = [...];
IconData mapIconName(String name);
```
From lib/core/database/database.dart (updated in Plan 01):
```dart
@DriftDatabase(tables: [Rooms, Tasks, TaskCompletions], daos: [RoomsDao, TasksDao])
class AppDatabase extends _$AppDatabase {
RoomsDao get roomsDao => ...; // auto-generated
TasksDao get tasksDao => ...; // auto-generated
}
```
From lib/core/providers/database_provider.dart (existing):
```dart
@Riverpod(keepAlive: true)
AppDatabase appDatabase(Ref ref) { ... }
```
From lib/l10n/app_de.arb (existing — 18 keys, needs expansion):
```json
{
"roomsEmptyTitle": "Hier ist noch alles leer!",
"roomsEmptyMessage": "Erstelle deinen ersten Raum, um loszulegen.",
"roomsEmptyAction": "Raum erstellen"
}
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Create Riverpod providers, room form screen, and icon picker</name>
<files>
lib/features/rooms/presentation/room_providers.dart,
lib/features/rooms/presentation/room_form_screen.dart,
lib/features/rooms/presentation/icon_picker_sheet.dart,
lib/core/router/router.dart,
lib/l10n/app_de.arb,
pubspec.yaml
</files>
<action>
1. **Add flutter_reorderable_grid_view dependency**: Run `flutter pub add flutter_reorderable_grid_view` to add it to pubspec.yaml.
2. **lib/features/rooms/presentation/room_providers.dart**: Create Riverpod providers using @riverpod code generation:
```dart
@riverpod
Stream<List<RoomWithStats>> roomWithStatsList(Ref ref) {
final db = ref.watch(appDatabaseProvider);
return db.roomsDao.watchRoomWithStats();
}
@riverpod
class RoomActions extends _$RoomActions {
@override
FutureOr<void> build() {}
Future<int> createRoom(String name, String iconName) async { ... }
Future<void> updateRoom(Room room) async { ... }
Future<void> deleteRoom(int roomId) async { ... }
Future<void> reorderRooms(List<int> roomIds) async { ... }
}
```
3. **lib/features/rooms/presentation/icon_picker_sheet.dart**: Create a bottom sheet widget showing a grid of curated Material Icons from `curatedRoomIcons`. Use a `GridView.count` with crossAxisCount 5. Each icon is an `InkWell` wrapping an `Icon` widget. Selected icon gets a colored circle background using `colorScheme.primaryContainer`. Takes `selectedIconName` and `onIconSelected` callback. Show as modal bottom sheet with `showModalBottomSheet`.
4. **lib/features/rooms/presentation/room_form_screen.dart**: Full-screen Scaffold form for creating and editing rooms. Constructor takes optional `roomId` (null for create, non-null for edit). Uses `ConsumerStatefulWidget`.
Form fields:
- Name: `TextFormField` with autofocus, validator (non-empty, max 100 chars), German label "Raumname"
- Icon: tappable preview showing current selected icon + "Symbol waehlen" label. Tapping opens `IconPickerSheet`. Default to first icon in curated list for new rooms.
- For edit mode: load existing room data via `ref.read(appDatabaseProvider).roomsDao.getRoomById(roomId)` in `initState`.
- Save button in AppBar (check icon). On save: validate form, call `roomActions.createRoom()` or `roomActions.updateRoom()`, then `context.pop()`.
- AppBar title: "Raum erstellen" for new, "Raum bearbeiten" for edit.
5. **lib/core/router/router.dart**: Add nested routes under the `/rooms` branch:
- `/rooms/new` -> `RoomFormScreen()` (create)
- `/rooms/:roomId` -> `TaskListScreen(roomId: roomId)` (will be created in Plan 03, use placeholder for now if needed)
- `/rooms/:roomId/edit` -> `RoomFormScreen(roomId: roomId)` (edit)
- `/rooms/:roomId/tasks/new` -> placeholder (Plan 03)
- `/rooms/:roomId/tasks/:taskId` -> placeholder (Plan 03)
For routes that reference screens from Plan 03 (TaskListScreen, TaskFormScreen), import them. If they don't exist yet, create minimal placeholder files `lib/features/tasks/presentation/task_list_screen.dart` and `lib/features/tasks/presentation/task_form_screen.dart` with a simple `Scaffold(body: Center(child: Text('Coming soon')))` and a `roomId`/`taskId` constructor parameter.
6. **lib/l10n/app_de.arb**: Add new localization keys for room management:
- "roomFormCreateTitle": "Raum erstellen"
- "roomFormEditTitle": "Raum bearbeiten"
- "roomFormNameLabel": "Raumname"
- "roomFormNameHint": "z.B. Kueche, Badezimmer..."
- "roomFormNameRequired": "Bitte einen Namen eingeben"
- "roomFormIconLabel": "Symbol waehlen"
- "roomDeleteConfirmTitle": "Raum loeschen?"
- "roomDeleteConfirmMessage": "Der Raum und alle zugehoerigen Aufgaben werden unwiderruflich geloescht."
- "roomDeleteConfirmAction": "Loeschen"
- "roomCardDueCount": "{count} faellig"
- "cancel": "Abbrechen"
Use proper German umlauts (Unicode escapes in ARB: \u00fc for ue, \u00f6 for oe, \u00e4 for ae, \u00dc for Ue, etc.).
7. Run `dart run build_runner build --delete-conflicting-outputs` to generate provider .g.dart files.
</action>
<verify>
<automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze lib/features/rooms/presentation/ lib/core/router/router.dart && flutter test</automated>
</verify>
<done>
Room providers connect to DAO layer. Room form screen handles create and edit with validation. Icon picker shows curated grid in bottom sheet. Router has nested room routes. New ARB keys added. All code analyzes clean.
</done>
</task>
<task type="auto">
<name>Task 2: Build rooms screen with reorderable card grid and room card component</name>
<files>
lib/features/rooms/presentation/rooms_screen.dart,
lib/features/rooms/presentation/room_card.dart
</files>
<action>
1. **lib/features/rooms/presentation/room_card.dart**: Create `RoomCard` StatelessWidget accepting `RoomWithStats` data. Per user decision (2-CONTEXT.md):
- Card with `InkWell` wrapping, `onTap` navigates to `/rooms/${room.id}` via `context.go()`
- Column layout: Icon (36px, using `mapIconName(room.iconName)`), room name (titleSmall), due task count badge if > 0 (bodySmall, using `colorScheme.error` color, text from `roomCardDueCount` ARB key)
- Thin cleanliness progress bar at BOTTOM of card: `LinearProgressIndicator` with `minHeight: 3`, value = `cleanlinessRatio`, color interpolated green->yellow->red using `Color.lerp` with coral `Color(0xFFE07A5F)` (overdue) and sage `Color(0xFF7A9A6D)` (clean), backgroundColor = `surfaceContainerHighest`
- Use `ValueKey(room.id)` for drag-and-drop compatibility
- Card styling: `surfaceContainerLow` background, rounded corners, subtle elevation
2. **lib/features/rooms/presentation/rooms_screen.dart**: Replace the existing placeholder with a `ConsumerWidget` that:
- Watches `roomWithStatsListProvider` for reactive room data
- Shows the Phase 1 empty state (icon + text + "Raum erstellen" button) when list is empty. The button navigates to `/rooms/new`
- When rooms exist, shows a `ReorderableBuilder` from `flutter_reorderable_grid_view` wrapping a `GridView.count(crossAxisCount: 2)` of `RoomCard` widgets
- Each card has `ValueKey(roomWithStats.room.id)`
- On reorder complete, calls `roomActions.reorderRooms()` with the new ID order
- FAB (FloatingActionButton) at bottom-right with `Icons.add` to navigate to `/rooms/new`
- Uses `AsyncValue.when(data: ..., loading: ..., error: ...)` pattern for the stream provider
- Loading state: `CircularProgressIndicator` centered
- Error state: error message with retry option
Add a `PopupMenuButton` or long-press menu on each card for edit and delete actions:
- Edit: navigates to `/rooms/${room.id}/edit`
- Delete: shows confirmation dialog per user decision. Dialog title from `roomDeleteConfirmTitle`, message from `roomDeleteConfirmMessage` (warns about cascade), cancel and delete buttons. On confirm, calls `roomActions.deleteRoom(roomId)`.
</action>
<verify>
<automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze lib/features/rooms/presentation/ && flutter test</automated>
</verify>
<done>
Rooms screen displays 2-column reorderable card grid with room icon, name, due task count, and cleanliness bar. Empty state shows when no rooms. FAB creates new room. Cards navigate to room detail. Long-press/popup menu offers edit and delete with confirmation dialog. Drag-and-drop reorder persists via DAO. All existing tests still pass.
</done>
</task>
</tasks>
<verification>
```bash
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze && flutter test
```
All code analyzes clean. All tests pass. Room creation, editing, deletion, and reorder are functional at the UI level.
</verification>
<success_criteria>
- Rooms screen shows 2-column card grid (or empty state)
- Room cards display: icon, name, due task count, thin cleanliness bar (green->yellow->red)
- Room form: create and edit with name field + icon picker bottom sheet
- Icon picker: curated grid of ~25 household Material Icons in bottom sheet
- Delete: confirmation dialog with cascade warning
- Drag-and-drop: reorder persists to database
- FAB for creating new rooms
- Router: nested routes /rooms/new, /rooms/:roomId, /rooms/:roomId/edit
- Localization: all room UI strings from ARB
</success_criteria>
<output>
After completion, create `.planning/phases/02-rooms-and-tasks/02-02-SUMMARY.md`
</output>

View File

@@ -0,0 +1,305 @@
---
phase: 02-rooms-and-tasks
plan: 03
type: execute
wave: 2
depends_on: ["02-01"]
files_modified:
- lib/features/tasks/presentation/task_list_screen.dart
- lib/features/tasks/presentation/task_row.dart
- lib/features/tasks/presentation/task_form_screen.dart
- lib/features/tasks/presentation/task_providers.dart
- lib/l10n/app_de.arb
autonomous: true
requirements:
- TASK-01
- TASK-02
- TASK-03
- TASK-04
- TASK-05
- TASK-06
- TASK-07
- TASK-08
must_haves:
truths:
- "User can see all tasks in a room sorted by due date"
- "User can create a task with name, optional description, frequency interval, and effort level"
- "User can edit a task's name, description, frequency interval, and effort level"
- "User can delete a task with a confirmation dialog"
- "User can mark a task done via leading checkbox, which records completion and auto-calculates next due"
- "Overdue tasks have their due date text displayed in warm coral/red color"
- "Each task row shows: task name, relative due date in German, frequency label"
artifacts:
- path: "lib/features/tasks/presentation/task_list_screen.dart"
provides: "Scaffold showing task list for a room with sorted tasks and FAB for new task"
min_lines: 50
- path: "lib/features/tasks/presentation/task_row.dart"
provides: "Task row widget with leading checkbox, name, relative due date, frequency label"
min_lines: 40
- path: "lib/features/tasks/presentation/task_form_screen.dart"
provides: "Full-screen form for task creation and editing with frequency picker and effort selector"
min_lines: 80
- path: "lib/features/tasks/presentation/task_providers.dart"
provides: "Riverpod providers wrapping TasksDao stream queries and mutations"
exports: ["tasksInRoomProvider", "taskActionsProvider"]
key_links:
- from: "lib/features/tasks/presentation/task_providers.dart"
to: "lib/features/tasks/data/tasks_dao.dart"
via: "providers watch appDatabaseProvider then access tasksDao"
pattern: "ref\\.watch\\(appDatabaseProvider\\)"
- from: "lib/features/tasks/presentation/task_list_screen.dart"
to: "lib/features/tasks/presentation/task_providers.dart"
via: "ConsumerWidget watches tasksInRoomProvider(roomId)"
pattern: "ref\\.watch\\(tasksInRoom"
- from: "lib/features/tasks/presentation/task_row.dart"
to: "lib/features/tasks/domain/relative_date.dart"
via: "formatRelativeDate for German due date labels"
pattern: "formatRelativeDate"
- from: "lib/features/tasks/presentation/task_row.dart"
to: "lib/features/tasks/presentation/task_providers.dart"
via: "checkbox onChanged calls taskActions.completeTask"
pattern: "completeTask"
---
<objective>
Build the complete task management UI: task list screen (sorted by due date), task row with checkbox completion and overdue highlighting, task creation/edit form with frequency and effort selectors, and Riverpod providers.
Purpose: Delivers TASK-01 through TASK-08 as a working user-facing feature. After this plan, users can create, view, edit, delete, and complete tasks with automatic rescheduling.
Output: Task list screen, task row component, task form, providers, and localization keys.
</objective>
<execution_context>
@/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md
@/home/jlmak/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-rooms-and-tasks/2-CONTEXT.md
@.planning/phases/02-rooms-and-tasks/02-RESEARCH.md
@.planning/phases/02-rooms-and-tasks/02-01-SUMMARY.md
<interfaces>
<!-- From Plan 01 (data layer) -->
From lib/features/tasks/data/tasks_dao.dart:
```dart
@DriftAccessor(tables: [Tasks, TaskCompletions])
class TasksDao extends DatabaseAccessor<AppDatabase> with _$TasksDaoMixin {
Stream<List<Task>> watchTasksInRoom(int roomId); // ordered by nextDueDate ASC
Future<int> insertTask(TasksCompanion task);
Future<bool> updateTask(Task task);
Future<void> deleteTask(int taskId);
Future<void> completeTask(int taskId, {DateTime? now});
}
```
From lib/features/tasks/domain/frequency.dart:
```dart
enum IntervalType { daily, everyNDays, weekly, biweekly, monthly, everyNMonths, quarterly, yearly }
class FrequencyInterval {
final IntervalType type;
final int days;
String label(); // German display string
static const List<FrequencyInterval> presets = [...]; // 10 preset intervals
}
```
From lib/features/tasks/domain/effort_level.dart:
```dart
enum EffortLevel { low, medium, high }
// Extension with label() -> "Gering", "Mittel", "Hoch"
```
From lib/features/tasks/domain/relative_date.dart:
```dart
String formatRelativeDate(DateTime dueDate, DateTime today);
// Returns "Heute", "Morgen", "in X Tagen", "Uberfaellig seit X Tagen"
```
From lib/core/database/database.dart (Tasks table generates):
```dart
class Task {
final int id;
final int roomId;
final String name;
final String? description;
final IntervalType intervalType;
final int intervalDays;
final int? anchorDay;
final EffortLevel effortLevel;
final DateTime nextDueDate;
final DateTime createdAt;
}
```
From lib/core/router/router.dart (updated in Plan 02):
```dart
// Routes already defined:
// /rooms/:roomId -> TaskListScreen(roomId: roomId)
// /rooms/:roomId/tasks/new -> TaskFormScreen(roomId: roomId)
// /rooms/:roomId/tasks/:taskId -> TaskFormScreen(taskId: taskId)
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Create task providers, task form screen with frequency and effort selectors</name>
<files>
lib/features/tasks/presentation/task_providers.dart,
lib/features/tasks/presentation/task_form_screen.dart,
lib/l10n/app_de.arb
</files>
<action>
1. **lib/features/tasks/presentation/task_providers.dart**: Create Riverpod providers:
```dart
@riverpod
Stream<List<Task>> tasksInRoom(Ref ref, int roomId) {
final db = ref.watch(appDatabaseProvider);
return db.tasksDao.watchTasksInRoom(roomId);
}
@riverpod
class TaskActions extends _$TaskActions {
@override
FutureOr<void> build() {}
Future<int> createTask({
required int roomId,
required String name,
String? description,
required IntervalType intervalType,
required int intervalDays,
int? anchorDay,
required EffortLevel effortLevel,
required DateTime nextDueDate,
}) async { ... }
Future<void> updateTask(Task task) async { ... }
Future<void> deleteTask(int taskId) async { ... }
Future<void> completeTask(int taskId) async { ... }
}
```
2. **lib/features/tasks/presentation/task_form_screen.dart**: Full-screen `ConsumerStatefulWidget` form for creating and editing tasks. Constructor takes either `roomId` (for create, required) or `taskId` (for edit, loads existing task).
Form fields in this order (per RESEARCH.md Open Question 3 recommendation):
- **Name** (required): `TextFormField` with autofocus, validator non-empty, max 200 chars. Label: "Aufgabenname"
- **Frequency** (required): A dropdown or selector showing preset intervals from `FrequencyInterval.presets` with their German labels. Display as a `DropdownButtonFormField<FrequencyInterval>` or a custom tappable field that opens a bottom sheet with the preset list. Include "Benutzerdefiniert" option at the end that expands to show a number field + unit picker (Tage/Wochen/Monate). For custom: two fields — a `TextFormField` for the number and a `SegmentedButton` for the unit.
- **Effort** (required): `SegmentedButton<EffortLevel>` with 3 segments showing German labels ("Gering", "Mittel", "Hoch"). Default to `medium`.
- **Description** (optional): `TextFormField` with maxLines: 3, label "Beschreibung (optional)"
- **Initial due date**: For new tasks, default to today. Show as a tappable field that opens `showDatePicker`. Label: "Erstes Faelligkeitsdatum". Format as German date (DD.MM.YYYY).
For calendar-anchored intervals (monthly, everyNMonths, quarterly, yearly), automatically set `anchorDay` to the selected due date's day-of-month.
AppBar title: "Aufgabe erstellen" (create) or "Aufgabe bearbeiten" (edit). Save button (check icon) in AppBar.
On save: validate, build `TasksCompanion` or updated `Task`, call provider method, `context.pop()`.
For edit mode: load task data in `initState` via `ref.read(appDatabaseProvider).tasksDao` and pre-fill all fields.
3. **lib/l10n/app_de.arb**: Add task-related localization keys:
- "taskFormCreateTitle": "Aufgabe erstellen"
- "taskFormEditTitle": "Aufgabe bearbeiten"
- "taskFormNameLabel": "Aufgabenname"
- "taskFormNameHint": "z.B. Staubsaugen, Fenster putzen..."
- "taskFormNameRequired": "Bitte einen Namen eingeben"
- "taskFormFrequencyLabel": "Wiederholung"
- "taskFormFrequencyCustom": "Benutzerdefiniert"
- "taskFormFrequencyEvery": "Alle"
- "taskFormFrequencyUnitDays": "Tage"
- "taskFormFrequencyUnitWeeks": "Wochen"
- "taskFormFrequencyUnitMonths": "Monate"
- "taskFormEffortLabel": "Aufwand"
- "taskFormDescriptionLabel": "Beschreibung (optional)"
- "taskFormDueDateLabel": "Erstes F\u00e4lligkeitsdatum"
- "taskDeleteConfirmTitle": "Aufgabe l\u00f6schen?"
- "taskDeleteConfirmMessage": "Die Aufgabe wird unwiderruflich gel\u00f6scht."
- "taskDeleteConfirmAction": "L\u00f6schen"
- "taskEmptyTitle": "Noch keine Aufgaben"
- "taskEmptyMessage": "Erstelle die erste Aufgabe f\u00fcr diesen Raum."
- "taskEmptyAction": "Aufgabe erstellen"
Use proper Unicode escapes for umlauts in ARB file.
4. Run `dart run build_runner build --delete-conflicting-outputs` to generate provider .g.dart files.
</action>
<verify>
<automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze lib/features/tasks/presentation/ && flutter test</automated>
</verify>
<done>
Task providers connect to DAO layer. Task form handles create and edit with all fields (name, frequency with presets + custom, effort segmented button, description, initial due date). ARB keys added. All code analyzes clean.
</done>
</task>
<task type="auto">
<name>Task 2: Build task list screen with task row component, completion, and overdue highlighting</name>
<files>
lib/features/tasks/presentation/task_list_screen.dart,
lib/features/tasks/presentation/task_row.dart
</files>
<action>
1. **lib/features/tasks/presentation/task_row.dart**: Create `TaskRow` StatelessWidget accepting a `Task` object and callbacks. Per user decision (2-CONTEXT.md):
- **Leading checkbox**: `Checkbox` widget. When checked, calls `taskActions.completeTask(task.id)`. Per user decision: "No undo on completion -- immediate and final." The checkbox should feel instant (optimistic UI).
- **Task name**: `Text` with `titleMedium` style
- **Relative due date**: Call `formatRelativeDate(task.nextDueDate, DateTime.now())` for German label. Per user decision: "Overdue visual: due date text turns warm red/coral color. Rest of row stays normal." Use `Color(0xFFE07A5F)` (warm coral/terracotta from the palette) for overdue due date text color. Normal dates use `onSurfaceVariant`.
- **Frequency label**: Show interval label from `FrequencyInterval` helper (e.g. "Woechentlich", "Alle 3 Tage"). Use `bodySmall` style with `onSurfaceVariant` color.
- Layout: `ListTile` with leading `Checkbox`, title = task name, subtitle = Row of relative due date + frequency label separated by a dot or dash.
- **Tap row (not checkbox) opens edit**: `onTap` navigates to `/rooms/${task.roomId}/tasks/${task.id}` per user decision.
- No swipe gesture per user decision: "Leading checkbox on each task row to mark done -- tap to toggle. No swipe gesture."
- Per user decision: "No effort indicator or description preview on list view"
2. **lib/features/tasks/presentation/task_list_screen.dart**: Replace the placeholder (created in Plan 02) with a `ConsumerWidget` that:
- Takes `roomId` as constructor parameter
- Watches `tasksInRoomProvider(roomId)` for reactive task list (already sorted by due date from DAO)
- Shows task empty state when no tasks: icon + "Noch keine Aufgaben" text + "Aufgabe erstellen" button (from ARB keys)
- When tasks exist: `ListView.builder` of `TaskRow` widgets
- Scaffold with AppBar showing room name (load from `appDatabaseProvider.roomsDao.getRoomById(roomId)`)
- AppBar actions: edit room icon (navigates to `/rooms/$roomId/edit`), delete room with confirmation dialog (same pattern as room delete)
- FAB with `Icons.add` to navigate to `/rooms/$roomId/tasks/new`
- Uses `AsyncValue.when()` for loading/error/data states
- Task deletion: long-press on task row shows confirmation dialog. On confirm, calls `taskActions.deleteTask(taskId)`.
Note on overdue detection: A task is overdue if `task.nextDueDate` is before today (compare date-only: `DateTime(now.year, now.month, now.day)`). The `formatRelativeDate` function already handles this labeling. The `TaskRow` widget checks if `dueDate.isBefore(today)` to apply coral color.
</action>
<verify>
<automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze lib/features/tasks/presentation/ && flutter test</automated>
</verify>
<done>
Task list screen shows tasks sorted by due date with empty state. Task rows display checkbox, name, relative due date (German), and frequency label. Overdue dates highlighted in warm coral. Checkbox marks task done immediately (optimistic UI, no undo). Row tap opens edit form. Long-press deletes with confirmation. FAB creates new task. AppBar has room edit and delete actions. All existing tests pass.
</done>
</task>
</tasks>
<verification>
```bash
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze && flutter test
```
All code analyzes clean. All tests pass. Task creation, viewing, editing, deletion, and completion are functional at the UI level.
</verification>
<success_criteria>
- Task list screen shows tasks in a room sorted by due date
- Task rows: leading checkbox, name, German relative due date, frequency label
- Overdue dates displayed in warm coral (0xFFE07A5F)
- Checkbox marks task done instantly, records completion, auto-schedules next due
- Task form: create/edit with name, frequency (presets + custom), effort (3-way segmented), description, initial due date
- Custom frequency: number + unit picker (Tage/Wochen/Monate)
- Delete task with confirmation dialog
- Empty state when room has no tasks
- All strings from ARB localization
</success_criteria>
<output>
After completion, create `.planning/phases/02-rooms-and-tasks/02-03-SUMMARY.md`
</output>

View File

@@ -0,0 +1,217 @@
---
phase: 02-rooms-and-tasks
plan: 04
type: execute
wave: 3
depends_on: ["02-02", "02-03"]
files_modified:
- lib/features/templates/presentation/template_picker_sheet.dart
- lib/features/rooms/presentation/room_form_screen.dart
- lib/l10n/app_de.arb
autonomous: true
requirements:
- TMPL-01
- TMPL-02
must_haves:
truths:
- "After creating a room whose name matches a known room type, user is prompted to add tasks from templates"
- "Template picker shows a checklist of German-language task templates with all unchecked by default"
- "User can check desired templates and they are created as tasks in the room with correct frequencies and effort levels"
- "If room name does not match any known type, no template prompt appears and room is created directly"
- "All 14 room types from TMPL-02 are covered by the template system"
artifacts:
- path: "lib/features/templates/presentation/template_picker_sheet.dart"
provides: "Bottom sheet with checklist of task templates for a detected room type"
min_lines: 40
key_links:
- from: "lib/features/rooms/presentation/room_form_screen.dart"
to: "lib/features/templates/data/task_templates.dart"
via: "After room save, calls detectRoomType on room name"
pattern: "detectRoomType"
- from: "lib/features/rooms/presentation/room_form_screen.dart"
to: "lib/features/templates/presentation/template_picker_sheet.dart"
via: "Shows template picker bottom sheet if room type detected"
pattern: "TemplatePicker|showModalBottomSheet"
- from: "lib/features/templates/presentation/template_picker_sheet.dart"
to: "lib/features/tasks/presentation/task_providers.dart"
via: "Creates tasks from selected templates via taskActions.createTask"
pattern: "createTask"
---
<objective>
Wire the template selection flow into room creation: detect room type from name, show template picker bottom sheet, and create selected templates as tasks in the newly created room.
Purpose: Delivers TMPL-01 and TMPL-02 -- the template-driven task creation that makes the app immediately useful. Users create a room and get relevant German-language task suggestions.
Output: Template picker bottom sheet, updated room form with template flow integration.
</objective>
<execution_context>
@/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md
@/home/jlmak/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-rooms-and-tasks/2-CONTEXT.md
@.planning/phases/02-rooms-and-tasks/02-RESEARCH.md
@.planning/phases/02-rooms-and-tasks/02-01-SUMMARY.md
@.planning/phases/02-rooms-and-tasks/02-02-SUMMARY.md
@.planning/phases/02-rooms-and-tasks/02-03-SUMMARY.md
<interfaces>
<!-- From Plan 01 (template data) -->
From lib/features/templates/data/task_templates.dart:
```dart
class TaskTemplate {
final String name;
final String? description;
final IntervalType intervalType;
final int intervalDays;
final EffortLevel effortLevel;
const TaskTemplate({...});
}
const Map<String, List<TaskTemplate>> roomTemplates = { ... };
String? detectRoomType(String roomName);
```
<!-- From Plan 02 (room form) -->
From lib/features/rooms/presentation/room_form_screen.dart:
```dart
// Full-screen form with name + icon fields
// On save: calls roomActions.createRoom(name, iconName) which returns roomId
// After save: context.pop() currently
// MODIFY: after save for new rooms, check detectRoomType before popping
```
<!-- From Plan 03 (task creation) -->
From lib/features/tasks/presentation/task_providers.dart:
```dart
@riverpod
class TaskActions extends _$TaskActions {
Future<int> createTask({
required int roomId,
required String name,
String? description,
required IntervalType intervalType,
required int intervalDays,
int? anchorDay,
required EffortLevel effortLevel,
required DateTime nextDueDate,
}) async { ... }
}
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Create template picker bottom sheet</name>
<files>
lib/features/templates/presentation/template_picker_sheet.dart,
lib/l10n/app_de.arb
</files>
<action>
**lib/features/templates/presentation/template_picker_sheet.dart**: Create a `StatefulWidget` (not Consumer -- it receives data via constructor) that displays a checklist of task templates.
Constructor parameters:
- `String roomType` -- the detected room type key
- `List<TaskTemplate> templates` -- the template list for this room type
- `void Function(List<TaskTemplate> selected) onConfirm` -- callback with selected templates
UI structure:
- Title: "Aufgaben aus Vorlagen hinzufuegen?" (from ARB)
- Subtitle hint: the detected room type display name (capitalize first letter)
- `ListView` of `CheckboxListTile` widgets, one per template
- Each tile shows: template name as title, frequency label as subtitle (e.g. "Woechentlich - Mittel")
- **All unchecked by default** per user decision
- Bottom action row: "Ueberspringen" (skip/cancel) button and "Hinzufuegen" (add) button
- "Hinzufuegen" button only enabled when at least one template is checked
- Use `showModalBottomSheet` with `isScrollControlled: true` and `DraggableScrollableSheet` for long lists
- Track checked state with a `Set<int>` (template index)
**lib/l10n/app_de.arb**: Add template-related keys:
- "templatePickerTitle": "Aufgaben aus Vorlagen hinzuf\u00fcgen?"
- "templatePickerSkip": "\u00dcberspringen"
- "templatePickerAdd": "Hinzuf\u00fcgen"
- "templatePickerSelected": "{count} ausgew\u00e4hlt"
</action>
<verify>
<automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze lib/features/templates/presentation/ && flutter test</automated>
</verify>
<done>
Template picker bottom sheet displays checklist of templates with frequency/effort info, all unchecked by default. Skip and add buttons. Localized German strings.
</done>
</task>
<task type="auto">
<name>Task 2: Wire template flow into room creation</name>
<files>
lib/features/rooms/presentation/room_form_screen.dart
</files>
<action>
Modify the room form screen's save flow for NEW rooms (not edit) to integrate the template selection:
1. After `roomActions.createRoom(name, iconName)` returns the new `roomId`:
2. Call `detectRoomType(name)` to check if the room name matches a known type
3. **If room type detected** (not null):
- Look up `roomTemplates[roomType]` to get the template list
- Show `TemplatePickerSheet` via `showModalBottomSheet`
- If user selects templates and confirms:
- For each selected `TaskTemplate`, call `taskActions.createTask()` with:
- `roomId`: the newly created room ID
- `name`: template.name
- `description`: template.description
- `intervalType`: template.intervalType
- `intervalDays`: template.intervalDays
- `anchorDay`: set based on interval type (for calendar-anchored: today's day-of-month)
- `effortLevel`: template.effortLevel
- `nextDueDate`: today (first due date = today for all template tasks)
- After creating all tasks, navigate to the new room: `context.go('/rooms/$roomId')`
- If user skips (taps "Ueberspringen"): navigate to `/rooms/$roomId` without creating tasks
4. **If no room type detected** (null):
- Navigate directly to `/rooms/$roomId` (no template prompt per user decision: "Users can create fully custom rooms with no template prompt if no room type matches")
Important per user decision: "The template prompt after room creation should feel like a helpful suggestion, not a required step -- easy to dismiss"
For EDIT mode: no template prompt (only on creation). The save flow for edit stays as-is (update room, pop).
Read `taskActionsProvider` via `ref.read(taskActionsProvider.notifier)` for the mutations (not watch -- this is in a callback).
</action>
<verify>
<automated>cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze lib/features/rooms/presentation/ lib/features/templates/presentation/ && flutter test</automated>
</verify>
<done>
Room creation flow: save room -> detect room type -> show template picker if match -> create selected templates as tasks -> navigate to room. Custom rooms skip templates. Edit mode unaffected. All existing tests pass.
</done>
</task>
</tasks>
<verification>
```bash
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze && flutter test
```
All code analyzes clean. All tests pass. Template selection flow is wired into room creation.
</verification>
<success_criteria>
- Creating a room with a recognized name (e.g. "Kueche") triggers template picker
- Template picker shows relevant German task templates, all unchecked
- Checking templates and confirming creates them as tasks in the room
- Skipping creates the room without tasks
- Unrecognized room names skip template prompt entirely
- Edit mode does not show template prompt
- All 14 room types accessible via detectRoomType
</success_criteria>
<output>
After completion, create `.planning/phases/02-rooms-and-tasks/02-04-SUMMARY.md`
</output>

View File

@@ -0,0 +1,110 @@
---
phase: 02-rooms-and-tasks
plan: 05
type: execute
wave: 4
depends_on: ["02-04"]
files_modified: []
autonomous: false
requirements:
- ROOM-01
- ROOM-02
- ROOM-03
- ROOM-04
- ROOM-05
- TASK-01
- TASK-02
- TASK-03
- TASK-04
- TASK-05
- TASK-06
- TASK-07
- TASK-08
- TMPL-01
- TMPL-02
must_haves:
truths:
- "All Phase 2 features are visually and functionally verified on a running app"
artifacts: []
key_links: []
---
<objective>
Visual and functional verification of all Phase 2 features on a running app. This is the final checkpoint before marking Phase 2 complete.
Purpose: Confirms that all room management, task management, template selection, completion scheduling, and overdue highlighting work correctly as an integrated whole.
Output: User confirmation that Phase 2 is feature-complete.
</objective>
<execution_context>
@/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md
@/home/jlmak/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/02-rooms-and-tasks/2-CONTEXT.md
@.planning/phases/02-rooms-and-tasks/02-04-SUMMARY.md
</context>
<tasks>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 1: Visual and functional verification of all Phase 2 features</name>
<files></files>
<action>
Run the app with `flutter run` and have the user verify all Phase 2 features.
Complete room and task management system built:
- Room CRUD with 2-column card grid, icon picker, drag-and-drop reorder, cascade delete
- Task CRUD with frequency intervals, effort levels, date picker
- Task completion with auto-scheduling of next due date
- Overdue highlighting (warm coral due date text)
- Room cards with cleanliness indicator bar (green to yellow to red)
- German-language template selection after room creation (14 room types)
- All UI strings localized via ARB
Verification steps for user:
Test 1 - Room creation with templates (ROOM-01, TMPL-01, TMPL-02): Tap "Raum erstellen" FAB, enter "Kueche", select kitchen icon, save. Verify template picker appears. Check 2-3 templates, tap "Hinzufuegen". Verify tasks created in room.
Test 2 - Custom room without templates: Create room "Mein Hobbyraum". Verify NO template picker, direct navigation to empty task list.
Test 3 - Task creation and editing (TASK-01, TASK-02, TASK-04, TASK-05): Create task with name, "Woechentlich" frequency, "Mittel" effort. Tap row to edit, change frequency. Verify update.
Test 4 - Task completion (TASK-07): Tap checkbox. Verify next due date updates.
Test 5 - Overdue highlighting (TASK-08): Verify overdue tasks show warm coral due date text.
Test 6 - Room cards (ROOM-05): Verify 2-column grid with icon, name, due count, cleanliness bar.
Test 7 - Room reorder (ROOM-04): Drag room card to new position. Verify order persists.
Test 8 - Room edit and delete (ROOM-02, ROOM-03): Edit room name/icon. Delete room with cascade warning.
Test 9 - Task delete (TASK-03): Long-press task, confirm deletion.
Test 10 - Task sorting (TASK-06): Create tasks with different dates, verify due-date sort order.
</action>
<verify>User types "approved" or describes issues</verify>
<done>All 10 test scenarios pass. Phase 2 features confirmed working on running app.</done>
</task>
</tasks>
<verification>
User confirms all Phase 2 features work correctly on a running device/emulator.
</verification>
<success_criteria>
- All 10 test scenarios pass visual/functional verification
- Room CRUD, task CRUD, templates, completion, overdue, cleanliness indicator all working
- No crashes or unexpected behavior
</success_criteria>
<output>
After completion, create `.planning/phases/02-rooms-and-tasks/02-05-SUMMARY.md`
</output>