diff --git a/client/cashcompass/lib/screens/categories_screen/categories_screen.dart b/client/cashcompass/lib/screens/categories_screen/categories_screen.dart index 4028140..dab523c 100644 --- a/client/cashcompass/lib/screens/categories_screen/categories_screen.dart +++ b/client/cashcompass/lib/screens/categories_screen/categories_screen.dart @@ -24,8 +24,8 @@ class _CategoriesScreenState extends State { late Map> incomesCategories; late Map> expenseCategories; - List incomes = List.empty(); - List expenses = List.empty(); + List incomes = List.empty(growable: true); + List expenses = List.empty(growable: true); @override void initState() { diff --git a/client/cashcompass/lib/screens/latest_transactions/latest_transactions_screen.dart b/client/cashcompass/lib/screens/latest_transactions/latest_transactions_screen.dart index 59dc677..d01c9a4 100644 --- a/client/cashcompass/lib/screens/latest_transactions/latest_transactions_screen.dart +++ b/client/cashcompass/lib/screens/latest_transactions/latest_transactions_screen.dart @@ -1,5 +1,6 @@ import 'package:cashcompass/controller/controller.dart'; import 'package:cashcompass/widgets/transactions_list_widgets/transactions_list.dart'; +import 'package:cashcompass_hook/src/chart_of_accounts.dart/chart_of_accounts.dart'; import 'package:flutter/cupertino.dart'; class LatestTransactionsScreen extends StatefulWidget { @@ -13,13 +14,14 @@ class LatestTransactionsScreen extends StatefulWidget { class _LatestTransactionsScreenState extends State { @override Widget build(BuildContext context) { + ChartOfAccounts chartOfAccounts = + ChartOfAccounts(Controller.accountManager); return CupertinoPageScaffold( navigationBar: const CupertinoNavigationBar( middle: Text("Latest Transactions"), ), child: SafeArea( - child: TransactionsList( - transactions: Controller.accountManager.data.transactions), + child: TransactionsList(), ), ); } diff --git a/client/cashcompass/lib/screens/transactions_detail_screen/transactions_detail_screen.dart b/client/cashcompass/lib/screens/transactions_detail_screen/transactions_detail_screen.dart new file mode 100644 index 0000000..0e0bf76 --- /dev/null +++ b/client/cashcompass/lib/screens/transactions_detail_screen/transactions_detail_screen.dart @@ -0,0 +1,341 @@ +import 'package:cashcompass/controller/controller.dart'; +import 'package:cashcompass/widgets/transactions_list_widgets/InterpretedTransaction.dart'; +import 'package:cashcompass_hook/src/accounts/category/category_icons.dart'; +import 'package:cashcompass_hook/src/transactions/transactions/transaction.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import 'package:cashcompass_hook/src/chart_of_accounts.dart/chart_of_accounts.dart'; + +import 'package:cashcompass_hook/src/data_storage/accout_manager.dart'; +import '../../widgets/transactions_list_widgets/transactions_list.dart'; + +enum Selection { income, expense } + +class TransactionsDetailScreen extends StatefulWidget { + bool editMode; + final Transaction? transaction; + TransactionsDetailScreen({Key? key, required this.editMode, this.transaction}) + : super(key: key); + + @override + _TransactionsDetailScreenState createState() => + _TransactionsDetailScreenState(); +} + +class _TransactionsDetailScreenState extends State { + Selection? selectedValue; + late TextEditingController _amountController; + late TextEditingController _walletController; + late TextEditingController _titleController; + late TextEditingController _noteController; + DateTime? _timestamp; + String _selectedDate = ""; + CategoryIcons selectedIcon = CategoryIcons.values.first; + + InterpretedTransaction? interpretedTransaction; + + @override + void initState() { + super.initState(); + _amountController = TextEditingController(); + _walletController = TextEditingController(); + _titleController = TextEditingController(); + _noteController = TextEditingController(); + + if (widget.transaction != null) { + interpretedTransaction = interpretTransaction(widget.transaction!); + _amountController.text = widget.transaction!.amount.toString(); + _walletController.text = interpretedTransaction!.walletName; + _titleController.text = widget.transaction!.label; + selectedValue = interpretedTransaction!.isExpense + ? Selection.expense + : Selection.income; + + // Set initial date from interpretedTransaction + DateTime transactionDate = + widget.transaction!.timestamp; // Assuming date format is yyyy-MM-dd + _selectedDate = + "${transactionDate.year}-${transactionDate.month.toString().padLeft(2, '0')}-${transactionDate.day.toString().padLeft(2, '0')}"; + } + } + + @override + void dispose() { + _amountController.dispose(); + _walletController.dispose(); + _titleController.dispose(); + _noteController.dispose(); + super.dispose(); + } + + Future _selectDate(BuildContext context) async { + _timestamp = await showCupertinoModalPopup( + context: context, + builder: (BuildContext context) { + return Container( + height: 216, + padding: const EdgeInsets.only(top: 6.0), + color: CupertinoColors.white, + child: DefaultTextStyle( + style: const TextStyle( + color: CupertinoColors.black, + fontSize: 22.0, + ), + child: GestureDetector( + onTap: () { + Navigator.of(context).pop(); + }, + child: SafeArea( + top: false, + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.date, + initialDateTime: DateTime.now(), + onDateTimeChanged: (DateTime newDate) { + setState(() { + _selectedDate = + "${newDate.year}-${newDate.month.toString().padLeft(2, '0')}-${newDate.day.toString().padLeft(2, '0')}"; + }); + }, + ), + ), + ), + ), + ); + }, + ); + } + + Future _showPicker() async { + await showCupertinoModalPopup( + context: context, + builder: (BuildContext context) { + return Container( + height: 200, + padding: EdgeInsets.all(8.0), + color: CupertinoColors.white, + child: CupertinoPicker( + itemExtent: 35, + onSelectedItemChanged: (index) { + setState(() { + selectedIcon = CategoryIcons.values[index]; + }); + }, + children: CategoryIcons.values + .map((icon) => Row( + children: [ + Icon(icon.icon), + SizedBox(width: 10), + Text(icon.name), + ], + )) + .toList(), + ), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text("Transaction Detail"), + trailing: IconButton( + icon: Icon(CupertinoIcons.pencil), + onPressed: () { + setState(() { + widget.editMode = !widget.editMode; + }); + }, + ), + ), + child: SafeArea( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + FractionallySizedBox( + widthFactor: 1, + child: Padding( + padding: const EdgeInsets.only(bottom: 20.0), + child: CupertinoSlidingSegmentedControl( + onValueChanged: widget.editMode + ? handleExpenseIncomeChanged + : (Selection? selection) {}, + children: { + Selection.income: Padding( + padding: EdgeInsets.all(10), + child: Text('Income'), + ), + Selection.expense: Padding( + padding: EdgeInsets.all(10), + child: Text('Expense'), + ), + }, + groupValue: selectedValue, + ), + ), + ), + Container( + decoration: BoxDecoration( + color: CupertinoColors.white, + borderRadius: BorderRadius.circular(15), + ), + padding: EdgeInsets.all(8), + child: Table( + columnWidths: { + 0: FractionColumnWidth(0.3), + }, + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + children: [ + TableRow( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Amount'), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: CupertinoTextField( + controller: _amountController, + readOnly: !widget.editMode, + keyboardType: TextInputType.number, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Wallet'), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: CupertinoTextField( + controller: _walletController, + readOnly: !widget.editMode, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Category'), + ), + CupertinoButton( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon(selectedIcon.icon), + SizedBox(width: 10), + Text(selectedIcon.name), + ], + ), + onPressed: widget.editMode ? _showPicker : null, + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Date'), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: GestureDetector( + onTap: widget.editMode + ? () => _selectDate(context) + : null, + child: Container( + alignment: Alignment.centerLeft, + height: 44.0, + child: Text( + _selectedDate.isEmpty + ? "Select Date" + : _selectedDate, + style: TextStyle( + color: _selectedDate.isEmpty + ? CupertinoColors.placeholderText + : CupertinoColors.black, + ), + ), + ), + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Title'), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: CupertinoTextField( + controller: _titleController, + readOnly: !widget.editMode, + ), + ), + ], + ), + TableRow( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Note'), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: CupertinoTextField( + controller: _noteController, + readOnly: !widget.editMode, + maxLines: 3, + ), + ), + ], + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(20.0), + child: CupertinoButton( + child: Text(!widget.editMode ? "DELETE" : "SAVE"), + onPressed: handleDeleteTransaction, + color: !widget.editMode + ? CupertinoColors.systemRed + : CupertinoColors.systemGreen, + ), + ), + ], + ), + ), + ), + ), + ); + } + + void handleExpenseIncomeChanged(Selection? selection) { + setState(() { + selectedValue = selection; + }); + } + + void handleDeleteTransaction() { + var chart = ChartOfAccounts(Controller.accountManager); + chart.createTransaction( + Controller.accountManager.getAccount(22)!, + Controller.accountManager.getAccount(11)!, + _titleController.text, + double.parse(_amountController.text), + _timestamp ?? DateTime.now()); + Navigator.of(context).pop(); + } +} diff --git a/client/cashcompass/lib/widgets/balance_overview/segmented_control.dart b/client/cashcompass/lib/widgets/balance_overview/segmented_control.dart index c768959..83e8633 100644 --- a/client/cashcompass/lib/widgets/balance_overview/segmented_control.dart +++ b/client/cashcompass/lib/widgets/balance_overview/segmented_control.dart @@ -19,7 +19,6 @@ class SegmentedControlWidget extends StatelessWidget { return Container( margin: const EdgeInsets.all(10.0), child: CupertinoSlidingSegmentedControl( - backgroundColor: CupertinoColors.systemGrey2, thumbColor: selectedSegment .color, // Set the color of the thumb based on the selected segment groupValue: selectedSegment, // currently selected value @@ -29,11 +28,12 @@ class SegmentedControlWidget extends StatelessWidget { (index, selection) => MapEntry( selection, Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Text( - selection.title, - style: const TextStyle(color: CupertinoColors.white), - ), + padding: const EdgeInsets.all(10), + child: Text(selection.title, + style: TextStyle( + color: selection == selectedSegment + ? CupertinoColors.white + : CupertinoColors.black)), ), ), ), diff --git a/client/cashcompass/lib/widgets/transactions_list_widgets/InterpretedTransaction.dart b/client/cashcompass/lib/widgets/transactions_list_widgets/InterpretedTransaction.dart index cd7ef20..a18d0ff 100644 --- a/client/cashcompass/lib/widgets/transactions_list_widgets/InterpretedTransaction.dart +++ b/client/cashcompass/lib/widgets/transactions_list_widgets/InterpretedTransaction.dart @@ -3,10 +3,12 @@ import 'package:flutter/cupertino.dart'; class InterpretedTransaction { String walletName; IconData categoryIcon; + bool isExpense; IconData signIcon; InterpretedTransaction( {this.walletName = "not set", this.categoryIcon = CupertinoIcons.info, - this.signIcon = CupertinoIcons.circle}); + this.signIcon = CupertinoIcons.circle, + this.isExpense = true}); } diff --git a/client/cashcompass/lib/widgets/transactions_list_widgets/transactions_list.dart b/client/cashcompass/lib/widgets/transactions_list_widgets/transactions_list.dart index a258106..738edc4 100644 --- a/client/cashcompass/lib/widgets/transactions_list_widgets/transactions_list.dart +++ b/client/cashcompass/lib/widgets/transactions_list_widgets/transactions_list.dart @@ -1,3 +1,5 @@ +import 'package:cashcompass/controller/controller.dart'; +import 'package:cashcompass/screens/transactions_detail_screen/transactions_detail_screen.dart'; import 'package:cashcompass/widgets/transactions_list_widgets/InterpretedTransaction.dart'; import 'package:cashcompass_hook/src/accounts/active_account/active_account.dart'; import 'package:cashcompass_hook/src/accounts/bookable.dart'; @@ -6,15 +8,53 @@ import 'package:cashcompass_hook/src/accounts/category/category_icons.dart'; import 'package:cashcompass_hook/src/transactions/transactions/transaction.dart'; import 'package:flutter/cupertino.dart'; +InterpretedTransaction interpretTransaction(Transaction transaction) { + final Bookable soll = transaction.soll; + final Bookable haben = transaction.haben; + late ActiveAccount wallet; + late Category category; + late IconData signIcon; + late bool isExpense; + + if (soll is Category && haben is Category) { + throw UnsupportedError("Transaction of two PassiveAccounts not managed!"); + } else if (soll is ActiveAccount && haben is ActiveAccount) { + throw UnsupportedError("Transfer not yet implemented!"); + } else if (soll is ActiveAccount && haben is Category) { + category = haben; + wallet = soll; + signIcon = CupertinoIcons.minus; + isExpense = true; + } else if (soll is Category && haben is ActiveAccount) { + category = soll; + wallet = haben; + signIcon = CupertinoIcons.plus; + isExpense = false; + } else { + throw UnsupportedError("Unsupported Account Types!"); + } + return InterpretedTransaction( + walletName: wallet.name, + categoryIcon: CategoryIcons.fromName(category.iconString).icon, + signIcon: signIcon, + isExpense: isExpense); +} + class TransactionsList extends StatefulWidget { - final List transactions; - const TransactionsList({super.key, required this.transactions}); + const TransactionsList({super.key}); @override State createState() => _TransactionsListState(); } class _TransactionsListState extends State { + List transactions = []; + @override + void initState() { + super.initState(); + transactions = Controller.accountManager.data.transactions; + } + @override Widget build(BuildContext context) { return Column( @@ -54,9 +94,9 @@ class _TransactionsListState extends State { ), ), ), - ...widget.transactions.map((transaction) { + ...transactions.map((transaction) { InterpretedTransaction interpretedTransaction = - _interpretTransaction(transaction); + interpretTransaction(transaction); return CupertinoListTile( title: Row( children: [ @@ -88,7 +128,7 @@ class _TransactionsListState extends State { interpretedTransaction.categoryIcon, color: CupertinoColors.black, ), - onTap: _handleTransactionListTileTapped, + onTap: () => _handleTransactionListTileTapped(transaction), ); }), ], @@ -97,38 +137,22 @@ class _TransactionsListState extends State { ); } - InterpretedTransaction _interpretTransaction(Transaction transaction) { - final Bookable soll = transaction.soll; - final Bookable haben = transaction.haben; - late ActiveAccount wallet; - late Category category; - late IconData signIcon; - - if (soll is Category && haben is Category) { - throw UnsupportedError("Transaction of two PassiveAccounts not managed!"); - } else if (soll is ActiveAccount && haben is ActiveAccount) { - throw UnsupportedError("Transfer not yet implemented!"); - } else if (soll is ActiveAccount && haben is Category) { - category = haben; - wallet = soll; - signIcon = CupertinoIcons.minus; - } else if (soll is Category && haben is ActiveAccount) { - category = soll; - wallet = haben; - signIcon = CupertinoIcons.plus; - } else { - throw UnsupportedError("Unsupported Account Types!"); - } - return InterpretedTransaction( - walletName: wallet.name, - categoryIcon: CategoryIcons.fromName(category.iconString).icon, - signIcon: signIcon, - ); + void handleAddTransaction() { + Navigator.of(context) + .push(CupertinoPageRoute( + builder: (context) => TransactionsDetailScreen(editMode: true))) + .then((v) { + setState(() { + transactions = Controller.accountManager.data.transactions; + }); + }); } - void handleAddTransaction() {} - - void _handleTransactionListTileTapped() { - throw UnsupportedError("Not yet implemented"); + void _handleTransactionListTileTapped(Transaction transaction) { + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => TransactionsDetailScreen( + editMode: false, + transaction: transaction, + ))); } } diff --git a/client/cashcompass_hook/lib/src/chart_of_accounts.dart/chart_of_accounts.dart b/client/cashcompass_hook/lib/src/chart_of_accounts.dart/chart_of_accounts.dart index fd605fb..4d9b326 100644 --- a/client/cashcompass_hook/lib/src/chart_of_accounts.dart/chart_of_accounts.dart +++ b/client/cashcompass_hook/lib/src/chart_of_accounts.dart/chart_of_accounts.dart @@ -84,15 +84,16 @@ class ChartOfAccounts { .toList(); } - Future createTransaction( - Bookable aktive, - Bookable passive, - String lable, - double amount, - ) { + Future createTransaction(Bookable aktive, Bookable passive, + String lable, double amount, DateTime timestamp) { return _accountmanager.appendTransaction( TransactionsFactory(_accountmanager) - .create(amount: amount, soll: aktive, haben: passive, label: lable) + .create( + amount: amount, + soll: aktive, + haben: passive, + label: lable, + timestamp: timestamp) .build()); } } diff --git a/client/cashcompass_hook/lib/src/connector/mock_classes/mock_data_adapter.dart b/client/cashcompass_hook/lib/src/connector/mock_classes/mock_data_adapter.dart index 741378d..5ce1df2 100644 --- a/client/cashcompass_hook/lib/src/connector/mock_classes/mock_data_adapter.dart +++ b/client/cashcompass_hook/lib/src/connector/mock_classes/mock_data_adapter.dart @@ -52,6 +52,7 @@ class MockDataAdapter extends DataAdapter { "name": "GiftsDonations", "account_number": 21, "icon": "GiftsDonations", + "color": "#ff6961", "soll": [], "haben": [] }, @@ -61,6 +62,7 @@ class MockDataAdapter extends DataAdapter { "name": "Groceries", "account_number": 22, "icon": "Groceries", + "color": "#ffb340", "soll": [], "haben": [] }, @@ -70,6 +72,7 @@ class MockDataAdapter extends DataAdapter { "name": "Transport", "account_number": 23, "icon": "Transport", + "color": "#409cff", "soll": [], "haben": [] }, @@ -79,6 +82,7 @@ class MockDataAdapter extends DataAdapter { "name": "Loans", "account_number": 24, "icon": "Loans", + "color": "#a05b00", "soll": [], "haben": [] }, diff --git a/client/cashcompass_hook/lib/src/transactions/transactions/transactions_factory.dart b/client/cashcompass_hook/lib/src/transactions/transactions/transactions_factory.dart index 9771b53..e26959d 100644 --- a/client/cashcompass_hook/lib/src/transactions/transactions/transactions_factory.dart +++ b/client/cashcompass_hook/lib/src/transactions/transactions/transactions_factory.dart @@ -20,13 +20,15 @@ class TransactionsFactory extends Factory