diff --git a/lib/messages.i18n.dart b/lib/messages.i18n.dart index 0e63f35..0a35408 100644 --- a/lib/messages.i18n.dart +++ b/lib/messages.i18n.dart @@ -785,6 +785,17 @@ class ItemFormChecklistsMessages { /// ``` String get repeat => """Repeat"""; + /// ```dart + /// "Once" + /// ``` + String get once => """Once"""; + + /// ```dart + /// "Delete this item once it is marked as done." + /// ``` + String get onceDescription => + """Delete this item once it is marked as done."""; + /// ```dart /// "Failed to save item." /// ``` @@ -1421,6 +1432,9 @@ Please complete login in your browser.""", """checklists.itemForm.categoryCreateFailed""": """Failed to create category.""", """checklists.itemForm.repeat""": """Repeat""", + """checklists.itemForm.once""": """Once""", + """checklists.itemForm.onceDescription""": + """Delete this item once it is marked as done.""", """checklists.itemForm.saveFailed""": """Failed to save item.""", """checklists.itemForm.deleteFailed""": """Failed to delete item.""", """checklists.itemForm.deleteConfirm""": """Delete this item?""", diff --git a/lib/messages.i18n.yaml b/lib/messages.i18n.yaml index 29ce561..c41bf8e 100644 --- a/lib/messages.i18n.yaml +++ b/lib/messages.i18n.yaml @@ -145,6 +145,8 @@ checklists: categoryCreated: Category created. categoryCreateFailed: Failed to create category. repeat: Repeat + once: Once + onceDescription: Delete this item once it is marked as done. saveFailed: Failed to save item. deleteFailed: Failed to delete item. deleteConfirm: Delete this item? diff --git a/lib/messages_de.i18n.dart b/lib/messages_de.i18n.dart index 4682e37..0498563 100644 --- a/lib/messages_de.i18n.dart +++ b/lib/messages_de.i18n.dart @@ -793,6 +793,17 @@ class ItemFormChecklistsMessagesDe extends ItemFormChecklistsMessages { /// ``` String get repeat => """Wiederholen"""; + /// ```dart + /// "Einmalig" + /// ``` + String get once => """Einmalig"""; + + /// ```dart + /// "Diesen Eintrag löschen, sobald er als erledigt markiert ist." + /// ``` + String get onceDescription => + """Diesen Eintrag löschen, sobald er als erledigt markiert ist."""; + /// ```dart /// "Eintrag konnte nicht gespeichert werden." /// ``` @@ -1437,6 +1448,9 @@ Bitte melde dich in deinem Browser an.""", """checklists.itemForm.categoryCreateFailed""": """Kategorie konnte nicht erstellt werden.""", """checklists.itemForm.repeat""": """Wiederholen""", + """checklists.itemForm.once""": """Einmalig""", + """checklists.itemForm.onceDescription""": + """Diesen Eintrag löschen, sobald er als erledigt markiert ist.""", """checklists.itemForm.saveFailed""": """Eintrag konnte nicht gespeichert werden.""", """checklists.itemForm.deleteFailed""": diff --git a/lib/messages_de.i18n.yaml b/lib/messages_de.i18n.yaml index c432941..8fa82c9 100644 --- a/lib/messages_de.i18n.yaml +++ b/lib/messages_de.i18n.yaml @@ -145,6 +145,8 @@ checklists: categoryCreated: Kategorie erstellt. categoryCreateFailed: Kategorie konnte nicht erstellt werden. repeat: Wiederholen + once: Einmalig + onceDescription: Diesen Eintrag löschen, sobald er als erledigt markiert ist. saveFailed: Eintrag konnte nicht gespeichert werden. deleteFailed: "Eintrag konnte nicht gelöscht werden." deleteConfirm: "Diesen Eintrag löschen?" diff --git a/lib/messages_es.i18n.dart b/lib/messages_es.i18n.dart index 715b468..d6633b4 100644 --- a/lib/messages_es.i18n.dart +++ b/lib/messages_es.i18n.dart @@ -791,6 +791,17 @@ class ItemFormChecklistsMessagesEs extends ItemFormChecklistsMessages { /// ``` String get repeat => """Repetir"""; + /// ```dart + /// "Una vez" + /// ``` + String get once => """Una vez"""; + + /// ```dart + /// "Eliminar este artículo cuando se marque como hecho." + /// ``` + String get onceDescription => + """Eliminar este artículo cuando se marque como hecho."""; + /// ```dart /// "No se pudo guardar el artículo." /// ``` @@ -1431,6 +1442,9 @@ Por favor, completa el inicio de sesión en tu navegador.""", """checklists.itemForm.categoryCreateFailed""": """No se pudo crear la categoría.""", """checklists.itemForm.repeat""": """Repetir""", + """checklists.itemForm.once""": """Una vez""", + """checklists.itemForm.onceDescription""": + """Eliminar este artículo cuando se marque como hecho.""", """checklists.itemForm.saveFailed""": """No se pudo guardar el artículo.""", """checklists.itemForm.deleteFailed""": """No se pudo eliminar el artículo.""", diff --git a/lib/messages_es.i18n.yaml b/lib/messages_es.i18n.yaml index c33d997..c12726d 100644 --- a/lib/messages_es.i18n.yaml +++ b/lib/messages_es.i18n.yaml @@ -145,6 +145,8 @@ checklists: categoryCreated: "Categoría creada." categoryCreateFailed: "No se pudo crear la categoría." repeat: Repetir + once: "Una vez" + onceDescription: "Eliminar este artículo cuando se marque como hecho." saveFailed: "No se pudo guardar el artículo." deleteFailed: "No se pudo eliminar el artículo." deleteConfirm: "¿Eliminar este artículo?" diff --git a/lib/messages_fr.i18n.dart b/lib/messages_fr.i18n.dart index c2d8e35..ce73fc6 100644 --- a/lib/messages_fr.i18n.dart +++ b/lib/messages_fr.i18n.dart @@ -791,6 +791,17 @@ class ItemFormChecklistsMessagesFr extends ItemFormChecklistsMessages { /// ``` String get repeat => """Répéter"""; + /// ```dart + /// "Une fois" + /// ``` + String get once => """Une fois"""; + + /// ```dart + /// "Supprimer cet article une fois qu'il est marqué comme fait." + /// ``` + String get onceDescription => + """Supprimer cet article une fois qu'il est marqué comme fait."""; + /// ```dart /// "Impossible d'enregistrer l'article." /// ``` @@ -1432,6 +1443,9 @@ Veuillez terminer la connexion dans votre navigateur.""", """checklists.itemForm.categoryCreateFailed""": """Impossible de créer la catégorie.""", """checklists.itemForm.repeat""": """Répéter""", + """checklists.itemForm.once""": """Une fois""", + """checklists.itemForm.onceDescription""": + """Supprimer cet article une fois qu'il est marqué comme fait.""", """checklists.itemForm.saveFailed""": """Impossible d'enregistrer l'article.""", """checklists.itemForm.deleteFailed""": diff --git a/lib/messages_fr.i18n.yaml b/lib/messages_fr.i18n.yaml index 40f797d..aa022c4 100644 --- a/lib/messages_fr.i18n.yaml +++ b/lib/messages_fr.i18n.yaml @@ -145,6 +145,8 @@ checklists: categoryCreated: "Catégorie créée." categoryCreateFailed: "Impossible de créer la catégorie." repeat: "Répéter" + once: "Une fois" + onceDescription: "Supprimer cet article une fois qu'il est marqué comme fait." saveFailed: "Impossible d'enregistrer l'article." deleteFailed: Impossible de supprimer l'article. deleteConfirm: Supprimer cet article ? diff --git a/lib/messages_he.i18n.dart b/lib/messages_he.i18n.dart index 8751272..8c88757 100644 --- a/lib/messages_he.i18n.dart +++ b/lib/messages_he.i18n.dart @@ -788,6 +788,16 @@ class ItemFormChecklistsMessagesHe extends ItemFormChecklistsMessages { /// ``` String get repeat => """חזרה"""; + /// ```dart + /// "פעם אחת" + /// ``` + String get once => """פעם אחת"""; + + /// ```dart + /// "מחק את הפריט ברגע שהוא מסומן כבוצע." + /// ``` + String get onceDescription => """מחק את הפריט ברגע שהוא מסומן כבוצע."""; + /// ```dart /// "שמירת הפריט נכשלה." /// ``` @@ -1421,6 +1431,9 @@ Map get messagesHeMap => { """checklists.itemForm.categoryCreated""": """הקטגוריה נוצרה.""", """checklists.itemForm.categoryCreateFailed""": """יצירת הקטגוריה נכשלה.""", """checklists.itemForm.repeat""": """חזרה""", + """checklists.itemForm.once""": """פעם אחת""", + """checklists.itemForm.onceDescription""": + """מחק את הפריט ברגע שהוא מסומן כבוצע.""", """checklists.itemForm.saveFailed""": """שמירת הפריט נכשלה.""", """checklists.itemForm.deleteFailed""": """מחיקת הפריט נכשלה.""", """checklists.itemForm.deleteConfirm""": """למחוק את הפריט?""", diff --git a/lib/messages_he.i18n.yaml b/lib/messages_he.i18n.yaml index e9f242b..8011d7b 100644 --- a/lib/messages_he.i18n.yaml +++ b/lib/messages_he.i18n.yaml @@ -145,6 +145,8 @@ checklists: categoryCreated: הקטגוריה נוצרה. categoryCreateFailed: יצירת הקטגוריה נכשלה. repeat: חזרה + once: פעם אחת + onceDescription: מחק את הפריט ברגע שהוא מסומן כבוצע. saveFailed: שמירת הפריט נכשלה. deleteFailed: מחיקת הפריט נכשלה. deleteConfirm: למחוק את הפריט? diff --git a/lib/models/checklist.dart b/lib/models/checklist.dart index d1c51f5..7ac75fd 100644 --- a/lib/models/checklist.dart +++ b/lib/models/checklist.dart @@ -54,6 +54,7 @@ class ListItem { final String? doneBy; final String? rrule; final bool repeatFromCompletion; + final bool deleteOnDone; final int? nextDueAt; final int? imageFileId; final String? imageUploadedBy; @@ -73,6 +74,7 @@ class ListItem { this.doneBy, this.rrule, required this.repeatFromCompletion, + required this.deleteOnDone, this.nextDueAt, this.imageFileId, this.imageUploadedBy, @@ -93,6 +95,7 @@ class ListItem { doneBy: json['doneBy'] as String?, rrule: json['rrule'] as String?, repeatFromCompletion: json['repeatFromCompletion'] as bool, + deleteOnDone: json['deleteOnDone'] as bool? ?? false, nextDueAt: json['nextDueAt'] as int?, imageFileId: json['imageFileId'] as int?, imageUploadedBy: json['imageUploadedBy'] as String?, @@ -113,6 +116,7 @@ class ListItem { 'doneBy': doneBy, 'rrule': rrule, 'repeatFromCompletion': repeatFromCompletion, + 'deleteOnDone': deleteOnDone, 'nextDueAt': nextDueAt, 'imageFileId': imageFileId, 'imageUploadedBy': imageUploadedBy, @@ -133,6 +137,7 @@ class ListItem { doneBy: doneBy ?? this.doneBy, rrule: rrule, repeatFromCompletion: repeatFromCompletion, + deleteOnDone: deleteOnDone, nextDueAt: nextDueAt, imageFileId: imageFileId, imageUploadedBy: imageUploadedBy, diff --git a/lib/services/checklist_service.dart b/lib/services/checklist_service.dart index 11df7ac..37bfb4d 100644 --- a/lib/services/checklist_service.dart +++ b/lib/services/checklist_service.dart @@ -134,6 +134,7 @@ class ChecklistService { String? quantity, int? categoryId, String? rrule, + bool? deleteOnDone, }) async { return ApiClient.instance.post, ListItem>( '/houses/$houseId/lists/$listId/items', @@ -144,6 +145,7 @@ class ChecklistService { if (quantity != null && quantity.isNotEmpty) 'quantity': quantity, if (categoryId != null) 'categoryId': categoryId, if (rrule != null && rrule.isNotEmpty) 'rrule': rrule, + if (deleteOnDone != null) 'deleteOnDone': deleteOnDone, }, fromJson: (data) => ListItem.fromJson(data), ); @@ -160,6 +162,7 @@ class ChecklistService { bool clearCategory = false, String? rrule, bool? repeatFromCompletion, + bool? deleteOnDone, }) async { return ApiClient.instance.patch, ListItem>( '/houses/$houseId/lists/$listId/items/$itemId', @@ -172,6 +175,7 @@ class ChecklistService { if (rrule != null) 'rrule': rrule, if (repeatFromCompletion != null) 'repeatFromCompletion': repeatFromCompletion, + if (deleteOnDone != null) 'deleteOnDone': deleteOnDone, }, fromJson: (data) => ListItem.fromJson(data), ); diff --git a/lib/views/checklists/checklists_controller.dart b/lib/views/checklists/checklists_controller.dart index dd9b80a..26ea613 100644 --- a/lib/views/checklists/checklists_controller.dart +++ b/lib/views/checklists/checklists_controller.dart @@ -278,6 +278,7 @@ class ChecklistsController extends ChangeNotifier { String? quantity, int? categoryId, String? rrule, + bool? deleteOnDone, }) async { final item = await _checklistService.createItem( houseId, @@ -287,6 +288,7 @@ class ChecklistsController extends ChangeNotifier { quantity: quantity, categoryId: categoryId, rrule: rrule, + deleteOnDone: deleteOnDone, ); _items.insert(0, item); _checklistService.cacheItems(_currentList!.id, List.of(_items)); @@ -303,6 +305,7 @@ class ChecklistsController extends ChangeNotifier { bool clearCategory = false, String? rrule, bool? repeatFromCompletion, + bool? deleteOnDone, }) async { final updated = await _checklistService.updateItem( houseId, @@ -315,6 +318,7 @@ class ChecklistsController extends ChangeNotifier { clearCategory: clearCategory, rrule: rrule, repeatFromCompletion: repeatFromCompletion, + deleteOnDone: deleteOnDone, ); final index = _items.indexWhere((i) => i.id == item.id); if (index != -1) { diff --git a/lib/views/checklists/item_form_view.dart b/lib/views/checklists/item_form_view.dart index 9274935..2dfab37 100644 --- a/lib/views/checklists/item_form_view.dart +++ b/lib/views/checklists/item_form_view.dart @@ -27,6 +27,7 @@ class _ItemFormViewState extends State { int? _selectedCategoryId; String? _rrule; bool _repeatFromCompletion = false; + bool _deleteOnDone = false; bool _saving = false; TextDirection _nameDir = TextDirection.ltr; TextDirection _descriptionDir = TextDirection.ltr; @@ -49,6 +50,7 @@ class _ItemFormViewState extends State { _selectedCategoryId = item?.categoryId; _rrule = item?.rrule; _repeatFromCompletion = item?.repeatFromCompletion ?? false; + _deleteOnDone = item?.deleteOnDone ?? false; _nameDir = detectTextDirection(item?.name); _nameController.addListener(() { final dir = detectTextDirection(_nameController.text); @@ -75,6 +77,10 @@ class _ItemFormViewState extends State { setState(() => _saving = true); try { + final effectiveRrule = _deleteOnDone ? '' : (_rrule ?? ''); + final effectiveRepeatFromCompletion = _deleteOnDone + ? false + : _repeatFromCompletion; if (_isEditing) { final item = widget.item!; await widget.controller.updateItem( @@ -84,8 +90,9 @@ class _ItemFormViewState extends State { quantity: _quantityController.text.trim(), categoryId: _selectedCategoryId, clearCategory: _selectedCategoryId == null && item.categoryId != null, - rrule: _rrule ?? '', - repeatFromCompletion: _repeatFromCompletion, + rrule: effectiveRrule, + repeatFromCompletion: effectiveRepeatFromCompletion, + deleteOnDone: _deleteOnDone, ); } else { await widget.controller.addItem( @@ -93,7 +100,8 @@ class _ItemFormViewState extends State { description: _descriptionController.text.trim(), quantity: _quantityController.text.trim(), categoryId: _selectedCategoryId, - rrule: _rrule, + rrule: _deleteOnDone ? null : _rrule, + deleteOnDone: _deleteOnDone, ); } if (mounted) Navigator.of(context).pop(true); @@ -173,23 +181,34 @@ class _ItemFormViewState extends State { setState(() => _selectedCategoryId = cat.id); }, ), - const SizedBox(height: 16), - RepeatButton( - rrule: _rrule, - onTap: () async { - final result = await showRecurrenceDialog( - context, - initialRrule: _rrule, - initialRepeatFromCompletion: _repeatFromCompletion, - ); - if (result != null) { - setState(() { - _rrule = result.rrule; - _repeatFromCompletion = result.repeatFromCompletion; - }); - } - }, + const SizedBox(height: 8), + CheckboxListTile( + value: _deleteOnDone, + onChanged: (v) => setState(() => _deleteOnDone = v ?? false), + title: Text(f.once), + subtitle: Text(f.onceDescription), + controlAffinity: ListTileControlAffinity.leading, + contentPadding: EdgeInsetsDirectional.zero, ), + if (!_deleteOnDone) ...[ + const SizedBox(height: 8), + RepeatButton( + rrule: _rrule, + onTap: () async { + final result = await showRecurrenceDialog( + context, + initialRrule: _rrule, + initialRepeatFromCompletion: _repeatFromCompletion, + ); + if (result != null) { + setState(() { + _rrule = result.rrule; + _repeatFromCompletion = result.repeatFromCompletion; + }); + } + }, + ), + ], ], ), ); diff --git a/test/helpers/test_models.dart b/test/helpers/test_models.dart index ae15dde..874a0d9 100644 --- a/test/helpers/test_models.dart +++ b/test/helpers/test_models.dart @@ -161,6 +161,7 @@ ListItem makeListItem({ String? doneBy, String? rrule, bool repeatFromCompletion = false, + bool deleteOnDone = false, int? nextDueAt, int? imageFileId, String? imageUploadedBy, @@ -179,6 +180,7 @@ ListItem makeListItem({ doneBy: doneBy, rrule: rrule, repeatFromCompletion: repeatFromCompletion, + deleteOnDone: deleteOnDone, nextDueAt: nextDueAt, imageFileId: imageFileId, imageUploadedBy: imageUploadedBy,