diff --git a/lib/backend/category.dart b/lib/backend/category.dart index 65cf5b0..35f600f 100644 --- a/lib/backend/category.dart +++ b/lib/backend/category.dart @@ -1,5 +1,7 @@ import 'package:flutter/widgets.dart'; +import 'package:graphic/graphic.dart'; import 'wyrm/database.dart'; +import 'package:smartspend/backend/payments.dart'; class Category { static final Wyrm database = Wyrm(); @@ -9,15 +11,6 @@ class Category { late num currentSpending; late int categoryColor; late String categoryName; - late Map lastWeekSpending = { - "Monday" : 1, - "Tuesday" : 1, - "Wednesday" : 1, - "Thursday" : 1, - "Friday" : 1, - "Saturday" : 1, - "Sunday" : 1 , - }; Category({ required this.userID, @@ -39,24 +32,53 @@ class Category { }; } - Future addExpense(String day, num expense) async { - await loadSpending(); - lastWeekSpending[day] = lastWeekSpending[day] !+ expense; - currentSpending += expense; + static List getWeekDates() { + final List res = []; + DateTime mostRecentWeekday(DateTime date, int weekday) => //code from stackoverflow, thanks Irn + DateTime(date.year, date.month, date.day - (date.weekday - weekday) % 7); + DateTime monday = mostRecentWeekday(DateTime.now(), 1); + res.add(monday); + for(int i = 1; i < 7; i++){ + res.add(monday.add(Duration(days: i))); + } + return res; + } - Map data = { - "categoryID" : categoryID, - }; + Future> getWeekSpending() async { + final Map res = {}; + final List thisWeekDates = getWeekDates(); + List> paymentsPerDay = await database.getPaymentsByDate(thisWeekDates, categoryID); + + for(int i = 0; i < paymentsPerDay.length; i++){ + String date; + try { + date = DateTime.parse(paymentsPerDay[i].first.paymentDate).day.toString(); + } catch (e) { + date = thisWeekDates[i].day.toString(); + } - for (var key in lastWeekSpending.keys) { - data[key] = lastWeekSpending[key]; + num amountSpent = 0; + for(int j = 0; j < paymentsPerDay[i].length; j++){ + amountSpent += paymentsPerDay[i][j].paymentAmount; + } + res[date] = amountSpent; } + return res; + } - await database.updateEntryInTable( - 'weeklyspending', - 'categoryID', - categoryID, - data, + Future addExpense(String date, num amount) async { + currentSpending += amount; + + Payment expense = Payment( + paymentID: DateTime.now().millisecondsSinceEpoch.toInt(), + categoryID: categoryID, + paymentDate: date, + paymentAmount: amount, + ); + + await database.insertToTable( + expense, + 'payment' ); await database.updateEntryInTable( @@ -68,24 +90,11 @@ class Category { } - Future loadSpending() async { - Map data = (await database.weeklySpending(categoryID)).first; - lastWeekSpending = { - "Monday" : data['Monday'], - "Tuesday" : data['Tuesday'], - "Wednesday" : data['Wednesday'], - "Thursday" : data['Thursday'], - "Friday" : data['Friday'], - "Saturday" : data['Saturday'], - "Sunday" : data['Sunday'], - }; - } - @override String toString() { Color tmp = Color(categoryColor); return "Category{ID: $categoryID, userID: $userID, Color: $tmp, " "Category Name: $categoryName, Spending Limit: $spendingLimit, " - "Current Spending: $currentSpending, weekly: $lastWeekSpending}"; + "Current Spending: $currentSpending}"; } } \ No newline at end of file diff --git a/lib/backend/payments.dart b/lib/backend/payments.dart new file mode 100644 index 0000000..ec1a312 --- /dev/null +++ b/lib/backend/payments.dart @@ -0,0 +1,29 @@ +class Payment { + final int paymentID; + final int categoryID; + final String paymentDate; + final num paymentAmount; + + + Payment({ + required this.paymentID, + required this.categoryID, + required this.paymentDate, + required this.paymentAmount, + }); + + Map toMap() { + return { + "paymentID" : paymentID, + "categoryID" : categoryID, + "paymentDate" : paymentDate, + "paymentAmount" : paymentAmount, + }; + } + + @override + String toString(){ + return 'Payment{ID: $paymentID, Category: $categoryID, ' + 'Payment Date: $paymentDate, Amount: $paymentAmount}'; + } +} \ No newline at end of file diff --git a/lib/backend/wyrm/database.dart b/lib/backend/wyrm/database.dart index bcaf980..1ed835c 100644 --- a/lib/backend/wyrm/database.dart +++ b/lib/backend/wyrm/database.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:path/path.dart'; +import 'package:smartspend/backend/payments.dart'; import 'package:sqflite/sqflite.dart'; import 'package:smartspend/backend/user.dart'; import 'package:smartspend/backend/category.dart'; @@ -20,34 +21,29 @@ class Wyrm { join(await getDatabasesPath(), '$name.db'), onCreate: (db, version) async { await db.execute( - 'CREATE TABLE user' - '(userID INT PRIMARY KEY,' - 'name TEXT,' - 'passcode TEXT,' - 'birthDate TEXT,' - 'isNewUser TINYINT,' - 'monthlyincome DECIMAL)' + 'CREATE TABLE user' + '(userID INT PRIMARY KEY,' + 'name TEXT,' + 'passcode TEXT,' + 'birthDate TEXT,' + 'isNewUser TINYINT,' + 'monthlyincome DECIMAL)' ); await db.execute( - 'CREATE TABLE category' - '(categoryID INT PRIMARY KEY,' - 'userID INT,' - 'categoryName TEXT,' - 'spendingLimit DECIMAL,' - 'currentSpending DECIMAL,' - 'categoryColor INT)' + 'CREATE TABLE category' + '(categoryID INT PRIMARY KEY,' + 'userID INT,' + 'categoryName TEXT,' + 'spendingLimit DECIMAL,' + 'currentSpending DECIMAL,' + 'categoryColor INT)' ); await db.execute( - 'CREATE TABLE weeklyspending(' + 'CREATE TABLE payment(' + 'paymentID INT,' 'categoryID INT,' - 'Monday DECIMAL,' - 'Tuesday DECIMAL,' - 'Wednesday DECIMAL,' - 'Thursday DECIMAL,' - 'Friday DECIMAL,' - 'Saturday DECIMAL,' - 'Sunday DECIMAL,' - 'FOREIGN KEY (categoryID) REFERENCES category(categoryID))' + 'paymentDate STRING,' + 'paymentAmount DECIMAL)' ); return; }, @@ -55,6 +51,34 @@ class Wyrm { ); } + Future>> getPaymentsByDate + (List dates, int categoryID) async { + await initDB(); + final db = database; + + final List> res = []; + + for(int j = 0; j < dates.length; j++){ + final List> data = await db.query( + 'payment', + where: "paymentDate=? and categoryID=? ", + whereArgs: ["${dates[j].year}-${dates[j].month}-${dates[j].day}".toString(), categoryID], + ); + List tmp = []; + for (int i = 0; i < data.length; i++){ + tmp.add(Payment( + paymentID: data[i]['paymentID'] as int, + categoryID: data[i]['categoryID'] as int , + paymentDate: data[i]['paymentDate'] as String, + paymentAmount: data[i]['paymentAmount'] as num, + )); + } + res.add(tmp); + } + + return res; + } + Future insertToTable(dynamic entry, String table) async { await initDB(); final db = database; @@ -73,27 +97,6 @@ class Wyrm { ); } - Future>> weeklySpending(int categoryID) async { - await initDB(); - final db = database; - - final List> maps = await db.query('weeklyspending', - where: 'categoryID = ?', whereArgs: [categoryID]); - - return List.generate(maps.length, (i) { - return { - "categoryID" : maps[i]['categoryID'] as int, - "Monday" : maps[i]['Monday'] as num, - "Tuesday" : maps[i]['Tuesday'] as num, - "Wednesday" : maps[i]['Wednesday'] as num, - "Thursday" : maps[i]['Thursday'] as num, - "Friday" : maps[i]['Friday'] as num, - "Saturday" : maps[i]['Saturday'] as num, - "Sunday" : maps[i]['Sunday'] as num, - }; - }); - } - Future> categories() async { await initDB(); final db = database; @@ -112,6 +115,22 @@ class Wyrm { }); } + Future> payments() async { + await initDB(); + final db = database; + + final List> maps = await db.query("payment"); + + return List.generate(maps.length, (i) { + return Payment( + paymentID: maps[i]['paymentID'] as int, + categoryID: maps[i]['categoryID'] as int , + paymentDate: maps[i]['paymentDate'] as String, + paymentAmount: maps[i]['paymentAmount'] as num, + ); + }); + } + Future> users() async { await initDB(); final db = database; diff --git a/lib/main.dart b/lib/main.dart index cc340b4..e1d5275 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -109,17 +109,6 @@ Future> initCategories(Wyrm database, User client) async { ]; for (int i = 0; i < categories.length; i++){ await database.insertToTable(categories[i], 'category'); - await database.insertToTable({ - "categoryID" : categories[i].categoryID, - "Monday" : 1, - "Tuesday" : 1, - "Wednesday" : 1, - "Thursday" : 1, - "Friday" : 1, - "Saturday" : 1, - "Sunday" : 1, - }, - "weeklyspending"); } } return categories; diff --git a/lib/pages/addexpense.dart b/lib/pages/addexpense.dart index a4c6064..6c79be0 100644 --- a/lib/pages/addexpense.dart +++ b/lib/pages/addexpense.dart @@ -27,15 +27,6 @@ class _AddExpensePageState extends State { bool _loading = true; bool expenseAdded = false; bool selectingCategory = false; - List daysList = [ - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sunday" - ]; late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; @override @@ -209,7 +200,7 @@ class _AddExpensePageState extends State { ); setState(() { widget.controllers[1].text = - "${date?.day}/${date?.month}/${date?.year}"; + "${date?.year}-${date?.month}-${date?.day}"; dateSelected = date!; }); } @@ -239,7 +230,7 @@ class _AddExpensePageState extends State { child: TextButton( onPressed: !isActivated() ? null : () { selectedCategory.addExpense( - daysList[dateSelected.weekday-1], + widget.controllers[1].text, int.parse(widget.controllers[0].text), ); setState(() { diff --git a/lib/pages/homepage.dart b/lib/pages/homepage.dart index aab03b1..382b347 100644 --- a/lib/pages/homepage.dart +++ b/lib/pages/homepage.dart @@ -99,12 +99,8 @@ class _HomePageState extends State{ child: GestureDetector( onDoubleTap: () async { List categories = await database.categories(); - for (var category in categories) { - await category.loadSpending(); - } Navigator.push(context, MaterialPageRoute(builder: (context) => MonthlyBudgetPage( - categories: categories, ))); }, child: Container( @@ -180,13 +176,9 @@ class _HomePageState extends State{ GestureDetector( onTap: () async { List categories = await database.categories(); - for (var category in categories) { - await category.loadSpending(); - } Navigator.push(context, MaterialPageRoute(builder: (context) => MonthlyBudgetPage( - categories: categories, ))); }, child: Container( @@ -218,13 +210,9 @@ class _HomePageState extends State{ child: IconButton( onPressed: () async { List categories = await database.categories(); - for (var category in categories) { - await category.loadSpending(); - } Navigator.push(context, MaterialPageRoute(builder: (context) => MonthlyBudgetPage( - categories: categories, ))); }, icon: const Icon( diff --git a/lib/pages/monthlybudgetpage.dart b/lib/pages/monthlybudgetpage.dart index 648b463..700feea 100644 --- a/lib/pages/monthlybudgetpage.dart +++ b/lib/pages/monthlybudgetpage.dart @@ -3,11 +3,44 @@ import 'package:smartspend/widgets/graphcard.dart'; import 'package:smartspend/widgets/trajectorywidget.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:smartspend/backend/category.dart'; +import 'package:smartspend/backend/wyrm/database.dart'; +class MonthlyBudgetPage extends StatefulWidget { + MonthlyBudgetPage({super.key}); -class MonthlyBudgetPage extends StatelessWidget{ - final List categories; - const MonthlyBudgetPage({super.key, required this.categories}); + @override + State createState() => _MonthlyBudgetPage(); +} + +class _MonthlyBudgetPage extends State{ + late final List categories; + late final List> data; + Wyrm database = Wyrm(); + bool _loading = true; + + Future>> initGraphs() async { + List> maps = []; + for(int i = 0; i < categories.length; i++){ + Map map = await categories[i].getWeekSpending(); + maps.add(map); + } + return maps; + } + + Future initArgs() async { + categories = await database.categories(); + data = await initGraphs(); + + setState(() { + _loading = false; + }); + } + + @override + void initState() { + super.initState(); + initArgs(); + } List graphs() { List res = []; @@ -18,7 +51,7 @@ class MonthlyBudgetPage extends StatelessWidget{ child: GraphCard( cardName: categories[i].categoryName, barColor: Color(categories[i].categoryColor), - dataMap: categories[i].lastWeekSpending, + dataMap: data[i], ) ) ); @@ -28,6 +61,9 @@ class MonthlyBudgetPage extends StatelessWidget{ @override Widget build(BuildContext context){ + if (_loading) { + return const CircularProgressIndicator(); + } return Scaffold( body: Stack( children: [ diff --git a/lib/widgets/bargraph.dart b/lib/widgets/bargraph.dart index 708985b..d86da59 100644 --- a/lib/widgets/bargraph.dart +++ b/lib/widgets/bargraph.dart @@ -9,20 +9,63 @@ class BarChart extends StatelessWidget{ BarChart({super.key, required this.dataMap, required this.width, required this.barColor}); num normalizeData(){ + print(dataMap); List keys = dataMap.keys.toList(); List vals = dataMap.values.toList(); vals.sort(); num min = 0; num max = vals[vals.length-1]; + if(max.isNaN || max == 0){ + return 1; + } for(int i = 0; i < keys.length; i++){ values.add(((dataMap[keys[i]] !- min) / (max - min))); } return max; } + List graphBars(double wi, double hi){ + List keys = dataMap.keys.toList(); + double factor = 1; + List bars = []; + print(values); + for(int i = 0; i < keys.length; i++){ + if(values.length == 0){ + values = [0,0,0,0,0,0,0]; + } + bars.add( + Column( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(5), + topRight: Radius.circular(5) + ), + color: barColor, + ), + width: wi * 0.08, + height: values[i] * (hi * 0.15), + ), + Container( + child: Text( + keys[i], + style: const TextStyle( + fontFamily: 'Montserrat', + fontWeight: FontWeight.w700, + fontSize: 12, + ), + ), + ), + ], + ) + ); + } + return bars; + } + @override Widget build(BuildContext context) { - List keys = dataMap.keys.toList(); num max = normalizeData(); return SizedBox( width: width * 0.95, @@ -36,178 +79,10 @@ class BarChart extends StatelessWidget{ mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(5) - ), - color: barColor, - ), - width: MediaQuery.of(context).size.width * 0.08, - height: values[0] * (MediaQuery.of(context).size.height * 0.15), - ), - Container( - child: Text(keys[0].substring(0, 3), - style: const TextStyle( - fontFamily: 'Montserrat', - fontWeight: FontWeight.w700, - fontSize: 12, - ), - ), - ), - ], - ), - Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(5) - ), - color: barColor, - ), - width: MediaQuery.of(context).size.width * 0.08, - height: values[1] * (MediaQuery.of(context).size.height * 0.15), - ), - Container( - child: Text(keys[1].substring(0, 3), - style: const TextStyle( - fontFamily: 'Montserrat', - fontWeight: FontWeight.w700, - fontSize: 12, - ), - ), - ), - ], - ), - Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(5) - ), - color: barColor, - ), - width: MediaQuery.of(context).size.width * 0.08, - height: values[2] * (MediaQuery.of(context).size.height * 0.15), - ), - Container( - child: Text(keys[2].substring(0, 3), - style: const TextStyle( - fontFamily: 'Montserrat', - fontWeight: FontWeight.w700, - fontSize: 12, - ), - ), - ), - ], - ), - Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(5) - ), - color: barColor, - ), - width: MediaQuery.of(context).size.width * 0.08, - height: values[3] * (MediaQuery.of(context).size.height * 0.15), - ), - Container( - child: Text(keys[3].substring(0, 3), - style: const TextStyle( - fontFamily: 'Montserrat', - fontWeight: FontWeight.w700, - fontSize: 12, - ), - ), - ), - ], - ), - Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(5) - ), - color: barColor, - ), - width: MediaQuery.of(context).size.width * 0.08, - height: values[4] * (MediaQuery.of(context).size.height * 0.15), - ), - Container( - child: Text(keys[4].substring(0, 3), - style: const TextStyle( - fontFamily: 'Montserrat', - fontWeight: FontWeight.w700, - fontSize: 12, - ), - ), - ), - ], - ), - Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(5) - ), - color: barColor, - ), - width: MediaQuery.of(context).size.width * 0.08, - height: values[5] * (MediaQuery.of(context).size.height * 0.15), - ), - Container( - child: Text( - keys[5].substring(0, 3), - style: const TextStyle( - fontFamily: 'Montserrat', - fontWeight: FontWeight.w700, - fontSize: 12, - ), - ), - ), - ], - ), - Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - topRight: Radius.circular(5) - ), - color: barColor, - ), - width: MediaQuery.of(context).size.width * 0.08, - height: values[6] * (MediaQuery.of(context).size.height * 0.15), - ), - Container( - child: Text( - keys[6].substring(0, 3), - style: const TextStyle( - fontFamily: 'Montserrat', - fontWeight: FontWeight.w700, - fontSize: 12, - ), - ), - ), - ], - ), - ], + children: graphBars( + MediaQuery.of(context).size.width, + MediaQuery.of(context).size.height, + ), ), Positioned( bottom: 15, diff --git a/pubspec.lock b/pubspec.lock index 240e830..83c3ca0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -764,6 +764,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" sqflite: dependency: "direct main" description: @@ -868,6 +876,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: bb55f38968b9427ce5dcdb8aaaa41049282195e0cfa4cf48593572fa3d1f36bc + url: "https://pub.dev" + source: hosted + version: "4.3.1" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e44aa71..2dd4fbc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,6 +47,7 @@ dependencies: restart_app: ^1.2.1 flutter_launcher_icons: ^0.13.1 flutter_svg: ^2.0.9 + uuid: ^4.3.1 dev_dependencies: