import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/controller/directory/directory_controller.dart'; import 'package:marco/controller/directory/manage_bucket_controller.dart'; import 'package:marco/controller/permission_controller.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/utils/permission_constants.dart'; import 'package:marco/helpers/widgets/team_members_bottom_sheet.dart'; import 'package:marco/model/directory/edit_bucket_bottom_sheet.dart'; class ManageBucketsScreen extends StatefulWidget { final PermissionController permissionController; const ManageBucketsScreen({super.key, required this.permissionController}); @override State createState() => _ManageBucketsScreenState(); } class _ManageBucketsScreenState extends State { final DirectoryController directoryController = Get.find(); final ManageBucketController manageBucketController = Get.put(ManageBucketController()); final ProjectController projectController = Get.find(); final Map _expandedMap = {}; final TextEditingController searchController = TextEditingController(); String searchText = ''; void _clearSearch() { searchController.clear(); setState(() { searchText = ''; }); } @override Widget build(BuildContext context) { final currentUserId = widget.permissionController.employeeInfo.value?.id; return Scaffold( backgroundColor: Colors.white, appBar: PreferredSize( preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, automaticallyImplyLeading: false, titleSpacing: 0, title: Padding( padding: MySpacing.xy(16, 0), child: Row( children: [ IconButton( icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black, size: 20), onPressed: () => Get.back(), ), MySpacing.width(8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.titleLarge('Manage Buckets', fontWeight: 700, color: Colors.black), MySpacing.height(2), GetBuilder(builder: (_) { final projectName = projectController.selectedProject?.name ?? 'Select Project'; return Row( children: [ const Icon(Icons.work_outline, size: 14, color: Colors.grey), MySpacing.width(4), Expanded( child: MyText.bodySmall( projectName, fontWeight: 600, overflow: TextOverflow.ellipsis, color: Colors.grey[700], ), ), ], ); }), ], ), ), ], ), ), ), ), body: SafeArea( child: Column( children: [ Padding( padding: MySpacing.xy(16, 12), child: SizedBox( height: 38, child: TextField( controller: searchController, onChanged: (value) { setState(() { searchText = value.toLowerCase(); }); }, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 0), prefixIcon: const Icon(Icons.search, size: 18, color: Colors.grey), suffixIcon: searchText.isNotEmpty ? IconButton( icon: const Icon(Icons.close, color: Colors.grey), onPressed: _clearSearch, ) : null, hintText: 'Search buckets...', filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade300), ), ), ), ), ), Expanded( child: Obx(() { final buckets = directoryController.contactBuckets.where((bucket) { return bucket.name.toLowerCase().contains(searchText) || bucket.description.toLowerCase().contains(searchText) || bucket.numberOfContacts.toString().contains(searchText); }).toList(); if (directoryController.isLoading.value || manageBucketController.isLoading.value) { return const Center(child: CircularProgressIndicator()); } if (buckets.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.folder_off, size: 48, color: Colors.grey), MySpacing.height(12), MyText.bodyMedium("No buckets available.", fontWeight: 600, color: Colors.grey[700]), ], ), ); } return ListView.separated( padding: MySpacing.fromLTRB(16, 0, 16, 24), itemCount: buckets.length, separatorBuilder: (_, __) => MySpacing.height(16), itemBuilder: (context, index) { final bucket = buckets[index]; final isOwner = currentUserId != null && bucket.createdBy.id == currentUserId; final canEdit = isOwner || widget.permissionController .hasPermission(Permissions.directoryAdmin) || widget.permissionController .hasPermission(Permissions.directoryManager); final isExpanded = _expandedMap[bucket.id] ?? false; final matchedEmployees = manageBucketController.allEmployees .where((emp) => bucket.employeeIds.contains(emp.id)) .toList(); return GestureDetector( onTap: () { TeamMembersBottomSheet.show( context, bucket, matchedEmployees, canEdit: canEdit, onEdit: () { Get.back(); Future.delayed(const Duration(milliseconds: 300), () { EditBucketBottomSheet.show(context, bucket, manageBucketController.allEmployees, ownerId: bucket.createdBy.id); }); }, ); }, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: MyText.titleSmall( bucket.name, fontWeight: 700, overflow: TextOverflow.ellipsis, ), ), if (canEdit) IconButton( icon: const Icon( Icons.delete_outline, color: Colors.red), onPressed: () { showDialog( context: context, builder: (ctx) => AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 16), ), title: Row( children: const [ Icon( Icons .warning_amber_rounded, color: Colors.red), SizedBox(width: 8), Text("Delete Bucket", style: TextStyle( fontWeight: FontWeight .bold)), ], ), content: const Text( "Are you sure you want to delete this bucket? This action cannot be undone.", style: TextStyle(fontSize: 15), ), actionsPadding: const EdgeInsets .symmetric( horizontal: 12, vertical: 8), actions: [ TextButton( onPressed: () => Navigator.of(ctx) .pop(), child: const Text("Cancel"), ), ElevatedButton.icon( style: ElevatedButton .styleFrom( foregroundColor: Colors.white, backgroundColor: Colors.red, shape: RoundedRectangleBorder( borderRadius: BorderRadius .circular(8), ), ), icon: const Icon( Icons.delete), label: const Text("Delete"), onPressed: () { Navigator.of(ctx).pop(); manageBucketController .deleteBucket( bucket.id); }, ), ], ), ); }, ) ], ), if (!canEdit) MySpacing.height(12), ], ), Row( children: [ const Icon(Icons.contacts_outlined, size: 14, color: Colors.grey), MySpacing.width(4), MyText.labelSmall( '${bucket.numberOfContacts} contact(s)', color: Colors.red, fontWeight: 600, ), MySpacing.width(12), const Icon(Icons.ios_share_outlined, size: 14, color: Colors.grey), MySpacing.width(4), MyText.labelSmall( 'Shared with (${matchedEmployees.length})', color: Colors.indigo, fontWeight: 600, ), ], ), MySpacing.height(4), if (bucket.description.isNotEmpty) LayoutBuilder( builder: (context, constraints) { final span = TextSpan( text: bucket.description, style: Theme.of(context) .textTheme .bodySmall ?.copyWith(color: Colors.grey[700]), ); final tp = TextPainter( text: span, maxLines: 2, textDirection: TextDirection.ltr, )..layout(maxWidth: constraints.maxWidth); final hasOverflow = tp.didExceedMaxLines; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodySmall( bucket.description, color: Colors.grey[700], maxLines: isExpanded ? null : 2, overflow: isExpanded ? TextOverflow.visible : TextOverflow.ellipsis, ), if (hasOverflow) GestureDetector( onTap: () { setState(() { _expandedMap[bucket.id] = !isExpanded; }); }, child: Padding( padding: const EdgeInsets.only( top: 4), child: MyText.labelSmall( isExpanded ? "Show less" : "Show more", fontWeight: 600, color: Colors.red, ), ), ), ], ); }, ), ], ), ), ], ), ); }, ); }), ), ], ), ), ); } }