diff --git a/lib/controller/expense/expense_screen_controller.dart b/lib/controller/expense/expense_screen_controller.dart index 6568aa3..9359c21 100644 --- a/lib/controller/expense/expense_screen_controller.dart +++ b/lib/controller/expense/expense_screen_controller.dart @@ -39,4 +39,34 @@ class ExpenseController extends GetxController { isLoading.value = false; } } + + /// Update expense status and refresh the list + Future updateExpenseStatus(String expenseId, String statusId) async { + isLoading.value = true; + errorMessage.value = ''; + + try { + logSafe("Updating status for expense: $expenseId -> $statusId"); + final success = await ApiService.updateExpenseStatusApi( + expenseId: expenseId, + statusId: statusId, + ); + + if (success) { + logSafe("Expense status updated successfully."); + await fetchExpenses(); + return true; + } else { + errorMessage.value = "Failed to update expense status."; + return false; + } + } catch (e, stack) { + errorMessage.value = 'An unexpected error occurred.'; + logSafe("Exception in updateExpenseStatus: $e", level: LogLevel.error); + logSafe("StackTrace: $stack", level: LogLevel.debug); + return false; + } finally { + isLoading.value = false; + } + } } diff --git a/lib/helpers/services/api_endpoints.dart b/lib/helpers/services/api_endpoints.dart index 4b7b24a..1e386b2 100644 --- a/lib/helpers/services/api_endpoints.dart +++ b/lib/helpers/services/api_endpoints.dart @@ -59,5 +59,5 @@ class ApiEndpoints { static const String getMasterPaymentModes = "/master/payment-modes"; static const String getMasterExpenseStatus = "/master/expenses-status"; static const String getMasterExpenseTypes = "/master/expenses-types"; - + static const String updateExpenseStatus = "/expense/action"; } diff --git a/lib/helpers/services/api_service.dart b/lib/helpers/services/api_service.dart index 405fe1f..d56a5ac 100644 --- a/lib/helpers/services/api_service.dart +++ b/lib/helpers/services/api_service.dart @@ -241,6 +241,51 @@ class ApiService { // === Expense APIs === // + /// Update Expense Status API + static Future updateExpenseStatusApi({ + required String expenseId, + required String statusId, + }) async { + final payload = { + "expenseId": expenseId, + "statusId": statusId, + }; + + const endpoint = ApiEndpoints.updateExpenseStatus; + logSafe("Updating expense status with payload: $payload"); + + try { + final response = + await _postRequest(endpoint, payload, customTimeout: extendedTimeout); + + if (response == null) { + logSafe("Update expense status failed: null response", + level: LogLevel.error); + return false; + } + + logSafe("Update expense status response status: ${response.statusCode}"); + logSafe("Update expense status response body: ${response.body}"); + + final json = jsonDecode(response.body); + if (json['success'] == true) { + logSafe("Expense status updated successfully: ${json['data']}"); + return true; + } else { + logSafe( + "Failed to update expense status: ${json['message'] ?? 'Unknown error'}", + level: LogLevel.warning, + ); + } + } catch (e, stack) { + logSafe("Exception during updateExpenseStatus API: $e", + level: LogLevel.error); + logSafe("StackTrace: $stack", level: LogLevel.debug); + } + + return false; + } + static Future?> getExpenseListApi() async { const endpoint = ApiEndpoints.getExpenseList; diff --git a/lib/view/expense/expense_detail_screen.dart b/lib/view/expense/expense_detail_screen.dart index 951e5d6..d5450ab 100644 --- a/lib/view/expense/expense_detail_screen.dart +++ b/lib/view/expense/expense_detail_screen.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/controller/project_controller.dart'; +import 'package:marco/controller/expense/expense_screen_controller.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/model/expense/expense_list_model.dart'; -import 'package:marco/helpers/utils/date_time_utils.dart'; // Import DateTimeUtils +import 'package:marco/helpers/utils/date_time_utils.dart'; class ExpenseDetailScreen extends StatelessWidget { const ExpenseDetailScreen({super.key}); @@ -31,6 +32,9 @@ class ExpenseDetailScreen extends StatelessWidget { final ExpenseModel expense = Get.arguments['expense'] as ExpenseModel; final statusColor = getStatusColor(expense.status.name); final projectController = Get.find(); + final expenseController = Get.find(); + print( + "Next Status List: ${expense.nextStatus.map((e) => e.toJson()).toList()}"); return Scaffold( backgroundColor: const Color(0xFFF7F7F7), @@ -110,6 +114,64 @@ class ExpenseDetailScreen extends StatelessWidget { ], ), ), + bottomNavigationBar: expense.nextStatus.isNotEmpty + ? SafeArea( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Wrap( + spacing: 8, + runSpacing: 8, + alignment: WrapAlignment.center, + children: expense.nextStatus.map((next) { + return ElevatedButton( + style: ElevatedButton.styleFrom( + minimumSize: const Size(100, 40), + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 12), + backgroundColor: Colors.red, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + onPressed: () async { + final success = + await expenseController.updateExpenseStatus( + expense.id, + next.id, + ); + + if (success) { + Get.snackbar( + 'Success', + 'Expense moved to ${next.name}', + backgroundColor: Colors.green.withOpacity(0.8), + colorText: Colors.white, + ); + Get.back(result: true); + } else { + Get.snackbar( + 'Error', + 'Failed to update status.', + backgroundColor: Colors.red.withOpacity(0.8), + colorText: Colors.white, + ); + } + }, + child: Text( + next.name, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + overflow: TextOverflow.ellipsis, + ), + ); + }).toList(), + ), + ), + ) + : null, ); } } @@ -228,8 +290,7 @@ class _ExpenseDetailsList extends StatelessWidget { _DetailRow(title: "Payment Mode", value: expense.paymentMode.name), _DetailRow( title: "Paid By", - value: - '${expense.paidBy.firstName} ${expense.paidBy.lastName}'), + value: '${expense.paidBy.firstName} ${expense.paidBy.lastName}'), _DetailRow( title: "Created By", value: @@ -243,8 +304,7 @@ class _ExpenseDetailsList extends StatelessWidget { title: "Next Status", value: expense.nextStatus.map((e) => e.name).join(", ")), _DetailRow( - title: "Pre-Approved", - value: expense.preApproved ? "Yes" : "No"), + title: "Pre-Approved", value: expense.preApproved ? "Yes" : "No"), ], ), );