import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/utils/base_bottom_sheet.dart'; import 'package:marco/model/employees/employee_model.dart'; import 'package:marco/helpers/services/api_service.dart'; class EmployeeSelectionBottomSheet extends StatefulWidget { final List initiallySelected; final bool multipleSelection; final String title; const EmployeeSelectionBottomSheet({ Key? key, this.initiallySelected = const [], this.multipleSelection = true, this.title = 'Select Employees', }) : super(key: key); @override State createState() => _EmployeeSelectionBottomSheetState(); } class _EmployeeSelectionBottomSheetState extends State { final TextEditingController _searchController = TextEditingController(); final RxBool _isSearching = false.obs; final RxList _searchResults = [].obs; late RxList _selectedEmployees; @override void initState() { super.initState(); _selectedEmployees = RxList.from(widget.initiallySelected); _searchEmployees(''); } @override void dispose() { _searchController.dispose(); super.dispose(); } Future _searchEmployees(String query) async { _isSearching.value = true; final data = await ApiService.searchEmployeesBasic(searchString: query); final results = (data as List) .map((e) => EmployeeModel.fromJson(e as Map)) .toList(); _searchResults.assignAll(results); _isSearching.value = false; } void _toggleEmployee(EmployeeModel emp) { if (widget.multipleSelection) { if (_selectedEmployees.contains(emp)) { _selectedEmployees.remove(emp); } else { _selectedEmployees.add(emp); } _selectedEmployees.refresh(); } else { _selectedEmployees.assignAll([emp]); _selectedEmployees.refresh(); } } void _handleSubmit() { if (widget.multipleSelection) { Navigator.of(context).pop(_selectedEmployees.toList()); } else { Navigator.of(context) .pop(_selectedEmployees.isNotEmpty ? _selectedEmployees.first : null); } } Widget _searchBar() => Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: TextField( controller: _searchController, onChanged: _searchEmployees, decoration: InputDecoration( hintText: 'Search employees...', filled: true, fillColor: Colors.grey.shade100, prefixIcon: const Icon(Icons.search, color: Colors.grey), suffixIcon: _searchController.text.isNotEmpty ? IconButton( icon: const Icon(Icons.close, color: Colors.grey), onPressed: () { _searchController.clear(); _searchEmployees(''); }, ) : null, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), border: OutlineInputBorder( borderRadius: BorderRadius.circular(30), borderSide: BorderSide.none, ), ), ), ); Widget _employeeList() => Expanded( child: ListView.builder( padding: const EdgeInsets.symmetric(vertical: 4), itemCount: _searchResults.length, itemBuilder: (context, index) { final emp = _searchResults[index]; return Obx(() { // wrap each tile final isSelected = _selectedEmployees.contains(emp); return ListTile( leading: CircleAvatar( backgroundColor: Colors.blueAccent, child: Text( (emp.firstName.isNotEmpty ? emp.firstName[0] : 'U') .toUpperCase(), style: const TextStyle(color: Colors.white), ), ), title: Text('${emp.firstName} ${emp.lastName}'), subtitle: Text(emp.email), trailing: Checkbox( value: isSelected, onChanged: (_) => _toggleEmployee(emp), fillColor: MaterialStateProperty.resolveWith( (states) => states.contains(MaterialState.selected) ? Colors.blueAccent : Colors.white, ), ), onTap: () => _toggleEmployee(emp), contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), ); }); }, ), ); @override Widget build(BuildContext context) { return BaseBottomSheet( title: widget.title, onCancel: () => Navigator.of(context).pop(), onSubmit: _handleSubmit, child: SizedBox( height: MediaQuery.of(context).size.height * 0.7, child: Column(children: [ _searchBar(), _employeeList(), ]), ), ); } }