From 63e5caae24d1e857fe0cc8cec95a47f05711e37d Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Wed, 6 Aug 2025 11:46:12 +0530 Subject: [PATCH] handelled the update expense --- .../expense/add_expense_controller.dart | 33 +- .../expense/add_expense_bottom_sheet.dart | 287 +++++++++--------- 2 files changed, 171 insertions(+), 149 deletions(-) diff --git a/lib/controller/expense/add_expense_controller.dart b/lib/controller/expense/add_expense_controller.dart index c715dcf..a127157 100644 --- a/lib/controller/expense/add_expense_controller.dart +++ b/lib/controller/expense/add_expense_controller.dart @@ -170,8 +170,14 @@ class AddExpenseController extends GetxController { // --- Existing Attachments --- existingAttachments.clear(); if (data['attachments'] != null && data['attachments'] is List) { - existingAttachments - .addAll(List>.from(data['attachments'])); + existingAttachments.addAll( + List>.from(data['attachments']).map((e) { + return { + ...e, + 'isActive': true, // default + }; + }), + ); } _logPrefilledData(); @@ -369,15 +375,20 @@ class AddExpenseController extends GetxController { final projectId = projectsMap[selectedProject.value]!; final selectedDate = selectedTransactionDate.value?.toUtc() ?? DateTime.now().toUtc(); - final existingAttachmentPayloads = existingAttachments - .map((e) => { - "fileName": e['fileName'], - "contentType": e['contentType'], - "fileSize": 0, // optional or populate if known - "description": "", - "url": e['url'], // custom field if your backend accepts - }) - .toList(); + final existingAttachmentPayloads = existingAttachments.map((e) { + final isActive = e['isActive'] ?? true; + + return { + "documentId": e['documentId'], + "fileName": e['fileName'], + "contentType": e['contentType'], + "fileSize": 0, + "description": "", + "url": e['url'], + "isActive": isActive, + "base64Data": isActive ? e['base64Data'] : null, + }; + }).toList(); final newAttachmentPayloads = await Future.wait(attachments.map((file) async { diff --git a/lib/model/expense/add_expense_bottom_sheet.dart b/lib/model/expense/add_expense_bottom_sheet.dart index ca93abe..4e6dfec 100644 --- a/lib/model/expense/add_expense_bottom_sheet.dart +++ b/lib/model/expense/add_expense_bottom_sheet.dart @@ -41,22 +41,20 @@ class _AddExpenseBottomSheetState extends State<_AddExpenseBottomSheet> { final AddExpenseController controller = Get.put(AddExpenseController()); void _showEmployeeList() async { - await showModalBottomSheet( - context: context, - isScrollControlled: true, - - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(16)), - ), - backgroundColor: Colors.transparent, - builder: (_) => EmployeeSelectorBottomSheet(), - ); - - // Optional cleanup - controller.employeeSearchController.clear(); - controller.employeeSearchResults.clear(); -} + await showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16)), + ), + backgroundColor: Colors.transparent, + builder: (_) => EmployeeSelectorBottomSheet(), + ); + // Optional cleanup + controller.employeeSearchController.clear(); + controller.employeeSearchResults.clear(); + } Future _showOptionList( List options, @@ -277,8 +275,13 @@ class _AddExpenseBottomSheetState extends State<_AddExpenseBottomSheet> { attachments: controller.attachments, existingAttachments: controller.existingAttachments, onRemoveNew: controller.removeAttachment, - onRemoveExisting: (item) => - controller.existingAttachments.remove(item), + onRemoveExisting: (item) { + final index = controller.existingAttachments.indexOf(item); + if (index != -1) { + controller.existingAttachments[index]['isActive'] = false; + controller.existingAttachments.refresh(); + } + }, onAdd: controller.pickAttachments, ), MySpacing.height(16), @@ -458,7 +461,7 @@ class _TileContainer extends StatelessWidget { class _AttachmentsSection extends StatelessWidget { final RxList attachments; - final List> existingAttachments; + final RxList> existingAttachments; final ValueChanged onRemoveNew; final ValueChanged>? onRemoveExisting; final VoidCallback onAdd; @@ -473,133 +476,141 @@ class _AttachmentsSection extends StatelessWidget { @override Widget build(BuildContext context) { - return Obx(() => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (existingAttachments.isNotEmpty) ...[ - Text( - "Existing Attachments", - style: const TextStyle(fontWeight: FontWeight.w600), - ), - const SizedBox(height: 8), - Wrap( - spacing: 8, - runSpacing: 8, - children: existingAttachments.map((doc) { - final isImage = - doc['contentType']?.toString().startsWith('image/') ?? - false; - final url = doc['url']; - final fileName = doc['fileName'] ?? 'Unnamed'; + return Obx(() { + final activeExistingAttachments = + existingAttachments.where((doc) => doc['isActive'] != false).toList(); - return Stack( - clipBehavior: Clip.none, - children: [ - GestureDetector( - onTap: () async { - if (isImage) { - final imageDocs = existingAttachments - .where((d) => (d['contentType'] - ?.toString() - .startsWith('image/') ?? - false)) - .toList(); - final initialIndex = - imageDocs.indexWhere((d) => d == doc); - showDialog( - context: context, - builder: (_) => ImageViewerDialog( - imageSources: - imageDocs.map((e) => e['url']).toList(), - initialIndex: initialIndex, - ), - ); - } else { - if (url != null && await canLaunchUrlString(url)) { - await launchUrlString(url, - mode: LaunchMode.externalApplication); - } else { - showAppSnackbar( - title: 'Error', - message: 'Could not open the document.', - type: SnackbarType.error, - ); - } - } - }, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - decoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade300), - borderRadius: BorderRadius.circular(6), - color: Colors.grey.shade100, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - isImage ? Icons.image : Icons.insert_drive_file, - size: 20, - color: Colors.grey[600], - ), - const SizedBox(width: 7), - ConstrainedBox( - constraints: - const BoxConstraints(maxWidth: 120), - child: Text( - fileName, - overflow: TextOverflow.ellipsis, - style: const TextStyle(fontSize: 12), - ), - ), - ], - ), - ), - ), - if (onRemoveExisting != null) - Positioned( - top: -6, - right: -6, - child: IconButton( - icon: const Icon(Icons.close, - color: Colors.red, size: 18), - onPressed: () => onRemoveExisting!(doc), - ), - ), - ], - ); - }).toList(), - ), - const SizedBox(height: 16), - ], - - // New attachments section - shows preview tiles + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (activeExistingAttachments.isNotEmpty) ...[ + Text( + "Existing Attachments", + style: const TextStyle(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, - children: [ - ...attachments.map((file) => _AttachmentTile( - file: file, - onRemove: () => onRemoveNew(file), - )), - GestureDetector( - onTap: onAdd, - child: Container( - width: 80, - height: 80, - decoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade400), - borderRadius: BorderRadius.circular(8), - color: Colors.grey.shade100, + children: activeExistingAttachments.map((doc) { + final isImage = + doc['contentType']?.toString().startsWith('image/') ?? + false; + final url = doc['url']; + final fileName = doc['fileName'] ?? 'Unnamed'; + + return Stack( + clipBehavior: Clip.none, + children: [ + GestureDetector( + onTap: () async { + if (isImage) { + final imageDocs = activeExistingAttachments + .where((d) => (d['contentType'] + ?.toString() + .startsWith('image/') ?? + false)) + .toList(); + final initialIndex = + imageDocs.indexWhere((d) => d == doc); + showDialog( + context: context, + builder: (_) => ImageViewerDialog( + imageSources: + imageDocs.map((e) => e['url']).toList(), + initialIndex: initialIndex, + ), + ); + } else { + if (url != null && await canLaunchUrlString(url)) { + await launchUrlString( + url, + mode: LaunchMode.externalApplication, + ); + } else { + showAppSnackbar( + title: 'Error', + message: 'Could not open the document.', + type: SnackbarType.error, + ); + } + } + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(6), + color: Colors.grey.shade100, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + isImage ? Icons.image : Icons.insert_drive_file, + size: 20, + color: Colors.grey[600], + ), + const SizedBox(width: 7), + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 120), + child: Text( + fileName, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12), + ), + ), + ], + ), + ), ), - child: const Icon(Icons.add, size: 30, color: Colors.grey), - ), - ), - ], + if (onRemoveExisting != null) + Positioned( + top: -6, + right: -6, + child: IconButton( + icon: const Icon(Icons.close, + color: Colors.red, size: 18), + onPressed: () { + onRemoveExisting?.call(doc); + }, + ), + ), + ], + ); + }).toList(), ), + const SizedBox(height: 16), ], - )); + + // New attachments section + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + ...attachments.map((file) => _AttachmentTile( + file: file, + onRemove: () => onRemoveNew(file), + )), + GestureDetector( + onTap: onAdd, + child: Container( + width: 80, + height: 80, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade400), + borderRadius: BorderRadius.circular(8), + color: Colors.grey.shade100, + ), + child: const Icon(Icons.add, size: 30, color: Colors.grey), + ), + ), + ], + ), + ], + ); + }); } }