feat: Update application ID in build.gradle and refactor imports and methods in service files for improved clarity and organization
This commit is contained in:
parent
658f3f26e0
commit
0ad8847b94
@ -21,7 +21,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId = "com.example.marco"
|
applicationId = "com.example.marcostage"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = flutter.minSdkVersion
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:logger/logger.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/auth_service.dart';
|
||||||
import 'package:marco/helpers/services/api_endpoints.dart';
|
import 'package:marco/helpers/services/api_endpoints.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||||
|
|
||||||
final Logger logger = Logger();
|
final Logger logger = Logger();
|
||||||
|
|
||||||
@ -14,13 +14,11 @@ class ApiService {
|
|||||||
static const Duration timeout = Duration(seconds: 10);
|
static const Duration timeout = Duration(seconds: 10);
|
||||||
static const bool enableLogs = true;
|
static const bool enableLogs = true;
|
||||||
|
|
||||||
// ===== Helpers =====
|
// === Helpers ===
|
||||||
|
|
||||||
static Future<String?> _getToken() async {
|
static Future<String?> _getToken() async {
|
||||||
final token = await LocalStorage.getJwtToken();
|
final token = await LocalStorage.getJwtToken();
|
||||||
if (token == null && enableLogs) {
|
if (token == null && enableLogs) logger.w("No JWT token found.");
|
||||||
logger.w("No JWT token found. Please log in.");
|
|
||||||
}
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,13 +45,12 @@ class ApiService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dynamic _parseResponseForAllData(http.Response response,
|
static dynamic _parseResponseForAllData(http.Response response, {String label = ''}) {
|
||||||
{String label = ''}) {
|
|
||||||
_log("$label Response: ${response.body}");
|
_log("$label Response: ${response.body}");
|
||||||
try {
|
try {
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
if (response.statusCode == 200 && json['success'] == true) {
|
if (response.statusCode == 200 && json['success'] == true) {
|
||||||
return json; // 👈 Return full response, not just json['data']
|
return json;
|
||||||
}
|
}
|
||||||
_log("API Error [$label]: ${json['message'] ?? 'Unknown error'}");
|
_log("API Error [$label]: ${json['message'] ?? 'Unknown error'}");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -62,28 +59,23 @@ class ApiService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<http.Response?> _getRequest(String endpoint,
|
static Future<http.Response?> _getRequest(
|
||||||
{Map<String, String>? queryParams, bool hasRetried = false}) async {
|
String endpoint, {
|
||||||
|
Map<String, String>? queryParams,
|
||||||
|
bool hasRetried = false,
|
||||||
|
}) async {
|
||||||
String? token = await _getToken();
|
String? token = await _getToken();
|
||||||
if (token == null) return null;
|
if (token == null) return null;
|
||||||
|
|
||||||
Uri uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint")
|
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint").replace(queryParameters: queryParams);
|
||||||
.replace(queryParameters: queryParams);
|
|
||||||
_log("GET $uri");
|
_log("GET $uri");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
http.Response response =
|
final response = await http.get(uri, headers: _headers(token)).timeout(timeout);
|
||||||
await http.get(uri, headers: _headers(token)).timeout(timeout);
|
|
||||||
|
|
||||||
if (response.statusCode == 401 && !hasRetried) {
|
if (response.statusCode == 401 && !hasRetried) {
|
||||||
_log("Unauthorized. Attempting token refresh...");
|
_log("Unauthorized. Attempting token refresh...");
|
||||||
bool refreshed = await AuthService.refreshToken();
|
if (await AuthService.refreshToken()) {
|
||||||
if (refreshed) {
|
return await _getRequest(endpoint, queryParams: queryParams, hasRetried: true);
|
||||||
token = await _getToken();
|
|
||||||
if (token != null) {
|
|
||||||
return await _getRequest(endpoint,
|
|
||||||
queryParams: queryParams, hasRetried: true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_log("Token refresh failed.");
|
_log("Token refresh failed.");
|
||||||
}
|
}
|
||||||
@ -98,22 +90,25 @@ class ApiService {
|
|||||||
String endpoint,
|
String endpoint,
|
||||||
dynamic body, {
|
dynamic body, {
|
||||||
Duration customTimeout = timeout,
|
Duration customTimeout = timeout,
|
||||||
|
bool hasRetried = false,
|
||||||
}) async {
|
}) async {
|
||||||
String? token = await _getToken();
|
String? token = await _getToken();
|
||||||
if (token == null) return null;
|
if (token == null) return null;
|
||||||
|
|
||||||
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint");
|
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint");
|
||||||
|
_log("POST $uri\nHeaders: ${_headers(token)}\nBody: $body");
|
||||||
_log("POST $uri");
|
|
||||||
_log("Headers: ${_headers(token)}");
|
|
||||||
_log("Body: $body");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http
|
final response = await http
|
||||||
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
||||||
.timeout(customTimeout);
|
.timeout(customTimeout);
|
||||||
|
|
||||||
_log("Response Status: ${response.statusCode}");
|
if (response.statusCode == 401 && !hasRetried) {
|
||||||
|
_log("Unauthorized POST. Attempting token refresh...");
|
||||||
|
if (await AuthService.refreshToken()) {
|
||||||
|
return await _postRequest(endpoint, body, customTimeout: customTimeout, hasRetried: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log("HTTP POST Exception: $e");
|
_log("HTTP POST Exception: $e");
|
||||||
@ -121,61 +116,39 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Attendence Screen API Calls =====
|
// === Attendance APIs ===
|
||||||
|
|
||||||
static Future<List<dynamic>?> getProjects() async {
|
static Future<List<dynamic>?> getProjects() async =>
|
||||||
final response = await _getRequest(ApiEndpoints.getProjects);
|
_getRequest(ApiEndpoints.getProjects).then((res) => res != null ? _parseResponse(res, label: 'Projects') : null);
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'Projects')
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
static Future<List<dynamic>?> getGlobalProjects() async {
|
|
||||||
final response = await _getRequest(ApiEndpoints.getProjects);
|
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'Global 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,
|
static Future<List<dynamic>?> getGlobalProjects() async =>
|
||||||
{DateTime? dateFrom, DateTime? dateTo}) async {
|
_getRequest(ApiEndpoints.getProjects).then((res) => res != null ? _parseResponse(res, label: 'Global Projects') : null);
|
||||||
|
|
||||||
|
static Future<List<dynamic>?> getEmployeesByProject(String projectId) async =>
|
||||||
|
_getRequest(ApiEndpoints.getEmployeesByProject, queryParams: {"projectId": projectId})
|
||||||
|
.then((res) => res != null ? _parseResponse(res, label: 'Employees') : null);
|
||||||
|
|
||||||
|
static Future<List<dynamic>?> getAttendanceLogs(
|
||||||
|
String projectId, {
|
||||||
|
DateTime? dateFrom,
|
||||||
|
DateTime? dateTo,
|
||||||
|
}) async {
|
||||||
final query = {
|
final query = {
|
||||||
"projectId": projectId,
|
"projectId": projectId,
|
||||||
if (dateFrom != null)
|
if (dateFrom != null) "dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
|
||||||
"dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
|
|
||||||
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
|
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
|
||||||
};
|
};
|
||||||
|
return _getRequest(ApiEndpoints.getAttendanceLogs, queryParams: query)
|
||||||
final response =
|
.then((res) => res != null ? _parseResponse(res, label: 'Attendance Logs') : null);
|
||||||
await _getRequest(ApiEndpoints.getAttendanceLogs, queryParams: query);
|
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'Attendance Logs')
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<dynamic>?> getAttendanceLogView(String id) async {
|
static Future<List<dynamic>?> getAttendanceLogView(String id) async =>
|
||||||
final response =
|
_getRequest("${ApiEndpoints.getAttendanceLogView}/$id")
|
||||||
await _getRequest("${ApiEndpoints.getAttendanceLogView}/$id");
|
.then((res) => res != null ? _parseResponse(res, label: 'Log Details') : null);
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'Log Details')
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<List<dynamic>?> getRegularizationLogs(String projectId) async {
|
static Future<List<dynamic>?> getRegularizationLogs(String projectId) async =>
|
||||||
final response = await _getRequest(ApiEndpoints.getRegularizationLogs,
|
_getRequest(ApiEndpoints.getRegularizationLogs, queryParams: {"projectId": projectId})
|
||||||
queryParams: {"projectId": projectId});
|
.then((res) => res != null ? _parseResponse(res, label: 'Regularization Logs') : null);
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'Regularization Logs')
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== Upload Attendance Image =====
|
|
||||||
|
|
||||||
static Future<bool> uploadAttendanceImage(
|
static Future<bool> uploadAttendanceImage(
|
||||||
String id,
|
String id,
|
||||||
@ -188,7 +161,7 @@ class ApiService {
|
|||||||
String comment = "",
|
String comment = "",
|
||||||
required int action,
|
required int action,
|
||||||
bool imageCapture = true,
|
bool imageCapture = true,
|
||||||
String? markTime, // <-- Optional markTime parameter
|
String? markTime,
|
||||||
}) async {
|
}) async {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final body = {
|
final body = {
|
||||||
@ -206,16 +179,14 @@ class ApiService {
|
|||||||
if (imageCapture && imageFile != null) {
|
if (imageCapture && imageFile != null) {
|
||||||
try {
|
try {
|
||||||
final bytes = await imageFile.readAsBytes();
|
final bytes = await imageFile.readAsBytes();
|
||||||
final base64Image = base64Encode(bytes);
|
|
||||||
final fileSize = await imageFile.length();
|
final fileSize = await imageFile.length();
|
||||||
final contentType = "image/${imageFile.path.split('.').last}";
|
final contentType = "image/${imageFile.path.split('.').last}";
|
||||||
|
|
||||||
body["image"] = {
|
body["image"] = {
|
||||||
"fileName": imageName,
|
"fileName": imageName,
|
||||||
"contentType": contentType,
|
"contentType": contentType,
|
||||||
"fileSize": fileSize,
|
"fileSize": fileSize,
|
||||||
"description": "Employee attendance photo",
|
"description": "Employee attendance photo",
|
||||||
"base64Data": base64Image,
|
"base64Data": base64Encode(bytes),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log("Image encoding error: $e");
|
_log("Image encoding error: $e");
|
||||||
@ -223,22 +194,16 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final response =
|
final response = await _postRequest(ApiEndpoints.uploadAttendanceImage, body);
|
||||||
await _postRequest(ApiEndpoints.uploadAttendanceImage, body);
|
|
||||||
if (response == null) return false;
|
if (response == null) return false;
|
||||||
|
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
if (response.statusCode == 200 && json['success'] == true) {
|
if (response.statusCode == 200 && json['success'] == true) return true;
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
_log("Failed to upload image: ${json['message'] ?? 'Unknown error'}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
_log("Failed to upload image: ${json['message'] ?? 'Unknown error'}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Utilities =====
|
|
||||||
|
|
||||||
static String generateImageName(String employeeId, int count) {
|
static String generateImageName(String employeeId, int count) {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final dateStr = DateFormat('yyyyMMdd_HHmmss').format(now);
|
final dateStr = DateFormat('yyyyMMdd_HHmmss').format(now);
|
||||||
@ -246,35 +211,19 @@ class ApiService {
|
|||||||
return "${employeeId}_${dateStr}_$imageNumber.jpg";
|
return "${employeeId}_${dateStr}_$imageNumber.jpg";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Employee Screen API Calls =====
|
// === Employee APIs ===
|
||||||
static Future<List<dynamic>?> getAllEmployeesByProject(
|
|
||||||
String projectId) async {
|
|
||||||
if (projectId.isEmpty) {
|
|
||||||
throw ArgumentError('projectId must not be empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
final String endpoint =
|
static Future<List<dynamic>?> getAllEmployeesByProject(String projectId) async {
|
||||||
"${ApiEndpoints.getAllEmployeesByProject}/$projectId";
|
if (projectId.isEmpty) throw ArgumentError('projectId must not be empty');
|
||||||
final response = await _getRequest(endpoint);
|
final endpoint = "${ApiEndpoints.getAllEmployeesByProject}/$projectId";
|
||||||
|
return _getRequest(endpoint).then((res) => res != null ? _parseResponse(res, label: 'Employees by Project') : null);
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'Employees by Project')
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<dynamic>?> getAllEmployees() async {
|
static Future<List<dynamic>?> getAllEmployees() async =>
|
||||||
final response = await _getRequest(ApiEndpoints.getAllEmployees);
|
_getRequest(ApiEndpoints.getAllEmployees).then((res) => res != null ? _parseResponse(res, label: 'All Employees') : null);
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'All Employees')
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<List<dynamic>?> getRoles() async {
|
static Future<List<dynamic>?> getRoles() async =>
|
||||||
final response = await _getRequest(ApiEndpoints.getRoles);
|
_getRequest(ApiEndpoints.getRoles).then((res) => res != null ? _parseResponse(res, label: 'Roles') : null);
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'All Employees')
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<bool> createEmployee({
|
static Future<bool> createEmployee({
|
||||||
required String firstName,
|
required String firstName,
|
||||||
@ -290,61 +239,33 @@ class ApiService {
|
|||||||
"gender": gender,
|
"gender": gender,
|
||||||
"jobRoleId": jobRoleId,
|
"jobRoleId": jobRoleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make the API request
|
|
||||||
final response = await _postRequest(ApiEndpoints.createEmployee, body);
|
final response = await _postRequest(ApiEndpoints.createEmployee, body);
|
||||||
|
if (response == null) return false;
|
||||||
if (response == null) {
|
|
||||||
_log("Error: No response from server.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
|
return response.statusCode == 200 && json['success'] == true;
|
||||||
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(
|
static Future<Map<String, dynamic>?> getEmployeeDetails(String employeeId) async {
|
||||||
String employeeId) async {
|
|
||||||
final url = "${ApiEndpoints.getEmployeeInfo}/$employeeId";
|
final url = "${ApiEndpoints.getEmployeeInfo}/$employeeId";
|
||||||
|
|
||||||
final response = await _getRequest(url);
|
final response = await _getRequest(url);
|
||||||
final data = response != null
|
final data = response != null ? _parseResponse(response, label: 'Employee Details') : null;
|
||||||
? _parseResponse(response, label: 'Employee Details')
|
return data is Map<String, dynamic> ? data : null;
|
||||||
: null;
|
|
||||||
if (data is Map<String, dynamic>) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Daily Tasks API Calls =====
|
// === Daily Task APIs ===
|
||||||
static Future<List<dynamic>?> getDailyTasks(String projectId,
|
|
||||||
{DateTime? dateFrom, DateTime? dateTo}) async {
|
static Future<List<dynamic>?> getDailyTasks(
|
||||||
|
String projectId, {
|
||||||
|
DateTime? dateFrom,
|
||||||
|
DateTime? dateTo,
|
||||||
|
}) async {
|
||||||
final query = {
|
final query = {
|
||||||
"projectId": projectId,
|
"projectId": projectId,
|
||||||
if (dateFrom != null)
|
if (dateFrom != null) "dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
|
||||||
"dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
|
|
||||||
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
|
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
|
||||||
};
|
};
|
||||||
|
return _getRequest(ApiEndpoints.getDailyTask, queryParams: query)
|
||||||
final response =
|
.then((res) => res != null ? _parseResponse(res, label: 'Daily Tasks') : null);
|
||||||
await _getRequest(ApiEndpoints.getDailyTask, queryParams: query);
|
|
||||||
return response != null
|
|
||||||
? _parseResponse(response, label: 'Daily Tasks')
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> reportTask({
|
static Future<bool> reportTask({
|
||||||
@ -363,34 +284,14 @@ class ApiService {
|
|||||||
if (images != null && images.isNotEmpty) "images": images,
|
if (images != null && images.isNotEmpty) "images": images,
|
||||||
};
|
};
|
||||||
|
|
||||||
String? token = await _getToken();
|
final response = await _postRequest(ApiEndpoints.reportTask, body);
|
||||||
if (token == null) return false;
|
if (response == null) return false;
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
final uri = Uri.parse("${ApiEndpoints.baseUrl}${ApiEndpoints.reportTask}");
|
if (response.statusCode == 200 && json['success'] == true) {
|
||||||
|
Get.back();
|
||||||
_log("POST $uri");
|
return true;
|
||||||
_log("Headers: ${_headers(token)}");
|
|
||||||
_log("Body: $body");
|
|
||||||
|
|
||||||
try {
|
|
||||||
final response = await http
|
|
||||||
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
|
||||||
.timeout(const Duration(seconds: 30));
|
|
||||||
|
|
||||||
_log("Response Status: ${response.statusCode}");
|
|
||||||
|
|
||||||
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'}");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
_log("HTTP POST Exception (reportTask): $e");
|
|
||||||
}
|
}
|
||||||
|
_log("Failed to report task: ${json['message'] ?? 'Unknown error'}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,45 +307,17 @@ class ApiService {
|
|||||||
if (images != null && images.isNotEmpty) "images": images,
|
if (images != null && images.isNotEmpty) "images": images,
|
||||||
};
|
};
|
||||||
|
|
||||||
String? token = await _getToken();
|
final response = await _postRequest(ApiEndpoints.commentTask, body);
|
||||||
if (token == null) return false;
|
if (response == null) return false;
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
final uri = Uri.parse("${ApiEndpoints.baseUrl}${ApiEndpoints.commentTask}");
|
return response.statusCode == 200 && json['success'] == true;
|
||||||
|
|
||||||
_log("POST $uri");
|
|
||||||
_log("Headers: ${_headers(token)}");
|
|
||||||
_log("Body: $body");
|
|
||||||
|
|
||||||
try {
|
|
||||||
final response = await http
|
|
||||||
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
|
||||||
.timeout(const Duration(seconds: 30));
|
|
||||||
|
|
||||||
_log("Response Status: ${response.statusCode}");
|
|
||||||
|
|
||||||
final json = jsonDecode(response.body);
|
|
||||||
if (response.statusCode == 200 && json['success'] == true) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
_log("Failed to comment task: ${json['message'] ?? 'Unknown error'}");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
_log("HTTP POST Exception (commentTask): $e");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daily Task Planing //
|
static Future<Map<String, dynamic>?> getDailyTasksDetails(String projectId) async {
|
||||||
|
|
||||||
static Future<Map<String, dynamic>?> getDailyTasksDetails(
|
|
||||||
String projectId) async {
|
|
||||||
final url = "${ApiEndpoints.dailyTaskDetails}/$projectId";
|
final url = "${ApiEndpoints.dailyTaskDetails}/$projectId";
|
||||||
|
|
||||||
final response = await _getRequest(url);
|
final response = await _getRequest(url);
|
||||||
return response != null
|
return response != null
|
||||||
? _parseResponseForAllData(response, label: 'Daily Task Details')
|
? _parseResponseForAllData(response, label: 'Daily Task Details') as Map<String, dynamic>?
|
||||||
as Map<String, dynamic>?
|
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,26 +333,16 @@ class ApiService {
|
|||||||
"plannedTask": plannedTask,
|
"plannedTask": plannedTask,
|
||||||
"description": description,
|
"description": description,
|
||||||
"taskTeam": taskTeam,
|
"taskTeam": taskTeam,
|
||||||
"assignmentDate":
|
"assignmentDate": (assignmentDate ?? DateTime.now()).toUtc().toIso8601String(),
|
||||||
(assignmentDate ?? DateTime.now()).toUtc().toIso8601String(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await _postRequest(ApiEndpoints.assignDailyTask, body);
|
final response = await _postRequest(ApiEndpoints.assignDailyTask, body);
|
||||||
|
if (response == null) return false;
|
||||||
if (response == null) {
|
|
||||||
_log("Error: No response from server.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
|
|
||||||
if (response.statusCode == 200 && json['success'] == true) {
|
if (response.statusCode == 200 && json['success'] == true) {
|
||||||
Get.back();
|
Get.back();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
_log(
|
|
||||||
"Failed to assign daily task: ${json['message'] ?? 'Unknown error'}");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
_log("Failed to assign daily task: ${json['message'] ?? 'Unknown error'}");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:marco/helpers/services/storage/local_storage.dart';
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:marco/controller/permission_controller.dart';
|
|
||||||
import 'package:logger/logger.dart';
|
import 'package:logger/logger.dart';
|
||||||
import 'package:marco/helpers/services/api_endpoints.dart';
|
import 'package:marco/controller/permission_controller.dart';
|
||||||
import 'package:marco/controller/project_controller.dart';
|
import 'package:marco/controller/project_controller.dart';
|
||||||
|
import 'package:marco/helpers/services/api_endpoints.dart';
|
||||||
|
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||||
|
|
||||||
final Logger logger = Logger();
|
final Logger logger = Logger();
|
||||||
|
|
||||||
@ -14,9 +14,11 @@ class AuthService {
|
|||||||
static const Map<String, String> _headers = {
|
static const Map<String, String> _headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool isLoggedIn = false;
|
static bool isLoggedIn = false;
|
||||||
static Future<Map<String, String>?> loginUser(
|
|
||||||
Map<String, dynamic> data) async {
|
/// Login with email and password
|
||||||
|
static Future<Map<String, String>?> loginUser(Map<String, dynamic> data) async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse("$_baseUrl/auth/login-mobile"),
|
Uri.parse("$_baseUrl/auth/login-mobile"),
|
||||||
@ -31,9 +33,7 @@ class AuthService {
|
|||||||
} else if (response.statusCode == 401) {
|
} else if (response.statusCode == 401) {
|
||||||
return {"password": "Invalid email or password"};
|
return {"password": "Invalid email or password"};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {"error": responseData['message'] ?? "Unexpected error occurred"};
|
||||||
"error": responseData['message'] ?? "Unexpected error occurred"
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Login error: $e");
|
logger.e("Login error: $e");
|
||||||
@ -41,16 +41,13 @@ class AuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refreshes the JWT token using the refresh token.
|
/// Refresh JWT token
|
||||||
static Future<bool> refreshToken() async {
|
static Future<bool> refreshToken() async {
|
||||||
final accessToken = await LocalStorage.getJwtToken();
|
final accessToken = await LocalStorage.getJwtToken();
|
||||||
final refreshToken = await LocalStorage.getRefreshToken();
|
final refreshToken = await LocalStorage.getRefreshToken();
|
||||||
|
|
||||||
if (accessToken == null ||
|
if (accessToken == null || refreshToken == null || accessToken.isEmpty || refreshToken.isEmpty) {
|
||||||
refreshToken == null ||
|
logger.w("Missing access/refresh token.");
|
||||||
accessToken.isEmpty ||
|
|
||||||
refreshToken.isEmpty) {
|
|
||||||
logger.w("Missing token or refresh token for refresh.");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,82 +56,50 @@ class AuthService {
|
|||||||
"refreshToken": refreshToken,
|
"refreshToken": refreshToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.i("Sending refresh token request with body: $requestBody");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse("$_baseUrl/auth/refresh-token"),
|
Uri.parse("$_baseUrl/auth/refresh-token"),
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: jsonEncode(requestBody),
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.i(
|
|
||||||
"Refresh token API response (${response.statusCode}): ${response.body}");
|
|
||||||
|
|
||||||
final data = jsonDecode(response.body);
|
|
||||||
if (response.statusCode == 200 && data['success'] == true) {
|
|
||||||
final newAccessToken = data['data']['token'];
|
|
||||||
final newRefreshToken = data['data']['refreshToken'];
|
|
||||||
|
|
||||||
if (newAccessToken == null || newRefreshToken == null) {
|
|
||||||
logger.w("Invalid tokens received during refresh.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await LocalStorage.setJwtToken(newAccessToken);
|
|
||||||
await LocalStorage.setRefreshToken(newRefreshToken);
|
|
||||||
await LocalStorage.setLoggedInUser(true);
|
|
||||||
|
|
||||||
logger.i("Token refreshed successfully.");
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
logger.w("Refresh failed: ${data['message']}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.e("Exception during token refresh: $e");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forgot password API
|
|
||||||
static Future<Map<String, String>?> forgotPassword(String email) async {
|
|
||||||
final requestBody = {"email": email};
|
|
||||||
|
|
||||||
logger.i("Sending forgot password request with email: $email");
|
|
||||||
|
|
||||||
try {
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse("$_baseUrl/auth/forgot-password"),
|
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
body: jsonEncode(requestBody),
|
body: jsonEncode(requestBody),
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.i(
|
final data = jsonDecode(response.body);
|
||||||
"Forgot password API response (${response.statusCode}): ${response.body}");
|
if (response.statusCode == 200 && data['success'] == true) {
|
||||||
|
await LocalStorage.setJwtToken(data['data']['token']);
|
||||||
final responseData = jsonDecode(response.body);
|
await LocalStorage.setRefreshToken(data['data']['refreshToken']);
|
||||||
|
await LocalStorage.setLoggedInUser(true);
|
||||||
if (response.statusCode == 200 && responseData['success'] == true) {
|
logger.i("Token refreshed.");
|
||||||
logger.i("Forgot password request successful.");
|
return true;
|
||||||
return null;
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
logger.w("Refresh token failed: ${data['message']}");
|
||||||
"error":
|
return false;
|
||||||
responseData['message'] ?? "Failed to send password reset link."
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Exception during forgot password request: $e");
|
logger.e("Token refresh error: $e");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Forgot password
|
||||||
|
static Future<Map<String, String>?> forgotPassword(String email) async {
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse("$_baseUrl/auth/forgot-password"),
|
||||||
|
headers: _headers,
|
||||||
|
body: jsonEncode({"email": email}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
||||||
|
return {"error": data['message'] ?? "Failed to send reset link."};
|
||||||
|
} catch (e) {
|
||||||
|
logger.e("Forgot password error: $e");
|
||||||
return {"error": "Network error. Please check your connection."};
|
return {"error": "Network error. Please check your connection."};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request demo API
|
/// Request demo
|
||||||
static Future<Map<String, String>?> requestDemo(
|
static Future<Map<String, String>?> requestDemo(Map<String, dynamic> demoData) async {
|
||||||
Map<String, dynamic> demoData) async {
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse("$_baseUrl/market/inquiry"),
|
Uri.parse("$_baseUrl/market/inquiry"),
|
||||||
@ -142,22 +107,16 @@ class AuthService {
|
|||||||
body: jsonEncode(demoData),
|
body: jsonEncode(demoData),
|
||||||
);
|
);
|
||||||
|
|
||||||
final responseData = jsonDecode(response.body);
|
final data = jsonDecode(response.body);
|
||||||
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
||||||
if (response.statusCode == 200 && responseData['success'] == true) {
|
return {"error": data['message'] ?? "Failed to submit demo request."};
|
||||||
logger.i("Request Demo submitted successfully.");
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
"error": responseData['message'] ?? "Failed to submit demo request."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Exception during request demo: $e");
|
logger.e("Request demo error: $e");
|
||||||
return {"error": "Network error. Please check your connection."};
|
return {"error": "Network error. Please check your connection."};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get list of industries
|
||||||
static Future<List<Map<String, dynamic>>?> getIndustries() async {
|
static Future<List<Map<String, dynamic>>?> getIndustries() async {
|
||||||
try {
|
try {
|
||||||
final response = await http.get(
|
final response = await http.get(
|
||||||
@ -165,201 +124,129 @@ class AuthService {
|
|||||||
headers: _headers,
|
headers: _headers,
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.i(
|
final data = jsonDecode(response.body);
|
||||||
"Get Industries API response (${response.statusCode}): ${response.body}");
|
if (response.statusCode == 200 && data['success'] == true) {
|
||||||
|
return List<Map<String, dynamic>>.from(data['data']);
|
||||||
final responseData = jsonDecode(response.body);
|
|
||||||
|
|
||||||
if (response.statusCode == 200 && responseData['success'] == true) {
|
|
||||||
// Return the list of industries as List<Map<String, dynamic>>
|
|
||||||
final List<dynamic> industriesData = responseData['data'];
|
|
||||||
return industriesData.cast<Map<String, dynamic>>();
|
|
||||||
} else {
|
|
||||||
logger.w("Failed to fetch industries: ${responseData['message']}");
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Exception during getIndustries: $e");
|
logger.e("Get industries error: $e");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a new MPIN for the user.
|
/// Generate MPIN
|
||||||
static Future<Map<String, String>?> generateMpin({
|
static Future<Map<String, String>?> generateMpin({
|
||||||
required String employeeId,
|
required String employeeId,
|
||||||
required String mpin,
|
required String mpin,
|
||||||
}) async {
|
}) async {
|
||||||
final jwtToken = await LocalStorage.getJwtToken();
|
final token = await LocalStorage.getJwtToken();
|
||||||
|
|
||||||
final requestBody = {
|
|
||||||
"employeeId": employeeId,
|
|
||||||
"mpin": mpin,
|
|
||||||
};
|
|
||||||
|
|
||||||
logger.i("Sending MPIN generation request: $requestBody");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse("$_baseUrl/auth/generate-mpin"),
|
Uri.parse("$_baseUrl/auth/generate-mpin"),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
..._headers,
|
||||||
if (jwtToken != null && jwtToken.isNotEmpty)
|
if (token != null && token.isNotEmpty) 'Authorization': 'Bearer $token',
|
||||||
'Authorization': 'Bearer $jwtToken',
|
|
||||||
},
|
},
|
||||||
body: jsonEncode(requestBody),
|
body: jsonEncode({"employeeId": employeeId, "mpin": mpin}),
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.i(
|
final data = jsonDecode(response.body);
|
||||||
"Generate MPIN API response (${response.statusCode}): ${response.body}");
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
||||||
|
return {"error": data['message'] ?? "Failed to generate MPIN."};
|
||||||
final responseData = jsonDecode(response.body);
|
|
||||||
|
|
||||||
if (response.statusCode == 200 && responseData['success'] == true) {
|
|
||||||
logger.i("MPIN generated successfully.");
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return {"error": responseData['message'] ?? "Failed to generate MPIN."};
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Exception during generate MPIN: $e");
|
logger.e("Generate MPIN error: $e");
|
||||||
return {"error": "Network error. Please check your connection."};
|
return {"error": "Network error. Please check your connection."};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify MPIN
|
||||||
static Future<Map<String, String>?> verifyMpin({
|
static Future<Map<String, String>?> verifyMpin({
|
||||||
required String mpin,
|
required String mpin,
|
||||||
required String mpinToken,
|
required String mpinToken,
|
||||||
}) async {
|
}) async {
|
||||||
// Get employee info from local storage
|
|
||||||
final employeeInfo = LocalStorage.getEmployeeInfo();
|
final employeeInfo = LocalStorage.getEmployeeInfo();
|
||||||
|
if (employeeInfo == null) return {"error": "Employee info not found."};
|
||||||
|
|
||||||
if (employeeInfo == null) {
|
final token = await LocalStorage.getJwtToken();
|
||||||
logger.w("Employee info not found in local storage.");
|
|
||||||
return {"error": "Employee info not found. Please login again."};
|
|
||||||
}
|
|
||||||
|
|
||||||
final employeeId = employeeInfo.id;
|
|
||||||
|
|
||||||
final jwtToken = await LocalStorage.getJwtToken();
|
|
||||||
|
|
||||||
final requestBody = {
|
|
||||||
"employeeId": employeeId,
|
|
||||||
"mpin": mpin,
|
|
||||||
"mpinToken": mpinToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
logger.i("Sending MPIN verification request: $requestBody");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse("$_baseUrl/auth/login-mpin"),
|
Uri.parse("$_baseUrl/auth/login-mpin"),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
..._headers,
|
||||||
if (jwtToken != null && jwtToken.isNotEmpty)
|
if (token != null && token.isNotEmpty) 'Authorization': 'Bearer $token',
|
||||||
'Authorization': 'Bearer $jwtToken',
|
|
||||||
},
|
},
|
||||||
body: jsonEncode(requestBody),
|
body: jsonEncode({
|
||||||
|
"employeeId": employeeInfo.id,
|
||||||
|
"mpin": mpin,
|
||||||
|
"mpinToken": mpinToken,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.i(
|
final data = jsonDecode(response.body);
|
||||||
"Verify MPIN API response (${response.statusCode}): ${response.body}");
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
||||||
|
return {"error": data['message'] ?? "MPIN verification failed."};
|
||||||
final responseData = jsonDecode(response.body);
|
|
||||||
|
|
||||||
if (response.statusCode == 200 && responseData['success'] == true) {
|
|
||||||
logger.i("MPIN verified successfully.");
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return {"error": responseData['message'] ?? "Failed to verify MPIN."};
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Exception during verify MPIN: $e");
|
logger.e("Verify MPIN error: $e");
|
||||||
return {"error": "Network error. Please check your connection."};
|
return {"error": "Network error. Please check your connection."};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate OTP API
|
/// Generate OTP
|
||||||
static Future<Map<String, String>?> generateOtp(String email) async {
|
static Future<Map<String, String>?> generateOtp(String email) async {
|
||||||
final requestBody = {"email": email};
|
|
||||||
|
|
||||||
logger.i("Sending generate OTP request: $requestBody");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse("$_baseUrl/auth/send-otp"),
|
Uri.parse("$_baseUrl/auth/send-otp"),
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
body: jsonEncode(requestBody),
|
body: jsonEncode({"email": email}),
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.i(
|
final data = jsonDecode(response.body);
|
||||||
"Generate OTP API response (${response.statusCode}): ${response.body}");
|
if (response.statusCode == 200 && data['success'] == true) return null;
|
||||||
|
return {"error": data['message'] ?? "Failed to generate OTP."};
|
||||||
final responseData = jsonDecode(response.body);
|
|
||||||
|
|
||||||
if (response.statusCode == 200 && responseData['success'] == true) {
|
|
||||||
logger.i("OTP generated successfully.");
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return {"error": responseData['message'] ?? "Failed to generate OTP."};
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Exception during generate OTP: $e");
|
logger.e("Generate OTP error: $e");
|
||||||
return {"error": "Network error. Please check your connection."};
|
return {"error": "Network error. Please check your connection."};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify OTP API
|
/// Verify OTP and login
|
||||||
static Future<Map<String, String>?> verifyOtp({
|
static Future<Map<String, String>?> verifyOtp({
|
||||||
required String email,
|
required String email,
|
||||||
required String otp,
|
required String otp,
|
||||||
}) async {
|
}) async {
|
||||||
final requestBody = {
|
|
||||||
"email": email,
|
|
||||||
"otp": otp,
|
|
||||||
};
|
|
||||||
|
|
||||||
logger.i("Sending verify OTP request: $requestBody");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse("$_baseUrl/auth/login-otp"),
|
Uri.parse("$_baseUrl/auth/login-otp"),
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
body: jsonEncode(requestBody),
|
body: jsonEncode({"email": email, "otp": otp}),
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.i(
|
final data = jsonDecode(response.body);
|
||||||
"Verify OTP API response (${response.statusCode}): ${response.body}");
|
if (response.statusCode == 200 && data['data'] != null) {
|
||||||
|
await _handleLoginSuccess(data['data']);
|
||||||
final responseData = jsonDecode(response.body);
|
|
||||||
|
|
||||||
if (response.statusCode == 200 && responseData['data'] != null) {
|
|
||||||
await _handleLoginSuccess(responseData['data']);
|
|
||||||
logger.i("OTP verified and login state initialized successfully.");
|
|
||||||
return null;
|
return null;
|
||||||
} else {
|
|
||||||
return {"error": responseData['message'] ?? "Failed to verify OTP."};
|
|
||||||
}
|
}
|
||||||
|
return {"error": data['message'] ?? "OTP verification failed."};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Exception during verify OTP: $e");
|
logger.e("Verify OTP error: $e");
|
||||||
return {"error": "Network error. Please check your connection."};
|
return {"error": "Network error. Please check your connection."};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle login success flow
|
||||||
static Future<void> _handleLoginSuccess(Map<String, dynamic> data) async {
|
static Future<void> _handleLoginSuccess(Map<String, dynamic> data) async {
|
||||||
final jwtToken = data['token'];
|
final jwtToken = data['token'];
|
||||||
final refreshToken = data['refreshToken'];
|
final refreshToken = data['refreshToken'];
|
||||||
final mpinToken = data['mpinToken'];
|
final mpinToken = data['mpinToken'];
|
||||||
|
|
||||||
logger.i("JWT Token: $jwtToken");
|
|
||||||
if (refreshToken != null) logger.i("Refresh Token: $refreshToken");
|
|
||||||
if (mpinToken != null) logger.i("MPIN Token: $mpinToken");
|
|
||||||
|
|
||||||
await LocalStorage.setJwtToken(jwtToken);
|
await LocalStorage.setJwtToken(jwtToken);
|
||||||
await LocalStorage.setLoggedInUser(true);
|
await LocalStorage.setLoggedInUser(true);
|
||||||
|
|
||||||
if (refreshToken != null) {
|
if (refreshToken != null) await LocalStorage.setRefreshToken(refreshToken);
|
||||||
await LocalStorage.setRefreshToken(refreshToken);
|
|
||||||
}
|
|
||||||
if (mpinToken != null && mpinToken.isNotEmpty) {
|
if (mpinToken != null && mpinToken.isNotEmpty) {
|
||||||
await LocalStorage.setMpinToken(mpinToken);
|
await LocalStorage.setMpinToken(mpinToken);
|
||||||
await LocalStorage.setIsMpin(true);
|
await LocalStorage.setIsMpin(true);
|
||||||
@ -368,11 +255,11 @@ class AuthService {
|
|||||||
await LocalStorage.removeMpinToken();
|
await LocalStorage.removeMpinToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Put and load PermissionController
|
|
||||||
final permissionController = Get.put(PermissionController());
|
final permissionController = Get.put(PermissionController());
|
||||||
await permissionController.loadData(jwtToken);
|
await permissionController.loadData(jwtToken);
|
||||||
await Get.find<ProjectController>().fetchProjects();
|
await Get.find<ProjectController>().fetchProjects();
|
||||||
|
|
||||||
isLoggedIn = true;
|
isLoggedIn = true;
|
||||||
|
logger.i("Login success initialized.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:logger/logger.dart';
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
import 'package:marco/model/user_permission.dart';
|
import 'package:marco/model/user_permission.dart';
|
||||||
import 'package:marco/model/employee_info.dart';
|
import 'package:marco/model/employee_info.dart';
|
||||||
import 'package:marco/model/projects_model.dart';
|
import 'package:marco/model/projects_model.dart';
|
||||||
@ -13,19 +14,21 @@ final Logger logger = Logger();
|
|||||||
class PermissionService {
|
class PermissionService {
|
||||||
static final Map<String, Map<String, dynamic>> _userDataCache = {};
|
static final Map<String, Map<String, dynamic>> _userDataCache = {};
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> fetchAllUserData(String token,
|
/// Fetches all user-related data (permissions, employee info, projects)
|
||||||
{bool hasRetried = false}) async {
|
static Future<Map<String, dynamic>> fetchAllUserData(
|
||||||
// Return from cache if available
|
String token, {
|
||||||
|
bool hasRetried = false,
|
||||||
|
}) async {
|
||||||
|
// Return cached data if already available
|
||||||
if (_userDataCache.containsKey(token)) {
|
if (_userDataCache.containsKey(token)) {
|
||||||
return _userDataCache[token]!;
|
return _userDataCache[token]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final uri = Uri.parse('https://stageapi.marcoaiot.com/api/user/profile');
|
||||||
|
final headers = {'Authorization': 'Bearer $token'};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.get(
|
final response = await http.get(uri, headers: headers);
|
||||||
Uri.parse('https://stageapi.marcoaiot.com/api/user/profile'),
|
|
||||||
// Uri.parse('https://api.marcoaiot.com/api/user/profile'),
|
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = json.decode(response.body)['data'];
|
final data = json.decode(response.body)['data'];
|
||||||
@ -40,11 +43,12 @@ class PermissionService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle 401 by attempting a single retry with refreshed token
|
||||||
if (response.statusCode == 401 && !hasRetried) {
|
if (response.statusCode == 401 && !hasRetried) {
|
||||||
final refreshed = await AuthService.refreshToken();
|
final refreshed = await AuthService.refreshToken();
|
||||||
if (refreshed) {
|
if (refreshed) {
|
||||||
final newToken = await LocalStorage.getJwtToken();
|
final newToken = await LocalStorage.getJwtToken();
|
||||||
if (newToken != null) {
|
if (newToken != null && newToken.isNotEmpty) {
|
||||||
return fetchAllUserData(newToken, hasRetried: true);
|
return fetchAllUserData(newToken, hasRetried: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,15 +57,15 @@ class PermissionService {
|
|||||||
throw Exception('Unauthorized. Token refresh failed.');
|
throw Exception('Unauthorized. Token refresh failed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final errorMessage =
|
final error = json.decode(response.body)['message'] ?? 'Unknown error';
|
||||||
json.decode(response.body)['message'] ?? 'Unknown error';
|
throw Exception('Failed to fetch user data: $error');
|
||||||
throw Exception('Failed to load data: $errorMessage');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e('Error fetching user data: $e');
|
logger.e('Error fetching user data: $e');
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears auth data and redirects to login
|
||||||
static Future<void> _handleUnauthorized() async {
|
static Future<void> _handleUnauthorized() async {
|
||||||
await LocalStorage.removeToken('jwt_token');
|
await LocalStorage.removeToken('jwt_token');
|
||||||
await LocalStorage.removeToken('refresh_token');
|
await LocalStorage.removeToken('refresh_token');
|
||||||
@ -69,15 +73,20 @@ class PermissionService {
|
|||||||
Get.offAllNamed('/auth/login-option');
|
Get.offAllNamed('/auth/login-option');
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<UserPermission> _parsePermissions(
|
/// Converts raw permission data into list of `UserPermission`
|
||||||
List<dynamic> featurePermissions) =>
|
static List<UserPermission> _parsePermissions(List<dynamic> permissions) {
|
||||||
featurePermissions
|
return permissions
|
||||||
.map((id) => UserPermission.fromJson({'id': id}))
|
.map((id) => UserPermission.fromJson({'id': id}))
|
||||||
.toList();
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
static EmployeeInfo _parseEmployeeInfo(Map<String, dynamic> employeeData) =>
|
/// Converts raw employee JSON into `EmployeeInfo`
|
||||||
EmployeeInfo.fromJson(employeeData);
|
static EmployeeInfo _parseEmployeeInfo(Map<String, dynamic> data) {
|
||||||
|
return EmployeeInfo.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
static List<ProjectInfo> _parseProjectsInfo(List<dynamic> projects) =>
|
/// Converts raw projects JSON into list of `ProjectInfo`
|
||||||
projects.map((proj) => ProjectInfo.fromJson(proj)).toList();
|
static List<ProjectInfo> _parseProjectsInfo(List<dynamic> projects) {
|
||||||
|
return projects.map((proj) => ProjectInfo.fromJson(proj)).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user