330 lines
14 KiB
Dart
330 lines
14 KiB
Dart
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<AddTransactionDialog> createState() =>
|
|
_AddTransactionDialogState();
|
|
}
|
|
|
|
class _AddTransactionDialogState extends ConsumerState<AddTransactionDialog> {
|
|
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<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;
|
|
});
|
|
}
|
|
}
|