import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; // For date formatting import '../models/transaction_record.dart'; // Use the model class class TransactionListItem extends StatelessWidget { final TransactionRecord transaction; // Use the model class final VoidCallback? onEdit; // Callback for edit action final VoidCallback? onDelete; // Callback for delete action const TransactionListItem({ Key? key, required this.transaction, this.onEdit, // Make callbacks optional this.onDelete, }) : super(key: key); // Function to show the options menu void _showOptionsMenu(BuildContext context) { showModalBottomSheet( context: context, builder: (context) { return SafeArea( // Use SafeArea to avoid system UI child: Column( mainAxisSize: MainAxisSize.min, // Take minimum space children: [ ListTile( leading: const Icon(Icons.edit_outlined), title: const Text('Редактировать'), onTap: () { Navigator.pop(context); // Close the bottom sheet onEdit?.call(); // Call the edit callback if it exists }, ), ListTile( leading: const Icon(Icons.delete_outline, color: Colors.red), title: const Text('Удалить', style: TextStyle(color: Colors.red)), onTap: () { Navigator.pop(context); // Close the bottom sheet onDelete?.call(); // Call the delete callback if it exists }, ), // Optional: Add a Cancel button ListTile( leading: const Icon(Icons.cancel_outlined), title: const Text('Отмена'), onTap: () => Navigator.pop(context), ), ], ), ); }, ); } @override Widget build(BuildContext context) { final isExpense = transaction.type == 'expense'; final theme = Theme.of(context); // Wrap the ListTile in an InkWell to handle long press return InkWell( onLongPress: () => _showOptionsMenu(context), // Show menu on long press child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), // Adjusted padding child: Row( children: [ // Category Icon (only for expenses) if (isExpense && transaction.category != null) Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: transaction.category!.colorCode.withOpacity(0.1), // Use category color shape: BoxShape.circle, ), child: Icon( transaction.category!.iconCode, // Use category icon color: transaction.category!.colorCode, size: 20, ), ) else if (!isExpense) // Icon for Income Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.green.shade100, // Specific color for income icon background shape: BoxShape.circle, ), child: Icon( Icons.attach_money_outlined, // Specific icon for income color: Colors.green.shade700, size: 20, ), ), const SizedBox(width: 16), // Space between icon and text // Transaction Details (Category/Merchant, Date) Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( isExpense ? transaction.merchant : transaction.merchant, // Display merchant for both for now style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w500, // Medium weight ), maxLines: 1, overflow: TextOverflow.ellipsis, // Prevent overflow ), const SizedBox(height: 4), Text( // Display category name for expense, or 'Income' for income isExpense ? transaction.category?.name ?? 'Unknown Category' : 'Income', style: theme.textTheme.bodySmall?.copyWith( color: theme.textTheme.bodySmall?.color?.withOpacity(0.7), // Subtle color ), ), ], ), ), // Amount and Date Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '${isExpense ? '-' : '+'} \$${NumberFormat.currency(symbol: '', decimalDigits: 2).format(transaction.amount)}', // Format amount with sign style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: isExpense ? Colors.red.shade600 : Colors.green.shade600, // Color based on type ), ), const SizedBox(height: 4), Text( DateFormat('MMM d, yyyy').format(transaction.date), // Format date style: theme.textTheme.bodySmall?.copyWith( color: theme.textTheme.bodySmall?.color?.withOpacity(0.7), // Subtle color ), ), ], ), ], ), ), ); } }