import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:rluv/global/utils.dart'; import '../../../global/api.dart'; import '../../../global/store.dart'; import '../../../global/styles.dart'; import '../../../global/widgets/ui_button.dart'; import '../../../models/budget_category_model.dart'; import '../../../models/transaction_model.dart'; import '../../../models/user.dart'; class TransactionDialog extends ConsumerStatefulWidget { const TransactionDialog({super.key, this.transaction}); final Transaction? transaction; @override ConsumerState createState() => _AddTransactionDialogState(); } class _AddTransactionDialogState extends ConsumerState { late DateTime selectedDate; late final TextEditingController amountController; late final TextEditingController memoController; User? user; 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; selectedDate = widget.transaction!.date; } else { amountController = TextEditingController(); memoController = TextEditingController(); selectedDate = DateTime.now(); } final categories = ref.read(budgetCategoriesProvider); if (categories.isNotEmpty) { selectedBudgetCategory = categories.first; } final u = ref.read(userProvider); if (u == null) { WidgetsBinding.instance.addPostFrameCallback((_) => ref.read(jwtProvider.notifier).revokeToken()); } else { user = u; } super.initState(); } @override Widget build(BuildContext context) { if (user == null) return Container(); final List budgetCategories = ref.read(budgetCategoriesProvider); return SizedBox( width: MediaQuery.of(context).size.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: MediaQuery.of(context).size.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: (_) {}, ), ), UiButton( showLoading: true, text: 'ADD', color: Styles.lavender, onPressed: () => submitTransaction().then((_) { if (mounted) { Navigator.pop(context); } showSnack( ref: ref, text: widget.transaction != null ? 'Transaction updated!' : 'Transaction added!', type: SnackType.success, ); }), ), ], ), )); } Future submitTransaction() async { Map? data; if (widget.transaction != null) { data = await ref.read(apiProvider.notifier).put( path: 'transaction', 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 ref.read(apiProvider.notifier).post( path: 'transaction', data: Transaction( amount: double.parse(amountController.text), createdByUserId: user!.id!, budgetCategoryId: transactionType == TransactionType.income ? null : selectedBudgetCategory.id, budgetId: user!.budgetId, 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(dashboardProvider.notifier).update({'transactions': data}); } else { ref.read(dashboardProvider.notifier).add({'transactions': data}); } } else { showSnack( ref: ref, text: widget.transaction != null ? 'Failed to edit transaction' : 'Failed to add transaction', type: SnackType.error); } } }