diff --git a/lib/helpers/widgets/dashbaord/expense_by_status_widget.dart b/lib/helpers/widgets/dashbaord/expense_by_status_widget.dart index 7c67c7f..1e66fe6 100644 --- a/lib/helpers/widgets/dashbaord/expense_by_status_widget.dart +++ b/lib/helpers/widgets/dashbaord/expense_by_status_widget.dart @@ -4,6 +4,9 @@ import 'package:marco/controller/dashboard/dashboard_controller.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/utils/utils.dart'; import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; +import 'package:marco/controller/expense/expense_screen_controller.dart'; +import 'package:marco/view/expense/expense_screen.dart'; +import 'package:collection/collection.dart'; class ExpenseByStatusWidget extends StatelessWidget { final DashboardController controller; @@ -16,34 +19,106 @@ class ExpenseByStatusWidget extends StatelessWidget { required String title, required String amount, required String count, + required VoidCallback onTap, }) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 6), - child: Row( - children: [ - CircleAvatar( - backgroundColor: color.withOpacity(0.15), - radius: 22, - child: Icon(icon, color: color, size: 24), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.bodyMedium(title, fontWeight: 600), - const SizedBox(height: 2), - MyText.titleMedium(amount, color: Colors.blue, fontWeight: 700), - ], + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Row( + children: [ + CircleAvatar( + backgroundColor: color.withOpacity(0.15), + radius: 22, + child: Icon(icon, color: color, size: 24), ), - ), - MyText.titleMedium(count, color: Colors.blue, fontWeight: 700), - const Icon(Icons.chevron_right, color: Colors.blue, size: 24), - ], + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium(title, fontWeight: 600), + const SizedBox(height: 2), + MyText.titleMedium(amount, + color: Colors.blue, fontWeight: 700), + ], + ), + ), + MyText.titleMedium(count, color: Colors.blue, fontWeight: 700), + const Icon(Icons.chevron_right, color: Colors.blue, size: 24), + ], + ), ), ); } +// Navigate with status filter + Future _navigateToExpenseWithFilter( + BuildContext context, String statusName) async { + final expenseController = Get.put(ExpenseController()); + + // 1️⃣ Ensure global projects and master data are loaded + if (expenseController.projectsMap.isEmpty) { + await expenseController.fetchGlobalProjects(); + } + + if (expenseController.expenseStatuses.isEmpty) { + await expenseController.fetchMasterData(); + } + + // 2️⃣ Auto-select current project from DashboardController + final dashboardController = Get.find(); + final currentProjectId = + dashboardController.projectController.selectedProjectId.value; + + final projectName = expenseController.projectsMap.entries + .firstWhereOrNull((entry) => entry.value == currentProjectId) + ?.key; + + expenseController.selectedProject.value = projectName ?? ''; + + // 3️⃣ Select status filter + final matchedStatus = expenseController.expenseStatuses.firstWhereOrNull( + (e) => e.name.toLowerCase() == statusName.toLowerCase(), + ); + expenseController.selectedStatus.value = matchedStatus?.id ?? ''; + + // 4️⃣ Fetch expenses immediately with applied filters + await expenseController.fetchExpenses(); + + // 5️⃣ Navigate to Expense screen + Get.to(() => const ExpenseMainScreen()); + } + +// Navigate without status filter + Future _navigateToExpenseWithoutFilter() async { + final expenseController = Get.put(ExpenseController()); + + // Ensure global projects loaded + if (expenseController.projectsMap.isEmpty) { + await expenseController.fetchGlobalProjects(); + } + + // Auto-select current project + final dashboardController = Get.find(); + final currentProjectId = + dashboardController.projectController.selectedProjectId.value; + + final projectName = expenseController.projectsMap.entries + .firstWhereOrNull((entry) => entry.value == currentProjectId) + ?.key; + + expenseController.selectedProject.value = projectName ?? ''; + expenseController.selectedStatus.value = ''; + + // Fetch expenses with project filter (no status) + await expenseController.fetchExpenses(); + + // Navigate to Expense screen + Get.to(() => const ExpenseMainScreen()); + } + @override Widget build(BuildContext context) { return Obx(() { @@ -78,12 +153,17 @@ class ExpenseByStatusWidget extends StatelessWidget { children: [ MyText.titleMedium("Expense - By Status", fontWeight: 700), const SizedBox(height: 16), + + // ✅ Status tiles _buildStatusTile( icon: Icons.currency_rupee, color: Colors.blue, title: "Pending Payment", amount: Utils.formatCurrency(data.processPending.totalAmount), count: data.processPending.count.toString(), + onTap: () { + _navigateToExpenseWithFilter(context, 'Payment Pending'); + }, ), _buildStatusTile( icon: Icons.check_circle_outline, @@ -91,6 +171,9 @@ class ExpenseByStatusWidget extends StatelessWidget { title: "Pending Approve", amount: Utils.formatCurrency(data.approvePending.totalAmount), count: data.approvePending.count.toString(), + onTap: () { + _navigateToExpenseWithFilter(context, 'Approval Pending'); + }, ), _buildStatusTile( icon: Icons.search, @@ -98,6 +181,9 @@ class ExpenseByStatusWidget extends StatelessWidget { title: "Pending Review", amount: Utils.formatCurrency(data.reviewPending.totalAmount), count: data.reviewPending.count.toString(), + onTap: () { + _navigateToExpenseWithFilter(context, 'Review Pending'); + }, ), _buildStatusTile( icon: Icons.insert_drive_file_outlined, @@ -105,27 +191,48 @@ class ExpenseByStatusWidget extends StatelessWidget { title: "Draft", amount: Utils.formatCurrency(data.draft.totalAmount), count: data.draft.count.toString(), + onTap: () { + _navigateToExpenseWithFilter(context, 'Draft'); + }, ), + const SizedBox(height: 16), Divider(color: Colors.grey.shade300), const SizedBox(height: 12), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, + + // ✅ Total row tap navigation (no filter) + InkWell( + onTap: _navigateToExpenseWithoutFilter, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - MyText.bodyMedium("Project Spendings:", fontWeight: 600), - MyText.bodySmall("(All Processed Payments)", - color: Colors.grey.shade600), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium("Project Spendings:", + fontWeight: 600), + MyText.bodySmall("(All Processed Payments)", + color: Colors.grey.shade600), + ], + ), + Row( + children: [ + MyText.titleLarge( + Utils.formatCurrency(data.totalAmount), + color: Colors.blue, + fontWeight: 700, + ), + const SizedBox(width: 6), + const Icon(Icons.chevron_right, + color: Colors.blue, size: 22), + ], + ) ], ), - MyText.titleLarge( - "${Utils.formatCurrency(data.totalAmount)} >", - color: Colors.blue, - fontWeight: 700, - ) - ], + ), ), ], ), diff --git a/lib/view/expense/expense_screen.dart b/lib/view/expense/expense_screen.dart index 39d14af..fb2fffa 100644 --- a/lib/view/expense/expense_screen.dart +++ b/lib/view/expense/expense_screen.dart @@ -28,12 +28,17 @@ class _ExpenseMainScreenState extends State final projectController = Get.find(); final permissionController = Get.find(); - @override - void initState() { - super.initState(); - _tabController = TabController(length: 2, vsync: this); +@override +void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + + // ✅ Delay fetch until after UI & controller are ready + WidgetsBinding.instance.addPostFrameCallback((_) { + final expenseController = Get.find(); expenseController.fetchExpenses(); - } + }); +} @override void dispose() {