marco.pms.mobileapp/lib/model/employees/multiple_select_role_bottomsheet.dart

219 lines
7.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:on_field_work/helpers/utils/base_bottom_sheet.dart';
import 'package:on_field_work/helpers/widgets/my_custom_skeleton.dart';
import 'package:on_field_work/model/employees/employee_model.dart';
import 'package:on_field_work/controller/task_Planning/daily_task_Planning_controller.dart';
class MultipleSelectRoleBottomSheet extends StatefulWidget {
final String title;
final bool multipleSelection;
final String projectId;
final String? serviceId;
final String? organizationId;
final String? roleId;
final ScrollController? scrollController;
final List<EmployeeModel> initiallySelected;
const MultipleSelectRoleBottomSheet({
super.key,
this.title = "Select Employees",
this.multipleSelection = true,
required this.projectId,
this.serviceId,
this.organizationId,
this.roleId,
this.initiallySelected = const [],
this.scrollController,
});
@override
State<MultipleSelectRoleBottomSheet> createState() =>
_MultipleSelectRoleBottomSheetState();
}
class _MultipleSelectRoleBottomSheetState
extends State<MultipleSelectRoleBottomSheet> {
final RxList<EmployeeModel> _employees = <EmployeeModel>[].obs;
final RxList<EmployeeModel> _filtered = <EmployeeModel>[].obs;
final RxBool _isLoading = true.obs;
late RxList<EmployeeModel> _selected;
final TextEditingController _searchController = TextEditingController();
late DailyTaskPlanningController controller;
@override
void initState() {
super.initState();
_selected = widget.initiallySelected.obs;
controller = Get.find<DailyTaskPlanningController>();
_fetchEmployeesFiltered();
}
Future<void> _fetchEmployeesFiltered() async {
_isLoading.value = true;
try {
List<EmployeeModel> employees = controller.employees.toList();
if (widget.roleId != null && widget.roleId!.isNotEmpty) {
employees =
employees.where((emp) => emp.jobRoleID == widget.roleId).toList();
}
employees.sort((a, b) {
final aSel = _selected.any((e) => e.id == a.id) ? 0 : 1;
final bSel = _selected.any((e) => e.id == b.id) ? 0 : 1;
return aSel != bSel
? aSel.compareTo(bSel)
: a.name.toLowerCase().compareTo(b.name.toLowerCase());
});
_employees.assignAll(employees);
_filtered.assignAll(employees);
} catch (e) {
print("Error fetching employees: $e");
} finally {
_isLoading.value = false;
}
}
void _onSearch(String text) {
if (text.isEmpty) {
_filtered.assignAll(_employees);
} else {
_filtered.assignAll(
_employees.where((e) =>
e.name.toLowerCase().contains(text.toLowerCase()) ||
e.designation.toLowerCase().contains(text.toLowerCase())),
);
}
_filtered.sort((a, b) {
final aSel = _selected.any((e) => e.id == a.id) ? 0 : 1;
final bSel = _selected.any((e) => e.id == b.id) ? 0 : 1;
return aSel != bSel
? aSel.compareTo(bSel)
: a.name.toLowerCase().compareTo(b.name.toLowerCase());
});
}
void _onTap(EmployeeModel emp) {
if (widget.multipleSelection) {
if (_selected.any((e) => e.id == emp.id)) {
_selected.removeWhere((e) => e.id == emp.id);
} else {
_selected.add(emp);
}
} else {
// Single selection → return immediately
Get.back(result: [emp]);
}
_onSearch(_searchController.text.trim());
}
bool _isSelected(EmployeeModel emp) {
return _selected.any((e) => e.id == emp.id);
}
Widget _searchBar() => Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextField(
controller: _searchController,
onChanged: _onSearch,
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();
_onSearch('');
},
)
: null,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide.none,
),
),
),
);
@override
Widget build(BuildContext context) {
return BaseBottomSheet(
title: widget.title,
onCancel: () => Get.back(),
onSubmit: () => Get.back(result: _selected.toList()), // Return plain list
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.55,
child: Column(
children: [
_searchBar(),
Expanded(
child: Obx(() {
if (_isLoading.value) {
return SkeletonLoaders.employeeSkeletonCard();
}
if (_filtered.isEmpty) {
return const Center(child: Text("No employees found"));
}
return ListView.builder(
controller: widget.scrollController,
padding: const EdgeInsets.only(bottom: 20),
itemCount: _filtered.length,
itemBuilder: (_, index) {
final emp = _filtered[index];
final isSelected = _isSelected(emp);
return ListTile(
onTap: () => _onTap(emp),
leading: CircleAvatar(
backgroundColor: Colors.blueAccent,
child: Text(
emp.name.isNotEmpty ? emp.name[0].toUpperCase() : "?",
style: const TextStyle(color: Colors.white),
),
),
title: Text(emp.name),
subtitle: Text(emp.designation),
trailing: Checkbox(
value: isSelected,
onChanged: (_) => _onTap(emp),
fillColor:
MaterialStateProperty.resolveWith<Color>((states) {
if (states.contains(MaterialState.selected)) {
return Colors.blueAccent;
}
return Colors.white;
}),
checkColor: Colors.white,
side: const BorderSide(color: Colors.grey),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 6,
),
);
},
);
}),
),
],
),
),
);
}
}