Skip to content

Commit

Permalink
Merge pull request #158 from K-w-e/feature/budget
Browse files Browse the repository at this point in the history
Feature/budget
  • Loading branch information
mikev-cw authored Mar 27, 2024
2 parents 5f30d1a + b6b3ab5 commit 2b2e877
Show file tree
Hide file tree
Showing 10 changed files with 548 additions and 137 deletions.
35 changes: 35 additions & 0 deletions lib/model/budget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,35 @@ class BudgetMethods extends SossoldiDatabase {
return item.copy(id: id);
}

Future<Budget> insertOrUpdate(Budget item) async {
final db = await database;

final exists = await checkIfExists(item);
if (exists) {
print("UPDATE ${item.toJson()}");
await db.rawQuery("UPDATE $budgetTable SET amountLimit = ${item.amountLimit} WHERE idCategory = ${item.idCategory}");
} else {
print("INSERT ${item.toJson()}");
await db.insert(budgetTable, item.toJson());
}

return item.copy(id: item.id);
}

Future<bool> checkIfExists(Budget item) async {
final db = await database;

try {
final exists = await db.rawQuery("SELECT * FROM ${budgetTable} WHERE ${item.idCategory} = idCategory");
if(exists.isNotEmpty) {
return true;
}
return false;
} catch (e) {
return false;
}
}

Future<Budget> selectById(int id) async {
final db = await database;

Expand Down Expand Up @@ -135,4 +164,10 @@ class BudgetMethods extends SossoldiDatabase {

return await db.delete(budgetTable, where: '${BudgetFields.id} = ?', whereArgs: [id]);
}

Future<int> deleteByCategory(int id) async {
final db = await database;

return await db.delete(budgetTable, where: '${BudgetFields.idCategory} = ?', whereArgs: [id]);
}
}
5 changes: 5 additions & 0 deletions lib/pages/more_info_page/collaborators_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ var collaborators = const [
"Backend Dev",
"github.com/sainzrow",
],
[
"Alessandro Guerra",
"Full Stack Dev",
"github.com/K-w-e",
]
];


Expand Down
175 changes: 175 additions & 0 deletions lib/pages/planning_page/manage_budget_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../constants/style.dart';
import '../../model/budget.dart';
import '../../model/category_transaction.dart';
import 'widget/budget_category_selector.dart';
import '../../../providers/categories_provider.dart';
import '../../../providers/budgets_provider.dart';

class ManageBudgetPage extends ConsumerStatefulWidget {
final Function() onRefreshBudgets;
const ManageBudgetPage({required this.onRefreshBudgets, super.key});

@override
ConsumerState<ManageBudgetPage> createState() => _ManageBudgetPageState();
}

