15 KiB
Purpose: This is the app's primary screen -- the first thing users see. It transforms the placeholder Home tab into the core daily workflow: see what's due, check it off, feel progress. Output: Complete HomeScreen rewrite, DailyPlanTaskRow widget, ProgressCard widget, and widget tests.
<execution_context> @/home/jlmak/.claude/get-shit-done/workflows/execute-plan.md @/home/jlmak/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-daily-plan-and-cleanliness/3-CONTEXT.md @.planning/phases/03-daily-plan-and-cleanliness/03-RESEARCH.md @.planning/phases/03-daily-plan-and-cleanliness/03-01-SUMMARY.md@lib/features/home/presentation/home_screen.dart @lib/features/tasks/presentation/task_row.dart @lib/features/tasks/presentation/task_providers.dart @lib/features/tasks/domain/relative_date.dart @lib/core/router/router.dart @lib/l10n/app_de.arb
From lib/features/home/domain/daily_plan_models.dart:
class TaskWithRoom {
final Task task;
final String roomName;
final int roomId;
const TaskWithRoom({required this.task, required this.roomName, required this.roomId});
}
class DailyPlanState {
final List<TaskWithRoom> overdueTasks;
final List<TaskWithRoom> todayTasks;
final List<TaskWithRoom> tomorrowTasks;
final int completedTodayCount;
final int totalTodayCount; // overdue + today + completedTodayCount
const DailyPlanState({...});
}
From lib/features/home/presentation/daily_plan_providers.dart:
final dailyPlanProvider = StreamProvider.autoDispose<DailyPlanState>((ref) { ... });
From lib/features/tasks/presentation/task_providers.dart:
@riverpod
class TaskActions extends _$TaskActions {
Future<void> completeTask(int taskId) async { ... }
}
From lib/features/tasks/domain/relative_date.dart:
String formatRelativeDate(DateTime dueDate, DateTime today);
// Returns: "Heute", "Morgen", "in X Tagen", "Uberfaellig seit X Tagen"
From lib/l10n/app_de.arb (Plan 01 additions):
dailyPlanProgress(completed, total) -> "{completed} von {total} erledigt"
dailyPlanSectionOverdue -> "Uberfaellig"
dailyPlanSectionToday -> "Heute"
dailyPlanSectionUpcoming -> "Demnachst"
dailyPlanUpcomingCount(count) -> "Demnachst ({count})"
dailyPlanAllClearTitle -> "Alles erledigt!"
dailyPlanAllClearMessage -> "Keine Aufgaben fuer heute. Geniesse den Moment!"
dailyPlanNoTasks -> "Noch keine Aufgaben angelegt"
2. Create `lib/features/home/presentation/progress_card.dart`:
- `class ProgressCard extends StatelessWidget`
- Constructor: `required int completed`, `required int total`
- Build a `Card` with `margin: EdgeInsets.all(16)`:
- `Text(l10n.dailyPlanProgress(completed, total))` in `titleMedium` with `fontWeight: FontWeight.bold`
- `SizedBox(height: 12)`
- `ClipRRect(borderRadius: 4)` wrapping `LinearProgressIndicator`:
- `value: total > 0 ? completed / total : 0.0`
- `minHeight: 8`
- `backgroundColor: colorScheme.surfaceContainerHighest`
- `color: colorScheme.primary`
- When total is 0 and completed is 0 (no tasks at all), the progress card should still render gracefully (0.0 progress, "0 von 0 erledigt")
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze lib/features/home/presentation/daily_plan_task_row.dart lib/features/home/presentation/progress_card.dart
- DailyPlanTaskRow renders task name, room name tag (tappable, navigates to room), relative date (coral if overdue)
- DailyPlanTaskRow has checkbox only when showCheckbox=true (overdue/today), hidden for tomorrow
- DailyPlanTaskRow has NO onTap or onLongPress on the row itself
- ProgressCard shows "X von Y erledigt" text with linear progress bar
- Both widgets pass dart analyze
Task 2: HomeScreen rewrite with daily plan sections, animated completion, empty state, and tests
lib/features/home/presentation/home_screen.dart,
test/features/home/presentation/home_screen_test.dart
1. COMPLETE REWRITE of `lib/features/home/presentation/home_screen.dart`:
- Change from `StatelessWidget` to `ConsumerStatefulWidget` (needs ref for providers AND state for AnimatedList keys)
- `ref.watch(dailyPlanProvider)` in build method
- Use `AsyncValue.when(loading: ..., error: ..., data: ...)` pattern:
- `loading`: Center(child: CircularProgressIndicator())
- `error`: Center(child: Text(error.toString()))
- `data`: Build the daily plan UI
DAILY PLAN UI STRUCTURE (data case):
a. **"No tasks at all" state**: If totalTodayCount == 0 AND tomorrowTasks.isEmpty AND completedTodayCount == 0, show the existing empty state pattern (homeEmptyTitle / homeEmptyMessage / homeEmptyAction button navigating to /rooms). This covers the case where the user has not created any rooms/tasks yet. Use `dailyPlanNoTasks` localization key for this.
b. **"All clear" state** (PLAN-06): If overdueTasks.isEmpty AND todayTasks.isEmpty AND completedTodayCount > 0, show celebration empty state: `Icons.celebration_outlined` (size 80, onSurface alpha 0.4), `dailyPlanAllClearTitle`, `dailyPlanAllClearMessage`. This means there WERE tasks today but they're all done.
c. **"Also all clear but nothing was ever due today"**: If overdueTasks.isEmpty AND todayTasks.isEmpty AND completedTodayCount == 0 AND tomorrowTasks.isNotEmpty, show same celebration empty state but with the progress card showing 0/0 and then the tomorrow section. (Edge case: nothing today, but stuff tomorrow.)
d. **Normal state** (tasks exist): `ListView` with:
1. `ProgressCard(completed: completedTodayCount, total: totalTodayCount)` -- always first
2. If overdueTasks.isNotEmpty: Section header "Uberfaellig" (titleMedium, warm coral color 0xFFE07A5F) with `Padding(horizontal: 16, vertical: 8)`, followed by `DailyPlanTaskRow` for each overdue task with `showCheckbox: true`
3. Section header "Heute" (titleMedium, primary color) with same padding, followed by `DailyPlanTaskRow` for each today task with `showCheckbox: true`
4. If tomorrowTasks.isNotEmpty: `ExpansionTile` with `initiallyExpanded: false`, title: `dailyPlanUpcomingCount(count)` in titleMedium. Children: `DailyPlanTaskRow` for each tomorrow task with `showCheckbox: false`
COMPLETION ANIMATION (PLAN-04):
- For the simplicity-first approach (avoiding AnimatedList desync pitfalls from research): When checkbox is tapped, call `ref.read(taskActionsProvider.notifier).completeTask(taskId)`. The Drift stream will naturally re-emit without the completed task (its nextDueDate moves to the future). This provides a seamless removal.
- To add visual feedback: Wrap each DailyPlanTaskRow in the overdue and today sections with an `AnimatedSwitcher` (or use `AnimatedList` if confident). The simplest approach: maintain a local `Set<int> _completingTaskIds` in state. When checkbox tapped, add taskId to set, triggering a rebuild that wraps the row in `SizeTransition` animating to zero height over 300ms. After animation, the stream re-emission removes it permanently.
- Alternative simpler approach: Use a plain ListView. On checkbox tap, fire completeTask(). The stream re-emission rebuilds the list without the task. No explicit animation, but the progress counter updates immediately giving visual feedback. This is acceptable for v1.
- RECOMMENDED: Use `AnimatedList` with `GlobalKey<AnimatedListState>` for overdue+today section. On completion, call `removeItem()` with `SizeTransition + SlideTransition` (slide right, 300ms, easeInOut). Fire `completeTask()` simultaneously. When the stream re-emits, compare with local list and reconcile. See research Pattern 3 for exact code. BUT if this proves complex during implementation, fall back to the simpler "let stream handle it" approach.
2. Create `test/features/home/presentation/home_screen_test.dart`:
- Use provider override pattern (same as app_shell_test.dart):
- Override `dailyPlanProvider` with a `StreamProvider` returning test data
- Override `appDatabaseProvider` if needed
- Test cases:
a. Empty state: When no tasks exist, shows homeEmptyTitle text and action button
b. All clear state: When overdue=[], today=[], completedTodayCount > 0, shows "Alles erledigt!" text
c. Normal state: When tasks exist, shows progress card with correct counts
d. Overdue section: When overdue tasks exist, shows "Uberfaellig" header
e. Tomorrow section: Shows collapsed "Demnachst" header with count
3. Run `flutter test` to confirm all tests pass (existing + new).
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test test/features/home/presentation/home_screen_test.dart && flutter test
- HomeScreen fully replaced with daily plan: progress card at top, overdue section (conditional), today section, tomorrow section (collapsed ExpansionTile)
- Checkbox on overdue/today tasks triggers completion via taskActionsProvider, task animates out or disappears on stream re-emission
- Tomorrow tasks are read-only (no checkbox)
- Room name tags navigate to room task list via context.go
- "All clear" empty state shown when all today's tasks are done
- "No tasks" empty state shown when no tasks exist at all
- Widget tests cover empty state, all clear state, normal state with sections
- Full test suite passes
- CLEAN-01 verified: room cards already show cleanliness indicator (no new work needed)
- `flutter test test/features/home/` -- all home feature tests pass
- `flutter test` -- full suite passes (no regressions)
- `dart analyze` -- clean analysis
- HomeScreen shows daily plan with all three sections
- CLEAN-01 confirmed via existing room card cleanliness indicator
<success_criteria>
- HomeScreen replaced with complete daily plan (no more placeholder)
- Progress card shows "X von Y erledigt" with accurate counts
- Overdue tasks highlighted with warm coral section header, only shown when overdue tasks exist
- Today tasks shown in dedicated section with checkboxes
- Tomorrow tasks in collapsed ExpansionTile, read-only
- Checkbox completion triggers database update and task disappears
- "All clear" empty state displays when all tasks done
- Room name tags navigate to room task list
- No row-tap navigation on task rows (daily plan is focused action screen)
- CLEAN-01 verified on room cards </success_criteria>