9.8 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 08-task-delete | 02 | execute | 2 |
|
|
true |
|
|
Purpose: Users can remove tasks they no longer need. The smart behavior (hard vs soft delete) is invisible to the user -- they just see "delete" with a confirmation.
Output: Working delete flow on the task edit form: red button -> confirmation dialog -> smart delete -> navigate back.
<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/08-task-delete/08-CONTEXT.md @.planning/phases/08-task-delete/08-01-SUMMARY.mdFrom lib/features/tasks/presentation/task_providers.dart (existing TaskActions):
@riverpod
class TaskActions extends _$TaskActions {
@override
FutureOr<void> build() {}
Future<int> createTask({...}) async { ... }
Future<void> updateTask(Task task) async { ... }
Future<void> deleteTask(int taskId) async { ... } // calls DAO hard delete
Future<void> completeTask(int taskId) async { ... }
}
From lib/features/tasks/data/tasks_dao.dart (after Plan 01):
class TasksDao {
Future<void> deleteTask(int taskId); // hard delete (cascade)
Future<void> softDeleteTask(int taskId); // sets isActive = false
Future<int> getCompletionCount(int taskId); // count completions
}
From lib/features/tasks/presentation/task_form_screen.dart (edit mode section):
// History section (edit mode only) — delete button goes AFTER this
if (widget.isEditing) ...[
const SizedBox(height: 24),
const Divider(),
ListTile(
leading: const Icon(Icons.history),
title: Text(l10n.taskHistoryTitle),
trailing: const Icon(Icons.chevron_right),
onTap: () => showTaskHistorySheet(
context: context,
taskId: widget.taskId!,
),
),
],
From lib/l10n/app_de.arb (existing delete l10n strings):
"taskDeleteConfirmTitle": "Aufgabe l\u00f6schen?",
"taskDeleteConfirmMessage": "Die Aufgabe wird unwiderruflich gel\u00f6scht.",
"taskDeleteConfirmAction": "L\u00f6schen"
Room delete dialog pattern (from lib/features/rooms/presentation/rooms_screen.dart:165-189):
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(l10n.roomDeleteConfirmTitle),
content: Text(l10n.roomDeleteConfirmMessage),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: Text(l10n.cancel),
),
FilledButton(
style: FilledButton.styleFrom(
backgroundColor: Theme.of(ctx).colorScheme.error,
),
onPressed: () { ... },
child: Text(l10n.roomDeleteConfirmAction),
),
],
),
);
/// Smart delete: hard-deletes tasks with no completions, soft-deletes tasks with completions.
Future<void> smartDeleteTask(int taskId) async {
final db = ref.read(appDatabaseProvider);
final completionCount = await db.tasksDao.getCompletionCount(taskId);
if (completionCount == 0) {
await db.tasksDao.deleteTask(taskId);
} else {
await db.tasksDao.softDeleteTask(taskId);
}
}
Keep the existing deleteTask method unchanged (it is still a valid hard delete for other uses like room cascade delete).
Run dart run build_runner build --delete-conflicting-outputs to regenerate the provider code.
cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && dart analyze --fatal-infos lib/features/tasks/presentation/task_providers.dart
- smartDeleteTask method exists on TaskActions
- Method checks completion count and routes to hard or soft delete
- dart analyze passes with zero issues
if (widget.isEditing) ...[
const SizedBox(height: 24),
const Divider(),
// History ListTile (existing)
ListTile(
leading: const Icon(Icons.history),
title: Text(l10n.taskHistoryTitle),
trailing: const Icon(Icons.chevron_right),
onTap: () => showTaskHistorySheet(
context: context,
taskId: widget.taskId!,
),
),
// DELETE BUTTON — new
const Divider(),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: FilledButton.icon(
style: FilledButton.styleFrom(
backgroundColor: theme.colorScheme.error,
foregroundColor: theme.colorScheme.onError,
),
onPressed: _isLoading ? null : _onDelete,
icon: const Icon(Icons.delete_outline),
label: Text(l10n.taskDeleteConfirmAction),
),
),
],
- Add a
_onDeletemethod to _TaskFormScreenState:
Future<void> _onDelete() async {
final l10n = AppLocalizations.of(context);
final confirmed = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: Text(l10n.taskDeleteConfirmTitle),
content: Text(l10n.taskDeleteConfirmMessage),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: Text(l10n.cancel),
),
FilledButton(
style: FilledButton.styleFrom(
backgroundColor: Theme.of(ctx).colorScheme.error,
),
onPressed: () => Navigator.pop(ctx, true),
child: Text(l10n.taskDeleteConfirmAction),
),
],
),
);
if (confirmed != true || !mounted) return;
setState(() => _isLoading = true);
try {
await ref.read(taskActionsProvider.notifier).smartDeleteTask(widget.taskId!);
if (mounted) {
context.pop();
}
} finally {
if (mounted) {
setState(() => _isLoading = false);
}
}
}
Note: The l10n.cancel string should already exist from the room delete dialog. If not, use MaterialLocalizations.of(context).cancelButtonLabel.
- Verify
cancell10n key exists. If it does not exist in app_de.arb, check for the existing cancel button pattern in rooms_screen.dart and use the same approach. cd /home/jlmak/Projects/jlmak/HouseHoldKeaper && flutter test --reporter compact && dart analyze --fatal-infos- Red delete button visible at bottom of task edit form (below history, separated by divider)
- Delete button only shows in edit mode (not create mode)
- Tapping delete shows AlertDialog with title "Aufgabe loschen?" and error-colored confirm button
- Canceling dialog does nothing
- Confirming dialog calls smartDeleteTask and pops back to room task list
- Button is disabled while loading (_isLoading)
- All existing tests pass, dart analyze clean
<success_criteria>
- Complete delete flow works: open task -> scroll to bottom -> tap delete -> confirm -> back to room task list
- Smart delete is invisible to user: tasks with completions are deactivated, tasks without are removed
- Delete button follows Material 3 error color pattern
- Confirmation dialog uses existing German l10n strings
- All 137+ tests pass </success_criteria>