Add functionality to manage account members: implement add and remove member events, update account repository methods, and integrate with trip details for participant management.
This commit is contained in:
@@ -17,6 +17,7 @@ import '../../models/group.dart';
|
||||
import '../../models/group_member.dart';
|
||||
import '../../services/user_service.dart';
|
||||
import '../../repositories/group_repository.dart';
|
||||
import '../../repositories/account_repository.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import '../../services/place_image_service.dart';
|
||||
@@ -71,6 +72,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
/// Services for user and group operations
|
||||
final _userService = UserService();
|
||||
final _groupRepository = GroupRepository();
|
||||
final _accountRepository = AccountRepository();
|
||||
final _placeImageService = PlaceImageService();
|
||||
final _tripGeocodingService = TripGeocodingService();
|
||||
|
||||
@@ -611,46 +613,42 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Dates
|
||||
Row(
|
||||
Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Début du voyage',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Début du voyage',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildDateField(
|
||||
date: _startDate,
|
||||
onTap: () => _selectStartDate(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildDateField(
|
||||
date: _startDate,
|
||||
onTap: () => _selectStartDate(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Fin du voyage',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Fin du voyage',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildDateField(
|
||||
date: _endDate,
|
||||
onTap: () => _selectEndDate(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildDateField(
|
||||
date: _endDate,
|
||||
onTap: () => _selectEndDate(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -673,82 +671,84 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Inviter des amis
|
||||
Text(
|
||||
'Invite tes amis',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
// Inviter des amis - seulement en mode création
|
||||
if (!isEditing) ...[
|
||||
Text(
|
||||
'Invite tes amis',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildModernTextField(
|
||||
controller: _participantController,
|
||||
label: 'adresse@email.com',
|
||||
icon: Icons.alternate_email,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
height: 56,
|
||||
width: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.teal,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: _addParticipant,
|
||||
icon: const Icon(Icons.add, color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Participants ajoutés
|
||||
if (_participants.isNotEmpty) ...[
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: _participants.map((email) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildModernTextField(
|
||||
controller: _participantController,
|
||||
label: 'adresse@email.com',
|
||||
icon: Icons.alternate_email,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
height: 56,
|
||||
width: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.teal.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Colors.teal,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
email,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.teal,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () => _removeParticipant(email),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 16,
|
||||
color: Colors.teal,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: IconButton(
|
||||
onPressed: _addParticipant,
|
||||
icon: const Icon(Icons.add, color: Colors.white),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Participants ajoutés
|
||||
if (_participants.isNotEmpty) ...[
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: _participants.map((email) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.teal.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
email,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.teal,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () => _removeParticipant(email),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 16,
|
||||
color: Colors.teal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
],
|
||||
|
||||
const SizedBox(height: 32),
|
||||
@@ -871,13 +871,15 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
});
|
||||
}
|
||||
|
||||
// Mettre à jour le groupe avec les nouveaux membres
|
||||
Future<void> _updateGroupMembers(
|
||||
// Mettre à jour le groupe ET le compte avec les nouveaux membres
|
||||
Future<void> _updateGroupAndAccountMembers(
|
||||
String tripId,
|
||||
user_state.UserModel currentUser,
|
||||
List<Map<String, dynamic>> participantsData,
|
||||
) async {
|
||||
final groupBloc = context.read<GroupBloc>();
|
||||
final accountBloc = context.read<AccountBloc>();
|
||||
|
||||
try {
|
||||
final group = await _groupRepository.getGroupByTripId(tripId);
|
||||
|
||||
@@ -889,6 +891,9 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Récupérer le compte associé au voyage
|
||||
final account = await _accountRepository.getAccountByTripId(tripId);
|
||||
|
||||
final newMembers = await _createMembers();
|
||||
|
||||
final currentMembers = await _groupRepository.getGroupMembers(group.id);
|
||||
@@ -901,21 +906,41 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
.where((m) => !newMemberIds.contains(m.userId) && m.role != 'admin')
|
||||
.toList();
|
||||
|
||||
// Ajouter les nouveaux membres au groupe ET au compte
|
||||
for (final member in membersToAdd) {
|
||||
if (mounted) {
|
||||
groupBloc.add(AddMemberToGroup(group.id, member));
|
||||
if (account != null) {
|
||||
accountBloc.add(AddMemberToAccount(account.id, member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer les membres supprimés du groupe ET du compte
|
||||
for (final member in membersToRemove) {
|
||||
if (mounted) {
|
||||
groupBloc.add(RemoveMemberFromGroup(group.id, member.userId));
|
||||
if (account != null) {
|
||||
accountBloc.add(RemoveMemberFromAccount(account.id, member.userId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Groupe et compte mis à jour avec succès !'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
_errorService.logError(
|
||||
'create_trip_content.dart',
|
||||
'Erreur lors de la mise à jour du groupe: $e',
|
||||
'Erreur lors de la mise à jour du groupe et du compte: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1067,11 +1092,8 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
// Géolocaliser le voyage avant de le sauvegarder
|
||||
Trip tripWithCoordinates;
|
||||
try {
|
||||
print('🌍 [CreateTrip] Géolocalisation en cours pour: ${trip.location}');
|
||||
tripWithCoordinates = await _tripGeocodingService.geocodeTrip(trip);
|
||||
print('✅ [CreateTrip] Géolocalisation réussie: ${tripWithCoordinates.latitude}, ${tripWithCoordinates.longitude}');
|
||||
} catch (e) {
|
||||
print('⚠️ [CreateTrip] Erreur de géolocalisation: $e');
|
||||
// Continuer sans coordonnées en cas d'erreur
|
||||
tripWithCoordinates = trip;
|
||||
if (mounted) {
|
||||
@@ -1089,14 +1111,16 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
// Mode mise à jour
|
||||
tripBloc.add(TripUpdateRequested(trip: tripWithCoordinates));
|
||||
|
||||
// Vérifier que l'ID du voyage existe avant de mettre à jour le groupe
|
||||
// Mettre à jour le groupe ET les comptes avec les nouveaux participants
|
||||
if (widget.tripToEdit != null && widget.tripToEdit!.id != null && widget.tripToEdit!.id!.isNotEmpty) {
|
||||
await _updateGroupMembers(
|
||||
print('🔄 [CreateTrip] Mise à jour du groupe et du compte pour le voyage ID: ${widget.tripToEdit!.id}');
|
||||
print('👥 Participants: ${participantsData.map((p) => p['id']).toList()}');
|
||||
await _updateGroupAndAccountMembers(
|
||||
widget.tripToEdit!.id!,
|
||||
currentUser,
|
||||
participantsData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Mode création - Le groupe sera créé dans le listener TripCreated
|
||||
|
||||
Reference in New Issue
Block a user