Vaibhav Surve 34100a4d9e -- Enhance layout with floating action button and navigation improvements
- Added a floating action button to the Layout widget for better accessibility.
- Updated the left bar navigation items for clarity and consistency.
- Introduced Daily Progress Report and Daily Task Planning screens with comprehensive UI.
- Implemented filtering and refreshing functionalities in task planning.
- Improved user experience with better spacing and layout adjustments.
- Updated pubspec.yaml to include new dependencies for image handling and path management.
2025-05-28 17:35:42 +05:30

448 lines
13 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'package:logger/logger.dart';
import 'package:marco/helpers/services/storage/local_storage.dart';
import 'package:marco/helpers/services/auth_service.dart';
import 'package:marco/helpers/services/api_endpoints.dart';
import 'package:get/get.dart';
final Logger logger = Logger();
class ApiService {
static const Duration timeout = Duration(seconds: 10);
static const bool enableLogs = true;
// ===== Helpers =====
static Future<String?> _getToken() async {
final token = await LocalStorage.getJwtToken();
if (token == null && enableLogs) {
logger.w("No JWT token found. Please log in.");
}
return token;
}
static Map<String, String> _headers(String token) => {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
};
static void _log(String message) {
if (enableLogs) logger.i(message);
}
static dynamic _parseResponse(http.Response response, {String label = ''}) {
_log("$label Response: ${response.body}");
try {
final json = jsonDecode(response.body);
if (response.statusCode == 200 && json['success'] == true) {
return json['data'];
}
_log("API Error [$label]: ${json['message'] ?? 'Unknown error'}");
} catch (e) {
_log("Response parsing error [$label]: $e");
}
return null;
}
static dynamic _parseResponseForAllData(http.Response response,
{String label = ''}) {
_log("$label Response: ${response.body}");
try {
final json = jsonDecode(response.body);
if (response.statusCode == 200 && json['success'] == true) {
return json; // 👈 Return full response, not just json['data']
}
_log("API Error [$label]: ${json['message'] ?? 'Unknown error'}");
} catch (e) {
_log("Response parsing error [$label]: $e");
}
return null;
}
static Future<http.Response?> _getRequest(String endpoint,
{Map<String, String>? queryParams, bool hasRetried = false}) async {
String? token = await _getToken();
if (token == null) return null;
Uri uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint")
.replace(queryParameters: queryParams);
_log("GET $uri");
try {
http.Response response =
await http.get(uri, headers: _headers(token)).timeout(timeout);
if (response.statusCode == 401 && !hasRetried) {
_log("Unauthorized. Attempting token refresh...");
bool refreshed = await AuthService.refreshToken();
if (refreshed) {
token = await _getToken();
if (token != null) {
return await _getRequest(endpoint,
queryParams: queryParams, hasRetried: true);
}
}
_log("Token refresh failed.");
}
return response;
} catch (e) {
_log("HTTP GET Exception: $e");
return null;
}
}
static Future<http.Response?> _postRequest(
String endpoint, dynamic body) async {
String? token = await _getToken();
if (token == null) return null;
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint");
_log("POST $uri");
_log("Headers: ${_headers(token)}");
_log("Body: $body");
try {
final response = await http
.post(uri, headers: _headers(token), body: jsonEncode(body))
.timeout(timeout);
_log("Response Status: ${response.statusCode}");
return response;
} catch (e) {
_log("HTTP POST Exception: $e");
return null;
}
}
// ===== Attendence Screen API Calls =====
static Future<List<dynamic>?> getProjects() async {
final response = await _getRequest(ApiEndpoints.getProjects);
return response != null
? _parseResponse(response, label: 'Projects')
: null;
}
static Future<List<dynamic>?> getEmployeesByProject(String projectId) async {
final response = await _getRequest(ApiEndpoints.getEmployeesByProject,
queryParams: {"projectId": projectId});
return response != null
? _parseResponse(response, label: 'Employees')
: null;
}
static Future<List<dynamic>?> getAttendanceLogs(String projectId,
{DateTime? dateFrom, DateTime? dateTo}) async {
final query = {
"projectId": projectId,
if (dateFrom != null)
"dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
};
final response =
await _getRequest(ApiEndpoints.getAttendanceLogs, queryParams: query);
return response != null
? _parseResponse(response, label: 'Attendance Logs')
: null;
}
static Future<List<dynamic>?> getAttendanceLogView(String id) async {
final response =
await _getRequest("${ApiEndpoints.getAttendanceLogView}/$id");
return response != null
? _parseResponse(response, label: 'Log Details')
: null;
}
static Future<List<dynamic>?> getRegularizationLogs(String projectId) async {
final response = await _getRequest(ApiEndpoints.getRegularizationLogs,
queryParams: {"projectId": projectId});
return response != null
? _parseResponse(response, label: 'Regularization Logs')
: null;
}
// ===== Upload Attendance Image =====
static Future<bool> uploadAttendanceImage(
String id,
String employeeId,
XFile? imageFile,
double latitude,
double longitude, {
required String imageName,
required String projectId,
String comment = "",
required int action,
bool imageCapture = true,
String? markTime, // <-- Optional markTime parameter
}) async {
final now = DateTime.now();
final body = {
"id": id,
"employeeId": employeeId,
"projectId": projectId,
"markTime": markTime ?? DateFormat('hh:mm a').format(now),
"comment": comment,
"action": action,
"date": DateFormat('yyyy-MM-dd').format(now),
if (imageCapture) "latitude": '$latitude',
if (imageCapture) "longitude": '$longitude',
};
if (imageCapture && imageFile != null) {
try {
final bytes = await imageFile.readAsBytes();
final base64Image = base64Encode(bytes);
final fileSize = await imageFile.length();
final contentType = "image/${imageFile.path.split('.').last}";
body["image"] = {
"fileName": imageName,
"contentType": contentType,
"fileSize": fileSize,
"description": "Employee attendance photo",
"base64Data": base64Image,
};
} catch (e) {
_log("Image encoding error: $e");
return false;
}
}
final response =
await _postRequest(ApiEndpoints.uploadAttendanceImage, body);
if (response == null) return false;
final json = jsonDecode(response.body);
if (response.statusCode == 200 && json['success'] == true) {
return true;
} else {
_log("Failed to upload image: ${json['message'] ?? 'Unknown error'}");
}
return false;
}
// ===== Utilities =====
static String generateImageName(String employeeId, int count) {
final now = DateTime.now();
final dateStr = DateFormat('yyyyMMdd_HHmmss').format(now);
final imageNumber = count.toString().padLeft(3, '0');
return "${employeeId}_${dateStr}_$imageNumber.jpg";
}
// ===== Employee Screen API Calls =====
static Future<List<dynamic>?> getAllEmployeesByProject(
String projectId) async {
if (projectId.isEmpty) {
throw ArgumentError('projectId must not be empty');
}
final String endpoint =
"${ApiEndpoints.getAllEmployeesByProject}/$projectId";
final response = await _getRequest(endpoint);
return response != null
? _parseResponse(response, label: 'Employees by Project')
: null;
}
static Future<List<dynamic>?> getAllEmployees() async {
final response = await _getRequest(ApiEndpoints.getAllEmployees);
return response != null
? _parseResponse(response, label: 'All Employees')
: null;
}
static Future<List<dynamic>?> getRoles() async {
final response = await _getRequest(ApiEndpoints.getRoles);
return response != null
? _parseResponse(response, label: 'All Employees')
: null;
}
static Future<bool> createEmployee({
required String firstName,
required String lastName,
required String phoneNumber,
required String gender,
required String jobRoleId,
}) async {
final body = {
"firstName": firstName,
"lastName": lastName,
"phoneNumber": phoneNumber,
"gender": gender,
"jobRoleId": jobRoleId,
};
// Make the API request
final response = await _postRequest(ApiEndpoints.createEmployee, body);
if (response == null) {
_log("Error: No response from server.");
return false;
}
final json = jsonDecode(response.body);
if (response.statusCode == 200) {
if (json['success'] == true) {
return true;
} else {
_log(
"Failed to create employee: ${json['message'] ?? 'Unknown error'}");
return false;
}
} else {
_log(
"Failed to create employee. Status code: ${response.statusCode}, Response: ${json['message'] ?? 'No message'}");
return false;
}
}
static Future<Map<String, dynamic>?> getEmployeeDetails(
String employeeId) async {
final url = "${ApiEndpoints.getEmployeeInfo}/$employeeId";
final response = await _getRequest(url);
final data = response != null
? _parseResponse(response, label: 'Employee Details')
: null;
if (data is Map<String, dynamic>) {
return data;
}
return null;
}
// ===== Daily Tasks API Calls =====
static Future<List<dynamic>?> getDailyTasks(String projectId,
{DateTime? dateFrom, DateTime? dateTo}) async {
final query = {
"projectId": projectId,
if (dateFrom != null)
"dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
};
final response =
await _getRequest(ApiEndpoints.getDailyTask, queryParams: query);
return response != null
? _parseResponse(response, label: 'Daily Tasks')
: null;
}
static Future<bool> reportTask({
required String id,
required int completedTask,
required String comment,
required List<Map<String, dynamic>> checkList,
}) async {
final body = {
"id": id,
"completedTask": completedTask,
"comment": comment,
"reportedDate": DateTime.now().toUtc().toIso8601String(),
"checkList": checkList,
};
final response = await _postRequest(ApiEndpoints.reportTask, body);
if (response == null) {
_log("Error: No response from server.");
return false;
}
final json = jsonDecode(response.body);
if (response.statusCode == 200 && json['success'] == true) {
Get.back();
return true;
} else {
_log("Failed to report task: ${json['message'] ?? 'Unknown error'}");
return false;
}
}
static Future<bool> commentTask({
required String id,
required String comment,
}) async {
final body = {
"taskAllocationId": id,
"comment": comment,
"commentDate": DateTime.now().toUtc().toIso8601String(),
};
final response = await _postRequest(ApiEndpoints.commentTask, body);
if (response == null) {
_log("Error: No response from server.");
return false;
}
final json = jsonDecode(response.body);
if (response.statusCode == 200 && json['success'] == true) {
return true;
} else {
_log("Failed to comment task: ${json['message'] ?? 'Unknown error'}");
return false;
}
}
// Daily Task Planing //
static Future<Map<String, dynamic>?> getDailyTasksDetails(
String projectId) async {
final url = "${ApiEndpoints.dailyTaskDetails}/$projectId";
final response = await _getRequest(url);
return response != null
? _parseResponseForAllData(response, label: 'Daily Task Details')
as Map<String, dynamic>?
: null;
}
static Future<bool> assignDailyTask({
required String workItemId,
required int plannedTask,
required String description,
required List<String> taskTeam,
DateTime? assignmentDate,
}) async {
final body = {
"workItemId": workItemId,
"plannedTask": plannedTask,
"description": description,
"taskTeam": taskTeam,
"assignmentDate":
(assignmentDate ?? DateTime.now()).toUtc().toIso8601String(),
};
final response = await _postRequest(ApiEndpoints.assignDailyTask, body);
if (response == null) {
_log("Error: No response from server.");
return false;
}
final json = jsonDecode(response.body);
if (response.statusCode == 200 && json['success'] == true) {
Get.back();
return true;
} else {
_log(
"Failed to assign daily task: ${json['message'] ?? 'Unknown error'}");
return false;
}
}
}