class _ManageBudgetPageState extends ConsumerState<ManageBudgetPage> {
List<CategoryTransaction> categories = [];
List<Budget> budgets = [];
List<Budget> deletedBudgets = [];

void _loadCategories() async {
categories = await ref.read(categoriesProvider.notifier).getCategories();
budgets = await ref.read(budgetsProvider.notifier).getBudgets();
setState(() {});
}

void updateBudget(Budget updatedBudget, int index) {
setState(() {
deletedBudgets.add(budgets[index]);
budgets[index] = updatedBudget;
});
}

void deleteBudget(Budget removedBudget, int index) {
setState(() {
budgets.removeAt(index);
deletedBudgets.add(removedBudget);
});
}

@override
void initState() {
_loadCategories();
super.initState();
}

@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(15),
child: Text("Select the categories to create your budget",
style: Theme.of(context).textTheme.titleLarge)),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Category',
textAlign: TextAlign.left,
),
Text(
'Amount',
textAlign: TextAlign.right,
),
],
)),
Expanded(
child: Column(children: [
ListView.builder(
shrinkWrap: true,
itemCount: budgets.length,
itemBuilder: (context, index) {
return Dismissible(
key: Key(budgets[index].idCategory.toString()),
background: Container(
padding: const EdgeInsets.only(right: 20.0),
alignment: Alignment.centerRight,
color: Colors.red,
child: const Text(
'Delete',
textAlign: TextAlign.right,
style: TextStyle(color: Colors.white),
),
),
onDismissed: (direction) {
deleteBudget(budgets[index], index);
},
child: BudgetCategorySelector(
categories: categories,
categoriesAlreadyUsed: categories.where((element) => budgets.map((e) => e.name).contains(element.name)).map((e) => e.name).toList(),
budget: budgets[index],
initSelectedCategory: categories
.where((element) =>
element.id == budgets[index].idCategory)
.isEmpty
? categories[0]
: categories
.where((element) =>
element.id == budgets[index].idCategory)
.first,
onBudgetChanged: (updatedBudget) {
updateBudget(updatedBudget, index);
},
));
},
),
Text("Swipe left to delete", style: Theme.of(context).textTheme.bodySmall),
TextButton.icon(
icon: Icon(
Icons.add_circle,
color: Theme.of(context).colorScheme.secondary,
),
onPressed: () {
setState(() {
budgets.add(Budget(
active: true,
amountLimit: 100,
idCategory: categories[0].id!,
name: categories[0].name));
});
},
label: Text(
"Add category budget",
style: Theme.of(context)
.textTheme
.titleSmall!
.apply(color: Theme.of(context).colorScheme.secondary),
)),
])),
const SizedBox(height: 10),
Text(
"Your monthly budget will be: ${budgets.isEmpty ? 0 : budgets.fold(0, (sum, e) => sum + e.amountLimit.toInt())}€"),
const SizedBox(height: 10),
const Divider(height: 1, color: grey2),
Padding(
padding: const EdgeInsets.all(16.0),
child: SizedBox(
width: double.infinity,
child: TextButton(
onPressed: () async {
for (var item in deletedBudgets) {
await BudgetMethods().deleteByCategory(item.idCategory);
}
for (var item in budgets) {
await BudgetMethods().insertOrUpdate(item);
}
setState(() {
widget.onRefreshBudgets();
Navigator.of(context).pop();
});
},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(12.0),
backgroundColor: Theme.of(context).colorScheme.secondary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
"SAVE BUDGET",
style: Theme.of(context).textTheme.bodyLarge!.copyWith(color: Theme.of(context).colorScheme.background),
),
),
),
)
],
);
}
}
50 changes: 40 additions & 10 deletions lib/pages/planning_page/planning_page.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'manage_budget_page.dart';
import 'widget/budget_card.dart';
import 'widget/recurring_payments_card.dart';

Expand All @@ -10,32 +11,61 @@ class PlanningPage extends StatefulWidget {
}

class _PlanningPageState extends State<PlanningPage> {
GlobalKey<_PlanningPageState> _key = GlobalKey<_PlanningPageState>();

void _forceRefresh() {
setState(() {
final key = GlobalKey<_PlanningPageState>();
_key.currentState?.dispose();
_key.currentState?.reassemble();

_key.currentState?._key = key;
});
}

@override
Widget build(BuildContext context) {
return Container(
key: _key,
color: Colors.white,
padding: const EdgeInsetsDirectional.symmetric(horizontal: 10),
child: ListView(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 45),
children: [
Row(
children: [
Text("Monthly budget",
style: Theme.of(context).textTheme.titleLarge),
const Spacer(),
GestureDetector(
onTap: () {
print("Manage budgets");
},
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
elevation: 10,
builder: (BuildContext context) {
return FractionallySizedBox(
heightFactor: 0.9,
child: ManageBudgetPage(
onRefreshBudgets: _forceRefresh));
},
);
},
child: Row(children: [
Text("MANAGE",
style: Theme.of(context).textTheme.labelLarge),
SizedBox(width: 5),
Icon(Icons.edit, size: 13)
]))
Text("MANAGE",
style: Theme.of(context).textTheme.labelLarge),
const SizedBox(width: 5),
const Icon(Icons.edit, size: 13)
]))
],
),
const SizedBox(height: 10),
BudgetCard(),
BudgetCard(_forceRefresh),
const SizedBox(height: 20),
Text("Recurring payments",
style: Theme.of(context).textTheme.titleLarge),
Expand Down
Loading

0 comments on commit 2b2e877

Please sign in to comment.