feat: Refactor account handling and improve group creation logic

This commit is contained in:
Dayron
2025-10-23 11:03:11 +02:00
parent 905948379a
commit 7cfc5eab6b
7 changed files with 97 additions and 66 deletions

View File

@@ -96,11 +96,11 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
) async { ) async {
try { try {
emit(GroupLoading()); emit(GroupLoading());
await _repository.createGroupWithMembers( final groupId = await _repository.createGroupWithMembers(
group: event.group, group: event.group,
members: event.members, members: event.members,
); );
emit(const GroupOperationSuccess('Groupe créé avec succès')); emit(GroupCreated(groupId: groupId));
} catch (e) { } catch (e) {
emit(GroupError('Erreur lors de la création: $e')); emit(GroupError('Erreur lors de la création: $e'));
} }
@@ -111,7 +111,6 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
Emitter<GroupState> emit, Emitter<GroupState> emit,
) async { ) async {
try { try {
// CORRECTION : Utiliser addMemberToGroup au lieu de addMember
await _repository.addMember(event.groupId, event.member); await _repository.addMember(event.groupId, event.member);
emit(const GroupOperationSuccess('Membre ajouté')); emit(const GroupOperationSuccess('Membre ajouté'));
} catch (e) { } catch (e) {
@@ -124,7 +123,6 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
Emitter<GroupState> emit, Emitter<GroupState> emit,
) async { ) async {
try { try {
// CORRECTION : Utiliser removeMemberFromGroup au lieu de removeMember
await _repository.removeMember(event.groupId, event.userId); await _repository.removeMember(event.groupId, event.userId);
emit(const GroupOperationSuccess('Membre supprimé')); emit(const GroupOperationSuccess('Membre supprimé'));
} catch (e) { } catch (e) {

View File

@@ -9,7 +9,6 @@ import '../../blocs/trip/trip_event.dart';
import '../../blocs/trip/trip_state.dart'; import '../../blocs/trip/trip_state.dart';
import '../../blocs/group/group_bloc.dart'; import '../../blocs/group/group_bloc.dart';
import '../../blocs/group/group_event.dart'; import '../../blocs/group/group_event.dart';
import '../../blocs/group/group_state.dart';
import '../../blocs/account/account_bloc.dart'; import '../../blocs/account/account_bloc.dart';
import '../../blocs/account/account_event.dart'; import '../../blocs/account/account_event.dart';
import '../../models/account.dart'; import '../../models/account.dart';
@@ -111,15 +110,13 @@ class _CreateTripContentState extends State<CreateTripContent> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocListener( return BlocListener<TripBloc, TripState>(
listeners: [
// Listener pour TripBloc
BlocListener<TripBloc, TripState>(
listener: (context, tripState) { listener: (context, tripState) {
if (tripState is TripCreated) { if (tripState is TripCreated) {
// Stocker l'ID du trip et créer le groupe // Stocker l'ID du trip et créer le groupe
_createdTripId = tripState.tripId; _createdTripId = tripState.tripId;
_createGroupForTrip(tripState.tripId); _createGroupForTrip(_createdTripId!);
_createAccountForTrip(_createdTripId!);
} else if (tripState is TripOperationSuccess) { } else if (tripState is TripOperationSuccess) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@@ -147,31 +144,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
} }
} }
}, },
), child: BlocBuilder<UserBloc, user_state.UserState>(
// Nouveau listener pour GroupBloc
BlocListener<GroupBloc, GroupState>(
listener: (context, groupState) {
if (groupState is GroupCreated && _createdTripId != null) {
// Le groupe a été créé, maintenant créer le compte
print('++++++++++++++ Creating account for trip ${_createdTripId!} and group ${groupState.groupId} ++++++++++++++');
_createAccountForTrip(_createdTripId!, groupState.groupId);
} else if (groupState is GroupError) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur lors de la création du groupe: ${groupState.message}'),
backgroundColor: Colors.red,
),
);
setState(() {
_isLoading = false;
});
}
}
},
),
],
child: BlocBuilder<UserBloc, user_state.UserState>(
builder: (context, userState) { builder: (context, userState) {
if (userState is! user_state.UserLoaded) { if (userState is! user_state.UserLoaded) {
return Scaffold( return Scaffold(
@@ -661,18 +634,15 @@ class _CreateTripContentState extends State<CreateTripContent> {
} }
} }
Future<void> _createAccountForTrip(String tripId, String groupId) async { Future<void> _createAccountForTrip(String tripId) async {
final accountBloc = context.read<AccountBloc>(); final accountBloc = context.read<AccountBloc>();
try { try {
final userState = context.read<UserBloc>().state; final userState = context.read<UserBloc>().state;
if (userState is! user_state.UserLoaded) return; if (userState is! user_state.UserLoaded) return;
print('Creating account for trip $tripId and group $groupId');
final account = Account( final account = Account(
id: '', id: '',
tripId: tripId, tripId: tripId,
groupId: groupId,
name: _titleController.text.trim(), name: _titleController.text.trim(),
); );
@@ -683,8 +653,6 @@ class _CreateTripContentState extends State<CreateTripContent> {
members: accountsMembers, members: accountsMembers,
)); ));
print('++++++++++++++ Created account for trip $tripId and group $groupId ++++++++++++++');
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(

View File

@@ -157,13 +157,15 @@ class _HomeContentState extends State<HomeContent> with AutomaticKeepAliveClient
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () async { onPressed: () async {
final tripBloc = context.read<TripBloc>();
final result = await Navigator.push( final result = await Navigator.push(
context, context,
MaterialPageRoute(builder: (context) => const CreateTripContent()), MaterialPageRoute(builder: (context) => const CreateTripContent()),
); );
if (result == true && mounted) { if (result == true && mounted) {
context.read<TripBloc>().add(LoadTripsByUserId(userId: user.id)); tripBloc.add(LoadTripsByUserId(userId: user.id));
} }
}, },
backgroundColor: Theme.of(context).colorScheme.primary, backgroundColor: Theme.of(context).colorScheme.primary,

View File

@@ -1,53 +1,61 @@
import 'dart:convert';
import 'group_member.dart'; import 'group_member.dart';
class Account { class Account {
final String id; final String id;
final String tripId; final String tripId;
final String groupId;
final String name; final String name;
final DateTime createdAt;
final DateTime updatedAt;
final List<GroupMember> members; final List<GroupMember> members;
Account({ Account({
required this.id, required this.id,
required this.tripId, required this.tripId,
required this.groupId,
required this.name, required this.name,
DateTime? createdAt,
DateTime? updatedAt,
List<GroupMember>? members, List<GroupMember>? members,
}) : members = members ?? []; }) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now(),
members = members ?? [];
factory Account.fromMap(Map<String, dynamic> map, String id) {
factory Account.fromMap(Map<String, dynamic> map) {
return Account( return Account(
id: map['id'] as String, id: id,
tripId: map['tripId'] as String, tripId: map['tripId'] ?? '',
groupId: map['groupId'] as String, name: map['name'] ?? '',
name: map['name'] as String, createdAt: DateTime.fromMillisecondsSinceEpoch(map['createdAt'] ?? 0),
updatedAt: DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] ?? 0),
members: [], members: [],
); );
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {
'id': id,
'tripId': tripId, 'tripId': tripId,
'groupId': groupId,
'name': name, 'name': name,
'members': members.map((member) => member.toMap()).toList(), 'createdAt': createdAt.millisecondsSinceEpoch,
'updatedAt': updatedAt.millisecondsSinceEpoch,
}; };
} }
String toJson() => json.encode(toMap());
Account copyWith({ Account copyWith({
String? id, String? id,
String? tripId, String? tripId,
String? groupId,
String? name, String? name,
DateTime? createdAt,
DateTime? updatedAt,
List<GroupMember>? members, List<GroupMember>? members,
}) { }) {
return Account( return Account(
id: id ?? this.id, id: id ?? this.id,
tripId: tripId ?? this.tripId, tripId: tripId ?? this.tripId,
groupId: groupId ?? this.groupId,
name: name ?? this.name, name: name ?? this.name,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
members: members ?? this.members, members: members ?? this.members,
); );
} }

View File

@@ -93,7 +93,7 @@ class Settlement extends Equatable {
String get formattedAmount => '${amount.toStringAsFixed(2)}'; String get formattedAmount => '${amount.toStringAsFixed(2)}';
String get description => '$fromUserName doit ${formattedAmount} à $toUserName'; String get description => '$fromUserName doit $formattedAmount à $toUserName';
String get shortDescription => '$fromUserName$toUserName'; String get shortDescription => '$fromUserName$toUserName';
@@ -125,6 +125,6 @@ class Settlement extends Equatable {
@override @override
String toString() { String toString() {
return 'Settlement(${shortDescription}: ${formattedAmount}, status: $status)'; return 'Settlement($shortDescription: $formattedAmount, status: $status)';
} }
} }

View File

@@ -54,9 +54,8 @@ class AccountRepository {
.get(); .get();
if (memberDoc.exists) { if (memberDoc.exists) {
final accountData = accountDoc.data() as Map<String, dynamic>; final accountData = accountDoc.data() as Map<String, dynamic>;
final account = Account.fromMap(accountData); final account = Account.fromMap(accountData, accountId); // ✅ Ajout de l'ID
final members = await getAccountMembers(accountId); final members = await getAccountMembers(accountId);
userAccounts.add(account.copyWith(members: members)); userAccounts.add(account.copyWith(members: members));
} else { } else {
_errorService.logInfo('account_repository.dart', 'Utilisateur NON membre de $accountId'); _errorService.logInfo('account_repository.dart', 'Utilisateur NON membre de $accountId');
@@ -99,15 +98,44 @@ class AccountRepository {
} }
} }
Future<DocumentSnapshot> getAccountById(String accountId) async { Future<Account?> getAccountById(String accountId) async {
return await _firestore.collection('accounts').doc(accountId).get(); try {
final doc = await _firestore.collection('accounts').doc(accountId).get();
if (doc.exists) {
return Account.fromMap(doc.data() as Map<String, dynamic>, doc.id);
}
return null;
} catch (e) {
throw Exception('Erreur lors de la récupération du compte: $e');
}
}
Future<Account?> getAccountByTripId(String tripId) async {
try {
final querySnapshot = await _firestore
.collection('accounts')
.where('tripId', isEqualTo: tripId)
.limit(1)
.get();
if (querySnapshot.docs.isNotEmpty) {
final doc = querySnapshot.docs.first;
return Account.fromMap(doc.data(), doc.id);
}
return null;
} catch (e) {
throw Exception('Erreur lors de la récupération du compte: $e');
}
} }
Future<void> updateAccount(String accountId, Account account) async { Future<void> updateAccount(String accountId, Account account) async {
try { try {
await _firestore.collection('accounts').doc(accountId).update(account.toMap()); // Mettre à jour la date de modification
final updatedAccount = account.copyWith(updatedAt: DateTime.now());
await _firestore.collection('accounts').doc(accountId).update(updatedAccount.toMap());
} catch (e) { } catch (e) {
_errorService.logError('account_repository.dart', 'Erreur lors de la mise à jour du compte: $e'); _errorService.logError('account_repository.dart', 'Erreur lors de la mise à jour du compte: $e');
throw Exception('Erreur lors de la mise à jour du compte: $e');
} }
} }
@@ -123,13 +151,17 @@ class AccountRepository {
final docId = querySnapshot.docs.first.id; final docId = querySnapshot.docs.first.id;
// Supprimer tous les membres
final membersSnapshot = await _membersCollection(docId).get(); final membersSnapshot = await _membersCollection(docId).get();
for (var memberDoc in membersSnapshot.docs) { for (var memberDoc in membersSnapshot.docs) {
await _membersCollection(docId).doc(memberDoc.id).delete(); await _membersCollection(docId).doc(memberDoc.id).delete();
} }
// Supprimer le compte
await _accountCollection.doc(docId).delete(); await _accountCollection.doc(docId).delete();
} catch (e) { } catch (e) {
_errorService.logError('account_repository.dart', 'Erreur lors de la suppression du compte: $e'); _errorService.logError('account_repository.dart', 'Erreur lors de la suppression du compte: $e');
throw Exception('Erreur lors de la suppression du compte: $e');
} }
} }
@@ -143,4 +175,27 @@ class AccountRepository {
.toList(), .toList(),
); );
} }
Stream<Account?> watchAccount(String accountId) {
return _accountCollection.doc(accountId).snapshots().map((doc) {
if (doc.exists) {
return Account.fromMap(doc.data() as Map<String, dynamic>, doc.id);
}
return null;
});
}
Stream<Account?> watchAccountByTripId(String tripId) {
return _accountCollection
.where('tripId', isEqualTo: tripId)
.limit(1)
.snapshots()
.map((snapshot) {
if (snapshot.docs.isNotEmpty) {
final doc = snapshot.docs.first;
return Account.fromMap(doc.data() as Map<String, dynamic>, doc.id);
}
return null;
});
}
} }

View File

@@ -9,7 +9,7 @@ class AccountService {
Stream<List<Account>> getAccountsStream() { Stream<List<Account>> getAccountsStream() {
return _firestore.collection('accounts').snapshots().map((snapshot) { return _firestore.collection('accounts').snapshots().map((snapshot) {
return snapshot.docs.map((doc) { return snapshot.docs.map((doc) {
return Account.fromMap(doc.data()); return Account.fromMap(doc.data(), doc.id);
}).toList(); }).toList();
}); });
} }
@@ -52,8 +52,8 @@ class AccountService {
.snapshots() .snapshots()
.map((snapshot) { .map((snapshot) {
return snapshot.docs.map((doc) { return snapshot.docs.map((doc) {
final account = Account.fromMap(doc.data()); final account = Account.fromMap(doc.data(), doc.id);
_errorService.logError('Compte: ${account.name}, Membres: ${account.members.length}', StackTrace.current); _errorService.logError('Compte: ${account.name}', StackTrace.current);
return account; return account;
}).toList(); }).toList();
}); });