From 38d827799c5e687326db975c3b988b5f731eba1f Mon Sep 17 00:00:00 2001
From: Florian Schindler <florianschndlr@gmail.com>
Date: Tue, 27 Jun 2023 22:07:42 +0200
Subject: [PATCH] tried adding functionality to top of home_view

TRIED bcs it not working, fixi dixi tomorrow, wenn ich nicht vergessen hab wie das alles funktioniert und was ich mir dabei gedacht hab
---
 .../frontend/utils/transaction_listtile.dart  |   4 +-
 trackeroo/lib/frontend/views/home_view.dart   | 339 ++++++++++--------
 trackeroo/lib/logic/services/locator.dart     |  16 +-
 .../services/transactions_controller.dart     |  26 +-
 4 files changed, 231 insertions(+), 154 deletions(-)

diff --git a/trackeroo/lib/frontend/utils/transaction_listtile.dart b/trackeroo/lib/frontend/utils/transaction_listtile.dart
index c5f403b..2c23f18 100644
--- a/trackeroo/lib/frontend/utils/transaction_listtile.dart
+++ b/trackeroo/lib/frontend/utils/transaction_listtile.dart
@@ -29,7 +29,9 @@ class _TransactionListtileState extends State<TransactionListtile> {
           key: UniqueKey(),
           direction: DismissDirection.startToEnd,
           onDismissed: (_) => {
-            locator.get<TransactionsController>().deleteTransaction(widget.transaction),
+            setState(() {
+              locator.get<TransactionsController>().deleteTransaction(widget.transaction);
+            }),
             ScaffoldMessenger.of(context).removeCurrentSnackBar(),
             ScaffoldMessenger.of(context).showSnackBar(
               SnackBar(
diff --git a/trackeroo/lib/frontend/views/home_view.dart b/trackeroo/lib/frontend/views/home_view.dart
index 7e09852..ce71ad1 100644
--- a/trackeroo/lib/frontend/views/home_view.dart
+++ b/trackeroo/lib/frontend/views/home_view.dart
@@ -3,7 +3,9 @@ import 'package:fl_chart/fl_chart.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:trackeroo/frontend/utils/transaction_listtile.dart';
 import 'package:trackeroo/frontend/views/onboarding_view.dart';
+import 'package:trackeroo/logic/models/category.dart';
 import 'package:trackeroo/logic/models/transaction.dart';
+import 'package:trackeroo/logic/services/categories_controller.dart';
 import 'package:trackeroo/logic/services/locator.dart';
 import 'package:trackeroo/logic/services/transactions_controller.dart';
 
@@ -28,176 +30,213 @@ class _HomeViewState extends State<HomeView> {
     return SingleChildScrollView(
       child: Padding(
         padding: const EdgeInsets.all(16.0),
-        child: Column(
-          mainAxisAlignment: MainAxisAlignment.start,
-          crossAxisAlignment: CrossAxisAlignment.start,
-          children: <Widget>[
-            const Text(
-              'Balance',
-              style: TextStyle(
-                fontSize: 14.0,
-                fontWeight: FontWeight.bold
-              ),
-            ),
-            const SizedBox(height: 5.0),
-            const Text(
-              '4932.32 €',
-              style: TextStyle(
-                fontSize: 40.0,
-                fontWeight: FontWeight.w300
-              ),
-            ),
-            const SizedBox(height: 10.0),
-            const Text(
-              'Overview',
-              style: TextStyle(
-                fontSize: 14.0,
-                fontWeight: FontWeight.bold
-              ),
-            ),
-            const SizedBox(height: 5.0),
-            Row(
-              mainAxisAlignment: MainAxisAlignment.spaceBetween,
-              children: [
-                const Column(
-                  crossAxisAlignment: CrossAxisAlignment.start,
+        child: ValueListenableBuilder(
+          valueListenable: transContr.transactionsBox.listenable(),
+          builder: (context, Box<Transaction> box, widget) {
+            transContr.transactionsList = box.values.toList();
+            transContr.transactionsList.sort((b, a) => a.createdAt.compareTo(b.createdAt));
+            return Column(
+              mainAxisAlignment: MainAxisAlignment.start,
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: <Widget>[
+                const Text(
+                  'Balance',
+                  style: TextStyle(
+                    fontSize: 14.0,
+                    fontWeight: FontWeight.bold
+                  ),
+                ),
+                const SizedBox(height: 5.0),
+                Text(
+                  transContr.balance.toStringAsFixed(2),
+                  style: const TextStyle(
+                    fontSize: 40.0,
+                    fontWeight: FontWeight.w300
+                  ),
+                ),
+                const SizedBox(height: 10.0),
+                const Text(
+                  'Overview',
+                  style: TextStyle(
+                    fontSize: 14.0,
+                    fontWeight: FontWeight.bold
+                  ),
+                ),
+                const SizedBox(height: 5.0),
+                Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                   children: [
-                    Row(
+                    Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
                       children: [
-                        Icon(Icons.arrow_downward, color: Colors.green, size: 16.0),
-                        Text('Income'),
+                        const Row(
+                          children: [
+                            Icon(Icons.arrow_downward, color: Colors.green, size: 16.0),
+                            Text('Income'),
+                          ],
+                        ),
+                        Text('${transContr.income.toStringAsFixed(2)} €')
                       ],
                     ),
-                    Text('1234.56€')
-                  ],
-                ),
-                Center(
-                  child: SizedBox(
-                    height: 200.0,
-                    width: 50.0,
-                    child: PieChart(
-                      PieChartData(
-                        centerSpaceRadius: 50.0,
-                        sections: [
-                          buildPieChartSection(context, 93.302),
-                          buildPieChartSection(context, 43.302),
-                          buildPieChartSection(context, 104.302),
-                          buildPieChartSection(context, 120.302),
-                          buildPieChartSection(context, 110.302)
-                        ]
-                      )
+                    Center(
+                      child: SizedBox(
+                        height: 200.0,
+                        width: 50.0,
+                        child: PieChart(
+                          PieChartData(
+                            centerSpaceRadius: 0,
+                            sections: transContr.transactionsList.isNotEmpty ? buildPieChartSectionList(context) : null,
+                          ),
+                          swapAnimationDuration: const Duration(milliseconds: 500),
+                          swapAnimationCurve: Curves.easeInOut,
+                        ),
+                      ),
                     ),
-                  ),
-                ),
-                const Column(
-                  crossAxisAlignment: CrossAxisAlignment.start,
-                  children: [
-                    Row(
+                    Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
                       children: [
-                        Icon(Icons.arrow_upward, color: Colors.red, size: 16.0),
-                        Text('Expenses'),
+                        const Row(
+                          children: [
+                            Icon(Icons.arrow_upward, color: Colors.red, size: 16.0),
+                            Text('Expenses'),
+                          ],
+                        ),
+                        Text('${transContr.expenses.toStringAsFixed(2)} €')
                       ],
                     ),
-                    Text('1234.56€')
                   ],
                 ),
-              ],
-            ),
-            const SizedBox(height: 10.0),
-            Row(
-              mainAxisAlignment: MainAxisAlignment.spaceBetween,
-              children: [
-                FilterChip(
-                  onSelected: (value) => setState(() {
-                    timespanView = Timespan.daily;
-                  }),
-                  label: const Text('Daily'),
-                  selected: timespanView == Timespan.daily,
-                  showCheckmark: false
-                ),
-                FilterChip(
-                  onSelected: (value) => setState(() {
-                    timespanView = Timespan.weekly;
-                  }),
-                  label: const Text('Weekly'),
-                  selected: timespanView == Timespan.weekly,
-                  showCheckmark: false
-                ),
-                FilterChip(
-                  onSelected: (value) => setState(() {
-                    timespanView = Timespan.monthly;
-                  }),
-                  label: const Text('Montly'),
-                  selected: timespanView == Timespan.monthly,
-                  showCheckmark: false
+                const SizedBox(height: 10.0),
+                Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  children: [
+                    FilterChip(
+                      onSelected: (value) => setState(() {
+                        timespanView = Timespan.daily;
+                      }),
+                      label: const Text('Daily'),
+                      selected: timespanView == Timespan.daily,
+                      showCheckmark: false
+                    ),
+                    FilterChip(
+                      onSelected: (value) => setState(() {
+                        timespanView = Timespan.weekly;
+                      }),
+                      label: const Text('Weekly'),
+                      selected: timespanView == Timespan.weekly,
+                      showCheckmark: false
+                    ),
+                    FilterChip(
+                      onSelected: (value) => setState(() {
+                        timespanView = Timespan.monthly;
+                      }),
+                      label: const Text('Montly'),
+                      selected: timespanView == Timespan.monthly,
+                      showCheckmark: false
+                    ),
+                    FilterChip(
+                      onSelected: (value) => setState(() {
+                        timespanView = Timespan.yearly;
+                      }),
+                      label: const Text('Yearly'),
+                      selected: timespanView == Timespan.yearly,
+                      showCheckmark: false
+                    ),
+                    FilterChip(
+                      onSelected: (value) => setState(() {
+                        timespanView = Timespan.all;
+                      }),
+                      label: const Text('All'),
+                      selected: timespanView == Timespan.all,
+                      showCheckmark: false
+                    )
+                  ],
                 ),
-                FilterChip(
-                  onSelected: (value) => setState(() {
-                    timespanView = Timespan.yearly;
-                  }),
-                  label: const Text('Yearly'),
-                  selected: timespanView == Timespan.yearly,
-                  showCheckmark: false
+                const SizedBox(height: 20.0),
+                const Text(
+                  'Transactions',
+                  style: TextStyle(
+                    fontSize: 14.0,
+                    fontWeight: FontWeight.bold
+                  ),
                 ),
-                FilterChip(
-                  onSelected: (value) => setState(() {
-                    timespanView = Timespan.all;
-                  }),
-                  label: const Text('All'),
-                  selected: timespanView == Timespan.all,
-                  showCheckmark: false
-                )
-              ],
-            ),
-            const SizedBox(height: 20.0),
-            const Text(
-              'Transactions',
-              style: TextStyle(
-                fontSize: 14.0,
-                fontWeight: FontWeight.bold
-              ),
-            ),
-            const SizedBox(height: 5.0),
-            ValueListenableBuilder(
-              valueListenable: transContr.transactionsBox.listenable(),
-              builder: (context, Box<Transaction> box, widget) {
-                transContr.transactionsList = box.values.toList();
-                transContr.transactionsList.sort((b, a) => a.createdAt.compareTo(b.createdAt));
-                if(box.isEmpty) {
-                  return const SizedBox(
-                    height: 200.0,
-                    child: Center(
-                      child: Text('no existing transactions yet'),
-                    ),
-                  );
-                }
-                return ListView.builder(
+                const SizedBox(height: 5.0),
+                box.isEmpty ? const SizedBox(
+                  height: 200.0,
+                  child: Center(
+                    child: Text('no existing transactions yet'),
+                  ),
+                ) : ListView.builder(
                   physics: const NeverScrollableScrollPhysics(),
                   shrinkWrap: true,
                   itemCount: transContr.transactionsList.length <= 25 ? transContr.transactionsList.length : 25,
                   itemBuilder: (context, index) => TransactionListtile(transaction: transContr.transactionsList[index])
-                );
-              },
-            ),
-            FilledButton(
-              onPressed: () {
-                Navigator.push(
-                  context,
-                  MaterialPageRoute(builder: (context) => const OnboardingView())
-                );
-              },
-              child: const Text('onboarding')
-            ),
-          ],
+                ),
+                FilledButton(
+                  onPressed: () {
+                    Navigator.push(
+                      context,
+                      MaterialPageRoute(builder: (context) => const OnboardingView())
+                    );
+                  },
+                  child: const Text('onboarding')
+                ),
+              ],
+            );
+          }
         ),
       ),
     );
   }
 
-  PieChartSectionData buildPieChartSection(BuildContext context, double value) {
-    return PieChartSectionData(
-      value: value,
-      color: Theme.of(context).colorScheme.secondaryContainer
-    );
+  List<PieChartSectionData> buildPieChartSectionList(BuildContext context) {
+    List<PieChartSectionData> sectionList = [];
+
+    int elapsedDays = transContr.transactionsList.last.createdAt.difference(DateTime.now()).inDays;
+    if(elapsedDays <= 0) elapsedDays = 1;
+
+    double other = 0;
+
+    for (Category cat in locator.get<CategoriesController>().categories.values) {
+      double categorySum = 0;
+      double avg = 0;
+      for(Transaction tr in transContr.transactionsList) {
+        if(tr.categoryId == cat.id) {
+          categorySum += tr.amount;
+        }
+      }
+
+      if(timespanView == Timespan.daily) {
+        avg = categorySum / elapsedDays;
+      } else if(timespanView == Timespan.weekly) {
+        avg = categorySum / (elapsedDays / 7);
+      } else if(timespanView == Timespan.monthly) {
+        avg = categorySum / (elapsedDays / 30);
+      } else if(timespanView == Timespan.yearly) {
+        avg = categorySum / (elapsedDays / 365);
+      } else {
+        avg = categorySum;
+      }
+
+      bool meetsThreshold = (categorySum / transContr.balance) > 0.2;
+
+      if(!meetsThreshold) {
+        other += avg;
+      }
+
+      if(!avg.isNaN && meetsThreshold) {
+        sectionList.add(PieChartSectionData(
+          value: double.parse(avg.toStringAsFixed(2)),
+          color: Color(cat.colorValue),
+          radius: 100.0
+        ));
+      }
+    }
+    sectionList.add(PieChartSectionData(
+      value: double.parse(other.toStringAsFixed(2)),
+      color: Colors.grey,
+      radius: 100.0
+    ));
+    return sectionList;
   }
 }
diff --git a/trackeroo/lib/logic/services/locator.dart b/trackeroo/lib/logic/services/locator.dart
index 5936372..756ebb9 100644
--- a/trackeroo/lib/logic/services/locator.dart
+++ b/trackeroo/lib/logic/services/locator.dart
@@ -49,9 +49,23 @@ Future<void> setupLocatorService() async {
   locator.registerLazySingleton<CategoriesController>(() => categoriesController);
 
   Box<Transaction> transactionsBox = await Hive.openBox<Transaction>('transactions_box');
+  double balance = 0;
+  double income = 0;
+  double expenses = 0;
+  for(Transaction tr in transactionsBox.values.toList()) {
+    balance += tr.amount;
+    if(tr.amount.isNegative) {
+      expenses += tr.amount;
+    } else {
+      income += tr.amount;
+    }
+  }
   TransactionsController transactionsController = TransactionsController(
     transactionsBox: transactionsBox,
-    transactionsList: transactionsBox.values.toList()
+    transactionsList: transactionsBox.values.toList(),
+    balance: balance,
+    income: income,
+    expenses: expenses
   );
   locator.registerLazySingleton<TransactionsController>(() => transactionsController);
 }
diff --git a/trackeroo/lib/logic/services/transactions_controller.dart b/trackeroo/lib/logic/services/transactions_controller.dart
index 88a7cfc..5f631a8 100644
--- a/trackeroo/lib/logic/services/transactions_controller.dart
+++ b/trackeroo/lib/logic/services/transactions_controller.dart
@@ -4,11 +4,14 @@ import 'package:trackeroo/logic/models/transaction.dart';
 import 'package:trackeroo/logic/services/categories_controller.dart';
 import 'package:trackeroo/logic/services/locator.dart';
 
-class TransactionsController {
-  TransactionsController({required this.transactionsBox, required this.transactionsList});
+class TransactionsController extends ChangeNotifier {
+  TransactionsController({required this.transactionsBox, required this.transactionsList, required this.balance, required this.income, required this.expenses});
 
   Box<Transaction> transactionsBox;
   List<Transaction> transactionsList;
+  double balance;
+  double income;
+  double expenses;
   late Transaction lastDeletedTransaction;
 
   Future<bool> saveTransaction(Transaction transaction) async {
@@ -18,6 +21,12 @@ class TransactionsController {
       transaction.id = id;
       await transactionsBox.put(transaction.id, transaction);
       transactionsList = transactionsBox.values.toList();
+      balance += transaction.amount;
+      if(transaction.amount.isNegative) {
+        expenses += transaction.amount;
+      } else {
+        income += transaction.amount;
+      }
     } catch (e) {
       return false;
     }
@@ -39,6 +48,12 @@ class TransactionsController {
       lastDeletedTransaction = transaction;
       await transactionsBox.delete(transaction.id);
       transactionsList = transactionsBox.values.toList();
+      balance -= transaction.amount;
+      if(transaction.amount.isNegative) {
+        expenses -= transaction.amount;
+      } else {
+        income -= transaction.amount;
+      }
     } catch (e) {
       return false;
     }
@@ -49,6 +64,13 @@ class TransactionsController {
     try {
       await transactionsBox.put(lastDeletedTransaction.id, lastDeletedTransaction);
       transactionsList = transactionsBox.values.toList();
+      balance += lastDeletedTransaction.amount;
+
+      if(lastDeletedTransaction.amount.isNegative) {
+        expenses += lastDeletedTransaction.amount;
+      } else {
+        income += lastDeletedTransaction.amount;
+      }
     } catch (e) {
       return false;
     }
-- 
GitLab