From 03f531f896de315c8c828d1f6f4126a9d97d94bc Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 15 Mar 2026 22:18:33 +0100 Subject: [PATCH] feat(02-04): wire template selection flow into room creation - After createRoom, detect room type via detectRoomType on room name - Show TemplatePickerSheet if room type matches one of 14 known types - Create tasks from selected templates with correct frequency, effort, and anchor day - Navigate to new room after creation (with or without templates) - Edit mode unchanged: no template prompt on room updates - Custom rooms (no type match) skip template prompt entirely Co-Authored-By: Claude Opus 4.6 --- .../rooms/presentation/room_form_screen.dart | 81 +++++++++++++++++-- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/lib/features/rooms/presentation/room_form_screen.dart b/lib/features/rooms/presentation/room_form_screen.dart index 4ce3cb4..b3711d6 100644 --- a/lib/features/rooms/presentation/room_form_screen.dart +++ b/lib/features/rooms/presentation/room_form_screen.dart @@ -6,6 +6,10 @@ import 'package:household_keeper/core/providers/database_provider.dart'; import 'package:household_keeper/features/rooms/domain/room_icons.dart'; import 'package:household_keeper/features/rooms/presentation/icon_picker_sheet.dart'; import 'package:household_keeper/features/rooms/presentation/room_providers.dart'; +import 'package:household_keeper/features/tasks/domain/frequency.dart'; +import 'package:household_keeper/features/tasks/presentation/task_providers.dart'; +import 'package:household_keeper/features/templates/data/task_templates.dart'; +import 'package:household_keeper/features/templates/presentation/template_picker_sheet.dart'; import 'package:household_keeper/l10n/app_localizations.dart'; /// Full-screen form for creating and editing rooms. @@ -62,21 +66,40 @@ class _RoomFormScreenState extends ConsumerState { setState(() => _isSaving = true); try { - final actions = ref.read(roomActionsProvider.notifier); + final roomActions = ref.read(roomActionsProvider.notifier); + final name = _nameController.text.trim(); + if (widget.isEditing) { + // Edit mode: update and pop (no template prompt). final db = ref.read(appDatabaseProvider); final existing = await db.roomsDao.getRoomById(widget.roomId!); - await actions.updateRoom(existing.copyWith( - name: _nameController.text.trim(), + await roomActions.updateRoom(existing.copyWith( + name: name, iconName: _selectedIconName, )); + if (mounted) context.pop(); } else { - await actions.createRoom( - _nameController.text.trim(), - _selectedIconName, - ); + // Create mode: save room, then offer template selection if type matches. + final roomId = await roomActions.createRoom(name, _selectedIconName); + if (!mounted) return; + + final roomType = detectRoomType(name); + if (roomType != null) { + final templates = roomTemplates[roomType]; + if (templates != null && templates.isNotEmpty) { + final selected = await showTemplatePickerSheet( + context: context, + roomType: roomType, + templates: templates, + ); + if (selected != null && selected.isNotEmpty && mounted) { + await _createTasksFromTemplates(roomId, selected); + } + } + } + + if (mounted) context.go('/rooms/$roomId'); } - if (mounted) context.pop(); } catch (e) { if (mounted) { setState(() => _isSaving = false); @@ -87,6 +110,48 @@ class _RoomFormScreenState extends ConsumerState { } } + /// Creates tasks from the selected templates in the newly created room. + Future _createTasksFromTemplates( + int roomId, + List templates, + ) async { + final taskActions = ref.read(taskActionsProvider.notifier); + final today = DateTime.now(); + final todayDateOnly = DateTime(today.year, today.month, today.day); + + for (final template in templates) { + await taskActions.createTask( + roomId: roomId, + name: template.name, + description: template.description, + intervalType: template.intervalType, + intervalDays: template.intervalDays, + anchorDay: _anchorDayForType(template.intervalType, todayDateOnly), + effortLevel: template.effortLevel, + nextDueDate: todayDateOnly, + ); + } + } + + /// Returns the anchor day for calendar-anchored interval types. + /// + /// Monthly/quarterly/yearly intervals anchor to today's day-of-month. + /// Day-count based intervals don't need an anchor. + int? _anchorDayForType(IntervalType type, DateTime today) { + switch (type) { + case IntervalType.monthly: + case IntervalType.everyNMonths: + case IntervalType.quarterly: + case IntervalType.yearly: + return today.day; + case IntervalType.daily: + case IntervalType.everyNDays: + case IntervalType.weekly: + case IntervalType.biweekly: + return null; + } + } + @override void dispose() { _nameController.dispose();