import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/controller/employee/assign_projects_controller.dart'; import 'package:marco/model/global_project_model.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; class AssignProjectBottomSheet extends StatefulWidget { final String employeeId; final String jobRoleId; const AssignProjectBottomSheet({ super.key, required this.employeeId, required this.jobRoleId, }); @override State createState() => _AssignProjectBottomSheetState(); } class _AssignProjectBottomSheetState extends State { late final AssignProjectController assignController; @override void initState() { super.initState(); assignController = Get.put( AssignProjectController( employeeId: widget.employeeId, jobRoleId: widget.jobRoleId, ), tag: '${widget.employeeId}_${widget.jobRoleId}', ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return SafeArea( top: false, child: DraggableScrollableSheet( expand: false, maxChildSize: 0.9, minChildSize: 0.4, initialChildSize: 0.7, builder: (_, scrollController) { return Container( decoration: BoxDecoration( color: theme.cardColor, borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 12, offset: Offset(0, -2), ), ], ), padding: MySpacing.all(16), child: Obx(() { if (assignController.isLoading.value) { return const Center(child: CircularProgressIndicator()); } final projects = assignController.allProjects; if (projects.isEmpty) { return const Center(child: Text('No projects available.')); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Drag Handle Center( child: Container( width: 40, height: 5, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(10), ), ), ), MySpacing.height(12), // Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MyText.titleMedium('Assign to Project', fontWeight: 700), ], ), MySpacing.height(4), // Sub Info MyText.bodySmall( 'Select the projects to assign this employee.', color: Colors.grey[600], ), MySpacing.height(8), // Select All Toggle Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Projects (${projects.length})', style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 16, ), ), TextButton( onPressed: () { assignController.toggleSelectAll(); }, child: Obx(() { return Text( assignController.areAllSelected() ? 'Deselect All' : 'Select All', style: const TextStyle( color: Colors.blueAccent, fontWeight: FontWeight.w600, ), ); }), ), ], ), // Project List Expanded( child: ListView.builder( controller: scrollController, itemCount: projects.length, padding: EdgeInsets.zero, itemBuilder: (context, index) { final GlobalProjectModel project = projects[index]; return Obx(() { final bool isSelected = assignController.isProjectSelected( project.id.toString(), ); return Theme( data: Theme.of(context).copyWith( checkboxTheme: CheckboxThemeData( fillColor: WidgetStateProperty.resolveWith( (states) { if (states.contains(WidgetState.selected)) { return Colors.blueAccent; } return Colors.white; }, ), side: const BorderSide( color: Colors.black, width: 2, ), checkColor: WidgetStateProperty.all(Colors.white), ), ), child: CheckboxListTile( dense: true, value: isSelected, title: Text( project.name, style: const TextStyle( fontWeight: FontWeight.w500, fontSize: 14, ), ), onChanged: (checked) { assignController.toggleProjectSelection( project.id.toString(), checked ?? false, ); }, activeColor: Colors.blueAccent, controlAffinity: ListTileControlAffinity.leading, contentPadding: EdgeInsets.zero, visualDensity: const VisualDensity( horizontal: -4, vertical: -4, ), ), ); }); }, ), ), MySpacing.height(16), // Cancel & Save Buttons Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close, color: Colors.red), label: MyText.bodyMedium("Cancel", color: Colors.red, fontWeight: 600), style: OutlinedButton.styleFrom( side: const BorderSide(color: Colors.red), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 7, ), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton.icon( onPressed: () async { if (assignController.selectedProjects.isEmpty) { showAppSnackbar( title: "Error", message: "Please select at least one project.", type: SnackbarType.error, ); return; } await _assignProjects(); }, icon: const Icon(Icons.check_circle_outline, color: Colors.white), label: MyText.bodyMedium("Assign", color: Colors.white, fontWeight: 600), style: ElevatedButton.styleFrom( backgroundColor: Colors.indigo, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 7, ), ), ), ), ], ), ], ); }), ); }, ), ); } Future _assignProjects() async { final success = await assignController.assignProjectsToEmployee(); if (success) { Get.back(); showAppSnackbar( title: "Success", message: "Employee assigned to selected projects.", type: SnackbarType.success, ); } else { showAppSnackbar( title: "Error", message: "Failed to assign employee.", type: SnackbarType.error, ); } } }