import 'package:flutter/material.dart'; import 'package:drift/drift.dart' show Value; import 'package:intl/intl.dart'; import '../database/database.dart' as db; import '../utils/category_utils.dart'; // For icon and color utilities class EditTransactionDialog extends StatefulWidget { final db.AppDatabase database; final db.Transaction transaction; // The transaction to edit final Stream> categoriesStream; // Stream of available categories const EditTransactionDialog({ Key? key, required this.database, required this.transaction, required this.categoriesStream, }) : super(key: key); @override State createState() => _EditTransactionDialogState(); } class _EditTransactionDialogState extends State { final _formKey = GlobalKey(); late TextEditingController _amountController; late TextEditingController _merchantController; late String? _selectedCategoryName; // Can be null for Income late DateTime _selectedDate; late db.TransactionType _selectedType; @override void initState() { super.initState(); // Initialize controllers and state with existing transaction data _amountController = TextEditingController(text: widget.transaction.amount.toString()); _merchantController = TextEditingController(text: widget.transaction.merchant); _selectedDate = widget.transaction.date; _selectedType = widget.transaction.type == 'income' ? db.TransactionType.income : db.TransactionType.expense; // Set initial category name based on transaction type if (_selectedType == db.TransactionType.expense) { _selectedCategoryName = widget.transaction.categoryName; } else { _selectedCategoryName = null; // Income doesn't have a selectable category } } @override void dispose() { _amountController.dispose(); _merchantController.dispose(); super.dispose(); } // Function to show the date and time pickers Future _selectDateTime(BuildContext context) async { // 1. Pick Date final DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedDate, firstDate: DateTime(2000), lastDate: DateTime.now().add(const Duration(days: 365)), ); if (pickedDate != null) { // If date was picked, proceed to pick time // 2. Pick Time final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: TimeOfDay.fromDateTime(_selectedDate), ); if (pickedTime != null) { // If time was also picked, combine date and time and update state setState(() { _selectedDate = DateTime( pickedDate.year, pickedDate.month, pickedDate.day, pickedTime.hour, pickedTime.minute, ); }); } else { // If only date was picked, update state with the picked date and existing time setState(() { _selectedDate = DateTime( pickedDate.year, pickedDate.month, pickedDate.day, _selectedDate.hour, // Keep existing hour _selectedDate.minute, // Keep existing minute ); }); } } } // Function to handle form submission (Update) void _updateTransaction() async { if (_formKey.currentState!.validate()) { final amount = double.tryParse(_amountController.text); if (amount == null || amount <= 0) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Пожалуйста, введите корректную положительную сумму.'), backgroundColor: Colors.red, ), ); return; } String categoryToSave; String typeString = _selectedType == db.TransactionType.income ? 'income' : 'expense'; if (_selectedType == db.TransactionType.income) { categoryToSave = 'Income'; // Fixed category for income } else { if (_selectedCategoryName == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Пожалуйста, выберите категорию для расхода.'), backgroundColor: Colors.red, ), ); return; } categoryToSave = _selectedCategoryName!; } // Create the updated transaction companion final updatedTransaction = db.TransactionsCompanion( id: Value(widget.transaction.id), // Include the ID for update categoryName: Value(categoryToSave), amount: Value(amount), date: Value(_selectedDate), merchant: Value(_merchantController.text), type: Value(typeString), ); try { // Update transaction in the database final success = await widget.database.updateTransaction(updatedTransaction); if (success) { // Close the dialog and return the updated transaction if (mounted) Navigator.of(context).pop(widget.transaction.copyWith( // Return a copy with updated values categoryName: categoryToSave, amount: amount, date: _selectedDate, merchant: _merchantController.text, type: typeString, )); } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Не удалось обновить транзакцию.'), backgroundColor: Colors.red, ), ); } } } catch (e) { print('Error updating transaction: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Ошибка при обновлении транзакции: $e'), backgroundColor: Colors.red, ), ); } } } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; final bool isIncome = _selectedType == db.TransactionType.income; return AlertDialog( title: const Text('Редактировать транзакцию'), content: SingleChildScrollView( // Use SingleChildScrollView for content child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // --- Transaction Type Selector --- Center( child: ToggleButtons( isSelected: [!isIncome, isIncome], onPressed: (int index) { setState(() { _selectedType = index == 0 ? db.TransactionType.expense : db.TransactionType.income; // Reset category selection if switching to income if (_selectedType == db.TransactionType.income) { _selectedCategoryName = null; } else { // If switching to expense, try to select the first expense category // This relies on the StreamBuilder below to update the dropdown // and potentially set a default if _selectedCategoryName is null. } }); }, borderRadius: BorderRadius.circular(12), // ИЗМЕНЕНО: Уменьшена минимальная ширина кнопок constraints: BoxConstraints(minWidth: (MediaQuery.of(context).size.width - 160) / 2, minHeight: 40), // Adjusted width for dialog selectedColor: Colors.white, fillColor: isIncome ? Colors.green.shade400 : Colors.red.shade400, color: isDark ? Colors.white70 : Colors.black54, selectedBorderColor: isIncome ? Colors.green.shade600 : Colors.red.shade600, borderColor: isDark ? Colors.grey.shade600 : Colors.grey.shade400, children: const [ Padding( padding: EdgeInsets.symmetric(horizontal: 16.0), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.arrow_upward_rounded, size: 18), SizedBox(width: 8), Text('Расход'), ], ), ), Padding( padding: EdgeInsets.symmetric(horizontal: 16.0), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.arrow_downward_rounded, size: 18), SizedBox(width: 8), Text('Доход'), ], ), ), ], ), ), const SizedBox(height: 20), // --- Amount Field --- TextFormField( controller: _amountController, decoration: InputDecoration( labelText: 'Сумма', prefixIcon: Icon(Icons.attach_money, color: isIncome ? Colors.green : theme.colorScheme.primary), border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), filled: true, fillColor: isDark ? Colors.grey.shade800.withOpacity(0.5) : Colors.grey.shade100, ), keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: (value) { if (value == null || value.isEmpty) { return 'Пожалуйста, введите сумму'; } if (double.tryParse(value) == null || double.parse(value) <= 0) { return 'Пожалуйста, введите корректное положительное число'; } return null; }, ), const SizedBox(height: 16), // --- Category Dropdown (Only for Expenses, uses StreamBuilder) --- if (!isIncome) StreamBuilder>( stream: widget.categoriesStream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting && !snapshot.hasData) { return const Center(child: CircularProgressIndicator(strokeWidth: 2)); } if (snapshot.hasError) { return Text('Ошибка загрузки категорий: ${snapshot.error}'); } final categoriesFromDb = snapshot.data ?? []; // Filter out 'Income' category for the dropdown final expenseCategories = categoriesFromDb.where((c) => c.name != 'Income').toList(); // Ensure _selectedCategoryName is valid or reset it if (_selectedCategoryName != null && !expenseCategories.any((c) => c.name == _selectedCategoryName)) { _selectedCategoryName = null; // Reset if selected category is no longer valid } // Set default selection if nothing is selected and list is not empty // This handles the case when switching from Income to Expense if (_selectedCategoryName == null && expenseCategories.isNotEmpty) { // Use WidgetsBinding to schedule state update after build WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { // Check if widget is still mounted setState(() { _selectedCategoryName = expenseCategories[0].name; }); } }); } return DropdownButtonFormField( value: _selectedCategoryName, decoration: InputDecoration( labelText: 'Категория', border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), filled: true, fillColor: isDark ? Colors.grey.shade800.withOpacity(0.5) : Colors.grey.shade100, contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 16.0), ), items: expenseCategories.map((db.CategoryDb category) { final iconData = CategoryUtils.getIconFromString(category.icon); final colorData = Color(category.color); return DropdownMenuItem( value: category.name, child: Row( children: [ Icon(iconData, color: colorData, size: 20), const SizedBox(width: 10), Text(category.name), ], ), ); }).toList(), onChanged: (String? newValue) { setState(() { _selectedCategoryName = newValue; }); }, validator: (value) { if (_selectedType == db.TransactionType.expense && value == null) { return 'Пожалуйста, выберите категорию'; } return null; }, ); }, ), if (!isIncome) const SizedBox(height: 16), // --- Date and Time Picker --- InkWell( onTap: () => _selectDateTime(context), child: InputDecorator( decoration: InputDecoration( labelText: 'Дата и время', prefixIcon: const Icon(Icons.calendar_today_outlined), border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), filled: true, fillColor: isDark ? Colors.grey.shade800.withOpacity(0.5) : Colors.grey.shade100, ), child: Text( DateFormat.yMMMd().add_jm().format(_selectedDate), style: theme.textTheme.bodyLarge, ), ), ), const SizedBox(height: 16), // --- Merchant / Source Field --- TextFormField( controller: _merchantController, decoration: InputDecoration( labelText: isIncome ? 'Источник' : 'Продавец / Магазин', prefixIcon: Icon(isIncome ? Icons.source_outlined : Icons.storefront_outlined), border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), filled: true, fillColor: isDark ? Colors.grey.shade800.withOpacity(0.5) : Colors.grey.shade100, ), textCapitalization: TextCapitalization.words, ), ], ), ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), // Close dialog child: const Text('Отмена'), ), ElevatedButton( onPressed: _updateTransaction, // Call update function child: const Text('Сохранить'), ), ], ); } }