removed employees by project
This commit is contained in:
parent
80d7ef96cb
commit
910bb5e6b4
@ -1,91 +1,56 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/model/attendance/attendance_model.dart';
|
||||
import 'package:marco/model/project_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_details_model.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
|
||||
class EmployeesScreenController extends GetxController {
|
||||
List<AttendanceModel> attendances = [];
|
||||
List<ProjectModel> projects = [];
|
||||
String? selectedProjectId;
|
||||
List<EmployeeDetailsModel> employeeDetails = [];
|
||||
RxBool isAllEmployeeSelected = false.obs;
|
||||
/// ✅ Data lists
|
||||
RxList<EmployeeModel> employees = <EmployeeModel>[].obs;
|
||||
|
||||
RxBool isLoading = false.obs;
|
||||
RxMap<String, RxBool> uploadingStates = <String, RxBool>{}.obs;
|
||||
Rxn<EmployeeDetailsModel> selectedEmployeeDetails =
|
||||
Rxn<EmployeeDetailsModel>();
|
||||
|
||||
/// ✅ Loading states
|
||||
RxBool isLoading = false.obs;
|
||||
RxBool isLoadingEmployeeDetails = false.obs;
|
||||
|
||||
/// ✅ Selection state
|
||||
RxBool isAllEmployeeSelected = false.obs;
|
||||
RxSet<String> selectedEmployeeIds = <String>{}.obs;
|
||||
|
||||
/// ✅ Upload state tracking (if needed later)
|
||||
RxMap<String, RxBool> uploadingStates = <String, RxBool>{}.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
isLoading.value = true;
|
||||
fetchAllProjects().then((_) {
|
||||
final projectId = Get.find<ProjectController>().selectedProject?.id;
|
||||
if (projectId != null) {
|
||||
selectedProjectId = projectId;
|
||||
fetchEmployeesByProject(projectId);
|
||||
} else if (isAllEmployeeSelected.value) {
|
||||
fetchAllEmployees();
|
||||
} else {
|
||||
clearEmployees();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> fetchAllProjects() async {
|
||||
isLoading.value = true;
|
||||
|
||||
await _handleApiCall(
|
||||
ApiService.getProjects,
|
||||
onSuccess: (data) {
|
||||
projects = data.map((json) => ProjectModel.fromJson(json)).toList();
|
||||
logSafe(
|
||||
"Projects fetched: ${projects.length} projects loaded.",
|
||||
level: LogLevel.info,
|
||||
);
|
||||
},
|
||||
onEmpty: () {
|
||||
logSafe("No project data found or API call failed.",
|
||||
level: LogLevel.warning);
|
||||
},
|
||||
);
|
||||
|
||||
isLoading.value = false;
|
||||
update();
|
||||
}
|
||||
|
||||
void clearEmployees() {
|
||||
employees.clear();
|
||||
logSafe("Employees cleared", level: LogLevel.info);
|
||||
update(['employee_screen_controller']);
|
||||
fetchAllEmployees();
|
||||
}
|
||||
|
||||
/// 🔹 Fetch all employees (no project filter)
|
||||
Future<void> fetchAllEmployees({String? organizationId}) async {
|
||||
isLoading.value = true;
|
||||
update(['employee_screen_controller']);
|
||||
|
||||
await _handleApiCall(
|
||||
() => ApiService.getAllEmployees(
|
||||
organizationId: organizationId), // pass orgId to API
|
||||
() => ApiService.getAllEmployees(organizationId: organizationId),
|
||||
onSuccess: (data) {
|
||||
employees.assignAll(data.map((json) => EmployeeModel.fromJson(json)));
|
||||
logSafe(
|
||||
"All Employees fetched: ${employees.length} employees loaded.",
|
||||
level: LogLevel.info,
|
||||
);
|
||||
|
||||
// Reset selection states when new data arrives
|
||||
selectedEmployeeIds.clear();
|
||||
isAllEmployeeSelected.value = false;
|
||||
},
|
||||
onEmpty: () {
|
||||
employees.clear();
|
||||
logSafe(
|
||||
"No Employee data found or API call failed",
|
||||
level: LogLevel.warning,
|
||||
);
|
||||
selectedEmployeeIds.clear();
|
||||
isAllEmployeeSelected.value = false;
|
||||
logSafe("No Employee data found or API call failed",
|
||||
level: LogLevel.warning);
|
||||
},
|
||||
);
|
||||
|
||||
@ -93,28 +58,7 @@ class EmployeesScreenController extends GetxController {
|
||||
update(['employee_screen_controller']);
|
||||
}
|
||||
|
||||
Future<void> fetchEmployeesByProject(String projectId,
|
||||
{String? organizationId}) async {
|
||||
if (projectId.isEmpty) return;
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
await _handleApiCall(
|
||||
() => ApiService.getAllEmployeesByProject(projectId,
|
||||
organizationId: organizationId),
|
||||
onSuccess: (data) {
|
||||
employees.assignAll(data.map((json) => EmployeeModel.fromJson(json)));
|
||||
for (var emp in employees) {
|
||||
uploadingStates[emp.id] = false.obs;
|
||||
}
|
||||
},
|
||||
onEmpty: () => employees.clear(),
|
||||
);
|
||||
|
||||
isLoading.value = false;
|
||||
update(['employee_screen_controller']);
|
||||
}
|
||||
|
||||
/// 🔹 Fetch details for a specific employee
|
||||
Future<void> fetchEmployeeDetails(String? employeeId) async {
|
||||
if (employeeId == null || employeeId.isEmpty) return;
|
||||
|
||||
@ -124,31 +68,34 @@ class EmployeesScreenController extends GetxController {
|
||||
() => ApiService.getEmployeeDetails(employeeId),
|
||||
onSuccess: (data) {
|
||||
selectedEmployeeDetails.value = EmployeeDetailsModel.fromJson(data);
|
||||
logSafe(
|
||||
"Employee details loaded for $employeeId",
|
||||
level: LogLevel.info,
|
||||
);
|
||||
logSafe("Employee details loaded for $employeeId",
|
||||
level: LogLevel.info);
|
||||
},
|
||||
onEmpty: () {
|
||||
selectedEmployeeDetails.value = null;
|
||||
logSafe(
|
||||
"No employee details found for $employeeId",
|
||||
level: LogLevel.warning,
|
||||
);
|
||||
logSafe("No employee details found for $employeeId",
|
||||
level: LogLevel.warning);
|
||||
},
|
||||
onError: (e) {
|
||||
selectedEmployeeDetails.value = null;
|
||||
logSafe(
|
||||
"Error fetching employee details for $employeeId",
|
||||
level: LogLevel.error,
|
||||
error: e,
|
||||
);
|
||||
logSafe("Error fetching employee details for $employeeId",
|
||||
level: LogLevel.error, error: e);
|
||||
},
|
||||
);
|
||||
|
||||
isLoadingEmployeeDetails.value = false;
|
||||
}
|
||||
|
||||
/// 🔹 Clear all employee data
|
||||
void clearEmployees() {
|
||||
employees.clear();
|
||||
selectedEmployeeIds.clear();
|
||||
isAllEmployeeSelected.value = false;
|
||||
logSafe("Employees cleared", level: LogLevel.info);
|
||||
update(['employee_screen_controller']);
|
||||
}
|
||||
|
||||
/// 🔹 Generic handler for list API responses
|
||||
Future<void> _handleApiCall(
|
||||
Future<List<dynamic>?> Function() apiCall, {
|
||||
required Function(List<dynamic>) onSuccess,
|
||||
@ -171,6 +118,7 @@ class EmployeesScreenController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 🔹 Generic handler for single-object API responses
|
||||
Future<void> _handleSingleApiCall(
|
||||
Future<Map<String, dynamic>?> Function() apiCall, {
|
||||
required Function(Map<String, dynamic>) onSuccess,
|
||||
|
||||
@ -537,13 +537,8 @@ class _AddEmployeeBottomSheetState extends State<AddEmployeeBottomSheet>
|
||||
|
||||
if (result != null && result['success'] == true) {
|
||||
final employeeController = Get.find<EmployeesScreenController>();
|
||||
final projectId = employeeController.selectedProjectId;
|
||||
|
||||
if (projectId == null) {
|
||||
await employeeController.fetchAllEmployees();
|
||||
} else {
|
||||
await employeeController.fetchEmployeesByProject(projectId);
|
||||
}
|
||||
await employeeController.fetchAllEmployees();
|
||||
|
||||
employeeController.update(['employee_screen_controller']);
|
||||
if (mounted) Navigator.pop(context, result['data']);
|
||||
|
||||
@ -1,171 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:marco/controller/permission_controller.dart';
|
||||
import 'package:marco/controller/employee/employees_screen_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
|
||||
class EmployeesScreenFilterSheet extends StatelessWidget {
|
||||
final EmployeesScreenController controller;
|
||||
final PermissionController permissionController;
|
||||
|
||||
const EmployeesScreenFilterSheet({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.permissionController,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String? tempSelectedProjectId;
|
||||
bool showProjectList = false;
|
||||
|
||||
final accessibleProjects = controller.projects
|
||||
.where((project) =>
|
||||
permissionController.isUserAssignedToProject(project.id.toString()))
|
||||
.toList();
|
||||
|
||||
return StatefulBuilder(builder: (context, setState) {
|
||||
List<Widget> filterWidgets;
|
||||
|
||||
if (showProjectList) {
|
||||
filterWidgets = [];
|
||||
|
||||
// Add "All Employees" option at the top
|
||||
filterWidgets.add(
|
||||
ListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
title: MyText.titleSmall('All Employees'),
|
||||
trailing:
|
||||
tempSelectedProjectId == null ? const Icon(Icons.check) : null,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
tempSelectedProjectId = null;
|
||||
showProjectList = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Add all accessible projects below
|
||||
if (accessibleProjects.isEmpty) {
|
||||
filterWidgets.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Center(
|
||||
child: MyText.titleSmall(
|
||||
'No Projects Assigned',
|
||||
fontWeight: 600,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
filterWidgets.addAll(accessibleProjects.map((project) {
|
||||
return ListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
title: MyText.titleSmall(project.name),
|
||||
trailing: tempSelectedProjectId == project.id.toString()
|
||||
? const Icon(Icons.check)
|
||||
: null,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
tempSelectedProjectId = project.id.toString();
|
||||
showProjectList = false;
|
||||
});
|
||||
},
|
||||
);
|
||||
}).toList());
|
||||
}
|
||||
} else {
|
||||
String selectedProjectName = 'All Employees';
|
||||
if (tempSelectedProjectId != null) {
|
||||
final selectedProject = accessibleProjects.isNotEmpty
|
||||
? accessibleProjects.firstWhere(
|
||||
(p) => p.id.toString() == tempSelectedProjectId,
|
||||
orElse: () => accessibleProjects[0],
|
||||
)
|
||||
: null;
|
||||
|
||||
if (selectedProject != null) {
|
||||
selectedProjectName = selectedProject.name;
|
||||
}
|
||||
}
|
||||
|
||||
filterWidgets = [
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 12, 16, 4),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: MyText.titleSmall(
|
||||
'Select Project',
|
||||
fontWeight: 600,
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
title: MyText.titleSmall(selectedProjectName),
|
||||
trailing: const Icon(Icons.arrow_drop_down),
|
||||
onTap: () => setState(() => showProjectList = true),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12, bottom: 8),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[400],
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
...filterWidgets,
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color.fromARGB(255, 95, 132, 255),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: MyText.titleSmall(
|
||||
'Apply Filter',
|
||||
fontWeight: 600,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, {
|
||||
'projectId': tempSelectedProjectId,
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -16,8 +16,6 @@ import 'package:marco/controller/permission_controller.dart';
|
||||
import 'package:marco/helpers/utils/permission_constants.dart';
|
||||
import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
|
||||
import 'package:marco/view/employees/employee_profile_screen.dart';
|
||||
import 'package:marco/controller/tenant/organization_selection_controller.dart';
|
||||
import 'package:marco/helpers/widgets/tenant/organization_selector.dart';
|
||||
|
||||
class EmployeesScreen extends StatefulWidget {
|
||||
const EmployeesScreen({super.key});
|
||||
@ -27,64 +25,32 @@ class EmployeesScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
final EmployeesScreenController _employeeController =
|
||||
Get.put(EmployeesScreenController());
|
||||
final PermissionController permissionController =
|
||||
Get.put(PermissionController());
|
||||
late final EmployeesScreenController _employeeController;
|
||||
late final PermissionController _permissionController;
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
final RxList<EmployeeModel> _filteredEmployees = <EmployeeModel>[].obs;
|
||||
final OrganizationController _organizationController =
|
||||
Get.put(OrganizationController());
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_initEmployees();
|
||||
_searchController
|
||||
.addListener(() => _filterEmployees(_searchController.text));
|
||||
_employeeController = Get.put(EmployeesScreenController());
|
||||
_permissionController = Get.put(PermissionController());
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await _initEmployees();
|
||||
_searchController.addListener(() {
|
||||
_filterEmployees(_searchController.text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _initEmployees() async {
|
||||
final projectId = Get.find<ProjectController>().selectedProject?.id;
|
||||
final orgId = _organizationController.selectedOrganization.value?.id;
|
||||
|
||||
if (projectId != null) {
|
||||
await _organizationController.fetchOrganizations(projectId);
|
||||
}
|
||||
|
||||
if (_employeeController.isAllEmployeeSelected.value) {
|
||||
_employeeController.selectedProjectId = null;
|
||||
await _employeeController.fetchAllEmployees(organizationId: orgId);
|
||||
} else if (projectId != null) {
|
||||
_employeeController.selectedProjectId = projectId;
|
||||
await _employeeController.fetchEmployeesByProject(projectId,
|
||||
organizationId: orgId);
|
||||
} else {
|
||||
_employeeController.clearEmployees();
|
||||
}
|
||||
|
||||
await _employeeController.fetchAllEmployees();
|
||||
_filterEmployees(_searchController.text);
|
||||
}
|
||||
|
||||
Future<void> _refreshEmployees() async {
|
||||
try {
|
||||
final projectId = Get.find<ProjectController>().selectedProject?.id;
|
||||
final orgId = _organizationController.selectedOrganization.value?.id;
|
||||
final allSelected = _employeeController.isAllEmployeeSelected.value;
|
||||
|
||||
_employeeController.selectedProjectId = allSelected ? null : projectId;
|
||||
|
||||
if (allSelected) {
|
||||
await _employeeController.fetchAllEmployees(organizationId: orgId);
|
||||
} else if (projectId != null) {
|
||||
await _employeeController.fetchEmployeesByProject(projectId,
|
||||
organizationId: orgId);
|
||||
} else {
|
||||
_employeeController.clearEmployees();
|
||||
}
|
||||
|
||||
await _employeeController.fetchAllEmployees();
|
||||
_filterEmployees(_searchController.text);
|
||||
_employeeController.update(['employee_screen_controller']);
|
||||
} catch (e, stackTrace) {
|
||||
@ -95,27 +61,16 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
|
||||
void _filterEmployees(String query) {
|
||||
final employees = _employeeController.employees;
|
||||
|
||||
List<EmployeeModel> filtered;
|
||||
|
||||
if (query.isEmpty) {
|
||||
filtered = List<EmployeeModel>.from(employees);
|
||||
} else {
|
||||
final q = query.toLowerCase();
|
||||
filtered = employees
|
||||
.where(
|
||||
(e) =>
|
||||
e.name.toLowerCase().contains(q) ||
|
||||
e.email.toLowerCase().contains(q) ||
|
||||
e.phoneNumber.toLowerCase().contains(q) ||
|
||||
e.jobRole.toLowerCase().contains(q),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
filtered
|
||||
.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
||||
|
||||
final searchQuery = query.toLowerCase();
|
||||
final filtered = query.isEmpty
|
||||
? List<EmployeeModel>.from(employees)
|
||||
: employees.where((e) =>
|
||||
e.name.toLowerCase().contains(searchQuery) ||
|
||||
e.email.toLowerCase().contains(searchQuery) ||
|
||||
e.phoneNumber.toLowerCase().contains(searchQuery) ||
|
||||
e.jobRole.toLowerCase().contains(searchQuery),
|
||||
).toList();
|
||||
filtered.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
||||
_filteredEmployees.assignAll(filtered);
|
||||
}
|
||||
|
||||
@ -134,7 +89,6 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
|
||||
final employeeData = result['data'];
|
||||
final employeeId = employeeData['id'] as String;
|
||||
final jobRoleId = employeeData['jobRoleId'] as String?;
|
||||
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
@ -143,15 +97,16 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => AssignProjectBottomSheet(
|
||||
builder: (_) => AssignProjectBottomSheet(
|
||||
employeeId: employeeId,
|
||||
jobRoleId: jobRoleId ?? '',
|
||||
jobRoleId: '', // no jobRoleId required
|
||||
),
|
||||
);
|
||||
|
||||
await _refreshEmployees();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -164,7 +119,6 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
tag: 'employee_screen_controller',
|
||||
builder: (_) {
|
||||
_filterEmployees(_searchController.text);
|
||||
|
||||
return MyRefreshIndicator(
|
||||
onRefresh: _refreshEmployees,
|
||||
child: SingleChildScrollView(
|
||||
@ -174,7 +128,10 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MySpacing.height(flexSpacing),
|
||||
_buildSearchAndActionRow(),
|
||||
Padding(
|
||||
padding: MySpacing.x(15),
|
||||
child: _buildSearchField(),
|
||||
),
|
||||
MySpacing.height(flexSpacing),
|
||||
Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
@ -203,8 +160,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new,
|
||||
color: Colors.black, size: 20),
|
||||
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black, size: 20),
|
||||
onPressed: () => Get.offNamed('/dashboard'),
|
||||
),
|
||||
MySpacing.width(8),
|
||||
@ -249,17 +205,9 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
|
||||
Widget _buildFloatingActionButton() {
|
||||
return Obx(() {
|
||||
// Show nothing while permissions are loading
|
||||
if (permissionController.isLoading.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
// Show FAB only if user has Manage Employees permission
|
||||
final hasPermission =
|
||||
permissionController.hasPermission(Permissions.manageEmployees);
|
||||
if (!hasPermission) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
if (_permissionController.isLoading.value) return const SizedBox.shrink();
|
||||
final hasPermission = _permissionController.hasPermission(Permissions.manageEmployees);
|
||||
if (!hasPermission) return const SizedBox.shrink();
|
||||
|
||||
return InkWell(
|
||||
onTap: _onAddNewEmployee,
|
||||
@ -270,11 +218,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
color: contentTheme.primary,
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 3),
|
||||
)
|
||||
BoxShadow(color: Colors.black26, blurRadius: 6, offset: Offset(0, 3)),
|
||||
],
|
||||
),
|
||||
child: const Row(
|
||||
@ -290,58 +234,6 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildSearchAndActionRow() {
|
||||
return Padding(
|
||||
padding: MySpacing.x(15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Search Field Row
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildSearchField()),
|
||||
const SizedBox(width: 8),
|
||||
_buildPopupMenu(),
|
||||
],
|
||||
),
|
||||
|
||||
// Organization Selector Row
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OrganizationSelector(
|
||||
controller: _organizationController,
|
||||
height: 36,
|
||||
onSelectionChanged: (org) async {
|
||||
// Make sure the selectedOrganization is updated immediately
|
||||
_organizationController.selectOrganization(org);
|
||||
|
||||
final projectId =
|
||||
Get.find<ProjectController>().selectedProject?.id;
|
||||
|
||||
if (_employeeController.isAllEmployeeSelected.value) {
|
||||
await _employeeController.fetchAllEmployees(
|
||||
organizationId: _organizationController
|
||||
.selectedOrganization.value?.id);
|
||||
} else if (projectId != null) {
|
||||
await _employeeController.fetchEmployeesByProject(
|
||||
projectId,
|
||||
organizationId: _organizationController
|
||||
.selectedOrganization.value?.id);
|
||||
}
|
||||
|
||||
_employeeController.update(['employee_screen_controller']);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
MySpacing.height(8),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSearchField() {
|
||||
return SizedBox(
|
||||
height: 36,
|
||||
@ -350,99 +242,30 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
style: const TextStyle(fontSize: 13, height: 1.2),
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
prefixIcon: const Icon(Icons.search, size: 18, color: Colors.grey),
|
||||
prefixIconConstraints:
|
||||
const BoxConstraints(minWidth: 32, minHeight: 32),
|
||||
hintText: 'Search contacts...',
|
||||
hintStyle: const TextStyle(fontSize: 13, color: Colors.grey),
|
||||
hintText: 'Search employees...',
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300, width: 1),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300, width: 1),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
_searchController.clear();
|
||||
_filterEmployees('');
|
||||
setState(() {});
|
||||
},
|
||||
child: const Icon(Icons.close, size: 18, color: Colors.grey),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
onChanged: (_) => setState(() {}),
|
||||
onChanged: (_) => _filterEmployees(_searchController.text),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPopupMenu() {
|
||||
return Obx(() {
|
||||
if (permissionController.isLoading.value ||
|
||||
!permissionController.hasPermission(Permissions.viewAllEmployees)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return PopupMenuButton<String>(
|
||||
icon: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
const Icon(Icons.tune, color: Colors.black),
|
||||
Obx(() => _employeeController.isAllEmployeeSelected.value
|
||||
? Positioned(
|
||||
right: -1,
|
||||
top: -1,
|
||||
child: Container(
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.red, shape: BoxShape.circle),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink()),
|
||||
],
|
||||
),
|
||||
onSelected: (value) async {
|
||||
if (value == 'all_employees') {
|
||||
_employeeController.isAllEmployeeSelected.toggle();
|
||||
await _initEmployees();
|
||||
_employeeController.update(['employee_screen_controller']);
|
||||
}
|
||||
},
|
||||
itemBuilder: (_) => [
|
||||
PopupMenuItem<String>(
|
||||
value: 'all_employees',
|
||||
child: Obx(
|
||||
() => Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: _employeeController.isAllEmployeeSelected.value,
|
||||
onChanged: (_) => Navigator.pop(context, 'all_employees'),
|
||||
checkColor: Colors.white,
|
||||
activeColor: contentTheme.primary,
|
||||
side: const BorderSide(color: Colors.black, width: 1.5),
|
||||
fillColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(states) => states.contains(MaterialState.selected)
|
||||
? contentTheme.primary
|
||||
: Colors.white),
|
||||
),
|
||||
const Text('All Employees'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildEmployeeList() {
|
||||
return Obx(() {
|
||||
if (_employeeController.isLoading.value) {
|
||||
@ -456,13 +279,11 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
}
|
||||
|
||||
final employees = _filteredEmployees;
|
||||
|
||||
if (employees.isEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 60),
|
||||
child: Center(
|
||||
child: MyText.bodySmall("No Employees Found",
|
||||
fontWeight: 600, color: Colors.grey[700]),
|
||||
child: MyText.bodySmall("No Employees Found", fontWeight: 600, color: Colors.grey[700]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user