added loggers
This commit is contained in:
parent
f5afed0d8b
commit
2a91ccc323
@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import 'package:marco/helpers/services/api_service.dart';
|
import 'package:marco/helpers/services/api_service.dart';
|
||||||
import 'package:marco/model/attendance_model.dart';
|
import 'package:marco/model/attendance_model.dart';
|
||||||
import 'package:marco/model/project_model.dart';
|
import 'package:marco/model/project_model.dart';
|
||||||
@ -11,6 +12,10 @@ import 'package:marco/model/attendance_log_model.dart';
|
|||||||
import 'package:marco/model/regularization_log_model.dart';
|
import 'package:marco/model/regularization_log_model.dart';
|
||||||
import 'package:marco/model/attendance_log_view_model.dart';
|
import 'package:marco/model/attendance_log_view_model.dart';
|
||||||
|
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
|
final Logger log = Logger();
|
||||||
|
|
||||||
class AttendanceController extends GetxController {
|
class AttendanceController extends GetxController {
|
||||||
List<AttendanceModel> attendances = [];
|
List<AttendanceModel> attendances = [];
|
||||||
List<ProjectModel> projects = [];
|
List<ProjectModel> projects = [];
|
||||||
@ -24,7 +29,7 @@ class AttendanceController extends GetxController {
|
|||||||
List<RegularizationLogModel> regularizationLogs = [];
|
List<RegularizationLogModel> regularizationLogs = [];
|
||||||
List<AttendanceLogViewModel> attendenceLogsView = [];
|
List<AttendanceLogViewModel> attendenceLogsView = [];
|
||||||
|
|
||||||
RxBool isLoading = false.obs; // Added loading flag
|
RxBool isLoading = false.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -41,59 +46,63 @@ class AttendanceController extends GetxController {
|
|||||||
final today = DateTime.now();
|
final today = DateTime.now();
|
||||||
startDateAttendance = today.subtract(const Duration(days: 7));
|
startDateAttendance = today.subtract(const Duration(days: 7));
|
||||||
endDateAttendance = today;
|
endDateAttendance = today;
|
||||||
|
log.i("Default date range set: $startDateAttendance to $endDateAttendance");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchProjects() async {
|
Future<void> fetchProjects() async {
|
||||||
isLoading.value = true; // Set loading to true before API call
|
isLoading.value = true;
|
||||||
final response = await ApiService.getProjects();
|
final response = await ApiService.getProjects();
|
||||||
isLoading.value = false; // Set loading to false after API call completes
|
isLoading.value = false;
|
||||||
|
|
||||||
if (response != null && response.isNotEmpty) {
|
if (response != null && response.isNotEmpty) {
|
||||||
projects = response.map((json) => ProjectModel.fromJson(json)).toList();
|
projects = response.map((json) => ProjectModel.fromJson(json)).toList();
|
||||||
selectedProjectId = projects.first.id.toString();
|
selectedProjectId = projects.first.id.toString();
|
||||||
|
log.i("Projects fetched: ${projects.length} projects loaded.");
|
||||||
await fetchProjectData(selectedProjectId);
|
await fetchProjectData(selectedProjectId);
|
||||||
update(['attendance_dashboard_controller']);
|
update(['attendance_dashboard_controller']);
|
||||||
} else {
|
} else {
|
||||||
print("No projects data found or failed to fetch data.");
|
log.w("No project data found or API call failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchProjectData(String? projectId) async {
|
Future<void> fetchProjectData(String? projectId) async {
|
||||||
if (projectId == null) return;
|
if (projectId == null) return;
|
||||||
|
|
||||||
isLoading.value = true; // Set loading to true before API call
|
isLoading.value = true;
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
fetchEmployeesByProject(projectId),
|
fetchEmployeesByProject(projectId),
|
||||||
fetchAttendanceLogs(projectId,
|
fetchAttendanceLogs(projectId,
|
||||||
dateFrom: startDateAttendance, dateTo: endDateAttendance),
|
dateFrom: startDateAttendance, dateTo: endDateAttendance),
|
||||||
fetchRegularizationLogs(projectId),
|
fetchRegularizationLogs(projectId),
|
||||||
]);
|
]);
|
||||||
isLoading.value = false; // Set loading to false after data is fetched
|
isLoading.value = false;
|
||||||
|
log.i("Project data fetched for project ID: $projectId");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchEmployeesByProject(String? projectId) async {
|
Future<void> fetchEmployeesByProject(String? projectId) async {
|
||||||
if (projectId == null) return;
|
if (projectId == null) return;
|
||||||
|
|
||||||
isLoading.value = true; // Set loading to true before API call
|
isLoading.value = true;
|
||||||
final response =
|
final response = await ApiService.getEmployeesByProject(projectId);
|
||||||
await ApiService.getEmployeesByProject(projectId);
|
isLoading.value = false;
|
||||||
isLoading.value = false; // Set loading to false after API call completes
|
|
||||||
|
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
employees = response.map((json) => EmployeeModel.fromJson(json)).toList();
|
employees = response.map((json) => EmployeeModel.fromJson(json)).toList();
|
||||||
|
log.i(
|
||||||
|
"Employees fetched: ${employees.length} employees for project $projectId");
|
||||||
update();
|
update();
|
||||||
} else {
|
} else {
|
||||||
print("Failed to fetch employees for project $projectId.");
|
log.e("Failed to fetch employees for project $projectId");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> captureAndUploadAttendance(
|
Future<bool> captureAndUploadAttendance(
|
||||||
String id, // Change from int to String
|
String id,
|
||||||
String employeeId, // Change from int to String
|
String employeeId,
|
||||||
String projectId, {
|
String projectId, {
|
||||||
String comment = "Marked via mobile app",
|
String comment = "Marked via mobile app",
|
||||||
required int action,
|
required int action,
|
||||||
bool imageCapture = true, // <- add this flag
|
bool imageCapture = true,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
XFile? image;
|
XFile? image;
|
||||||
@ -102,7 +111,10 @@ class AttendanceController extends GetxController {
|
|||||||
source: ImageSource.camera,
|
source: ImageSource.camera,
|
||||||
imageQuality: 80,
|
imageQuality: 80,
|
||||||
);
|
);
|
||||||
if (image == null) return false;
|
if (image == null) {
|
||||||
|
log.w("Image capture cancelled.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final position = await Geolocator.getCurrentPosition(
|
final position = await Geolocator.getCurrentPosition(
|
||||||
@ -111,9 +123,9 @@ class AttendanceController extends GetxController {
|
|||||||
|
|
||||||
final imageName = imageCapture
|
final imageName = imageCapture
|
||||||
? ApiService.generateImageName(employeeId, employees.length + 1)
|
? ApiService.generateImageName(employeeId, employees.length + 1)
|
||||||
: ""; // Empty or null if not capturing image
|
: "";
|
||||||
|
|
||||||
return await ApiService.uploadAttendanceImage(
|
final result = await ApiService.uploadAttendanceImage(
|
||||||
id,
|
id,
|
||||||
employeeId,
|
employeeId,
|
||||||
image,
|
image,
|
||||||
@ -123,10 +135,13 @@ class AttendanceController extends GetxController {
|
|||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
comment: comment,
|
comment: comment,
|
||||||
action: action,
|
action: action,
|
||||||
imageCapture: imageCapture, // <- pass flag down
|
imageCapture: imageCapture,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
|
||||||
print("Error capturing or uploading attendance: $e");
|
log.i("Attendance uploaded for $employeeId, action: $action");
|
||||||
|
return result;
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
log.e("Error uploading attendance", error: e, stackTrace: stacktrace);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,6 +165,8 @@ class AttendanceController extends GetxController {
|
|||||||
startDateAttendance = picked.start;
|
startDateAttendance = picked.start;
|
||||||
endDateAttendance = picked.end;
|
endDateAttendance = picked.end;
|
||||||
|
|
||||||
|
log.i("Date range selected: $startDateAttendance to $endDateAttendance");
|
||||||
|
|
||||||
await controller.fetchAttendanceLogs(
|
await controller.fetchAttendanceLogs(
|
||||||
controller.selectedProjectId,
|
controller.selectedProjectId,
|
||||||
dateFrom: picked.start,
|
dateFrom: picked.start,
|
||||||
@ -165,39 +182,39 @@ class AttendanceController extends GetxController {
|
|||||||
}) async {
|
}) async {
|
||||||
if (projectId == null) return;
|
if (projectId == null) return;
|
||||||
|
|
||||||
isLoading.value = true; // Set loading to true before API call
|
isLoading.value = true;
|
||||||
final response = await ApiService.getAttendanceLogs(
|
final response = await ApiService.getAttendanceLogs(
|
||||||
projectId,
|
projectId,
|
||||||
dateFrom: dateFrom,
|
dateFrom: dateFrom,
|
||||||
dateTo: dateTo,
|
dateTo: dateTo,
|
||||||
);
|
);
|
||||||
isLoading.value = false; // Set loading to false after API call completes
|
isLoading.value = false;
|
||||||
|
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
attendanceLogs =
|
attendanceLogs =
|
||||||
response.map((json) => AttendanceLogModel.fromJson(json)).toList();
|
response.map((json) => AttendanceLogModel.fromJson(json)).toList();
|
||||||
print("Attendance logs fetched: ${response}");
|
log.i("Attendance logs fetched: ${attendanceLogs.length}");
|
||||||
update();
|
update();
|
||||||
} else {
|
} else {
|
||||||
print("Failed to fetch attendance logs for project $projectId.");
|
log.e("Failed to fetch attendance logs for project $projectId");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map<String, List<AttendanceLogModel>> groupLogsByCheckInDate() {
|
|
||||||
final groupedLogs = <String, List<AttendanceLogModel>>{};
|
|
||||||
|
|
||||||
for (var log in attendanceLogs) {
|
Map<String, List<AttendanceLogModel>> groupLogsByCheckInDate() {
|
||||||
final checkInDate = log.checkIn != null
|
final groupedLogs = <String, List<AttendanceLogModel>>{};
|
||||||
? DateFormat('dd MMM yyyy').format(log.checkIn!)
|
|
||||||
: 'Unknown';
|
|
||||||
|
|
||||||
if (!groupedLogs.containsKey(checkInDate)) {
|
for (var logItem in attendanceLogs) {
|
||||||
groupedLogs[checkInDate] = [];
|
final checkInDate = logItem.checkIn != null
|
||||||
|
? DateFormat('dd MMM yyyy').format(logItem.checkIn!)
|
||||||
|
: 'Unknown';
|
||||||
|
|
||||||
|
groupedLogs.putIfAbsent(checkInDate, () => []);
|
||||||
|
groupedLogs[checkInDate]!.add(logItem);
|
||||||
}
|
}
|
||||||
groupedLogs[checkInDate]!.add(log);
|
|
||||||
}
|
|
||||||
|
|
||||||
return groupedLogs;
|
log.i("Logs grouped by check-in date.");
|
||||||
}
|
return groupedLogs;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> fetchRegularizationLogs(
|
Future<void> fetchRegularizationLogs(
|
||||||
String? projectId, {
|
String? projectId, {
|
||||||
@ -207,34 +224,35 @@ Map<String, List<AttendanceLogModel>> groupLogsByCheckInDate() {
|
|||||||
if (projectId == null) return;
|
if (projectId == null) return;
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
final response =
|
final response = await ApiService.getRegularizationLogs(projectId);
|
||||||
await ApiService.getRegularizationLogs(projectId);
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
regularizationLogs = response
|
regularizationLogs = response
|
||||||
.map((json) => RegularizationLogModel.fromJson(json))
|
.map((json) => RegularizationLogModel.fromJson(json))
|
||||||
.toList();
|
.toList();
|
||||||
|
log.i("Regularization logs fetched: ${regularizationLogs.length}");
|
||||||
update();
|
update();
|
||||||
} else {
|
} else {
|
||||||
print("Failed to fetch regularization logs for project $projectId.");
|
log.e("Failed to fetch regularization logs for project $projectId");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchLogsView(String? id) async {
|
Future<void> fetchLogsView(String? id) async {
|
||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
|
|
||||||
isLoading.value = true; // Set loading to true before API call
|
isLoading.value = true;
|
||||||
final response = await ApiService.getAttendanceLogView(id);
|
final response = await ApiService.getAttendanceLogView(id);
|
||||||
isLoading.value = false; // Set loading to false after API call completes
|
isLoading.value = false;
|
||||||
|
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
attendenceLogsView = response
|
attendenceLogsView = response
|
||||||
.map((json) => AttendanceLogViewModel.fromJson(json))
|
.map((json) => AttendanceLogViewModel.fromJson(json))
|
||||||
.toList();
|
.toList();
|
||||||
|
log.i("Attendance log view fetched for ID: $id");
|
||||||
update();
|
update();
|
||||||
} else {
|
} else {
|
||||||
print("Failed to fetch regularization logs for project $id.");
|
log.e("Failed to fetch attendance log view for ID $id");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,15 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
import 'package:marco/helpers/services/permission_service.dart';
|
import 'package:marco/helpers/services/permission_service.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';
|
||||||
|
|
||||||
|
final log = Logger();
|
||||||
|
|
||||||
class PermissionController extends GetxController {
|
class PermissionController extends GetxController {
|
||||||
var permissions = <UserPermission>[].obs;
|
var permissions = <UserPermission>[].obs;
|
||||||
var employeeInfo = Rxn<EmployeeInfo>();
|
var employeeInfo = Rxn<EmployeeInfo>();
|
||||||
@ -20,84 +24,99 @@ class PermissionController extends GetxController {
|
|||||||
_startAutoRefresh();
|
_startAutoRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store all data at once to reduce redundant operations
|
|
||||||
Future<void> _storeData() async {
|
Future<void> _storeData() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
try {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
// Store permissions
|
|
||||||
await prefs.setString(
|
|
||||||
'user_permissions', jsonEncode(permissions.map((e) => e.toJson()).toList()));
|
|
||||||
|
|
||||||
// Store employee info if available
|
|
||||||
if (employeeInfo.value != null) {
|
|
||||||
await prefs.setString(
|
await prefs.setString(
|
||||||
'employee_info', jsonEncode(employeeInfo.value!.toJson()));
|
'user_permissions',
|
||||||
}
|
jsonEncode(permissions.map((e) => e.toJson()).toList()),
|
||||||
|
);
|
||||||
|
|
||||||
// Store projects info if available
|
if (employeeInfo.value != null) {
|
||||||
if (projectsInfo.isNotEmpty) {
|
await prefs.setString(
|
||||||
await prefs.setString('projects_info',
|
'employee_info',
|
||||||
jsonEncode(projectsInfo.map((e) => e.toJson()).toList()));
|
jsonEncode(employeeInfo.value!.toJson()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectsInfo.isNotEmpty) {
|
||||||
|
await prefs.setString(
|
||||||
|
'projects_info',
|
||||||
|
jsonEncode(projectsInfo.map((e) => e.toJson()).toList()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.i("User data successfully stored in SharedPreferences.");
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
log.e("Error storing data", error: e, stackTrace: stacktrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch and load all required data (permissions, employee info, and projects)
|
|
||||||
Future<void> _loadDataFromAPI() async {
|
Future<void> _loadDataFromAPI() async {
|
||||||
final token = await _getAuthToken();
|
final token = await _getAuthToken();
|
||||||
if (token?.isNotEmpty ?? false) {
|
if (token?.isNotEmpty ?? false) {
|
||||||
await loadData(token!);
|
await loadData(token!);
|
||||||
} else {
|
} else {
|
||||||
print("No token available for fetching data.");
|
log.w("No token found for loading API data.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch data and update the state (permissions, employee info, and projects)
|
|
||||||
Future<void> loadData(String token) async {
|
Future<void> loadData(String token) async {
|
||||||
try {
|
try {
|
||||||
final userData = await PermissionService.fetchAllUserData(token);
|
final userData = await PermissionService.fetchAllUserData(token);
|
||||||
|
|
||||||
// Update state variables
|
|
||||||
_updateState(userData);
|
_updateState(userData);
|
||||||
|
|
||||||
// Store all data after fetching
|
|
||||||
await _storeData();
|
await _storeData();
|
||||||
} catch (e) {
|
log.i("Data loaded and state updated successfully.");
|
||||||
print('Error loading data from API: $e');
|
} catch (e, stacktrace) {
|
||||||
|
log.e("Error loading data from API", error: e, stackTrace: stacktrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update state variables (permissions, employeeInfo, and projects)
|
|
||||||
void _updateState(Map<String, dynamic> userData) {
|
void _updateState(Map<String, dynamic> userData) {
|
||||||
permissions.assignAll(userData['permissions']);
|
try {
|
||||||
employeeInfo.value = userData['employeeInfo'];
|
permissions.assignAll(userData['permissions']);
|
||||||
projectsInfo.assignAll(userData['projects']);
|
employeeInfo.value = userData['employeeInfo'];
|
||||||
|
projectsInfo.assignAll(userData['projects']);
|
||||||
|
log.i("State updated with new user data.");
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
log.e("Error updating state", error: e, stackTrace: stacktrace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the auth token from SharedPreferences
|
|
||||||
Future<String?> _getAuthToken() async {
|
Future<String?> _getAuthToken() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
try {
|
||||||
return prefs.getString('jwt_token');
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
return prefs.getString('jwt_token');
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
log.e("Error retrieving auth token", error: e, stackTrace: stacktrace);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up automatic data refresh every 30 minutes
|
|
||||||
void _startAutoRefresh() {
|
void _startAutoRefresh() {
|
||||||
_refreshTimer = Timer.periodic(Duration(minutes: 30), (timer) async {
|
_refreshTimer = Timer.periodic(Duration(minutes: 30), (timer) async {
|
||||||
|
log.i("Auto-refresh triggered.");
|
||||||
await _loadDataFromAPI();
|
await _loadDataFromAPI();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user has the given permission
|
|
||||||
bool hasPermission(String permissionId) {
|
bool hasPermission(String permissionId) {
|
||||||
return permissions.any((p) => p.id == permissionId);
|
final hasPerm = permissions.any((p) => p.id == permissionId);
|
||||||
|
log.d("Checking permission $permissionId: $hasPerm");
|
||||||
|
return hasPerm;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isUserAssignedToProject(String projectId) {
|
bool isUserAssignedToProject(String projectId) {
|
||||||
return projectsInfo.any((project) => project.id == projectId);
|
final assigned = projectsInfo.any((project) => project.id == projectId);
|
||||||
|
log.d("Checking project assignment for $projectId: $assigned");
|
||||||
|
return assigned;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
_refreshTimer?.cancel();
|
_refreshTimer?.cancel();
|
||||||
|
log.i("PermissionController disposed and timer cancelled.");
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,24 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
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:marco/helpers/services/storage/local_storage.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||||
|
import 'package:marco/helpers/services/auth_service.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
|
final Logger logger = Logger();
|
||||||
|
|
||||||
class ApiService {
|
class ApiService {
|
||||||
static const String baseUrl = "https://stageapi.marcoaiot.com/api";
|
static const String baseUrl = "https://stageapi.marcoaiot.com/api";
|
||||||
|
static const Duration timeout = Duration(seconds: 10);
|
||||||
|
static const bool enableLogs = true;
|
||||||
|
|
||||||
// ===== Common Helpers =====
|
// ===== Helpers =====
|
||||||
|
|
||||||
static Future<String?> _getToken() async {
|
static Future<String?> _getToken() async {
|
||||||
final token = LocalStorage.getJwtToken();
|
final token = await LocalStorage.getJwtToken();
|
||||||
if (token == null) {
|
if (token == null && enableLogs) {
|
||||||
print("No JWT token found. Please log in.");
|
logger.w("No JWT token found. Please log in."); // <-- Use logger
|
||||||
}
|
}
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@ -23,122 +28,121 @@ class ApiService {
|
|||||||
'Authorization': 'Bearer $token',
|
'Authorization': 'Bearer $token',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void _log(String message) {
|
||||||
|
if (enableLogs) logger.i(message); // <-- Use logger
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Future<http.Response?> _getRequest(String endpoint,
|
static Future<http.Response?> _getRequest(String endpoint,
|
||||||
{Map<String, String>? queryParams}) async {
|
{Map<String, String>? queryParams, bool hasRetried = false}) async {
|
||||||
final token = await _getToken();
|
String? token = await _getToken();
|
||||||
if (token == null) return null;
|
if (token == null) return null;
|
||||||
|
|
||||||
final uri =
|
Uri uri = Uri.parse("$baseUrl$endpoint").replace(queryParameters: queryParams);
|
||||||
Uri.parse("$baseUrl$endpoint").replace(queryParameters: queryParams);
|
_log('GET request: $uri');
|
||||||
print('GET request: $uri');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await http.get(uri, headers: _headers(token));
|
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("Refresh failed.");
|
||||||
|
}
|
||||||
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("HTTP GET Exception: $e");
|
_log("HTTP GET Exception: $e");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static dynamic _parseResponse(http.Response response, {String label = ''}) {
|
static Future<http.Response?> _postRequest(String endpoint, dynamic body) async {
|
||||||
print("$label Response: ${response.body}");
|
String? token = await _getToken();
|
||||||
if (response.statusCode == 200) {
|
if (token == null) return null;
|
||||||
final json = jsonDecode(response.body);
|
|
||||||
if (json['success'] == true) return json['data'];
|
final uri = Uri.parse("$baseUrl$endpoint");
|
||||||
print("API Error: ${json['message']}");
|
_log("POST request to $uri with body: $body");
|
||||||
} else {
|
|
||||||
print("HTTP Error [$label]: ${response.statusCode}");
|
try {
|
||||||
|
final response = await http
|
||||||
|
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
||||||
|
.timeout(timeout);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
_log("HTTP POST Exception: $e");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== API Calls =====
|
// ===== API Calls =====
|
||||||
|
|
||||||
static Future<List<dynamic>?> getProjects() async {
|
static Future<List<dynamic>?> getProjects() async {
|
||||||
final response = await _getRequest("/project/list");
|
final response = await _getRequest("/project/list");
|
||||||
return response != null
|
return response != null ? _parseResponse(response, label: 'Projects') : null;
|
||||||
? _parseResponse(response, label: 'Projects')
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<dynamic>?> getEmployeesByProject(String projectId) async {
|
static Future<List<dynamic>?> getEmployeesByProject(String projectId) async {
|
||||||
final response = await _getRequest("/attendance/project/team",
|
final response = await _getRequest("/attendance/project/team",
|
||||||
queryParams: {"projectId": "$projectId"});
|
queryParams: {"projectId": projectId});
|
||||||
return response != null
|
return response != null ? _parseResponse(response, label: 'Employees') : null;
|
||||||
? _parseResponse(response, label: 'Employees')
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<dynamic>?> getAttendanceLogs(
|
static Future<List<dynamic>?> getAttendanceLogs(String projectId,
|
||||||
String projectId, {
|
{DateTime? dateFrom, DateTime? dateTo}) async {
|
||||||
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),
|
||||||
};
|
};
|
||||||
|
|
||||||
final response =
|
final response = await _getRequest("/attendance/project/log", queryParams: query);
|
||||||
await _getRequest("/attendance/project/log", queryParams: query);
|
return response != null ? _parseResponse(response, label: 'Attendance Logs') : null;
|
||||||
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 = await _getRequest("/attendance/log/attendance/$id");
|
final response = await _getRequest("/attendance/log/attendance/$id");
|
||||||
return response != null
|
return response != null ? _parseResponse(response, label: 'Log Details') : null;
|
||||||
? _parseResponse(response, label: 'Attendance Log Details')
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<dynamic>?> getRegularizationLogs(String projectId) async {
|
static Future<List<dynamic>?> getRegularizationLogs(String projectId) async {
|
||||||
final response = await _getRequest("/attendance/regularize",
|
final response = await _getRequest("/attendance/regularize",
|
||||||
queryParams: {"projectId": "$projectId"});
|
queryParams: {"projectId": projectId});
|
||||||
return response != null
|
return response != null ? _parseResponse(response, label: 'Regularization Logs') : null;
|
||||||
? _parseResponse(response, label: 'Regularization')
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Upload Image =====
|
// ===== 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, // <- add this flag
|
|
||||||
}) async {
|
|
||||||
final token = await _getToken();
|
|
||||||
if (token == null) return false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Map<String, dynamic>? imageObject;
|
|
||||||
if (imageCapture && imageFile != null) {
|
|
||||||
final bytes = await imageFile.readAsBytes();
|
|
||||||
final base64Image = base64Encode(bytes);
|
|
||||||
final fileSize = await imageFile.length();
|
|
||||||
final contentType = "image/${imageFile.path.split('.').last}";
|
|
||||||
|
|
||||||
imageObject = {
|
|
||||||
"fileName": '$imageName',
|
|
||||||
"contentType": '$contentType',
|
|
||||||
"fileSize": fileSize,
|
|
||||||
"description": "Employee attendance photo",
|
|
||||||
"base64Data": '$base64Image',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
}) async {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
|
|
||||||
final body = {
|
final body = {
|
||||||
"id": id,
|
"id": id,
|
||||||
"employeeId": employeeId,
|
"employeeId": employeeId,
|
||||||
@ -147,40 +151,44 @@ class ApiService {
|
|||||||
"comment": comment,
|
"comment": comment,
|
||||||
"action": action,
|
"action": action,
|
||||||
"date": DateFormat('yyyy-MM-dd').format(now),
|
"date": DateFormat('yyyy-MM-dd').format(now),
|
||||||
|
if (imageCapture) "latitude": '$latitude',
|
||||||
|
if (imageCapture) "longitude": '$longitude',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only include latitude and longitude if imageCapture is true
|
if (imageCapture && imageFile != null) {
|
||||||
if (imageCapture) {
|
try {
|
||||||
body["latitude"] = '$latitude';
|
final bytes = await imageFile.readAsBytes();
|
||||||
body["longitude"] = '$longitude';
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only add imageObject if it's not null
|
final response = await _postRequest("/attendance/record-image", body);
|
||||||
if (imageObject != null) {
|
if (response == null) return false;
|
||||||
body["image"] = imageObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
print("Attendance Image Upload Body: $body");
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse("$baseUrl/attendance/record-image"),
|
|
||||||
headers: _headers(token),
|
|
||||||
body: jsonEncode(body),
|
|
||||||
);
|
|
||||||
print("Attendance Image Upload Response: ${response.body}");
|
|
||||||
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 {
|
} else {
|
||||||
print("Failed to upload image. API Error: ${json['message']}");
|
_log("Failed to upload image: ${json['message'] ?? 'Unknown error'}");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
print("Exception during image upload: $e");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// ===== Utilities =====
|
||||||
}
|
|
||||||
|
|
||||||
// ===== Utilities =====
|
|
||||||
|
|
||||||
static String generateImageName(String employeeId, int count) {
|
static String generateImageName(String employeeId, int count) {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
|
|||||||
@ -1,52 +1,104 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:marco/helpers/services/storage/local_storage.dart';
|
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||||
import 'package:marco/controller/permission_controller.dart';
|
import 'package:marco/controller/permission_controller.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:logger/logger.dart'; // <-- Make sure this import is present
|
||||||
|
|
||||||
|
final Logger logger = Logger();
|
||||||
|
|
||||||
class AuthService {
|
class AuthService {
|
||||||
|
static const String _baseUrl = "https://stageapi.marcoaiot.com/api";
|
||||||
|
static const Map<String, String> _headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
static bool isLoggedIn = false;
|
static bool isLoggedIn = false;
|
||||||
|
|
||||||
static Future<Map<String, String>?> loginUser(Map<String, dynamic> data) async {
|
/// Logs in the user and stores tokens if successful.
|
||||||
|
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('https://stageapi.marcoaiot.com/api/auth/login'),
|
Uri.parse("$_baseUrl/auth/login"),
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: _headers,
|
||||||
body: jsonEncode(data),
|
body: jsonEncode(data),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
final responseData = jsonDecode(response.body);
|
||||||
|
if (response.statusCode == 200 && responseData['data'] != null) {
|
||||||
isLoggedIn = true;
|
isLoggedIn = true;
|
||||||
|
|
||||||
// Parse the response to get the JWT and refresh tokens
|
final jwtToken = responseData['data']['token'];
|
||||||
final responseData = jsonDecode(response.body);
|
|
||||||
|
|
||||||
// Adjusted for the actual response structure
|
|
||||||
final jwtToken = responseData['data']['token']; // Ensure this matches your actual response
|
|
||||||
|
|
||||||
// Save the JWT token in local storage
|
|
||||||
await LocalStorage.setJwtToken(jwtToken);
|
|
||||||
print("JWT Token: $jwtToken");
|
|
||||||
|
|
||||||
// Optionally save refresh token if available
|
|
||||||
final refreshToken = responseData['data']['refreshToken'];
|
final refreshToken = responseData['data']['refreshToken'];
|
||||||
|
|
||||||
|
await LocalStorage.setJwtToken(jwtToken);
|
||||||
|
await LocalStorage.setLoggedInUser(true);
|
||||||
|
|
||||||
if (refreshToken != null) {
|
if (refreshToken != null) {
|
||||||
await LocalStorage.setRefreshToken(refreshToken);
|
await LocalStorage.setRefreshToken(refreshToken);
|
||||||
print("Refresh Token: $refreshToken");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the login state in local storage
|
|
||||||
await LocalStorage.setLoggedInUser(true);
|
|
||||||
Get.put(PermissionController());
|
Get.put(PermissionController());
|
||||||
// Return null to indicate success
|
|
||||||
return null;
|
logger.i("JWT Token: $jwtToken");
|
||||||
|
if (refreshToken != null) logger.i("Refresh Token: $refreshToken");
|
||||||
|
|
||||||
|
return null; // Success
|
||||||
} 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 {"error": "Something went wrong. Please try again."};
|
return {
|
||||||
|
"error": responseData['message'] ?? "Unexpected error occurred"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logger.e("Login error: $e");
|
||||||
return {"error": "Network error. Please check your connection."};
|
return {"error": "Network error. Please check your connection."};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Refreshes the JWT token using the refresh token.
|
||||||
|
static Future<bool> refreshToken() async {
|
||||||
|
final refreshToken = await LocalStorage.getRefreshToken();
|
||||||
|
if (refreshToken == null || refreshToken.isEmpty) {
|
||||||
|
logger.w("No refresh token available.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse("$_baseUrl/auth/refresh-token"),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode({"refreshToken": refreshToken}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
|
if (response.statusCode == 200 && data['success'] == true) {
|
||||||
|
final newAccessToken = data['data']['accessToken'];
|
||||||
|
final newRefreshToken = data['data']['refreshToken'];
|
||||||
|
|
||||||
|
// Check if the tokens are valid before saving them
|
||||||
|
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']}");
|
||||||
|
await LocalStorage.removeToken('jwt_token');
|
||||||
|
await LocalStorage.removeToken('refresh_token');
|
||||||
|
await LocalStorage.setLoggedInUser(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.e("Exception during token refresh: $e");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,25 @@
|
|||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:get/get.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';
|
||||||
|
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||||
|
import 'package:marco/helpers/services/auth_service.dart';
|
||||||
|
|
||||||
|
final Logger logger = Logger();
|
||||||
|
|
||||||
class PermissionService {
|
class PermissionService {
|
||||||
// Cache to store the fetched user profile data per token
|
|
||||||
static final Map<String, Map<String, dynamic>> _userDataCache = {};
|
static final Map<String, Map<String, dynamic>> _userDataCache = {};
|
||||||
|
|
||||||
// Method to fetch the user profile data (permissions, employee info, and projects)
|
static Future<Map<String, dynamic>> fetchAllUserData(String token, {bool hasRetried = false}) async {
|
||||||
static Future<Map<String, dynamic>> fetchAllUserData(String token) async {
|
// Return from cache if available
|
||||||
// Check if the data for this token is already cached
|
|
||||||
if (_userDataCache.containsKey(token)) {
|
if (_userDataCache.containsKey(token)) {
|
||||||
return _userDataCache[token]!;
|
return _userDataCache[token]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch data from the API
|
|
||||||
final response = await http.get(
|
final response = await http.get(
|
||||||
Uri.parse('https://stageapi.marcoaiot.com/api/user/profile'),
|
Uri.parse('https://stageapi.marcoaiot.com/api/user/profile'),
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
headers: {'Authorization': 'Bearer $token'},
|
||||||
@ -25,44 +28,50 @@ class PermissionService {
|
|||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = json.decode(response.body)['data'];
|
final data = json.decode(response.body)['data'];
|
||||||
|
|
||||||
// Parse and extract relevant information
|
final result = {
|
||||||
final permissions = _parsePermissions(data['featurePermissions']);
|
'permissions': _parsePermissions(data['featurePermissions']),
|
||||||
final employeeInfo = _parseEmployeeInfo(data['employeeInfo']);
|
'employeeInfo': _parseEmployeeInfo(data['employeeInfo']),
|
||||||
final projectsInfo = _parseProjectsInfo(data['projects']);
|
'projects': _parseProjectsInfo(data['projects']),
|
||||||
|
|
||||||
// Cache the processed data for later use
|
|
||||||
final allUserData = {
|
|
||||||
'permissions': permissions,
|
|
||||||
'employeeInfo': employeeInfo,
|
|
||||||
'projects': projectsInfo,
|
|
||||||
};
|
};
|
||||||
_userDataCache[token] = allUserData;
|
|
||||||
|
|
||||||
return allUserData;
|
_userDataCache[token] = result;
|
||||||
} else {
|
return result;
|
||||||
final errorData = json.decode(response.body);
|
|
||||||
throw Exception('Failed to load data: ${errorData['message']}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.statusCode == 401 && !hasRetried) {
|
||||||
|
final refreshed = await AuthService.refreshToken();
|
||||||
|
if (refreshed) {
|
||||||
|
final newToken = await LocalStorage.getJwtToken();
|
||||||
|
if (newToken != null) {
|
||||||
|
return fetchAllUserData(newToken, hasRetried: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _handleUnauthorized();
|
||||||
|
throw Exception('Unauthorized. Token refresh failed.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final errorMessage = json.decode(response.body)['message'] ?? 'Unknown error';
|
||||||
|
throw Exception('Failed to load data: $errorMessage');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error fetching user data: $e');
|
logger.e('Error fetching user data: $e'); // <-- Use logger here
|
||||||
throw Exception('Error fetching user data: $e');
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to parse permissions from raw data
|
static Future<void> _handleUnauthorized() async {
|
||||||
static List<UserPermission> _parsePermissions(List<dynamic> featurePermissions) {
|
await LocalStorage.removeToken('jwt_token');
|
||||||
return featurePermissions
|
await LocalStorage.removeToken('refresh_token');
|
||||||
.map<UserPermission>((id) => UserPermission.fromJson({'id': id}))
|
await LocalStorage.setLoggedInUser(false);
|
||||||
.toList();
|
Get.offAllNamed('/auth/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to parse employee info from raw data
|
static List<UserPermission> _parsePermissions(List<dynamic> featurePermissions) =>
|
||||||
static EmployeeInfo _parseEmployeeInfo(Map<String, dynamic> employeeData) {
|
featurePermissions.map((id) => UserPermission.fromJson({'id': id})).toList();
|
||||||
return EmployeeInfo.fromJson(employeeData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to parse projects from raw data
|
static EmployeeInfo _parseEmployeeInfo(Map<String, dynamic> employeeData) =>
|
||||||
static List<ProjectInfo> _parseProjectsInfo(List<dynamic> projectIds) {
|
EmployeeInfo.fromJson(employeeData);
|
||||||
return projectIds.map<ProjectInfo>((id) => ProjectInfo.fromJson(id)).toList();
|
|
||||||
}
|
static List<ProjectInfo> _parseProjectsInfo(List<dynamic> projects) =>
|
||||||
|
projects.map((proj) => ProjectInfo.fromJson(proj)).toList();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,17 +11,23 @@ import 'package:marco/helpers/theme/theme_customizer.dart';
|
|||||||
import 'package:marco/routes.dart';
|
import 'package:marco/routes.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_strategy/url_strategy.dart';
|
import 'package:url_strategy/url_strategy.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
setPathUrlStrategy();
|
setPathUrlStrategy();
|
||||||
|
|
||||||
await LocalStorage.init();
|
try {
|
||||||
AppStyle.init();
|
await LocalStorage.init();
|
||||||
await ThemeCustomizer.init();
|
await ThemeCustomizer.init();
|
||||||
|
AppStyle.init();
|
||||||
|
} catch (e) {
|
||||||
|
print('Error during app initialization: $e');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
runApp(ChangeNotifierProvider<AppNotifier>(
|
runApp(ChangeNotifierProvider<AppNotifier>(
|
||||||
create: (context) => AppNotifier(),
|
create: (context) => AppNotifier(),
|
||||||
child: MyApp(),
|
child: MyApp(),
|
||||||
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,7 @@ dependencies:
|
|||||||
permission_handler: ^11.3.0
|
permission_handler: ^11.3.0
|
||||||
image: ^4.0.17
|
image: ^4.0.17
|
||||||
image_picker: ^1.0.7
|
image_picker: ^1.0.7
|
||||||
|
logger: ^2.0.2
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user