Added working edit transaction

This commit is contained in:
Nathan Anderson
2023-07-22 21:29:32 -06:00
parent eba628bb4c
commit 18aad2b3d5
28 changed files with 1959 additions and 430 deletions
@@ -1,6 +1,7 @@
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';
@@ -9,7 +10,9 @@ import '../../../models/budget_category_model.dart';
import '../../../models/transaction_model.dart';
class AddTransactionDialog extends ConsumerStatefulWidget {
const AddTransactionDialog({super.key});
const AddTransactionDialog({super.key, this.transaction});
final Transaction? transaction;
@override
ConsumerState<AddTransactionDialog> createState() =>
@@ -17,146 +20,310 @@ class AddTransactionDialog extends ConsumerStatefulWidget {
}
class _AddTransactionDialogState extends ConsumerState<AddTransactionDialog> {
final amountController = TextEditingController();
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) {
final List<BudgetCategory> budgetCategories =
ref.read(Store().budgetCategoriesProvider).requireValue;
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
side: const BorderSide(color: Styles.purpleNurple, width: 5.0),
),
child: StatefulBuilder(builder: (context, setState) {
TransactionType transactionType = TransactionType.expense;
BudgetCategory selectedBudgetCategory = budgetCategories.first;
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Styles.purpleNurple,
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(children: [
InkWell(
onTap: transactionType == TransactionType.expense
? null
: () {
setState(() =>
transactionType = TransactionType.expense);
},
child: AnimatedContainer(
height: 30,
width: 70,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0)),
color: transactionType == TransactionType.expense
? Styles.lavender
: Styles.sand),
duration: const Duration(milliseconds: 300),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text('Expense'),
),
),
),
InkWell(
onTap: transactionType == TransactionType.income
? null
: () {
setState(
() => transactionType = TransactionType.income);
},
child: AnimatedContainer(
height: 30,
width: 70,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0)),
color: transactionType == TransactionType.income
? Styles.lavender
: Styles.sand),
duration: const Duration(milliseconds: 300),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text('Income'),
),
),
),
]),
Row(
children: [
const Text('Amount:'),
SizedBox(
width: 80,
child: TextField(
controller: amountController,
decoration: InputDecoration(
fillColor: Styles.lavender,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide:
const BorderSide(color: Colors.transparent),
),
),
),
),
],
),
Row(
children: [
const Padding(
padding: EdgeInsets.only(right: 28.0),
child: Text('Category:'),
),
DropdownButton<BudgetCategory>(
value: selectedBudgetCategory,
items: budgetCategories
.map(
(e) => DropdownMenuItem(
value: e,
child: Text(e.name),
),
)
.toList(),
onChanged: (BudgetCategory? value) {
if (value != null) {
if (kDebugMode) {
print('${value.name} selected');
}
setState(() => selectedBudgetCategory = value);
}
},
),
],
),
ElevatedButton(
child: const Text('Add'),
onPressed: () {
Api().put(
path: 'transactions',
data: Transaction(
amount: double.parse(amountController.text),
addedByUserId: 1,
budgetCategoryId: 1,
budgetId: 1,
date: DateTime.now(),
transactionType: transactionType));
Navigator.pop(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<BudgetCategory> 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<BudgetCategory>(
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<String, dynamic>? 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;
});
}
}