feat: Implement account management features
- Added ExpenseDetailDialog for displaying expense details and actions. - Created ExpensesTab to list expenses for a group. - Developed GroupExpensesPage to manage group expenses with tabs for expenses, balances, and settlements. - Introduced SettlementsTab to show optimized repayment plans. - Refactored create_trip_content.dart to remove CountBloc and related logic. - Added Account model to manage user accounts and group members. - Replaced CountRepository with AccountRepository for account-related operations. - Removed CountService and CountRepository as part of the refactor. - Updated main.dart and home.dart to integrate new account management components.
This commit is contained in:
0
lib/blocs/account/account_bloc.dart
Normal file
0
lib/blocs/account/account_bloc.dart
Normal file
0
lib/blocs/account/account_event.dart
Normal file
0
lib/blocs/account/account_event.dart
Normal file
0
lib/blocs/account/account_state.dart
Normal file
0
lib/blocs/account/account_state.dart
Normal file
@@ -1,196 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../data/models/expense.dart';
|
||||
import '../../services/count_service.dart';
|
||||
import '../../repositories/count_repository.dart';
|
||||
import 'count_event.dart';
|
||||
import 'count_state.dart';
|
||||
|
||||
class CountBloc extends Bloc<CountEvent, CountState> {
|
||||
final CountService _countService;
|
||||
StreamSubscription<List<Expense>>? _expensesSubscription;
|
||||
Map<ExpenseCurrency, double> _exchangeRates = {};
|
||||
|
||||
CountBloc({CountService? countService})
|
||||
: _countService = countService ?? CountService(
|
||||
countRepository: CountRepository(),
|
||||
),
|
||||
super(CountInitial()) {
|
||||
on<LoadExpenses>(_onLoadExpenses);
|
||||
on<CreateExpense>(_onCreateExpense);
|
||||
on<UpdateExpense>(_onUpdateExpense);
|
||||
on<DeleteExpense>(_onDeleteExpense);
|
||||
on<ArchiveExpense>(_onArchiveExpense);
|
||||
on<MarkSplitAsPaid>(_onMarkSplitAsPaid);
|
||||
on<LoadExchangeRates>(_onLoadExchangeRates);
|
||||
on<_ExpensesUpdated>(_onExpensesUpdated);
|
||||
}
|
||||
|
||||
Future<void> _onLoadExpenses(
|
||||
LoadExpenses event,
|
||||
Emitter<CountState> emit,
|
||||
) async {
|
||||
emit(CountLoading());
|
||||
|
||||
// Charger les taux de change
|
||||
if (_exchangeRates.isEmpty) {
|
||||
_exchangeRates = await _countService.getExchangeRates();
|
||||
}
|
||||
|
||||
await _expensesSubscription?.cancel();
|
||||
|
||||
_expensesSubscription = _countService
|
||||
.getExpensesStream(event.groupId, includeArchived: event.includeArchived)
|
||||
.listen(
|
||||
(expenses) {
|
||||
add(_ExpensesUpdated(
|
||||
groupId: event.groupId,
|
||||
expenses: expenses,
|
||||
));
|
||||
},
|
||||
onError: (error) {
|
||||
add(_ExpensesError('Erreur lors du chargement des dépenses: $error'));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _onExpensesUpdated(
|
||||
_ExpensesUpdated event,
|
||||
Emitter<CountState> emit,
|
||||
) {
|
||||
// Récupérer les membres du groupe et calculer les balances
|
||||
final memberIds = <String>{};
|
||||
final memberNames = <String, String>{};
|
||||
|
||||
for (final expense in event.expenses) {
|
||||
memberIds.add(expense.paidById);
|
||||
memberNames[expense.paidById] = expense.paidByName;
|
||||
|
||||
for (final split in expense.splits) {
|
||||
memberIds.add(split.userId);
|
||||
memberNames[split.userId] = split.userName;
|
||||
}
|
||||
}
|
||||
|
||||
final balances = _countService.calculateBalances(
|
||||
event.expenses,
|
||||
memberIds.toList(),
|
||||
memberNames,
|
||||
);
|
||||
|
||||
final settlements = _countService.calculateOptimizedSettlements(balances);
|
||||
|
||||
emit(ExpensesLoaded(
|
||||
groupId: event.groupId,
|
||||
expenses: event.expenses,
|
||||
balances: balances,
|
||||
settlements: settlements,
|
||||
exchangeRates: _exchangeRates,
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _onCreateExpense(
|
||||
CreateExpense event,
|
||||
Emitter<CountState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _countService.createExpense(
|
||||
event.expense,
|
||||
receiptImage: event.receiptImage,
|
||||
);
|
||||
} catch (e) {
|
||||
emit(CountError('Erreur lors de la création de la dépense: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onUpdateExpense(
|
||||
UpdateExpense event,
|
||||
Emitter<CountState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _countService.updateExpense(
|
||||
event.expense,
|
||||
newReceiptImage: event.newReceiptImage,
|
||||
);
|
||||
} catch (e) {
|
||||
emit(CountError('Erreur lors de la modification de la dépense: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onDeleteExpense(
|
||||
DeleteExpense event,
|
||||
Emitter<CountState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _countService.deleteExpense(event.groupId, event.expenseId);
|
||||
} catch (e) {
|
||||
emit(CountError('Erreur lors de la suppression de la dépense: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onArchiveExpense(
|
||||
ArchiveExpense event,
|
||||
Emitter<CountState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _countService.archiveExpense(event.groupId, event.expenseId);
|
||||
} catch (e) {
|
||||
emit(CountError('Erreur lors de l\'archivage de la dépense: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onMarkSplitAsPaid(
|
||||
MarkSplitAsPaid event,
|
||||
Emitter<CountState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _countService.markSplitAsPaid(
|
||||
event.groupId,
|
||||
event.expenseId,
|
||||
event.userId,
|
||||
);
|
||||
} catch (e) {
|
||||
emit(CountError('Erreur lors du marquage du paiement: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLoadExchangeRates(
|
||||
LoadExchangeRates event,
|
||||
Emitter<CountState> emit,
|
||||
) async {
|
||||
try {
|
||||
_exchangeRates = await _countService.getExchangeRates();
|
||||
} catch (e) {
|
||||
emit(CountError('Erreur lors du chargement des taux de change: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_expensesSubscription?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Events internes
|
||||
class _ExpensesUpdated extends CountEvent {
|
||||
final String groupId;
|
||||
final List<Expense> expenses;
|
||||
|
||||
const _ExpensesUpdated({
|
||||
required this.groupId,
|
||||
required this.expenses,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId, expenses];
|
||||
}
|
||||
|
||||
class _ExpensesError extends CountEvent {
|
||||
final String error;
|
||||
|
||||
const _ExpensesError(this.error);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [error];
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import 'dart:io';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../data/models/expense.dart';
|
||||
|
||||
abstract class CountEvent extends Equatable {
|
||||
const CountEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class LoadExpenses extends CountEvent {
|
||||
final String groupId;
|
||||
final bool includeArchived;
|
||||
|
||||
const LoadExpenses(this.groupId, {this.includeArchived = false});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId, includeArchived];
|
||||
}
|
||||
|
||||
class CreateExpense extends CountEvent {
|
||||
final Expense expense;
|
||||
final File? receiptImage;
|
||||
|
||||
const CreateExpense({
|
||||
required this.expense,
|
||||
this.receiptImage,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [expense, receiptImage];
|
||||
}
|
||||
|
||||
class UpdateExpense extends CountEvent {
|
||||
final Expense expense;
|
||||
final File? newReceiptImage;
|
||||
|
||||
const UpdateExpense({
|
||||
required this.expense,
|
||||
this.newReceiptImage,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [expense, newReceiptImage];
|
||||
}
|
||||
|
||||
class DeleteExpense extends CountEvent {
|
||||
final String groupId;
|
||||
final String expenseId;
|
||||
|
||||
const DeleteExpense({
|
||||
required this.groupId,
|
||||
required this.expenseId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId, expenseId];
|
||||
}
|
||||
|
||||
class ArchiveExpense extends CountEvent {
|
||||
final String groupId;
|
||||
final String expenseId;
|
||||
|
||||
const ArchiveExpense({
|
||||
required this.groupId,
|
||||
required this.expenseId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId, expenseId];
|
||||
}
|
||||
|
||||
class MarkSplitAsPaid extends CountEvent {
|
||||
final String groupId;
|
||||
final String expenseId;
|
||||
final String userId;
|
||||
|
||||
const MarkSplitAsPaid({
|
||||
required this.groupId,
|
||||
required this.expenseId,
|
||||
required this.userId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId, expenseId, userId];
|
||||
}
|
||||
|
||||
class LoadExchangeRates extends CountEvent {
|
||||
const LoadExchangeRates();
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../data/models/expense.dart';
|
||||
import '../../data/models/balance.dart';
|
||||
|
||||
abstract class CountState extends Equatable {
|
||||
const CountState();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class CountInitial extends CountState {}
|
||||
|
||||
class CountLoading extends CountState {}
|
||||
|
||||
class ExpensesLoaded extends CountState {
|
||||
final String groupId;
|
||||
final List<Expense> expenses;
|
||||
final List<Balance> balances;
|
||||
final List<Settlement> settlements;
|
||||
final Map<ExpenseCurrency, double> exchangeRates;
|
||||
|
||||
const ExpensesLoaded({
|
||||
required this.groupId,
|
||||
required this.expenses,
|
||||
required this.balances,
|
||||
required this.settlements,
|
||||
required this.exchangeRates,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId, expenses, balances, settlements, exchangeRates];
|
||||
}
|
||||
|
||||
class CountError extends CountState {
|
||||
final String message;
|
||||
|
||||
const CountError(this.message);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
Reference in New Issue
Block a user