Skip to content

Commit

Permalink
add expense page
Browse files Browse the repository at this point in the history
  • Loading branch information
praslnx8 committed Jul 3, 2024
1 parent e6d5d24 commit f7d7fee
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 3 deletions.
3 changes: 3 additions & 0 deletions lib/domain/models/expense.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import 'package:wealth_wave/api/db/app_database.dart';

class Expense {
final int id;
final double amount;
final String? description;
final List<String> tags;
final DateTime createdOn;

Expense._({
required this.id,
required this.amount,
required this.createdOn,
required this.description,
required this.tags,
});

factory Expense.from({required ExpenseDO expenseDO}) => Expense._(
id: expenseDO.id,
amount: expenseDO.amount,
createdOn: expenseDO.createdOn,
description: expenseDO.description,
Expand Down
8 changes: 8 additions & 0 deletions lib/domain/services/expense_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ class ExpenseService {
.getBy(id: id)
.then((expenseDO) => Expense.from(expenseDO: expenseDO));

Future<List<Expense>> getExpensesForMonthDate({required final DateTime monthDate}) {
return _expenseApi
.getByMonth(monthDate: monthDate)
.then((expenseDOs) => expenseDOs
.map((expenseDO) => Expense.from(expenseDO: expenseDO))
.toList());
}

Future<void> deleteBy({required final int id}) =>
_expenseApi.getBy(id: id).then((expenseDO) {
DateTime monthDate =
Expand Down
25 changes: 25 additions & 0 deletions lib/ui/models/expense_vo.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:wealth_wave/domain/models/expense.dart';

class ExpenseVO {
final int id;
final double amount;
final String? description;
final DateTime createdOn;
final List<String> tags;

ExpenseVO._(
{required this.id,
required this.description,
required this.amount,
required this.createdOn,
required this.tags});

factory ExpenseVO.from({required final Expense expense}) {
return ExpenseVO._(
id: expense.id,
description: expense.description,
amount: expense.amount,
createdOn: expense.createdOn,
tags: expense.tags);
}
}
59 changes: 56 additions & 3 deletions lib/ui/pages/expense_page.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:pair/pair.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:wealth_wave/core/page_state.dart';
import 'package:wealth_wave/ui/presentation/expense_presenter.dart';
import 'package:wealth_wave/ui/widgets/create_expense_dialog.dart';
import 'package:wealth_wave/ui/widgets/view_monthly_expenses_dialog.dart';

class ExpensePage extends StatefulWidget {
const ExpensePage({super.key});
Expand All @@ -24,9 +30,18 @@ class _ExpensePage
final BuildContext context, final ExpenseViewState snapshot) {
Map<DateTime, double> monthlyExpenses = snapshot.monthlyExpenses;
return Scaffold(
body: Center(
child: Text('Monthly Expenses: $monthlyExpenses',
style: Theme.of(context).textTheme.titleMedium)),
body: Column(
children: [
Expanded(
flex: 1,
child: _showMonthlyExpenseGraph(
context: context, expenses: monthlyExpenses)),
Expanded(
flex: 1,
child: _showMonthlyExpenseList(
context: context, expenses: monthlyExpenses)),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showCreateExpenseDialog(context: context).then((value) {
Expand All @@ -43,4 +58,42 @@ class _ExpensePage
ExpensePresenter initializePresenter() {
return ExpensePresenter();
}

Widget _showMonthlyExpenseGraph(
{required BuildContext context,
required Map<DateTime, double> expenses}) {
List<Pair<String, double>> chartData = expenses.entries
.map((entry) => Pair(DateFormat('MMM').format(entry.key), entry.value))
.toList();
return SfCartesianChart(
primaryXAxis: const CategoryAxis(),
primaryYAxis: const NumericAxis(),
series: <ColumnSeries>[
ColumnSeries<Pair<String, double>, String>(
dataSource: chartData,
xValueMapper: (Pair<String, double> data, _) => data.key,
yValueMapper: (Pair<String, double> data, _) => data.value,
name: 'Expenses',
)
],
);
}

Widget _showMonthlyExpenseList(
{required BuildContext context,
required Map<DateTime, double> expenses}) {
return ListView.builder(
itemCount: expenses.length,
itemBuilder: (BuildContext context, int index) {
final entry = expenses.entries.elementAt(index);
return ListTile(
title: Text(DateFormat('MMM').format(entry.key)),
subtitle: Text(entry.value.toString()),
onTap: () {
showViewMonthlyExpensesDialog(context: context, monthDate: entry.key);
},
);
},
);
}
}
33 changes: 33 additions & 0 deletions lib/ui/presentation/monthly_expense_presenter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:wealth_wave/core/presenter.dart';
import 'package:wealth_wave/domain/services/expense_service.dart';
import 'package:wealth_wave/ui/models/expense_vo.dart';

class MonthlyExpensePresenter extends Presenter<MonthlyExpenseViewState> {
final DateTime _monthDate;
final ExpenseService _expenseService;

MonthlyExpensePresenter(
{required final DateTime monthDate, final ExpenseService? expenseService})
: _monthDate = monthDate,
_expenseService = expenseService ?? ExpenseService(),
super(MonthlyExpenseViewState());

void getExpenses() {
_expenseService
.getExpensesForMonthDate(monthDate: _monthDate)
.then((expenses) => expenses
.map((expense) => ExpenseVO.from(expense: expense))
.toList())
.then((expenses) => updateViewState((viewState) {
viewState.expenses = expenses;
}));
}

void deleteExpense({required final int id}) {
_expenseService.deleteBy(id: id).then((_) => getExpenses());
}
}

class MonthlyExpenseViewState {
List<ExpenseVO> expenses = [];
}
103 changes: 103 additions & 0 deletions lib/ui/widgets/view_monthly_expenses_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import 'package:wealth_wave/core/page_state.dart';
import 'package:wealth_wave/ui/app_dimen.dart';
import 'package:wealth_wave/ui/models/expense_vo.dart';
import 'package:wealth_wave/ui/presentation/monthly_expense_presenter.dart';
import 'package:wealth_wave/ui/widgets/create_expense_dialog.dart';
import 'package:wealth_wave/utils/ui_utils.dart';

Future<void> showViewMonthlyExpensesDialog(
{required final BuildContext context, required final DateTime monthDate}) {
return showDialog(
context: context,
builder: (context) => _MonthlyExpensesDialog(
monthDate: monthDate,
));
}

class _MonthlyExpensesDialog extends StatefulWidget {
final DateTime monthDate;

const _MonthlyExpensesDialog({required this.monthDate});

@override
State<_MonthlyExpensesDialog> createState() => _MonthlyExpensesPage();
}

class _MonthlyExpensesPage extends PageState<MonthlyExpenseViewState,
_MonthlyExpensesDialog, MonthlyExpensePresenter> {
@override
void initState() {
super.initState();
presenter.getExpenses();
}

@override
Widget buildWidget(BuildContext context, MonthlyExpenseViewState snapshot) {
return AlertDialog(
title: const Text('Expenses'),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
shrinkWrap: true,
itemCount: snapshot.expenses.length,
itemBuilder: (context, index) {
ExpenseVO expense = snapshot.expenses[index];
return ListTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(formatDate(expense.createdOn)),
const Text(' | '),
Text(expense.description ?? ''),
],
),
const SizedBox(
height: AppDimen.minPadding), // Add some spacing
Text('Amount: ${formatToCurrency(expense.amount)}'),
],
),
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
showCreateExpenseDialog(
context: context, expenseIdTOUpdate: expense.id)
.then((value) => presenter.getExpenses());
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
presenter.deleteExpense(id: expense.id);
},
)
]),
);
},
)),
actions: <Widget>[
OutlinedButton(
child: const Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
),
OutlinedButton(
child: const Text('Add Expense'),
onPressed: () {
showCreateExpenseDialog(context: context)
.then((value) => presenter.getExpenses());
},
),
],
);
}

@override
MonthlyExpensePresenter initializePresenter() {
return MonthlyExpensePresenter(monthDate: widget.monthDate);
}
}

0 comments on commit f7d7fee

Please sign in to comment.