diff --git a/lib/controller/dashboard/add_employee_controller.dart b/lib/controller/dashboard/add_employee_controller.dart index 6b0e5c0..f016ab3 100644 --- a/lib/controller/dashboard/add_employee_controller.dart +++ b/lib/controller/dashboard/add_employee_controller.dart @@ -110,7 +110,8 @@ class AddEmployeeController extends MyController { logSafe("Failed to fetch roles: null result", level: LogLevel.error); } } catch (e, st) { - logSafe("Error fetching roles", level: LogLevel.error, error: e, stackTrace: st); + logSafe("Error fetching roles", + level: LogLevel.error, error: e, stackTrace: st); } } @@ -120,7 +121,7 @@ class AddEmployeeController extends MyController { update(); } - Future createEmployees() async { + Future?> createEmployees() async { logSafe("Starting employee creation..."); if (selectedGender == null || selectedRoleId == null) { logSafe("Missing gender or role.", level: LogLevel.warning); @@ -129,14 +130,13 @@ class AddEmployeeController extends MyController { message: "Please select both Gender and Role.", type: SnackbarType.warning, ); - return false; + return null; } final firstName = basicValidator.getController("first_name")?.text.trim(); final lastName = basicValidator.getController("last_name")?.text.trim(); - final phoneNumber = basicValidator.getController("phone_number")?.text.trim(); - - logSafe("Creating employee", level: LogLevel.info); + final phoneNumber = + basicValidator.getController("phone_number")?.text.trim(); try { final response = await ApiService.createEmployee( @@ -146,20 +146,24 @@ class AddEmployeeController extends MyController { gender: selectedGender!.name, jobRoleId: selectedRoleId!, ); -logSafe("Response: $response"); - if (response == true) { + + logSafe("Response: $response"); + + if (response != null && response['success'] == true) { logSafe("Employee created successfully."); showAppSnackbar( title: "Success", message: "Employee created successfully!", type: SnackbarType.success, ); - return true; + return response; } else { - logSafe("Failed to create employee (response false)", level: LogLevel.error); + logSafe("Failed to create employee (response false)", + level: LogLevel.error); } } catch (e, st) { - logSafe("Error creating employee", level: LogLevel.error, error: e, stackTrace: st); + logSafe("Error creating employee", + level: LogLevel.error, error: e, stackTrace: st); } showAppSnackbar( @@ -167,7 +171,7 @@ logSafe("Response: $response"); message: "Failed to create employee.", type: SnackbarType.error, ); - return false; + return null; } Future _checkAndRequestContactsPermission() async { @@ -181,7 +185,8 @@ logSafe("Response: $response"); showAppSnackbar( title: "Permission Required", - message: "Please allow Contacts permission from settings to pick a contact.", + message: + "Please allow Contacts permission from settings to pick a contact.", type: SnackbarType.warning, ); return false; @@ -195,7 +200,8 @@ logSafe("Response: $response"); final picked = await FlutterContacts.openExternalPick(); if (picked == null) return; - final contact = await FlutterContacts.getContact(picked.id, withProperties: true); + final contact = + await FlutterContacts.getContact(picked.id, withProperties: true); if (contact == null) { showAppSnackbar( title: "Error", @@ -216,7 +222,8 @@ logSafe("Response: $response"); final indiaPhones = contact.phones.where((p) { final normalized = p.number.replaceAll(RegExp(r'[^0-9+]'), ''); - return normalized.startsWith('+91') || RegExp(r'^\d{10}$').hasMatch(normalized); + return normalized.startsWith('+91') || + RegExp(r'^\d{10}$').hasMatch(normalized); }).toList(); if (indiaPhones.isEmpty) { @@ -256,10 +263,12 @@ logSafe("Response: $response"); ? normalizedPhone.substring(normalizedPhone.length - 10) : normalizedPhone; - basicValidator.getController('phone_number')?.text = phoneWithoutCountryCode; + basicValidator.getController('phone_number')?.text = + phoneWithoutCountryCode; update(); } catch (e, st) { - logSafe("Error fetching contacts", level: LogLevel.error, error: e, stackTrace: st); + logSafe("Error fetching contacts", + level: LogLevel.error, error: e, stackTrace: st); showAppSnackbar( title: "Error", message: "Failed to fetch contacts.", diff --git a/lib/helpers/services/api_endpoints.dart b/lib/helpers/services/api_endpoints.dart index 640d023..3949b40 100644 --- a/lib/helpers/services/api_endpoints.dart +++ b/lib/helpers/services/api_endpoints.dart @@ -18,7 +18,7 @@ class ApiEndpoints { static const String getAllEmployeesByProject = "/employee/list"; static const String getAllEmployees = "/employee/list"; static const String getRoles = "/roles/jobrole"; - static const String createEmployee = "/employee/manage"; + static const String createEmployee = "/employee/manage-mobile"; static const String getEmployeeInfo = "/employee/profile/get"; static const String assignEmployee = "/employee/profile/get"; static const String getAssignedProjects = "/project/assigned-projects"; diff --git a/lib/helpers/services/api_service.dart b/lib/helpers/services/api_service.dart index e0a3b64..bd6e005 100644 --- a/lib/helpers/services/api_service.dart +++ b/lib/helpers/services/api_service.dart @@ -479,37 +479,37 @@ class ApiService { /// Get list of assigned projects for a specific employee /// Get list of assigned projects for a specific employee -static Future?> getAssignedProjects(String employeeId) async { - if (employeeId.isEmpty) { - throw ArgumentError("employeeId must not be empty"); - } - - final endpoint = "${ApiEndpoints.getAssignedProjects}/$employeeId"; - - try { - final response = await _getRequest(endpoint); - - if (response == null) { - logSafe("Failed to fetch assigned projects: null response", - level: LogLevel.error); - return null; + static Future?> getAssignedProjects(String employeeId) async { + if (employeeId.isEmpty) { + throw ArgumentError("employeeId must not be empty"); } - final parsed = _parseResponse(response, label: "Assigned Projects"); - if (parsed is List) { - return parsed; - } else { - logSafe("Unexpected response format for assigned projects.", + final endpoint = "${ApiEndpoints.getAssignedProjects}/$employeeId"; + + try { + final response = await _getRequest(endpoint); + + if (response == null) { + logSafe("Failed to fetch assigned projects: null response", + level: LogLevel.error); + return null; + } + + final parsed = _parseResponse(response, label: "Assigned Projects"); + if (parsed is List) { + return parsed; + } else { + logSafe("Unexpected response format for assigned projects.", + level: LogLevel.error); + return null; + } + } catch (e, stack) { + logSafe("Exception during getAssignedProjects API: $e", level: LogLevel.error); + logSafe("StackTrace: $stack", level: LogLevel.debug); return null; } - } catch (e, stack) { - logSafe("Exception during getAssignedProjects API: $e", - level: LogLevel.error); - logSafe("StackTrace: $stack", level: LogLevel.debug); - return null; } -} /// Assign projects to a specific employee static Future assignProjects({ @@ -839,8 +839,7 @@ static Future?> getAssignedProjects(String employeeId) async { static Future?> getRoles() async => _getRequest(ApiEndpoints.getRoles).then( (res) => res != null ? _parseResponse(res, label: 'Roles') : null); - - static Future createEmployee({ + static Future?> createEmployee({ required String firstName, required String lastName, required String phoneNumber, @@ -854,15 +853,20 @@ static Future?> getAssignedProjects(String employeeId) async { "gender": gender, "jobRoleId": jobRoleId, }; + final response = await _postRequest( ApiEndpoints.createEmployee, body, customTimeout: extendedTimeout, ); - if (response == null) return false; + if (response == null) return null; + final json = jsonDecode(response.body); - return response.statusCode == 200 && json['success'] == true; + return { + "success": response.statusCode == 200 && json['success'] == true, + "data": json + }; } static Future?> getEmployeeDetails( diff --git a/lib/model/employees/add_employee_bottom_sheet.dart b/lib/model/employees/add_employee_bottom_sheet.dart index 52d0cf5..f111c4f 100644 --- a/lib/model/employees/add_employee_bottom_sheet.dart +++ b/lib/model/employees/add_employee_bottom_sheet.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:flutter/services.dart'; import 'package:marco/controller/dashboard/add_employee_controller.dart'; import 'package:marco/controller/dashboard/employees_screen_controller.dart'; @@ -274,6 +275,12 @@ class _AddEmployeeBottomSheetState extends State return null; }, keyboardType: TextInputType.phone, + inputFormatters: [ + // Allow only digits + FilteringTextInputFormatter.digitsOnly, + // Limit to 10 digits + LengthLimitingTextInputFormatter(10), + ], decoration: _inputDecoration("e.g., 9876543210") .copyWith( suffixIcon: IconButton( @@ -355,9 +362,12 @@ class _AddEmployeeBottomSheetState extends State onPressed: () async { if (_controller.basicValidator .validateForm()) { - final success = + final result = await _controller.createEmployees(); - if (success) { + + if (result != null && + result['success'] == true) { + final employeeData = result['data']; final employeeController = Get.find(); final projectId = @@ -387,7 +397,7 @@ class _AddEmployeeBottomSheetState extends State _controller.selectedRoleId = null; _controller.update(); - Navigator.pop(context); + Navigator.pop(context, employeeData); } } }, diff --git a/lib/view/employees/employees_screen.dart b/lib/view/employees/employees_screen.dart index 3bcae42..a1a59a9 100644 --- a/lib/view/employees/employees_screen.dart +++ b/lib/view/employees/employees_screen.dart @@ -13,6 +13,7 @@ import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; import 'package:marco/view/employees/employee_detail_screen.dart'; import 'package:marco/model/employee_model.dart'; import 'package:marco/helpers/utils/launcher_utils.dart'; +import 'package:marco/view/employees/assign_employee_bottom_sheet.dart'; class EmployeesScreen extends StatefulWidget { const EmployeesScreen({super.key}); @@ -162,7 +163,8 @@ class _EmployeesScreenState extends State with UIMixin { ), floatingActionButton: InkWell( onTap: () async { - final result = await showModalBottomSheet( + // Step 1: Open AddEmployeeBottomSheet + final result = await showModalBottomSheet>( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( @@ -172,7 +174,29 @@ class _EmployeesScreenState extends State with UIMixin { builder: (context) => AddEmployeeBottomSheet(), ); - if (result == true) { + // Step 2: Check if employee creation was successful + if (result != null && result['success'] == true) { + print("Employee data from bottom sheet: $result"); + + final employeeData = result['data']; + final employeeId = employeeData['id'] as String; + final jobRoleId = employeeData['jobRoleId'] as String?; + + // Step 3: Open AssignProjectBottomSheet for the new employee + await showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(24)), + ), + backgroundColor: Colors.transparent, + builder: (context) => AssignProjectBottomSheet( + employeeId: employeeId, + jobRoleId: jobRoleId ?? '', + ), + ); + + // Step 4: Refresh employee list after project assignment await _refreshEmployees(); } },