253 lines
11 KiB
Markdown
253 lines
11 KiB
Markdown
---
|
|
phase: 11-issue-3-tasks-management-allow-task-checking-anytime-and-pre-populate-recurring-tasks
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- lib/features/home/presentation/calendar_day_list.dart
|
|
- lib/features/home/presentation/calendar_task_row.dart
|
|
- lib/features/tasks/presentation/task_row.dart
|
|
- lib/features/tasks/data/tasks_dao.dart
|
|
- test/features/tasks/data/tasks_dao_test.dart
|
|
autonomous: true
|
|
requirements:
|
|
- TM-01
|
|
- TM-02
|
|
|
|
must_haves:
|
|
truths:
|
|
- "User can check off a task on any calendar day including future days"
|
|
- "When completing a task on a non-due day, nextDueDate recalculates from today not from the original due date"
|
|
- "Overdue tasks remain completable (no regression)"
|
|
artifacts:
|
|
- path: "lib/features/home/presentation/calendar_day_list.dart"
|
|
provides: "Checkbox always enabled for all day tasks"
|
|
contains: "canComplete: true"
|
|
- path: "lib/features/home/presentation/calendar_task_row.dart"
|
|
provides: "CalendarTaskRow with always-enabled checkbox"
|
|
contains: "canComplete"
|
|
- path: "lib/features/tasks/presentation/task_row.dart"
|
|
provides: "TaskRow with always-enabled checkbox"
|
|
- path: "lib/features/tasks/data/tasks_dao.dart"
|
|
provides: "completeTask with today-based recalculation"
|
|
contains: "calculateNextDueDate"
|
|
key_links:
|
|
- from: "lib/features/home/presentation/calendar_day_list.dart"
|
|
to: "CalendarTaskRow"
|
|
via: "canComplete: true always passed"
|
|
pattern: "canComplete: true"
|
|
- from: "lib/features/tasks/data/tasks_dao.dart"
|
|
to: "scheduling.dart"
|
|
via: "calculateNextDueDate uses today as base when completing on non-due day"
|
|
pattern: "calculateNextDueDate"
|
|
---
|
|
|
|
<objective>
|
|
Enable anytime task completion: remove all checkbox-disable restrictions so users can mark tasks done on any calendar day (past, today, or future). When completing a task on a non-due day, recalculate nextDueDate from today (per D-02).
|
|
|
|
Purpose: Users should never be blocked from marking a task as done. The current behavior of disabling checkboxes for future tasks creates friction and confusion.
|
|
Output: All checkboxes always enabled. completeTask() uses today as base for nextDueDate calculation when task is completed on a non-due day.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/11-issue-3-tasks-management-allow-task-checking-anytime-and-pre-populate-recurring-tasks/11-CONTEXT.md
|
|
|
|
<interfaces>
|
|
<!-- Key types and contracts the executor needs -->
|
|
|
|
From lib/features/home/presentation/calendar_task_row.dart:
|
|
```dart
|
|
class CalendarTaskRow extends StatelessWidget {
|
|
const CalendarTaskRow({
|
|
super.key,
|
|
required this.taskWithRoom,
|
|
required this.onCompleted,
|
|
this.isOverdue = false,
|
|
this.showRoomTag = true,
|
|
this.canComplete = true, // Currently defaults to true but overridden with !isFuture
|
|
});
|
|
final bool canComplete; // When false, checkbox is disabled
|
|
}
|
|
```
|
|
|
|
From lib/features/tasks/data/tasks_dao.dart:
|
|
```dart
|
|
Future<void> completeTask(int taskId, {DateTime? now}) {
|
|
// Step 3: calculates next due from task.nextDueDate (original due date)
|
|
var nextDue = calculateNextDueDate(
|
|
currentDueDate: task.nextDueDate, // <-- This is the line to change for non-due-day
|
|
...
|
|
);
|
|
}
|
|
```
|
|
|
|
From lib/features/tasks/domain/scheduling.dart:
|
|
```dart
|
|
DateTime calculateNextDueDate({
|
|
required DateTime currentDueDate,
|
|
required IntervalType intervalType,
|
|
required int intervalDays,
|
|
int? anchorDay,
|
|
});
|
|
|
|
DateTime catchUpToPresent({
|
|
required DateTime nextDue,
|
|
required DateTime today,
|
|
required IntervalType intervalType,
|
|
required int intervalDays,
|
|
int? anchorDay,
|
|
});
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Remove checkbox-disable restrictions in all three UI files</name>
|
|
<files>
|
|
lib/features/home/presentation/calendar_day_list.dart
|
|
lib/features/home/presentation/calendar_task_row.dart
|
|
lib/features/tasks/presentation/task_row.dart
|
|
</files>
|
|
<read_first>
|
|
lib/features/home/presentation/calendar_day_list.dart
|
|
lib/features/home/presentation/calendar_task_row.dart
|
|
lib/features/tasks/presentation/task_row.dart
|
|
</read_first>
|
|
<action>
|
|
**Per D-01: Remove isFuture / canComplete restrictions.**
|
|
|
|
1. **calendar_day_list.dart** (line ~271): In the `_buildTaskList` method, change the day tasks loop to always pass `canComplete: true`:
|
|
- Remove the line `final isFuture = state.selectedDate.isAfter(today);` (line ~245)
|
|
- Change `canComplete: !isFuture` (line ~271) to `canComplete: true`
|
|
- The `isFuture` variable can be removed entirely since it is only used for `canComplete`
|
|
- Keep the `today` variable — it is still used for `isToday` check in _buildContent
|
|
|
|
2. **calendar_task_row.dart**: No changes needed. The `canComplete` parameter already defaults to `true` and the widget itself has no internal disable logic. The restriction was applied by the caller (calendar_day_list.dart).
|
|
|
|
3. **task_row.dart** (lines ~45, ~60-62): Remove the `isFuture` check that disables the checkbox:
|
|
- Remove line `final isFuture = dueDate.isAfter(today);` (line ~45)
|
|
- Change the `onChanged` from the ternary `isFuture ? null : (_) { ... }` to always-enabled:
|
|
```dart
|
|
onChanged: (_) {
|
|
ref.read(taskActionsProvider.notifier).completeTask(task.id);
|
|
},
|
|
```
|
|
- The `dueDate` and `isOverdue` variables remain — they are used for styling the relative date text color
|
|
</action>
|
|
<verify>
|
|
<automated>grep -n "isFuture" lib/features/home/presentation/calendar_day_list.dart lib/features/tasks/presentation/task_row.dart; echo "---"; grep -n "canComplete: true" lib/features/home/presentation/calendar_day_list.dart</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- calendar_day_list.dart does NOT contain the string `isFuture`
|
|
- calendar_day_list.dart contains `canComplete: true` in the _buildAnimatedTaskRow call for dayTasks
|
|
- task_row.dart does NOT contain the string `isFuture`
|
|
- task_row.dart does NOT contain `? null` in the Checkbox onChanged handler
|
|
- calendar_task_row.dart is unchanged (canComplete param still exists with default true)
|
|
</acceptance_criteria>
|
|
<done>All checkboxes are always enabled across calendar and task list views. No isFuture guard remains in UI code.</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Update completeTask to recalculate nextDueDate from today on non-due-day completion</name>
|
|
<files>
|
|
lib/features/tasks/data/tasks_dao.dart
|
|
test/features/tasks/data/tasks_dao_test.dart
|
|
</files>
|
|
<read_first>
|
|
lib/features/tasks/data/tasks_dao.dart
|
|
lib/features/tasks/domain/scheduling.dart
|
|
test/features/tasks/data/tasks_dao_test.dart
|
|
</read_first>
|
|
<behavior>
|
|
- Test: Completing a task ON its due date recalculates nextDueDate from the original due date (existing behavior preserved)
|
|
- Test: Completing a weekly task 3 days BEFORE its due date recalculates nextDueDate from today (not original due date) — e.g., task due Friday, completed Tuesday, next due = next Tuesday
|
|
- Test: Completing a daily task on a non-due day still produces tomorrow as next due
|
|
- Test: Completing a monthly task early recalculates from today with anchor day preserved
|
|
</behavior>
|
|
<action>
|
|
**Per D-02 and D-03: When completing a task on a non-due day, recalculate nextDueDate from today.**
|
|
|
|
In `lib/features/tasks/data/tasks_dao.dart`, method `completeTask()`:
|
|
|
|
Change step 3 from:
|
|
```dart
|
|
// 3. Calculate next due date (from original due date, not today)
|
|
var nextDue = calculateNextDueDate(
|
|
currentDueDate: task.nextDueDate,
|
|
intervalType: task.intervalType,
|
|
intervalDays: task.intervalDays,
|
|
anchorDay: task.anchorDay,
|
|
);
|
|
```
|
|
|
|
To:
|
|
```dart
|
|
// 3. Calculate next due date
|
|
// If completing on the due date, use original due date as base (keeps rhythm).
|
|
// If completing on a different day (early or late), use today as base (per D-02).
|
|
final todayStart = DateTime(currentTime.year, currentTime.month, currentTime.day);
|
|
final taskDueDay = DateTime(task.nextDueDate.year, task.nextDueDate.month, task.nextDueDate.day);
|
|
final baseDate = todayStart == taskDueDay ? task.nextDueDate : todayStart;
|
|
|
|
var nextDue = calculateNextDueDate(
|
|
currentDueDate: baseDate,
|
|
intervalType: task.intervalType,
|
|
intervalDays: task.intervalDays,
|
|
anchorDay: task.anchorDay,
|
|
);
|
|
```
|
|
|
|
Note: The existing `todayDateOnly` variable (line 66-70) can be replaced by `todayStart` since they are the same. Rename to avoid duplication. The catch-up step (step 4) remains unchanged — it still ensures nextDue is not in the past.
|
|
|
|
Write 4 new test cases in `test/features/tasks/data/tasks_dao_test.dart`:
|
|
1. `completeTask on due date preserves rhythm` — weekly task due 2026-03-24, complete on 2026-03-24, next due = 2026-03-31
|
|
2. `completeTask before due date recalculates from today` — weekly task due 2026-03-28, complete on 2026-03-24, next due = 2026-03-31 (7 days from today)
|
|
3. `completeTask daily task on non-due day` — daily task due 2026-03-26, complete on 2026-03-24, next due = 2026-03-25 (tomorrow)
|
|
4. `completeTask monthly task early preserves anchor` — monthly task due 2026-03-28 anchorDay=28, complete on 2026-03-24, next due = 2026-04-28
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/jean-luc-makiola/Development/projects/HouseHoldKeaper && flutter test test/features/tasks/data/tasks_dao_test.dart --reporter compact</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- tasks_dao.dart completeTask() contains `final baseDate = todayStart == taskDueDay ? task.nextDueDate : todayStart;`
|
|
- tasks_dao.dart completeTask() passes `currentDueDate: baseDate` to calculateNextDueDate
|
|
- tasks_dao_test.dart contains test with name matching `completeTask.*before due date.*recalculates from today`
|
|
- tasks_dao_test.dart contains test with name matching `completeTask.*on due date.*preserves rhythm`
|
|
- All tests in tasks_dao_test.dart pass (exit code 0)
|
|
</acceptance_criteria>
|
|
<done>completeTask() uses today as base for non-due-day completions. 4 new tests verify the behavior for on-due-day, before-due-day, daily, and monthly scenarios. All existing tests still pass.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
1. `flutter test` — all existing + new tests pass
|
|
2. `dart analyze` — zero issues
|
|
3. `grep -rn "isFuture" lib/features/home/presentation/calendar_day_list.dart lib/features/tasks/presentation/task_row.dart` — no matches
|
|
4. `grep -n "canComplete: true" lib/features/home/presentation/calendar_day_list.dart` — found in dayTasks loop
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Checkboxes are always enabled on all calendar days (past, today, future)
|
|
- Checkboxes are always enabled in room task list view
|
|
- Completing a task on its due date preserves the original interval rhythm
|
|
- Completing a task on a non-due day recalculates from today
|
|
- All existing tests pass with zero regressions
|
|
- dart analyze reports zero issues
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/11-issue-3-tasks-management-allow-task-checking-anytime-and-pre-populate-recurring-tasks/11-01-SUMMARY.md`
|
|
</output>
|