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/controller/permission_controller.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; import 'package:marco/model/expense/expense_list_model.dart'; import 'package:marco/model/expense/add_expense_bottom_sheet.dart'; import 'package:marco/view/expense/expense_filter_bottom_sheet.dart'; import 'package:marco/helpers/widgets/expense_main_components.dart'; import 'package:marco/helpers/utils/permission_constants.dart'; import 'package:marco/helpers/widgets/my_refresh_indicator.dart'; class ExpenseMainScreen extends StatefulWidget { const ExpenseMainScreen({super.key}); @override State createState() => _ExpenseMainScreenState(); } class _ExpenseMainScreenState extends State { bool isHistoryView = false; final searchController = TextEditingController(); final expenseController = Get.put(ExpenseController()); final projectController = Get.find(); final permissionController = Get.find(); @override void initState() { super.initState(); expenseController.fetchExpenses(); } Future _refreshExpenses() async { await expenseController.fetchExpenses(); } void _openFilterBottomSheet() { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (_) => ExpenseFilterBottomSheet( expenseController: expenseController, scrollController: ScrollController(), ), ); } List _getFilteredExpenses() { final query = searchController.text.trim().toLowerCase(); final now = DateTime.now(); final filtered = expenseController.expenses.where((e) { return query.isEmpty || e.expensesType.name.toLowerCase().contains(query) || e.supplerName.toLowerCase().contains(query) || e.paymentMode.name.toLowerCase().contains(query); }).toList() ..sort((a, b) => b.transactionDate.compareTo(a.transactionDate)); return isHistoryView ? filtered .where((e) => e.transactionDate.isBefore(DateTime(now.year, now.month))) .toList() : filtered .where((e) => e.transactionDate.month == now.month && e.transactionDate.year == now.year) .toList(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: ExpenseAppBar(projectController: projectController), body: SafeArea( child: Column( children: [ SearchAndFilter( controller: searchController, onChanged: (_) => setState(() {}), onFilterTap: _openFilterBottomSheet, expenseController: expenseController, ), ToggleButtonsRow( isHistoryView: isHistoryView, onToggle: (v) => setState(() => isHistoryView = v), ), Expanded( child: Obx(() { // Loader while fetching first time if (expenseController.isLoading.value && expenseController.expenses.isEmpty) { return SkeletonLoaders.expenseListSkeletonLoader(); } final filteredList = _getFilteredExpenses(); return MyRefreshIndicator( onRefresh: _refreshExpenses, child: filteredList.isEmpty ? ListView( physics: const AlwaysScrollableScrollPhysics(), children: [ SizedBox( height: MediaQuery.of(context).size.height * 0.5, child: Center( child: MyText.bodyMedium( expenseController.errorMessage.isNotEmpty ? expenseController.errorMessage.value : "No expenses found", color: expenseController.errorMessage.isNotEmpty ? Colors.red : Colors.grey, ), ), ), ], ) : NotificationListener( onNotification: (scrollInfo) { if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && !expenseController.isLoading.value) { expenseController.loadMoreExpenses(); } return false; }, child: ExpenseList( expenseList: filteredList, onViewDetail: () => expenseController.fetchExpenses(), ), ), ); }), ) ], ), ), // ✅ FAB only if user has expenseUpload permission floatingActionButton: permissionController.hasPermission(Permissions.expenseUpload) ? FloatingActionButton( backgroundColor: Colors.red, onPressed: showAddExpenseBottomSheet, child: const Icon(Icons.add, color: Colors.white), ) : null, ); } }