Merge pull request 'displayed only accesable projects' (#9) from Vaibhav_Task-#175 into main
Reviewed-on: #9
This commit is contained in:
commit
f4ed63a8ab
@ -4,91 +4,97 @@ import 'package:get/get.dart';
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.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 the EmployeeInfo model
|
import 'package:marco/model/employee_info.dart';
|
||||||
|
import 'package:marco/model/projects_model.dart';
|
||||||
|
|
||||||
class PermissionController extends GetxController {
|
class PermissionController extends GetxController {
|
||||||
var permissions = <UserPermission>[].obs;
|
var permissions = <UserPermission>[].obs;
|
||||||
var employeeInfo = Rxn<EmployeeInfo>(); // Observable for employee info
|
var employeeInfo = Rxn<EmployeeInfo>();
|
||||||
|
var projectsInfo = <ProjectInfo>[].obs;
|
||||||
Timer? _refreshTimer;
|
Timer? _refreshTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@override
|
void onInit() {
|
||||||
void onInit() {
|
super.onInit();
|
||||||
super.onInit();
|
_loadDataFromAPI();
|
||||||
_loadDataFromAPI(); // Always fetch from API at start
|
_startAutoRefresh();
|
||||||
_startAutoRefresh(); // Schedule auto-refresh
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Save permissions and employee info to SharedPreferences
|
// Store all data at once to reduce redundant operations
|
||||||
Future<void> _storeData() async {
|
Future<void> _storeData() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
// Store permissions
|
// Store permissions
|
||||||
final permissionsJson = permissions.map((e) => e.toJson()).toList();
|
await prefs.setString(
|
||||||
print("Storing Permissions: $permissionsJson");
|
'user_permissions', jsonEncode(permissions.map((e) => e.toJson()).toList()));
|
||||||
await prefs.setString('user_permissions', jsonEncode(permissionsJson));
|
|
||||||
|
|
||||||
// Store employee info
|
// Store employee info if available
|
||||||
if (employeeInfo.value != null) {
|
if (employeeInfo.value != null) {
|
||||||
final employeeInfoJson = employeeInfo.value!.toJson();
|
await prefs.setString(
|
||||||
print("Storing Employee Info: $employeeInfoJson");
|
'employee_info', jsonEncode(employeeInfo.value!.toJson()));
|
||||||
await prefs.setString('employee_info', jsonEncode(employeeInfoJson));
|
}
|
||||||
|
|
||||||
|
// Store projects info if available
|
||||||
|
if (projectsInfo.isNotEmpty) {
|
||||||
|
await prefs.setString('projects_info',
|
||||||
|
jsonEncode(projectsInfo.map((e) => e.toJson()).toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public method to load permissions and employee info (usually called from outside)
|
// Fetch and load all required data (permissions, employee info, and projects)
|
||||||
Future<void> loadData(String token) async {
|
|
||||||
try {
|
|
||||||
final result = await PermissionService.fetchPermissions(token);
|
|
||||||
print("Fetched Permissions from API: $result");
|
|
||||||
|
|
||||||
permissions.assignAll(result); // Update observable list
|
|
||||||
await _storeData(); // Cache locally
|
|
||||||
|
|
||||||
// Also fetch employee info from the API (you can extend the service if needed)
|
|
||||||
await _loadEmployeeInfoFromAPI(token);
|
|
||||||
} catch (e) {
|
|
||||||
print('Error loading data from API: $e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal helper to load token and fetch permissions and employee info from API
|
|
||||||
Future<void> _loadDataFromAPI() async {
|
Future<void> _loadDataFromAPI() async {
|
||||||
final token = await _getAuthToken();
|
final token = await _getAuthToken();
|
||||||
if (token != null && token.isNotEmpty) {
|
if (token?.isNotEmpty ?? false) {
|
||||||
await loadData(token);
|
await loadData(token!);
|
||||||
} else {
|
} else {
|
||||||
print("No token available for fetching data.");
|
print("No token available for fetching data.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve token from SharedPreferences
|
// Fetch data and update the state (permissions, employee info, and projects)
|
||||||
Future<String?> _getAuthToken() async {
|
Future<void> loadData(String token) async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
try {
|
||||||
return prefs.getString('jwt_token'); // Or 'auth_token' if that’s the key you're using
|
final userData = await PermissionService.fetchAllUserData(token);
|
||||||
|
|
||||||
|
// Update state variables
|
||||||
|
_updateState(userData);
|
||||||
|
|
||||||
|
// Store all data after fetching
|
||||||
|
await _storeData();
|
||||||
|
} catch (e) {
|
||||||
|
print('Error loading data from API: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-refresh every 30 minutes
|
// Update state variables (permissions, employeeInfo, and projects)
|
||||||
|
void _updateState(Map<String, dynamic> userData) {
|
||||||
|
permissions.assignAll(userData['permissions']);
|
||||||
|
employeeInfo.value = userData['employeeInfo'];
|
||||||
|
projectsInfo.assignAll(userData['projects']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the auth token from SharedPreferences
|
||||||
|
Future<String?> _getAuthToken() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
return prefs.getString('jwt_token');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
await _loadDataFromAPI();
|
await _loadDataFromAPI();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load employee info from the API
|
// Check if the user has the given permission
|
||||||
Future<void> _loadEmployeeInfoFromAPI(String token) async {
|
|
||||||
final employeeInfoResponse = await PermissionService.fetchEmployeeInfo(token);
|
|
||||||
print("Fetched Employee Info from API: $employeeInfoResponse");
|
|
||||||
|
|
||||||
employeeInfo.value = employeeInfoResponse; // Update observable employee info
|
|
||||||
await _storeData(); // Cache employee info locally
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for specific permission
|
|
||||||
bool hasPermission(String permissionId) {
|
bool hasPermission(String permissionId) {
|
||||||
return permissions.any((p) => p.id == permissionId);
|
return permissions.any((p) => p.id == permissionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isUserAssignedToProject(String projectId) {
|
||||||
|
return projectsInfo.any((project) => project.id == projectId);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
_refreshTimer?.cancel();
|
_refreshTimer?.cancel();
|
||||||
|
@ -1,53 +1,68 @@
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
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';
|
||||||
|
|
||||||
class PermissionService {
|
class PermissionService {
|
||||||
static Future<List<UserPermission>> fetchPermissions(String token) async {
|
// Cache to store the fetched user profile data per token
|
||||||
|
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) async {
|
||||||
|
// Check if the data for this token is already cached
|
||||||
|
if (_userDataCache.containsKey(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'},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final decoded = json.decode(response.body);
|
final data = json.decode(response.body)['data'];
|
||||||
final List<dynamic> featurePermissions = decoded['data']['featurePermissions'];
|
|
||||||
|
|
||||||
return featurePermissions
|
// Parse and extract relevant information
|
||||||
.map<UserPermission>((permissionId) => UserPermission.fromJson({'id': permissionId}))
|
final permissions = _parsePermissions(data['featurePermissions']);
|
||||||
.toList();
|
final employeeInfo = _parseEmployeeInfo(data['employeeInfo']);
|
||||||
|
final projectsInfo = _parseProjectsInfo(data['projects']);
|
||||||
|
|
||||||
|
// Cache the processed data for later use
|
||||||
|
final allUserData = {
|
||||||
|
'permissions': permissions,
|
||||||
|
'employeeInfo': employeeInfo,
|
||||||
|
'projects': projectsInfo,
|
||||||
|
};
|
||||||
|
_userDataCache[token] = allUserData;
|
||||||
|
|
||||||
|
return allUserData;
|
||||||
} else {
|
} else {
|
||||||
final errorData = json.decode(response.body);
|
final errorData = json.decode(response.body);
|
||||||
throw Exception('Failed to load permissions: ${errorData['message']}');
|
throw Exception('Failed to load data: ${errorData['message']}');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error fetching permissions: $e');
|
print('Error fetching user data: $e');
|
||||||
throw Exception('Error fetching permissions: $e');
|
throw Exception('Error fetching user data: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New method to fetch employee info
|
// Helper method to parse permissions from raw data
|
||||||
static Future<EmployeeInfo> fetchEmployeeInfo(String token) async {
|
static List<UserPermission> _parsePermissions(List<dynamic> featurePermissions) {
|
||||||
try {
|
return featurePermissions
|
||||||
final response = await http.get(
|
.map<UserPermission>((id) => UserPermission.fromJson({'id': id}))
|
||||||
Uri.parse('https://stageapi.marcoaiot.com/api/user/profile'),
|
.toList();
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
// Helper method to parse employee info from raw data
|
||||||
final decoded = json.decode(response.body);
|
static EmployeeInfo _parseEmployeeInfo(Map<String, dynamic> employeeData) {
|
||||||
final employeeData = decoded['data']['employeeInfo'];
|
return EmployeeInfo.fromJson(employeeData);
|
||||||
|
}
|
||||||
|
|
||||||
return EmployeeInfo.fromJson(employeeData);
|
// Helper method to parse projects from raw data
|
||||||
} else {
|
static List<ProjectInfo> _parseProjectsInfo(List<dynamic> projectIds) {
|
||||||
final errorData = json.decode(response.body);
|
return projectIds.map<ProjectInfo>((id) => ProjectInfo.fromJson(id)).toList();
|
||||||
throw Exception('Failed to load employee info: ${errorData['message']}');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print('Error fetching employee info: $e');
|
|
||||||
throw Exception('Error fetching employee info: $e');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
lib/model/projects_model.dart
Normal file
15
lib/model/projects_model.dart
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
class ProjectInfo {
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
ProjectInfo({required this.id});
|
||||||
|
|
||||||
|
// Deserialize from a string
|
||||||
|
factory ProjectInfo.fromJson(dynamic json) {
|
||||||
|
return ProjectInfo(id: json.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize to a string
|
||||||
|
dynamic toJson() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
@ -115,8 +115,26 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
)
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return attendanceController.projects
|
// Filter projects based on permissions
|
||||||
.map((project) {
|
final accessibleProjects = attendanceController
|
||||||
|
.projects
|
||||||
|
.where((project) => permissionController
|
||||||
|
.isUserAssignedToProject(
|
||||||
|
project.id.toString()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (accessibleProjects.isEmpty) {
|
||||||
|
return [
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: '',
|
||||||
|
child: MyText.bodySmall(
|
||||||
|
'No Projects Assigned',
|
||||||
|
fontWeight: 600),
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessibleProjects.map((project) {
|
||||||
return PopupMenuItem<String>(
|
return PopupMenuItem<String>(
|
||||||
value: project.id.toString(),
|
value: project.id.toString(),
|
||||||
height: 32,
|
height: 32,
|
||||||
@ -138,7 +156,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
null
|
null
|
||||||
? attendanceController.projects
|
? attendanceController.projects
|
||||||
.firstWhereOrNull((proj) =>
|
.firstWhereOrNull((proj) =>
|
||||||
proj.id==
|
proj.id ==
|
||||||
attendanceController
|
attendanceController
|
||||||
.selectedProjectId)
|
.selectedProjectId)
|
||||||
?.name ??
|
?.name ??
|
||||||
@ -861,7 +879,7 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
|||||||
.captureAndUploadAttendance(
|
.captureAndUploadAttendance(
|
||||||
log.id,
|
log.id,
|
||||||
log.employeeId,
|
log.employeeId,
|
||||||
attendanceController.selectedProjectId!,
|
attendanceController.selectedProjectId!,
|
||||||
comment: "Rejected",
|
comment: "Rejected",
|
||||||
action: 5, // Reject action
|
action: 5, // Reject action
|
||||||
imageCapture: false,
|
imageCapture: false,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user