import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:mobile_pos/Screens/cash%20and%20bank/cansh%20in%20hand/provider/cash_in_hand_provider.dart'; import 'package:mobile_pos/Screens/cash%20and%20bank/cansh%20in%20hand/repo/cash_in_hand_repo.dart'; import 'package:mobile_pos/constant.dart'; import 'package:mobile_pos/currency.dart'; import 'package:mobile_pos/Screens/hrm/widgets/model_bottom_sheet.dart'; import 'package:mobile_pos/Screens/hrm/widgets/deleteing_alart_dialog.dart'; import 'package:mobile_pos/generated/l10n.dart' as l; import '../widgets/cheques_filter_search.dart'; import 'adjust_cash_screen.dart'; import 'cash_to_bank_transfer_screen.dart'; import 'model/cash_transaction_list_model.dart'; class CashInHandScreen extends ConsumerStatefulWidget { const CashInHandScreen({super.key}); @override ConsumerState createState() => _CashInHandScreenState(); } class _CashInHandScreenState extends ConsumerState { String _currentSearchQuery = ''; DateTime? _currentFromDate; DateTime? _currentToDate; final DateFormat _displayFormat = DateFormat('dd/MM/yyyy'); final DateFormat _apiFormat = DateFormat('yyyy-MM-dd'); // List of filter options needed for the reusable widget final List _timeFilterOptions = [ 'Today', 'Yesterday', 'Last 7 Days', 'Last 30 Days', 'Current Month', 'Last Month', 'Current Year', 'Custom Date' ]; final Map timeFilterBn = { 'Today': l.S.current.today, 'Yesterday': l.S.current.yesterday, 'Last 7 Days': l.S.current.last7Days, 'Last 30 Days': l.S.current.last30Days, 'Current Month': l.S.current.currentMonth, 'Last Month': l.S.current.lastMonth, 'Current Year': l.S.current.currentYear, 'Custom Date': l.S.current.customDate, }; @override void initState() { super.initState(); // Initialize default date range for local filtering (e.g., Current Year) final now = DateTime.now(); _currentFromDate = DateTime(now.year, 1, 1); _currentToDate = DateTime(now.year, now.month, now.day, 23, 59, 59); } @override void dispose() { super.dispose(); } String _formatDate(String? date) { if (date == null) return 'N/A'; try { return DateFormat('dd MMM, yyyy').format(DateTime.parse(date)); } catch (_) { return date; } } // --- DELETE Logic (Unchanged) --- Future _confirmAndDeleteTransaction(CashTransactionData transaction) async { final transactionId = transaction.id; if (transactionId == null) return; final confirmed = await showDeleteConfirmationDialog( context: context, itemName: 'cash transaction', ); if (confirmed == true) { final repo = CashTransactionRepo(); await repo.deleteCashTransaction( ref: ref, context: context, transactionId: transactionId, ); } } // --- Logic Helpers (Unchanged) --- String _getListName(CashTransactionData t) { final nameFromUser = t.user?.name ?? 'System'; if (t.transactionType == 'cash_to_bank') { return 'To: Bank (ID: ${t.toBank})'; } else if (t.transactionType == 'bank_to_cash') { return 'From: Bank (ID: ${t.fromBank})'; } else if (t.transactionType == 'adjust_cash') { return t.type == 'credit' ? 'Adjustment (Credit)' : 'Adjustment (Debit)'; } return nameFromUser; } Map _getAmountDetails(CashTransactionData t) { bool isOutgoing = false; if (t.transactionType == 'adjust_cash') { isOutgoing = t.type == 'debit'; } else if (t.transactionType == 'cash_to_bank') { isOutgoing = true; } else if (t.transactionType == 'bank_to_cash') { isOutgoing = false; } final color = isOutgoing ? Colors.red.shade700 : Colors.green.shade700; final sign = isOutgoing ? '-' : '+'; return {'sign': sign, 'color': color}; } // --- Filter Callback Handler --- // 🔔 FIX: Callback now uses the defined CashFilterState type void _handleFilterChange(CashFilterState filterState) { setState(() { _currentSearchQuery = filterState.searchQuery; _currentFromDate = filterState.fromDate; _currentToDate = filterState.toDate; }); } // --- LOCAL FILTERING FUNCTION (Unchanged) --- List _filterTransactionsLocally(List transactions) { // 1. Filter by Date Range Iterable dateFiltered = transactions.where((t) { if (_currentFromDate == null && _currentToDate == null) return true; if (t.date == null) return false; try { final transactionDate = DateTime.parse(t.date!); final start = _currentFromDate; final end = _currentToDate; bool afterStart = start == null || transactionDate.isAfter(start) || transactionDate.isAtSameMomentAs(start); bool beforeEnd = end == null || transactionDate.isBefore(end) || transactionDate.isAtSameMomentAs(end); return afterStart && beforeEnd; } catch (e) { return false; } }); // 2. Filter by Search Query final query = _currentSearchQuery.toLowerCase(); if (query.isEmpty) { return dateFiltered.toList(); } return dateFiltered.where((c) { return (c.transactionType ?? '').toLowerCase().contains(query) || (c.user?.name ?? '').toLowerCase().contains(query) || (c.amount?.toString() ?? '').contains(query) || (c.invoiceNo ?? '').toLowerCase().contains(query); }).toList(); } // --- END LOCAL FILTERING FUNCTION --- // --- Navigation Helpers for App Bar Menu (Unchanged) --- void _navigateToTransfer() { Navigator.push(context, MaterialPageRoute(builder: (context) => const CashToBankTransferScreen())); } void _navigateToAdjust() { Navigator.push(context, MaterialPageRoute(builder: (context) => const AdjustCashBalanceScreen())); } // --- UI Builders (Only _buildBalanceCard and _buildActionMenu shown for brevity) --- Widget _buildBalanceCard(ThemeData theme, num balance) { return Container( width: double.infinity, color: kMainColor.withOpacity(0.9), padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l.S.of(context).cashInHand, style: theme.textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold, color: Colors.white), ), const SizedBox(height: 15), // Balance Info Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(4), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '$currency${balance.toStringAsFixed(2)}', style: theme.textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold, color: Colors.white), ), Text(l.S.of(context).currentCashBalance, style: theme.textTheme.bodySmall?.copyWith(color: Colors.white70)), ], ), ), ], ), ); } Widget _buildActionMenu(CashTransactionData transaction, BuildContext context) { final _lang = l.S.of(context); // ... (Implementation unchanged) ... Map _compileDetails() { final details = {}; details[_lang.transactionType] = (transaction.transactionType ?? 'N/A').replaceAll('_', ' ').toUpperCase(); details[_lang.date] = _formatDate(transaction.date); details[_lang.amount] = '$currency${transaction.amount?.toStringAsFixed(2) ?? '0.00'}'; details[_lang.user] = transaction.user?.name ?? 'System'; details[_lang.invoiceNumber] = transaction.invoiceNo ?? 'N/A'; details[_lang.note] = transaction.note ?? 'No Note'; if (transaction.transactionType == 'cash_to_bank') { details[_lang.toAccount] = 'Bank ID ${transaction.toBank}'; } else if (transaction.transactionType == 'bank_to_cash') { details[_lang.fromAccount] = 'Bank ID ${transaction.fromBank}'; } return details; } return SizedBox( width: 30, child: PopupMenuButton( onSelected: (value) { if (transaction.id == null) return; if (value == 'view') { viewModalSheet( context: context, item: _compileDetails(), descriptionTitle: '${_lang.description}:', description: transaction.note ?? 'N/A', ); } else if (value == 'edit') { if (transaction.transactionType != 'adjust_cash') { Navigator.push( context, MaterialPageRoute( builder: (context) => CashToBankTransferScreen( transaction: transaction, ), )); } else { Navigator.push( context, MaterialPageRoute( builder: (context) => AdjustCashBalanceScreen( transaction: transaction, ), )); } } else if (value == 'delete') { _confirmAndDeleteTransaction(transaction); } }, itemBuilder: (context) => [ PopupMenuItem(value: 'view', child: Text(l.S.of(context).view)), if ((transaction.transactionType == 'cash_to_bank') || (transaction.transactionType == 'adjust_cash')) PopupMenuItem(value: 'edit', child: Text(l.S.of(context).edit)), PopupMenuItem(value: 'delete', child: Text(l.S.of(context).delete, style: TextStyle(color: Colors.red))), ], icon: const Icon(Icons.more_vert, color: kNeutral800), ), ); } @override Widget build(BuildContext context) { final _lang = l.S.of(context); final theme = Theme.of(context); final historyAsync = ref.watch(cashTransactionHistoryProvider); return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text(_lang.cashInHand), centerTitle: true, toolbarHeight: 80, bottom: PreferredSize( preferredSize: Size.fromHeight(50), child: Column( children: [ ChequesFilterSearch( displayFormat: _displayFormat, timeOptions: _timeFilterOptions, onFilterChanged: (filterState) { // Cast the dynamic output to the expected CashFilterState _handleFilterChange(filterState as CashFilterState); }, ), Divider(thickness: 1, color: kLineColor), ], ), ), ), body: historyAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (err, stack) => Center(child: Text('Error: ${err.toString()}')), data: (model) { final allTransactions = model.data ?? []; // Apply local date and search filtering final filteredTransactions = _filterTransactionsLocally(allTransactions); return RefreshIndicator( onRefresh: () => ref.refresh(cashTransactionHistoryProvider.future), child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 1. Balance and Account Info Card // _buildBalanceCard(theme, model.totalBalance ?? 0), // 2. Filters and Search (Using Reusable Widget) // 🔔 FIX: Using ChequesFilterSearch from external file // 3. Transaction List if (filteredTransactions.isEmpty) Center( child: Padding( padding: EdgeInsets.all(40), child: Text( _lang.noTransactionFoundForThisFilter, textAlign: TextAlign.center, ))) else ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: filteredTransactions.length, separatorBuilder: (_, __) => const Divider(color: kBackgroundColor), itemBuilder: (_, index) { final transaction = filteredTransactions[index]; final amountDetails = _getAmountDetails(transaction); return ListTile( visualDensity: VisualDensity(vertical: -4, horizontal: -4), contentPadding: EdgeInsets.symmetric(horizontal: 16), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( (transaction.transactionType ?? 'N/A').replaceAll('_', ' ').toUpperCase(), style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), Text( '$currency${transaction.amount?.toStringAsFixed(2) ?? '0.00'}', style: theme.textTheme.titleMedium?.copyWith( color: amountDetails['color'], ), ), ], ), subtitle: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( _formatDate(transaction.date), style: theme.textTheme.bodyMedium?.copyWith( color: kGrey6, ), ), Text( transaction.platform.toString(), style: theme.textTheme.bodyMedium?.copyWith( color: kGrey6, ), ), ], ), trailing: _buildActionMenu(transaction, context), ); }, ), ], ), ), ); }, ), bottomNavigationBar: Padding( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Expanded( child: OutlinedButton( onPressed: () => _navigateToTransfer(), child: Text(_lang.transfer), ), ), const SizedBox(width: 16), Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: kMainColor, ), onPressed: () => _navigateToAdjust(), child: Text(_lang.adjustCash, style: TextStyle(color: Colors.white)), ), ), ], ), ), ); } }