stacked bar graph, edit / remove transaction & budget page base
This commit is contained in:
@@ -14,6 +14,7 @@ import 'package:tunas/domains/account/models/transaction_line.dart';
|
||||
import 'package:tunas/domains/account/models/transaction_value.dart';
|
||||
import 'package:tunas/repositories/account/account_repository.dart';
|
||||
import 'package:tunas/repositories/account/models/transaction.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
part 'account_event.dart';
|
||||
part 'account_state.dart';
|
||||
@@ -24,7 +25,6 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
AccountBloc({required AccountRepository accountRepository})
|
||||
: _accountRepository = accountRepository,
|
||||
super(const AccountState()) {
|
||||
on<AccountAddTransaction>(_onTransactionAdd);
|
||||
on<AccountLoad>(_onAccountLoad);
|
||||
on<AccountImportJSON>(_onAccountImportJSON);
|
||||
on<AccountImportCSV>(_onAccountImportCSV);
|
||||
@@ -38,23 +38,14 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
on<TransactionOpenAddDialog>(_onTransactionOpenAddDialog);
|
||||
on<TransactionHideAddDialog>(_onTransactionHideAddDialog);
|
||||
on<TransactionAdd>(_onTransactionAddDialog);
|
||||
on<TransactionSetCurrent>(_onTransactionSetCurrent);
|
||||
on<TransactionDeleteCurrent>(_onTransactionDeleteCurrent);
|
||||
|
||||
_accountRepository
|
||||
.getTransactionsStream()
|
||||
.listen((transactions) => add(AccountLoad(transactions)));
|
||||
}
|
||||
|
||||
_onTransactionAdd(AccountAddTransaction event, Emitter<AccountState> emit) {
|
||||
state.transactions.add(event.transaction);
|
||||
var computeResult = _computeTransactionLine(state.transactions);
|
||||
|
||||
emit(state.copyWith(
|
||||
transactionsLines: computeResult.list,
|
||||
globalTotal: computeResult.globalTotal,
|
||||
accountsTotals: computeResult.accountsTotals,
|
||||
));
|
||||
}
|
||||
|
||||
_onAccountLoad(AccountLoad event, Emitter<AccountState> emit) {
|
||||
var computeResult = _computeTransactionLine(event.transactions);
|
||||
emit(state.copyWith(
|
||||
@@ -78,6 +69,7 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
|
||||
final transactions = csvList
|
||||
.map((line) => Transaction(
|
||||
uuid: const Uuid().v8(),
|
||||
date: DateTime.parse(line[0]),
|
||||
category: line[1],
|
||||
description: line[2],
|
||||
@@ -200,18 +192,83 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
|
||||
_onTransactionAddDialog(
|
||||
TransactionAdd event, Emitter<AccountState> emit
|
||||
) {
|
||||
) async {
|
||||
if (state.isValid) {
|
||||
state.transactions.add(Transaction(
|
||||
List<Transaction> transactions = state.transactions;
|
||||
Transaction? currentTransaction = state.currentTransaction;
|
||||
if (currentTransaction != null) {
|
||||
transactions.removeWhere((transaction) => transaction.uuid == currentTransaction.uuid);
|
||||
}
|
||||
|
||||
transactions.add(Transaction(
|
||||
uuid: const Uuid().v8(),
|
||||
date: state.transactionDate.value ?? DateTime.now(),
|
||||
category: state.transactionCategory.value,
|
||||
description: state.transactionDescription.value,
|
||||
account: state.transactionAccount.value,
|
||||
value: state.transactionValue.value
|
||||
));
|
||||
var computeResult = _computeTransactionLine(state.transactions);
|
||||
final computeResult = _computeTransactionLine(transactions);
|
||||
|
||||
await _accountRepository.saveTransactions(transactions);
|
||||
|
||||
emit(state.copyWith(
|
||||
currentTransaction: null,
|
||||
transactionDate: const TransactionDate.pure(),
|
||||
transactionCategory: const TransactionCategory.pure(),
|
||||
transactionDescription: const TransactionDescription.pure(),
|
||||
transactionAccount: const TransactionAccount.pure(),
|
||||
transactionValue: const TransactionValue.pure(),
|
||||
transactions: transactions,
|
||||
transactionsLines: computeResult.list,
|
||||
globalTotal: computeResult.globalTotal,
|
||||
accountsTotals: computeResult.accountsTotals,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
_onTransactionSetCurrent(
|
||||
TransactionSetCurrent event, Emitter<AccountState> emit
|
||||
) {
|
||||
Transaction? transaction = event.transaction;
|
||||
if (transaction == null) {
|
||||
emit(state.copyWith(
|
||||
currentTransaction: event.transaction,
|
||||
transactionDate: const TransactionDate.pure(),
|
||||
transactionCategory: const TransactionCategory.pure(),
|
||||
transactionDescription: const TransactionDescription.pure(),
|
||||
transactionAccount: const TransactionAccount.pure(),
|
||||
transactionValue: const TransactionValue.pure(),
|
||||
));
|
||||
} else {
|
||||
emit(state.copyWith(
|
||||
currentTransaction: transaction,
|
||||
transactionDate: TransactionDate.dirty(transaction.date),
|
||||
transactionCategory: TransactionCategory.dirty(transaction.category),
|
||||
transactionDescription: TransactionDescription.dirty(transaction.description),
|
||||
transactionAccount: TransactionAccount.dirty(transaction.account),
|
||||
transactionValue: TransactionValue.dirty(transaction.value),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
_onTransactionDeleteCurrent(
|
||||
TransactionDeleteCurrent event, Emitter<AccountState> emit
|
||||
) async {
|
||||
Transaction? currentTransaction = state.currentTransaction;
|
||||
if (currentTransaction != null) {
|
||||
List<Transaction> transactions = state.transactions;
|
||||
transactions.removeWhere((transaction) => transaction.uuid == currentTransaction.uuid);
|
||||
final computeResult = _computeTransactionLine(transactions);
|
||||
await _accountRepository.saveTransactions(transactions);
|
||||
emit(state.copyWith(
|
||||
currentTransaction: null,
|
||||
transactionDate: const TransactionDate.pure(),
|
||||
transactionCategory: const TransactionCategory.pure(),
|
||||
transactionDescription: const TransactionDescription.pure(),
|
||||
transactionAccount: const TransactionAccount.pure(),
|
||||
transactionValue: const TransactionValue.pure(),
|
||||
transactions: transactions,
|
||||
transactionsLines: computeResult.list,
|
||||
globalTotal: computeResult.globalTotal,
|
||||
accountsTotals: computeResult.accountsTotals,
|
||||
|
||||
@@ -7,14 +7,6 @@ sealed class AccountEvent extends Equatable {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class AccountAddTransaction extends AccountEvent {
|
||||
final Transaction transaction;
|
||||
const AccountAddTransaction(this.transaction);
|
||||
|
||||
@override
|
||||
List<Object> get props => [transaction];
|
||||
}
|
||||
|
||||
final class AccountLoad extends AccountEvent {
|
||||
final List<Transaction> transactions;
|
||||
const AccountLoad(this.transactions);
|
||||
@@ -86,4 +78,13 @@ final class TransactionOpenAddDialog extends AccountEvent {
|
||||
|
||||
final class TransactionHideAddDialog extends AccountEvent {
|
||||
const TransactionHideAddDialog();
|
||||
}
|
||||
|
||||
final class TransactionSetCurrent extends AccountEvent {
|
||||
final Transaction? transaction;
|
||||
const TransactionSetCurrent(this.transaction);
|
||||
}
|
||||
|
||||
final class TransactionDeleteCurrent extends AccountEvent {
|
||||
const TransactionDeleteCurrent();
|
||||
}
|
||||
@@ -16,6 +16,8 @@ final class AccountState extends Equatable {
|
||||
final bool isValid;
|
||||
final bool showAddDialog;
|
||||
|
||||
final Transaction? currentTransaction;
|
||||
|
||||
const AccountState({
|
||||
this.transactions = const [],
|
||||
this.transactionsLines = const [],
|
||||
@@ -29,6 +31,7 @@ final class AccountState extends Equatable {
|
||||
this.transactionValue = const TransactionValue.pure(),
|
||||
this.isValid = false,
|
||||
this.showAddDialog = false,
|
||||
this.currentTransaction
|
||||
});
|
||||
|
||||
AccountState copyWith({
|
||||
@@ -44,6 +47,7 @@ final class AccountState extends Equatable {
|
||||
TransactionValue? transactionValue,
|
||||
bool? isValid,
|
||||
bool? showAddDialog,
|
||||
Transaction? currentTransaction,
|
||||
}) {
|
||||
return AccountState(
|
||||
transactions: transactions ?? this.transactions,
|
||||
@@ -58,6 +62,7 @@ final class AccountState extends Equatable {
|
||||
transactionValue: transactionValue ?? this.transactionValue,
|
||||
isValid: isValid ?? this.isValid,
|
||||
showAddDialog: showAddDialog ?? this.showAddDialog,
|
||||
currentTransaction: currentTransaction ?? this.currentTransaction,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,5 +80,6 @@ final class AccountState extends Equatable {
|
||||
transactionValue,
|
||||
isValid,
|
||||
showAddDialog,
|
||||
currentTransaction,
|
||||
];
|
||||
}
|
||||
|
||||
10
lib/domains/budget/budget_bloc.dart
Normal file
10
lib/domains/budget/budget_bloc.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
part 'budget_event.dart';
|
||||
part 'budget_state.dart';
|
||||
|
||||
class BudgetBloc extends Bloc<BudgetEvent, BudgetState> {
|
||||
BudgetBloc(super.initialState);
|
||||
|
||||
}
|
||||
8
lib/domains/budget/budget_event.dart
Normal file
8
lib/domains/budget/budget_event.dart
Normal file
@@ -0,0 +1,8 @@
|
||||
part of 'budget_bloc.dart';
|
||||
|
||||
sealed class BudgetEvent extends Equatable {
|
||||
const BudgetEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
6
lib/domains/budget/budget_state.dart
Normal file
6
lib/domains/budget/budget_state.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
part of 'budget_bloc.dart';
|
||||
|
||||
final class BudgetState extends Equatable {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tunas/domains/account/models/transaction_line.dart';
|
||||
import 'package:tunas/domains/charts/models/chart_item.dart';
|
||||
@@ -9,6 +12,29 @@ import 'package:tunas/repositories/account/models/transaction.dart';
|
||||
part 'chart_event.dart';
|
||||
part 'chart_state.dart';
|
||||
|
||||
final colors = [
|
||||
Colors.purple.shade300,
|
||||
Colors.purple.shade500,
|
||||
Colors.purple.shade700,
|
||||
Colors.purple.shade900,
|
||||
Colors.blue.shade300,
|
||||
Colors.blue.shade500,
|
||||
Colors.blue.shade700,
|
||||
Colors.blue.shade900,
|
||||
Colors.green.shade300,
|
||||
Colors.green.shade500,
|
||||
Colors.green.shade700,
|
||||
Colors.green.shade900,
|
||||
Colors.yellow.shade300,
|
||||
Colors.yellow.shade500,
|
||||
Colors.yellow.shade700,
|
||||
Colors.yellow.shade900,
|
||||
Colors.red.shade300,
|
||||
Colors.red.shade500,
|
||||
Colors.red.shade700,
|
||||
Colors.red.shade900,
|
||||
];
|
||||
|
||||
class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
final AccountRepository _accountRepository;
|
||||
|
||||
@@ -95,7 +121,9 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
List<ChartItem> scopedCategoriesPositiveTotals = [];
|
||||
List<ChartItem> scopedCategoriesNegativeTotals = [];
|
||||
Map<int, FlSpot> scopedMonthlyTotals = {};
|
||||
Map<String, Map<String, double>> scopedCategoriesMonthlyTotals = {};
|
||||
Map<int, Map<String, double>> scopedCategoriesMonthlyTotals = {};
|
||||
Map<String, Color> categoriesColors = {};
|
||||
int colorIndex = 0;
|
||||
|
||||
for(var transaction in state.transactions) {
|
||||
double subTotal = globalTotal + transaction.value;
|
||||
@@ -120,6 +148,11 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (categoriesColors[transaction.category] == null) {
|
||||
categoriesColors[transaction.category] = colors[colorIndex];
|
||||
colorIndex++;
|
||||
}
|
||||
|
||||
if (transaction.value >= 0) {
|
||||
ChartItem? chartItem = scopedCategoriesPositiveTotals.firstWhere(
|
||||
(item) => item.label == transaction.category,
|
||||
@@ -142,7 +175,16 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
}
|
||||
);
|
||||
chartItem.value += transaction.value;
|
||||
|
||||
Map<String, double>? a = scopedCategoriesMonthlyTotals[transaction.date.month];
|
||||
if (scopedCategoriesMonthlyTotals[transaction.date.month] == null) {
|
||||
a = {};
|
||||
}
|
||||
|
||||
a?[transaction.category] = transaction.value.abs() + (a[transaction.category] ?? 0);
|
||||
scopedCategoriesMonthlyTotals[transaction.date.month] = a!;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
List<ChartItem> scopedCategoriesPositiveTotalsPercents = [];
|
||||
@@ -202,6 +244,7 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
scopedSimplifiedCategoriesNegativeTotalsPercents: scopedSimplifiedCategoriesNegativeTotalsPercents,
|
||||
scopedMonthlyTotals: scopedMonthlyTotals.values.toList(),
|
||||
scopedCategoriesMonthlyTotals: scopedCategoriesMonthlyTotals,
|
||||
categoriesColors: categoriesColors,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ final class ChartState extends Equatable {
|
||||
final List<ChartItem> scopedSimplifiedCategoriesNegativeTotalsPercents;
|
||||
|
||||
final List<FlSpot> scopedMonthlyTotals;
|
||||
final Map<String, Map<String, double>> scopedCategoriesMonthlyTotals;
|
||||
final Map<int, Map<String, double>> scopedCategoriesMonthlyTotals;
|
||||
|
||||
final Map<String, Color> categoriesColors;
|
||||
|
||||
const ChartState({
|
||||
this.transactions = const [],
|
||||
@@ -50,6 +52,7 @@ final class ChartState extends Equatable {
|
||||
this.scopedSimplifiedCategoriesNegativeTotalsPercents = const [],
|
||||
this.scopedMonthlyTotals = const [],
|
||||
this.scopedCategoriesMonthlyTotals = const {},
|
||||
this.categoriesColors = const {},
|
||||
});
|
||||
|
||||
ChartState copyWith({
|
||||
@@ -72,7 +75,8 @@ final class ChartState extends Equatable {
|
||||
List<ChartItem>? scopedSimplifiedCategoriesNegativeTotals,
|
||||
List<ChartItem>? scopedSimplifiedCategoriesNegativeTotalsPercents,
|
||||
List<FlSpot>? scopedMonthlyTotals,
|
||||
Map<String, Map<String, double>>? scopedCategoriesMonthlyTotals,
|
||||
Map<int, Map<String, double>>? scopedCategoriesMonthlyTotals,
|
||||
Map<String, Color>? categoriesColors,
|
||||
}) {
|
||||
return ChartState(
|
||||
transactions: transactions ?? this.transactions,
|
||||
@@ -95,6 +99,7 @@ final class ChartState extends Equatable {
|
||||
scopedSimplifiedCategoriesNegativeTotalsPercents: scopedSimplifiedCategoriesNegativeTotalsPercents ?? this.scopedSimplifiedCategoriesNegativeTotalsPercents,
|
||||
scopedMonthlyTotals: scopedMonthlyTotals ?? this.scopedMonthlyTotals,
|
||||
scopedCategoriesMonthlyTotals: scopedCategoriesMonthlyTotals ?? this.scopedCategoriesMonthlyTotals,
|
||||
categoriesColors: categoriesColors ?? this.categoriesColors,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,6 +123,7 @@ final class ChartState extends Equatable {
|
||||
scopedSimplifiedCategoriesNegativeTotalsPercents,
|
||||
scopedMonthlyTotals,
|
||||
scopedCategoriesMonthlyTotals,
|
||||
categoriesColors,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user