feat: Update employee creation process and API endpoints for improved functionality

This commit is contained in:
Vaibhav Surve 2025-07-23 11:58:09 +05:30
parent 943c7c7b50
commit 69e64ec789
5 changed files with 99 additions and 52 deletions

View File

@ -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<bool> createEmployees() async {
Future<Map<String, dynamic>?> 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<bool> _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.",

View File

@ -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";

View File

@ -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<List<dynamic>?> 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<List<dynamic>?> 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<bool> assignProjects({
@ -839,8 +839,7 @@ static Future<List<dynamic>?> getAssignedProjects(String employeeId) async {
static Future<List<dynamic>?> getRoles() async =>
_getRequest(ApiEndpoints.getRoles).then(
(res) => res != null ? _parseResponse(res, label: 'Roles') : null);
static Future<bool> createEmployee({
static Future<Map<String, dynamic>?> createEmployee({
required String firstName,
required String lastName,
required String phoneNumber,
@ -854,15 +853,20 @@ static Future<List<dynamic>?> 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<Map<String, dynamic>?> getEmployeeDetails(

View File

@ -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<AddEmployeeBottomSheet>
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<AddEmployeeBottomSheet>
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<EmployeesScreenController>();
final projectId =
@ -387,7 +397,7 @@ class _AddEmployeeBottomSheetState extends State<AddEmployeeBottomSheet>
_controller.selectedRoleId = null;
_controller.update();
Navigator.pop(context);
Navigator.pop(context, employeeData);
}
}
},

View File

@ -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<EmployeesScreen> with UIMixin {
),
floatingActionButton: InkWell(
onTap: () async {
final result = await showModalBottomSheet<bool>(
// Step 1: Open AddEmployeeBottomSheet
final result = await showModalBottomSheet<Map<String, dynamic>>(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
@ -172,7 +174,29 @@ class _EmployeesScreenState extends State<EmployeesScreen> 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();
}
},