import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:helpers/helpers/misc_build/build_media.dart'; import '../../../global/api.dart'; import '../../../global/store.dart'; import '../../../global/styles.dart'; import '../../../models/budget_category_model.dart'; import '../../../models/transaction_model.dart'; class AddTransactionDialog extends ConsumerStatefulWidget { const AddTransactionDialog({super.key, this.transaction}); final Transaction? transaction; @override ConsumerState createState() => _AddTransactionDialogState(); } class _AddTransactionDialogState extends ConsumerState { bool loading = false; bool complete = false; late final TextEditingController amountController; late final TextEditingController memoController; final amountFocusNode = FocusNode(); final memoFocusNode = FocusNode(); TransactionType transactionType = TransactionType.expense; late BudgetCategory selectedBudgetCategory; @override void initState() { if (widget.transaction != null) { amountController = TextEditingController(text: widget.transaction!.amount.toString()); memoController = TextEditingController(text: widget.transaction!.memo); transactionType = widget.transaction!.type; } else { amountController = TextEditingController(); memoController = TextEditingController(); } final categories = ref.read(Store().budgetCategoriesProvider); if (categories.isNotEmpty) { selectedBudgetCategory = categories.first; } super.initState(); } @override Widget build(BuildContext context) { if (complete) { WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(widget.transaction != null ? 'Transaction updated!' : 'Transaction added!'), ), ); }); return Container(); } final List budgetCategories = ref.read(Store().budgetCategoriesProvider); return SizedBox( width: BuildMedia(context).width * Styles.dialogScreenWidthFactor, child: budgetCategories.isEmpty ? const Padding( padding: EdgeInsets.all(8.0), child: Text( 'Add budget categories to sort your transactions', textAlign: TextAlign.center, ), ) : Padding( padding: const EdgeInsets.all(18.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ if (widget.transaction == null) Padding( padding: const EdgeInsets.only(bottom: 8.0, left: 8.0), child: Row(children: [ if (budgetCategories.isNotEmpty) InkWell( onTap: transactionType == TransactionType.expense ? null : () { setState(() => transactionType = TransactionType.expense); }, child: AnimatedContainer( height: 38, width: 80, decoration: BoxDecoration( borderRadius: const BorderRadius.only( topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0)), color: transactionType == TransactionType.expense ? Styles.lavender : Styles.washedStone), duration: const Duration(milliseconds: 300), child: const Padding( padding: EdgeInsets.all(8.0), child: Text('Expense', style: TextStyle(fontSize: 16)), ), ), ), InkWell( onTap: transactionType == TransactionType.income ? null : () { setState(() => transactionType = TransactionType.income); }, child: AnimatedContainer( height: 38, width: 80, decoration: BoxDecoration( borderRadius: const BorderRadius.only( topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0)), color: transactionType == TransactionType.income ? Styles.lavender : Styles.washedStone), duration: const Duration(milliseconds: 300), child: const Padding( padding: EdgeInsets.all(8.0), child: Text('Income', style: TextStyle(fontSize: 16)), ), ), ), ]), ), Row( children: [ const Padding( padding: EdgeInsets.all(8.0), child: Text( 'Amount:', style: TextStyle(fontSize: 18.0), ), ), Container( width: 120, height: 40, decoration: Styles.boxLavenderBubble, child: TextFormField( keyboardType: TextInputType.number, textAlign: TextAlign.center, style: const TextStyle(fontSize: 18.0), cursorColor: Styles.blushingPink, decoration: Styles.inputLavenderBubble(), focusNode: amountFocusNode, controller: amountController, onFieldSubmitted: (_) { memoFocusNode.requestFocus(); }, ), ), ], ), if (budgetCategories.isEmpty) const Padding( padding: EdgeInsets.all(18.0), child: Text( 'Add budget categories to sort your transactions', textAlign: TextAlign.center, ), ), if (budgetCategories.isNotEmpty) AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: transactionType == TransactionType.income ? const SizedBox(height: 12.5) : Padding( padding: const EdgeInsets.only(left: 8.0), child: Row( children: [ const Padding( padding: EdgeInsets.only(right: 28.0), child: Text( 'Category:', style: TextStyle(fontSize: 18.0), ), ), DropdownButton( dropdownColor: Styles.lavender, value: selectedBudgetCategory, items: budgetCategories .map( (e) => DropdownMenuItem( value: e, child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( height: 18, width: 18, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.black, width: 1.5), color: e.color, ), ), const SizedBox(width: 12), Text(e.name), ], ), ), ) .toList(), onChanged: (BudgetCategory? value) { if (value != null) { if (kDebugMode) { print('${value.name} selected'); } setState(() => selectedBudgetCategory = value); } }, ), ], ), ), ), const Padding( padding: EdgeInsets.all(8.0), child: Row( children: [ Text( 'Memo', style: TextStyle(fontSize: 18.0), ), ], ), ), Container( width: BuildMedia(context).width * 0.65, height: 100, decoration: Styles.boxLavenderBubble, child: TextFormField( style: const TextStyle(fontSize: 18.0), maxLines: 4, focusNode: memoFocusNode, textAlign: TextAlign.left, cursorColor: Styles.blushingPink, controller: memoController, decoration: Styles.inputLavenderBubble(), onFieldSubmitted: (_) {}, ), ), ElevatedButton( onPressed: loading ? null : () => submitTransaction(), child: const Text('Add'), ), ], ), )); } Future submitTransaction() async { setState(() { loading = true; }); Map? data; if (widget.transaction != null) { data = await Api().put( path: 'transactions', data: Transaction.copyWith(widget.transaction!, { 'memo': memoController.text.isNotEmpty ? memoController.text : null, 'amount': double.parse(amountController.text), 'budget_category_id': transactionType == TransactionType.income ? null : selectedBudgetCategory.id, }).toJson()); } else { data = await Api().post( path: 'transactions', data: Transaction( amount: double.parse(amountController.text), addedByUserId: 1, budgetCategoryId: transactionType == TransactionType.income ? null : selectedBudgetCategory.id, budgetId: 1, date: DateTime.now(), type: transactionType, memo: memoController.text.isNotEmpty ? memoController.text : null) .toJson()); } final success = data != null ? data['success'] as bool : false; if (success) { if (widget.transaction != null) { ref .read(Store().dashboardProvider.notifier) .update({'transactions': data}); } else { ref .read(Store().dashboardProvider.notifier) .add({'transactions': data}); } complete = true; } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(widget.transaction != null ? 'Failed to edit transaction' : 'Failed to add transaction'), ), ); } setState(() { loading = false; }); } }