feat: Enhance group and trip management with group creation and account linking

NOT FUNCTIONNAL
This commit is contained in:
Dayron
2025-10-22 12:23:26 +02:00
parent 4edbd1cf34
commit 905948379a
4 changed files with 178 additions and 70 deletions

View File

@@ -79,10 +79,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: [], members: [],
); );
emit(GroupCreated(groupId: groupId));
emit(const GroupOperationSuccess('Groupe créé avec succès')); emit(const GroupOperationSuccess('Groupe créé avec succès'));
} catch (e) { } catch (e) {
emit(GroupError('Erreur lors de la création: $e')); emit(GroupError('Erreur lors de la création: $e'));

View File

@@ -30,6 +30,18 @@ class GroupLoaded extends GroupState {
const GroupLoaded(this.groups); const GroupLoaded(this.groups);
} }
class GroupCreated extends GroupState {
final String groupId;
final String message;
const GroupCreated({
required this.groupId,
this.message = 'Groupe créé avec succès',
});
@override
List<Object?> get props => [groupId, message];
}
// Succès d'une opération // Succès d'une opération
class GroupOperationSuccess extends GroupState { class GroupOperationSuccess extends GroupState {
final String message; final String message;

View File

@@ -21,7 +21,6 @@ class TripLoaded extends TripState {
List<Object?> get props => [trips]; List<Object?> get props => [trips];
} }
// NOUVEAU : État pour indiquer qu'un voyage a été créé avec succès
class TripCreated extends TripState { class TripCreated extends TripState {
final String tripId; final String tripId;
final String message; final String message;

View File

@@ -9,6 +9,10 @@ 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_event.dart';
import '../../models/account.dart';
import '../../models/group.dart'; import '../../models/group.dart';
import '../../models/group_member.dart'; import '../../models/group_member.dart';
import '../../services/user_service.dart'; import '../../services/user_service.dart';
@@ -38,6 +42,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
DateTime? _startDate; DateTime? _startDate;
DateTime? _endDate; DateTime? _endDate;
bool _isLoading = false; bool _isLoading = false;
String? _createdTripId;
final List<String> _participants = []; final List<String> _participants = [];
final _participantController = TextEditingController(); final _participantController = TextEditingController();
@@ -106,38 +111,66 @@ class _CreateTripContentState extends State<CreateTripContent> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocListener<TripBloc, TripState>( return MultiBlocListener(
listener: (context, tripState) { listeners: [
// Écouter l'état TripCreated pour récupérer l'ID du voyage // Listener pour TripBloc
if (tripState is TripCreated) { BlocListener<TripBloc, TripState>(
_createGroupForTrip(tripState.tripId); listener: (context, tripState) {
} else if (tripState is TripOperationSuccess) { if (tripState is TripCreated) {
if (mounted) { // Stocker l'ID du trip et créer le groupe
ScaffoldMessenger.of(context).showSnackBar( _createdTripId = tripState.tripId;
SnackBar( _createGroupForTrip(tripState.tripId);
content: Text(tripState.message), } else if (tripState is TripOperationSuccess) {
backgroundColor: Colors.green, if (mounted) {
), ScaffoldMessenger.of(context).showSnackBar(
); SnackBar(
Navigator.pop(context); content: Text(tripState.message),
if (isEditing) { backgroundColor: Colors.green,
Navigator.pop(context); // Retour supplémentaire en mode édition ),
);
Navigator.pop(context);
if (isEditing) {
Navigator.pop(context);
}
}
} else if (tripState is TripError) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(tripState.message),
backgroundColor: Colors.red,
),
);
setState(() {
_isLoading = false;
});
}
} }
} },
} else if (tripState is TripError) { ),
if (mounted) { // Nouveau listener pour GroupBloc
ScaffoldMessenger.of(context).showSnackBar( BlocListener<GroupBloc, GroupState>(
SnackBar( listener: (context, groupState) {
content: Text(tripState.message), if (groupState is GroupCreated && _createdTripId != null) {
backgroundColor: Colors.red, // Le groupe a été créé, maintenant créer le compte
), print('++++++++++++++ Creating account for trip ${_createdTripId!} and group ${groupState.groupId} ++++++++++++++');
); _createAccountForTrip(_createdTripId!, groupState.groupId);
setState(() { } else if (groupState is GroupError) {
_isLoading = false; 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>( child: BlocBuilder<UserBloc, user_state.UserState>(
builder: (context, userState) { builder: (context, userState) {
if (userState is! user_state.UserLoaded) { if (userState is! user_state.UserLoaded) {
@@ -506,6 +539,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
user_state.UserModel currentUser, user_state.UserModel currentUser,
List<Map<String, String>> participantsData, List<Map<String, String>> participantsData,
) async { ) async {
final groupBloc = context.read<GroupBloc>();
try { try {
final group = await _groupRepository.getGroupByTripId(tripId); final group = await _groupRepository.getGroupByTripId(tripId);
@@ -517,20 +551,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
return; return;
} }
final newMembers = <GroupMember>[ final newMembers = await _createMembers();
GroupMember(
userId: currentUser.id,
firstName: currentUser.prenom,
pseudo: currentUser.prenom,
role: 'admin',
),
...participantsData.map((p) => GroupMember(
userId: p['id'] as String,
firstName: p['firstName'] as String,
pseudo: p['firstName'] as String,
role: 'member',
)),
];
final currentMembers = await _groupRepository.getGroupMembers(group.id); final currentMembers = await _groupRepository.getGroupMembers(group.id);
final currentMemberIds = currentMembers.map((m) => m.userId).toSet(); final currentMemberIds = currentMembers.map((m) => m.userId).toSet();
@@ -543,11 +564,15 @@ class _CreateTripContentState extends State<CreateTripContent> {
.toList(); .toList();
for (final member in membersToAdd) { for (final member in membersToAdd) {
context.read<GroupBloc>().add(AddMemberToGroup(group.id, member)); if (mounted) {
groupBloc.add(AddMemberToGroup(group.id, member));
}
} }
for (final member in membersToRemove) { for (final member in membersToRemove) {
context.read<GroupBloc>().add(RemoveMemberFromGroup(group.id, member.userId)); if (mounted) {
groupBloc.add(RemoveMemberFromGroup(group.id, member.userId));
}
} }
} catch (e) { } catch (e) {
_errorService.logError( _errorService.logError(
@@ -557,39 +582,53 @@ class _CreateTripContentState extends State<CreateTripContent> {
} }
} }
// NOUVELLE MÉTHODE : Créer le groupe après la création du voyage Future<List<GroupMember>> _createMembers() async {
final userState = context.read<UserBloc>().state;
if (userState is! user_state.UserLoaded) return [];
final currentUser = userState.user;
final participantsData = await _getParticipantsData(_participants);
final groupMembers = <GroupMember>[
GroupMember(
userId: currentUser.id,
firstName: currentUser.prenom,
pseudo: currentUser.prenom,
role: 'admin',
),
...participantsData.map((p) => GroupMember(
userId: p['id'] as String,
firstName: p['firstName'] as String,
pseudo: p['firstName'] as String,
role: 'member',
)),
];
return groupMembers;
}
Future<void> _createGroupForTrip(String tripId) async { Future<void> _createGroupForTrip(String tripId) async {
final groupBloc = context.read<GroupBloc>();
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;
final currentUser = userState.user; final currentUser = userState.user;
final participantsData = await _getParticipantsData(_participants);
// Créer le groupe avec le tripId récupéré // Créer le groupe avec le tripId récupéré
final group = Group( final group = Group(
id: '', // Sera généré par Firestore id: '', // Sera généré par Firestore
name: _titleController.text.trim(), name: _titleController.text.trim(),
tripId: tripId, // ✅ ID du voyage récupéré tripId: tripId,
createdBy: currentUser.id, createdBy: currentUser.id,
); );
final groupMembers = <GroupMember>[ final groupMembers = await _createMembers();
GroupMember(
userId: currentUser.id,
firstName: currentUser.prenom,
pseudo: currentUser.prenom,
role: 'admin',
),
...participantsData.map((p) => GroupMember(
userId: p['id'] as String,
firstName: p['firstName'] as String,
pseudo: p['firstName'] as String,
role: 'member',
)),
];
context.read<GroupBloc>().add(CreateGroupWithMembers( if (groupMembers.isEmpty) {
throw Exception('Erreur lors de la création des membres du groupe');
}
groupBloc.add(CreateGroupWithMembers(
group: group, group: group,
members: groupMembers, members: groupMembers,
)); ));
@@ -622,6 +661,60 @@ class _CreateTripContentState extends State<CreateTripContent> {
} }
} }
Future<void> _createAccountForTrip(String tripId, String groupId) async {
final accountBloc = context.read<AccountBloc>();
try {
final userState = context.read<UserBloc>().state;
if (userState is! user_state.UserLoaded) return;
print('Creating account for trip $tripId and group $groupId');
final account = Account(
id: '',
tripId: tripId,
groupId: groupId,
name: _titleController.text.trim(),
);
final accountsMembers = await _createMembers();
accountBloc.add(CreateAccountWithMembers(
account: account,
members: accountsMembers,
));
print('++++++++++++++ Created account for trip $tripId and group $groupId ++++++++++++++');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Compte créé avec succès !'),
backgroundColor: Colors.green,
),
);
setState(() {
_isLoading = false;
});
Navigator.pop(context);
}
} catch (e) {
_errorService.logError(
'create_trip_content.dart',
'Erreur lors de la création du compte: $e',
);
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
Future<void> _saveTrip(user_state.UserModel currentUser) async { Future<void> _saveTrip(user_state.UserModel currentUser) async {
if (!_formKey.currentState!.validate()) { if (!_formKey.currentState!.validate()) {
return; return;
@@ -640,6 +733,8 @@ class _CreateTripContentState extends State<CreateTripContent> {
_isLoading = true; _isLoading = true;
}); });
final tripBloc = context.read<TripBloc>();
try { try {
final participantsData = await _getParticipantsData(_participants); final participantsData = await _getParticipantsData(_participants);
List<String> participantIds = participantsData.map((p) => p['id'] as String).toList(); List<String> participantIds = participantsData.map((p) => p['id'] as String).toList();
@@ -664,16 +759,17 @@ class _CreateTripContentState extends State<CreateTripContent> {
if (isEditing) { if (isEditing) {
// Mode mise à jour // Mode mise à jour
context.read<TripBloc>().add(TripUpdateRequested(trip: trip)); tripBloc.add(TripUpdateRequested(trip: trip));
await _updateGroupMembers( await _updateGroupMembers(
widget.tripToEdit!.id!, widget.tripToEdit!.id!,
currentUser, currentUser,
participantsData, participantsData,
); );
} else { } else {
// Mode création - Le groupe sera créé dans le listener TripCreated // Mode création - Le groupe sera créé dans le listener TripCreated
context.read<TripBloc>().add(TripCreateRequested(trip: trip)); tripBloc.add(TripCreateRequested(trip: trip));
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {