import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:collection/collection.dart'; import 'package:marco/controller/directory/manage_bucket_controller.dart'; import 'package:marco/controller/directory/directory_controller.dart'; import 'package:marco/helpers/utils/base_bottom_sheet.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/model/employees/employee_model.dart'; import 'package:marco/model/directory/contact_bucket_list_model.dart'; class EditBucketBottomSheet { static void show( BuildContext context, ContactBucket bucket, List allEmployees, { required String ownerId, }) { final ManageBucketController controller = Get.find(); final nameController = TextEditingController(text: bucket.name); final descController = TextEditingController(text: bucket.description); final searchController = TextEditingController(); final selectedIds = RxSet({...bucket.employeeIds}); final searchText = ''.obs; InputDecoration _inputDecoration(String label) { return InputDecoration( labelText: label, filled: true, fillColor: Colors.grey.shade100, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(12)), borderSide: BorderSide(color: Colors.blueAccent, width: 1.5), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), isDense: true, ); } Future _handleSubmit() async { final newName = nameController.text.trim(); final newDesc = descController.text.trim(); final newEmployeeIds = selectedIds.toList()..sort(); final originalEmployeeIds = [...bucket.employeeIds]..sort(); final nameChanged = newName != bucket.name; final descChanged = newDesc != bucket.description; final employeeChanged = !(const ListEquality().equals(newEmployeeIds, originalEmployeeIds)); if (!nameChanged && !descChanged && !employeeChanged) { showAppSnackbar( title: "No Changes", message: "No changes were made to update the bucket.", type: SnackbarType.warning, ); return; } final success = await controller.updateBucket( id: bucket.id, name: newName, description: newDesc, employeeIds: newEmployeeIds, originalEmployeeIds: originalEmployeeIds, ); if (success) { final directoryController = Get.find(); await directoryController.fetchBuckets(); Navigator.of(context).pop(); } } Widget _formContent() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: nameController, decoration: _inputDecoration('Bucket Name'), ), MySpacing.height(16), TextField( controller: descController, maxLines: 2, decoration: _inputDecoration('Description'), ), MySpacing.height(20), MyText.labelLarge('Shared With', fontWeight: 600), MySpacing.height(8), Obx(() => TextField( controller: searchController, onChanged: (value) => searchText.value = value.toLowerCase(), decoration: InputDecoration( hintText: 'Search employee...', prefixIcon: const Icon(Icons.search, size: 20), suffixIcon: searchText.value.isNotEmpty ? IconButton( icon: const Icon(Icons.clear, size: 18), onPressed: () { searchController.clear(); searchText.value = ''; }, ) : null, isDense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), filled: true, fillColor: Colors.grey.shade100, border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade300), ), ), )), MySpacing.height(8), Obx(() { final filtered = allEmployees.where((emp) { final fullName = '${emp.firstName} ${emp.lastName}'.toLowerCase(); return fullName.contains(searchText.value); }).toList(); return SizedBox( height: 180, child: ListView.separated( itemCount: filtered.length, separatorBuilder: (_, __) => const SizedBox(height: 2), itemBuilder: (context, index) { final emp = filtered[index]; final fullName = '${emp.firstName} ${emp.lastName}'.trim(); return Obx(() => Theme( data: Theme.of(context).copyWith( unselectedWidgetColor: Colors.grey.shade500, checkboxTheme: CheckboxThemeData( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4)), side: const BorderSide(color: Colors.grey), fillColor: MaterialStateProperty.resolveWith((states) { if (states.contains(MaterialState.selected)) { return Colors.blueAccent; } return Colors.white; }), checkColor: MaterialStateProperty.all(Colors.white), ), ), child: CheckboxListTile( dense: true, contentPadding: EdgeInsets.zero, visualDensity: const VisualDensity(vertical: -4), controlAffinity: ListTileControlAffinity.leading, value: selectedIds.contains(emp.id), onChanged: emp.id == ownerId ? null : (val) { if (val == true) { selectedIds.add(emp.id); } else { selectedIds.remove(emp.id); } }, title: Row( children: [ Expanded( child: MyText.bodyMedium( fullName.isNotEmpty ? fullName : 'Unnamed', fontWeight: 600, ), ), if (emp.id == ownerId) Container( margin: const EdgeInsets.only(left: 6), padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(4), ), child: MyText.labelSmall( "Owner", fontWeight: 600, color: Colors.red, ), ), ], ), subtitle: emp.jobRole.isNotEmpty ? MyText.bodySmall( emp.jobRole, color: Colors.grey.shade600, ) : null, ), )); }, ), ); }), ], ); } showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) { return BaseBottomSheet( title: "Edit Bucket", onCancel: () => Navigator.pop(context), onSubmit: _handleSubmit, child: _formContent(), ); }, ); } }