From 31966f4bc594bb595bbe107bd3093c648ba62fef Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Thu, 31 Jul 2025 16:44:05 +0530 Subject: [PATCH] feat: add permission handling in ExpenseDetailScreen and enhance ExpenseDetailController with permission parsing --- .../expense/expense_detail_controller.dart | 40 +++++++++++++++---- lib/controller/permission_controller.dart | 17 ++++++++ lib/helpers/utils/date_time_utils.dart | 5 --- lib/view/expense/expense_detail_screen.dart | 9 ++++- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/lib/controller/expense/expense_detail_controller.dart b/lib/controller/expense/expense_detail_controller.dart index 8023c0a..88b71cd 100644 --- a/lib/controller/expense/expense_detail_controller.dart +++ b/lib/controller/expense/expense_detail_controller.dart @@ -33,18 +33,21 @@ class ExpenseDetailController extends GetxController { try { logSafe("Fetching expense details for ID: $_expenseId"); - final result = await ApiService.getExpenseDetailsApi(expenseId: _expenseId); + final result = + await ApiService.getExpenseDetailsApi(expenseId: _expenseId); if (result != null) { try { expense.value = ExpenseDetailModel.fromJson(result); logSafe("Expense details loaded successfully: ${expense.value?.id}"); } catch (e) { errorMessage.value = 'Failed to parse expense details: $e'; - logSafe("Parse error in fetchExpenseDetails: $e", level: LogLevel.error); + logSafe("Parse error in fetchExpenseDetails: $e", + level: LogLevel.error); } } else { errorMessage.value = 'Failed to fetch expense details from server.'; - logSafe("fetchExpenseDetails failed: null response", level: LogLevel.error); + logSafe("fetchExpenseDetails failed: null response", + level: LogLevel.error); } } catch (e, stack) { errorMessage.value = 'An unexpected error occurred.'; @@ -55,6 +58,27 @@ class ExpenseDetailController extends GetxController { } } +// In ExpenseDetailController + List parsePermissionIds(dynamic permissionData) { + if (permissionData == null) return []; + if (permissionData is List) { + // If it's already a List, return a list of Strings. + return permissionData + .map((e) => e.toString().trim()) + .where((e) => e.isNotEmpty) + .toList(); + } + if (permissionData is String) { + final clean = permissionData.replaceAll(RegExp(r'[\[\]]'), ''); + return clean + .split(',') + .map((e) => e.trim()) + .where((e) => e.isNotEmpty) + .toList(); + } + return []; + } + /// Fetch all employees Future fetchAllEmployees() async { isLoading.value = true; @@ -64,7 +88,8 @@ class ExpenseDetailController extends GetxController { final response = await ApiService.getAllEmployees(); if (response != null && response.isNotEmpty) { allEmployees.assignAll(response.map((e) => EmployeeModel.fromJson(e))); - logSafe("All Employees fetched: ${allEmployees.length}", level: LogLevel.info); + logSafe("All Employees fetched: ${allEmployees.length}", + level: LogLevel.info); } else { allEmployees.clear(); logSafe("No employees found.", level: LogLevel.warning); @@ -103,7 +128,7 @@ class ExpenseDetailController extends GetxController { if (success) { logSafe("Reimbursement submitted successfully."); - await fetchExpenseDetails(); // refresh latest + await fetchExpenseDetails(); return true; } else { errorMessage.value = "Failed to submit reimbursement."; @@ -111,7 +136,8 @@ class ExpenseDetailController extends GetxController { } } catch (e, stack) { errorMessage.value = 'An unexpected error occurred.'; - logSafe("Exception in updateExpenseStatusWithReimbursement: $e", level: LogLevel.error); + logSafe("Exception in updateExpenseStatusWithReimbursement: $e", + level: LogLevel.error); logSafe("StackTrace: $stack", level: LogLevel.debug); return false; } finally { @@ -134,7 +160,7 @@ class ExpenseDetailController extends GetxController { if (success) { logSafe("Expense status updated successfully."); - await fetchExpenseDetails(); // refresh + await fetchExpenseDetails(); return true; } else { errorMessage.value = "Failed to update expense status."; diff --git a/lib/controller/permission_controller.dart b/lib/controller/permission_controller.dart index 0802f33..75b1105 100644 --- a/lib/controller/permission_controller.dart +++ b/lib/controller/permission_controller.dart @@ -117,6 +117,23 @@ class PermissionController extends GetxController { return assigned; } + List get allowedPermissionIds { + final ids = permissions.map((p) => p.id).toList(); + logSafe("[PermissionController] Allowed Permission IDs: $ids", + level: LogLevel.debug); + return ids; + } + + bool hasAnyPermission(List ids) { + logSafe("[PermissionController] Checking if any of these are allowed: $ids", + level: LogLevel.debug); + final allowed = allowedPermissionIds; + final result = ids.any((id) => allowed.contains(id)); + logSafe("[PermissionController] Permission match result: $result", + level: LogLevel.debug); + return result; + } + @override void onClose() { _refreshTimer?.cancel(); diff --git a/lib/helpers/utils/date_time_utils.dart b/lib/helpers/utils/date_time_utils.dart index 1dca3da..01bae1f 100644 --- a/lib/helpers/utils/date_time_utils.dart +++ b/lib/helpers/utils/date_time_utils.dart @@ -5,8 +5,6 @@ class DateTimeUtils { /// Converts a UTC datetime string to local time and formats it. static String convertUtcToLocal(String utcTimeString, {String format = 'dd-MM-yyyy'}) { try { - logSafe('convertUtcToLocal: input="$utcTimeString", format="$format"'); - final parsed = DateTime.parse(utcTimeString); final utcDateTime = DateTime.utc( parsed.year, @@ -18,13 +16,10 @@ class DateTimeUtils { parsed.millisecond, parsed.microsecond, ); - logSafe('Parsed (assumed UTC): $utcDateTime'); final localDateTime = utcDateTime.toLocal(); - logSafe('Converted to Local: $localDateTime'); final formatted = _formatDateTime(localDateTime, format: format); - logSafe('Formatted Local Time: $formatted'); return formatted; } catch (e, stackTrace) { diff --git a/lib/view/expense/expense_detail_screen.dart b/lib/view/expense/expense_detail_screen.dart index 86fb009..6718ecd 100644 --- a/lib/view/expense/expense_detail_screen.dart +++ b/lib/view/expense/expense_detail_screen.dart @@ -3,6 +3,8 @@ import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:marco/controller/expense/expense_detail_controller.dart'; import 'package:marco/controller/project_controller.dart'; +import 'package:marco/controller/permission_controller.dart'; + import 'package:marco/helpers/utils/date_time_utils.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; @@ -40,6 +42,7 @@ class ExpenseDetailScreen extends StatelessWidget { final controller = Get.put(ExpenseDetailController()); final projectController = Get.find(); controller.init(expenseId); + final permissionController = Get.find(); return Scaffold( backgroundColor: const Color(0xFFF7F7F7), @@ -169,7 +172,11 @@ class ExpenseDetailScreen extends StatelessWidget { alignment: WrapAlignment.center, spacing: 10, runSpacing: 10, - children: expense.nextStatus.map((next) { + children: expense.nextStatus.where((next) { + return permissionController.hasAnyPermission( + controller.parsePermissionIds(next.permissionIds), + ); + }).map((next) { Color buttonColor = Colors.red; if (next.color.isNotEmpty) { try {