Mise en place suppression membre
This commit is contained in:
@@ -127,18 +127,44 @@ class MembreRepository extends ChangeNotifier {
|
||||
// Appeler l'API users
|
||||
final response = await ApiService.instance.post('/users', data: data);
|
||||
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
// Créer le membre avec les données retournées par l'API
|
||||
final createdMember = MembreModel.fromJson(response.data);
|
||||
if (response.statusCode == 201) {
|
||||
// Extraire l'ID de la réponse API
|
||||
final responseData = response.data;
|
||||
debugPrint('🎉 Réponse API création utilisateur: $responseData');
|
||||
|
||||
// Sauvegarder localement
|
||||
// L'API retourne {"status":"success","message":"Utilisateur créé avec succès","id":"10027748"}
|
||||
final userId = responseData['id'] is String ? int.parse(responseData['id']) : responseData['id'] as int;
|
||||
|
||||
// Créer le nouveau membre avec l'ID retourné par l'API
|
||||
final createdMember = MembreModel(
|
||||
id: userId,
|
||||
fkEntite: membre.fkEntite,
|
||||
role: membre.role,
|
||||
fkTitre: membre.fkTitre,
|
||||
name: membre.name,
|
||||
firstName: membre.firstName,
|
||||
username: membre.username,
|
||||
sectName: membre.sectName,
|
||||
email: membre.email,
|
||||
phone: membre.phone,
|
||||
mobile: membre.mobile,
|
||||
dateNaissance: membre.dateNaissance,
|
||||
dateEmbauche: membre.dateEmbauche,
|
||||
createdAt: DateTime.now(),
|
||||
isActive: membre.isActive,
|
||||
);
|
||||
|
||||
// Sauvegarder localement dans Hive
|
||||
await saveMembreBox(createdMember);
|
||||
|
||||
return createdMember; // Retourner le membre créé
|
||||
debugPrint('✅ Membre créé avec l\'ID: $userId et sauvegardé localement');
|
||||
return createdMember;
|
||||
}
|
||||
|
||||
debugPrint('❌ Échec création membre - Code: ${response.statusCode}');
|
||||
return null;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la création du membre: $e');
|
||||
debugPrint('❌ Erreur lors de la création du membre: $e');
|
||||
rethrow; // Propager l'exception pour la gestion d'erreurs
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
@@ -174,25 +200,45 @@ class MembreRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer un membre via l'API
|
||||
Future<bool> deleteMembre(int id) async {
|
||||
// Supprimer un membre via l'API avec transfert optionnel
|
||||
Future<bool> deleteMembre(int membreId, [int? transferToUserId, int? operationId]) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Appeler l'API users au lieu de membres (correction ici)
|
||||
final response = await ApiService.instance.delete('/users/$id');
|
||||
String endpoint = '/users/$membreId';
|
||||
|
||||
// Construire les paramètres query SEULEMENT si on a des passages à transférer
|
||||
List<String> queryParams = [];
|
||||
|
||||
if (transferToUserId != null && transferToUserId > 0) {
|
||||
queryParams.add('transfer_to=$transferToUserId');
|
||||
|
||||
// Ajouter operation_id SEULEMENT s'il y a un transfert
|
||||
if (operationId != null && operationId > 0) {
|
||||
queryParams.add('operation_id=$operationId');
|
||||
}
|
||||
}
|
||||
|
||||
// Ajouter les paramètres à l'endpoint
|
||||
if (queryParams.isNotEmpty) {
|
||||
endpoint += '?${queryParams.join('&')}';
|
||||
}
|
||||
|
||||
debugPrint('🔗 DELETE endpoint: $endpoint');
|
||||
|
||||
final response = await ApiService.instance.delete(endpoint);
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
// Supprimer le membre localement
|
||||
await deleteMembreBox(id);
|
||||
await deleteMembreBox(membreId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la suppression du membre: $e');
|
||||
return false;
|
||||
rethrow;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
|
||||
@@ -41,6 +41,44 @@ class OperationRepository extends ChangeNotifier {
|
||||
return _operationBox.get(id);
|
||||
}
|
||||
|
||||
OperationModel? getCurrentOperation() {
|
||||
try {
|
||||
// Récupérer toutes les opérations actives
|
||||
final activeOperations = _operationBox.values.where((operation) => operation.isActive == true).toList();
|
||||
|
||||
if (activeOperations.isEmpty) {
|
||||
debugPrint('⚠️ Aucune opération active trouvée');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Trier par ID décroissant et prendre la première (ID le plus élevé)
|
||||
activeOperations.sort((a, b) => b.id.compareTo(a.id));
|
||||
final currentOperation = activeOperations.first;
|
||||
|
||||
debugPrint('🎯 Opération courante: ${currentOperation.id} - ${currentOperation.name}');
|
||||
return currentOperation;
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors de la récupération de l\'opération courante: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode helper pour récupérer seulement l'ID de l'opération courante
|
||||
int? getCurrentOperationId() {
|
||||
final currentOperation = getCurrentOperation();
|
||||
return currentOperation?.id;
|
||||
}
|
||||
|
||||
// Méthode pour récupérer toutes les opérations actives (utile pour debug/admin)
|
||||
List<OperationModel> getActiveOperations() {
|
||||
try {
|
||||
return _operationBox.values.where((operation) => operation.isActive == true).toList()..sort((a, b) => b.id.compareTo(a.id)); // Tri par ID décroissant
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors de la récupération des opérations actives: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Sauvegarder une opération
|
||||
Future<void> saveOperation(OperationModel operation) async {
|
||||
await _operationBox.put(operation.id, operation);
|
||||
|
||||
@@ -9,6 +9,9 @@ class PassageRepository extends ChangeNotifier {
|
||||
// Constructeur sans paramètres - utilise ApiService.instance
|
||||
PassageRepository();
|
||||
|
||||
// Cache pour les statistiques
|
||||
Map<String, dynamic>? _cachedStats;
|
||||
|
||||
// Utiliser un getter lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
// et vérifier qu'elle est ouverte avant accès
|
||||
Box<PassageModel> get _passageBox {
|
||||
@@ -16,6 +19,19 @@ class PassageRepository extends ChangeNotifier {
|
||||
return Hive.box<PassageModel>(AppKeys.passagesBoxName);
|
||||
}
|
||||
|
||||
// Méthode pour exposer la Box Hive (nécessaire pour ValueListenableBuilder)
|
||||
Box<PassageModel> getPassagesBox() {
|
||||
try {
|
||||
if (!Hive.isBoxOpen(AppKeys.passagesBoxName)) {
|
||||
throw Exception('La boîte passages n\'est pas ouverte');
|
||||
}
|
||||
return Hive.box<PassageModel>(AppKeys.passagesBoxName);
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de l\'accès à la boîte passages: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Stream pour notifier des changements de passages
|
||||
StreamController<List<PassageModel>>? _passageStreamController;
|
||||
|
||||
@@ -73,6 +89,16 @@ class PassageRepository extends ChangeNotifier {
|
||||
return _passageBox.values.where((passage) => passage.fkOperation == operationId).toList();
|
||||
}
|
||||
|
||||
// Récupérer les passages par utilisateur
|
||||
List<PassageModel> getPassagesByUser(int userId) {
|
||||
try {
|
||||
return _passageBox.values.where((passage) => passage.fkUser == userId).toList();
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération des passages par utilisateur: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer les passages par date
|
||||
List<PassageModel> getPassagesByDate(DateTime date) {
|
||||
return _passageBox.values.where((passage) {
|
||||
@@ -263,11 +289,14 @@ class PassageRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Statistiques
|
||||
Map<String, int> getPassageStatistics() {
|
||||
// Recalculer les statistiques (appelée par le ValueListenableBuilder)
|
||||
Map<String, dynamic> calculatePassageStatistics() {
|
||||
final allPassages = getAllPassages();
|
||||
|
||||
return {
|
||||
debugPrint('📊 Calcul des statistiques: ${allPassages.length} passages');
|
||||
|
||||
// Statistiques globales
|
||||
final globalStats = {
|
||||
'total': allPassages.length,
|
||||
'effectues': allPassages.where((p) => p.fkType == 1).length,
|
||||
'a_finaliser': allPassages.where((p) => p.fkType == 2).length,
|
||||
@@ -276,6 +305,57 @@ class PassageRepository extends ChangeNotifier {
|
||||
'lots': allPassages.where((p) => p.fkType == 5).length,
|
||||
'maisons_vides': allPassages.where((p) => p.fkType == 6).length,
|
||||
};
|
||||
|
||||
// Statistiques par utilisateur
|
||||
final Map<int, Map<String, int>> statsByUser = {};
|
||||
|
||||
// Grouper les passages par utilisateur
|
||||
final passagesByUser = <int, List<PassageModel>>{};
|
||||
for (final passage in allPassages) {
|
||||
passagesByUser.putIfAbsent(passage.fkUser, () => []).add(passage);
|
||||
}
|
||||
|
||||
// Calculer les statistiques pour chaque utilisateur
|
||||
for (final entry in passagesByUser.entries) {
|
||||
final userId = entry.key;
|
||||
final userPassages = entry.value;
|
||||
|
||||
statsByUser[userId] = {
|
||||
'total': userPassages.length,
|
||||
'effectues': userPassages.where((p) => p.fkType == 1).length,
|
||||
'a_finaliser': userPassages.where((p) => p.fkType == 2).length,
|
||||
'refuses': userPassages.where((p) => p.fkType == 3).length,
|
||||
'dons': userPassages.where((p) => p.fkType == 4).length,
|
||||
'lots': userPassages.where((p) => p.fkType == 5).length,
|
||||
'maisons_vides': userPassages.where((p) => p.fkType == 6).length,
|
||||
};
|
||||
}
|
||||
|
||||
// Statistiques par type
|
||||
final statsByType = {
|
||||
1: allPassages.where((p) => p.fkType == 1).length,
|
||||
2: allPassages.where((p) => p.fkType == 2).length,
|
||||
3: allPassages.where((p) => p.fkType == 3).length,
|
||||
4: allPassages.where((p) => p.fkType == 4).length,
|
||||
5: allPassages.where((p) => p.fkType == 5).length,
|
||||
6: allPassages.where((p) => p.fkType == 6).length,
|
||||
};
|
||||
|
||||
// Mettre en cache et retourner
|
||||
_cachedStats = {
|
||||
'global': globalStats,
|
||||
'by_user': statsByUser,
|
||||
'by_type': statsByType,
|
||||
'user_count': statsByUser.length,
|
||||
'calculated_at': DateTime.now().toIso8601String(),
|
||||
};
|
||||
|
||||
return _cachedStats!;
|
||||
}
|
||||
|
||||
// Getter simple pour les statistiques (optionnel, pour usage sans ValueListenableBuilder)
|
||||
Map<String, dynamic> getPassageStatistics() {
|
||||
return _cachedStats ?? calculatePassageStatistics();
|
||||
}
|
||||
|
||||
// Vider tous les passages
|
||||
|
||||
@@ -12,6 +12,9 @@ import 'package:geosector_app/core/repositories/membre_repository.dart';
|
||||
import 'package:geosector_app/presentation/widgets/amicale_table_widget.dart';
|
||||
import 'package:geosector_app/presentation/widgets/membre_table_widget.dart';
|
||||
import 'package:geosector_app/core/utils/api_exception.dart';
|
||||
import 'package:geosector_app/core/data/models/passage_model.dart';
|
||||
import 'package:geosector_app/core/repositories/passage_repository.dart';
|
||||
import 'package:geosector_app/core/repositories/operation_repository.dart';
|
||||
|
||||
/// Class pour dessiner les petits points blancs sur le fond
|
||||
class DotsPainter extends CustomPainter {
|
||||
@@ -42,12 +45,16 @@ class AdminAmicalePage extends StatefulWidget {
|
||||
final UserRepository userRepository;
|
||||
final AmicaleRepository amicaleRepository;
|
||||
final MembreRepository membreRepository;
|
||||
final PassageRepository passageRepository;
|
||||
final OperationRepository operationRepository;
|
||||
|
||||
const AdminAmicalePage({
|
||||
super.key,
|
||||
required this.userRepository,
|
||||
required this.amicaleRepository,
|
||||
required this.membreRepository,
|
||||
required this.passageRepository,
|
||||
required this.operationRepository,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -57,11 +64,13 @@ class AdminAmicalePage extends StatefulWidget {
|
||||
class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
UserModel? _currentUser;
|
||||
String? _errorMessage;
|
||||
int? _currentOperationId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadCurrentUser();
|
||||
_loadCurrentOperation();
|
||||
}
|
||||
|
||||
void _loadCurrentUser() {
|
||||
@@ -94,6 +103,19 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
});
|
||||
}
|
||||
|
||||
// Méthode pour charger l'opération courante
|
||||
void _loadCurrentOperation() {
|
||||
final currentOperation = widget.operationRepository.getCurrentOperation();
|
||||
_currentOperationId = currentOperation?.id;
|
||||
|
||||
if (currentOperation != null) {
|
||||
debugPrint('🎯 Opération courante: ${currentOperation.id} - ${currentOperation.name}');
|
||||
debugPrint('📅 Période: ${currentOperation.dateDebut.toString().substring(0, 10)} → ${currentOperation.dateFin.toString().substring(0, 10)}');
|
||||
} else {
|
||||
debugPrint('⚠️ Aucune opération courante trouvée');
|
||||
}
|
||||
}
|
||||
|
||||
void _handleEditMembre(MembreModel membre) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -141,12 +163,62 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
);
|
||||
}
|
||||
|
||||
void _handleDeleteMembre(MembreModel membre) {
|
||||
void _handleDeleteMembre(MembreModel membre) async {
|
||||
try {
|
||||
debugPrint('🗑️ Début suppression du membre: ${membre.firstName} ${membre.name} (ID: ${membre.id})');
|
||||
|
||||
// Vérifier qu'on a une opération courante
|
||||
if (_currentOperationId == null) {
|
||||
debugPrint('❌ Aucune opération courante');
|
||||
ApiException.showError(context, Exception('Aucune opération active trouvée. Impossible de supprimer le membre.'));
|
||||
return;
|
||||
}
|
||||
|
||||
debugPrint('🎯 Opération courante: $_currentOperationId');
|
||||
|
||||
// Filtrer les passages par opération courante ET par utilisateur
|
||||
final allUserPassages = widget.passageRepository.getPassagesByUser(membre.id);
|
||||
debugPrint('📊 Total passages du membre: ${allUserPassages.length}');
|
||||
|
||||
final passagesRealises = allUserPassages.where((passage) => passage.fkOperation == _currentOperationId && passage.fkType != 2).toList();
|
||||
|
||||
final passagesAFinaliser = allUserPassages.where((passage) => passage.fkOperation == _currentOperationId && passage.fkType == 2).toList();
|
||||
|
||||
final totalPassages = passagesRealises.length + passagesAFinaliser.length;
|
||||
|
||||
debugPrint('🔍 Passages réalisés (opération $_currentOperationId): ${passagesRealises.length}');
|
||||
debugPrint('🔍 Passages à finaliser (opération $_currentOperationId): ${passagesAFinaliser.length}');
|
||||
debugPrint('🔍 Total passages pour l\'opération $_currentOperationId: $totalPassages');
|
||||
|
||||
// Récupérer les autres membres de l'amicale (pour le transfert)
|
||||
final autresmembres = widget.membreRepository.getMembresByAmicale(_currentUser!.fkEntite!).where((m) => m.id != membre.id && m.isActive == true).toList();
|
||||
|
||||
debugPrint('👥 Autres membres disponibles: ${autresmembres.length}');
|
||||
|
||||
// Afficher le dialog de confirmation approprié
|
||||
if (totalPassages > 0) {
|
||||
debugPrint('➡️ Affichage dialog avec passages');
|
||||
_showDeleteMemberWithPassagesDialog(membre, totalPassages, autresmembres);
|
||||
} else {
|
||||
debugPrint('➡️ Affichage dialog simple (pas de passages)');
|
||||
_showSimpleDeleteConfirmation(membre);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors de la vérification des passages: $e');
|
||||
if (mounted) {
|
||||
ApiException.showError(context, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _showSimpleDeleteConfirmation(MembreModel membre) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Confirmer la suppression'),
|
||||
content: Text('Voulez-vous vraiment supprimer le membre ${membre.firstName} ${membre.name} ?\n\nCette action est irréversible.'),
|
||||
content: Text('Voulez-vous vraiment supprimer le membre ${membre.firstName} ${membre.name} ?\n\n'
|
||||
'Ce membre n\'a aucun passage enregistré pour l\'opération courante.\n'
|
||||
'Cette action est irréversible.'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
@@ -155,20 +227,8 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
try {
|
||||
// Utiliser la méthode qui passe par l'API
|
||||
final success = await widget.membreRepository.deleteMembre(membre.id);
|
||||
|
||||
if (success && mounted) {
|
||||
ApiException.showSuccess(context, 'Membre ${membre.firstName} ${membre.name} supprimé avec succès');
|
||||
} else if (!success && mounted) {
|
||||
ApiException.showError(context, Exception('Erreur lors de la suppression'));
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ApiException.showError(context, e);
|
||||
}
|
||||
}
|
||||
// Suppression simple : pas de passages, donc pas de paramètres
|
||||
await _deleteMemberAPI(membre.id, 0, hasPassages: false);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
@@ -181,6 +241,245 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showDeleteMemberWithPassagesDialog(
|
||||
MembreModel membre,
|
||||
int totalPassages,
|
||||
List<MembreModel> autresmembres,
|
||||
) {
|
||||
int? selectedMemberForTransfer; // Déclarer la variable à l'extérieur du builder
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setDialogState) {
|
||||
return AlertDialog(
|
||||
title: const Row(
|
||||
children: [
|
||||
Icon(Icons.warning, color: Colors.orange),
|
||||
SizedBox(width: 8),
|
||||
Expanded(child: Text('Attention - Passages détectés')),
|
||||
],
|
||||
),
|
||||
content: SizedBox(
|
||||
width: 500,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Le membre ${membre.firstName} ${membre.name} a $totalPassages passage(s) enregistré(s).',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Section transfert
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.blue.withOpacity(0.3)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'📋 Transférer les passages',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blue.shade700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Sélectionnez un membre pour récupérer tous les passages ($totalPassages) :',
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
DropdownButtonFormField<int>(
|
||||
value: selectedMemberForTransfer,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Membre destinataire',
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
),
|
||||
items: autresmembres
|
||||
.map((m) => DropdownMenuItem(
|
||||
value: m.id,
|
||||
child: Text('${m.firstName} ${m.name}'),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
setDialogState(() {
|
||||
selectedMemberForTransfer = value;
|
||||
});
|
||||
debugPrint('✅ Membre destinataire sélectionné: $value');
|
||||
},
|
||||
),
|
||||
|
||||
// Indicateur visuel de sélection
|
||||
if (selectedMemberForTransfer != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.check_circle, color: Colors.green, size: 16),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Membre sélectionné',
|
||||
style: TextStyle(
|
||||
color: Colors.green.shade700,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Option de désactivation
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.green.withOpacity(0.3)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'💡 Alternative recommandée',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.green.shade700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'Vous pouvez désactiver ce membre au lieu de le supprimer. '
|
||||
'Cela préservera l\'historique des passages tout en empêchant la connexion.',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
await _deactivateMember(membre);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.green,
|
||||
),
|
||||
child: const Text('Désactiver seulement'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: selectedMemberForTransfer != null
|
||||
? () async {
|
||||
debugPrint('🗑️ Suppression avec transfert vers ID: $selectedMemberForTransfer');
|
||||
Navigator.of(context).pop();
|
||||
// Suppression avec passages : inclure les paramètres
|
||||
await _deleteMemberAPI(membre.id, selectedMemberForTransfer!, hasPassages: true);
|
||||
}
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: selectedMemberForTransfer != null ? Colors.red : null,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (selectedMemberForTransfer != null) const Icon(Icons.delete_forever, size: 16),
|
||||
if (selectedMemberForTransfer != null) const SizedBox(width: 4),
|
||||
Text(
|
||||
selectedMemberForTransfer != null ? 'Supprimer et transférer' : 'Sélectionner un membre',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode unifiée pour appeler l'API de suppression
|
||||
Future<void> _deleteMemberAPI(int membreId, int transferToUserId, {bool hasPassages = false}) async {
|
||||
try {
|
||||
bool success;
|
||||
|
||||
if (hasPassages && transferToUserId > 0 && _currentOperationId != null) {
|
||||
// Suppression avec transfert de passages (inclure operation_id)
|
||||
debugPrint('🔄 Suppression avec transfert - Opération: $_currentOperationId, Vers: $transferToUserId');
|
||||
success = await widget.membreRepository.deleteMembre(
|
||||
membreId,
|
||||
transferToUserId,
|
||||
_currentOperationId,
|
||||
);
|
||||
} else {
|
||||
// Suppression simple (pas de passages, donc pas de paramètres)
|
||||
debugPrint('🗑️ Suppression simple - Aucun passage à transférer');
|
||||
success = await widget.membreRepository.deleteMembre(membreId);
|
||||
}
|
||||
|
||||
if (success && mounted) {
|
||||
String message = 'Membre supprimé avec succès';
|
||||
|
||||
if (hasPassages && transferToUserId > 0) {
|
||||
final transferMember = widget.membreRepository.getMembreById(transferToUserId);
|
||||
final currentOperation = widget.operationRepository.getCurrentOperation();
|
||||
message += '\nPassages de l\'opération "${currentOperation?.name}" transférés à ${transferMember?.firstName} ${transferMember?.name}';
|
||||
}
|
||||
|
||||
ApiException.showSuccess(context, message);
|
||||
} else if (mounted) {
|
||||
ApiException.showError(context, Exception('Erreur lors de la suppression'));
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur suppression membre: $e');
|
||||
if (mounted) {
|
||||
ApiException.showError(context, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deactivateMember(MembreModel membre) async {
|
||||
try {
|
||||
final updatedMember = membre.copyWith(isActive: false);
|
||||
final success = await widget.membreRepository.updateMembre(updatedMember);
|
||||
|
||||
if (success && mounted) {
|
||||
ApiException.showSuccess(context, 'Membre ${membre.firstName} ${membre.name} désactivé avec succès');
|
||||
} else if (mounted) {
|
||||
ApiException.showError(context, Exception('Erreur lors de la désactivation'));
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ApiException.showError(context, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _handleAddMembre() {
|
||||
if (_currentUser?.fkEntite == null) return;
|
||||
|
||||
@@ -243,17 +542,23 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
|
||||
// Créer le membre via l'API (retourne maintenant le membre créé)
|
||||
final createdMembre = await widget.membreRepository.createMembre(newMembre);
|
||||
|
||||
if (createdMembre != null && mounted) {
|
||||
// Fermer le dialog
|
||||
Navigator.of(context).pop();
|
||||
ApiException.showSuccess(context, 'Membre ${createdMembre.firstName} ${createdMembre.name} ajouté avec succès');
|
||||
|
||||
// Afficher le message de succès avec les informations du membre créé
|
||||
ApiException.showSuccess(context, 'Membre ${createdMembre.firstName} ${createdMembre.name} ajouté avec succès (ID: ${createdMembre.id})');
|
||||
} else if (mounted) {
|
||||
// En cas d'échec, ne pas fermer le dialog pour permettre la correction
|
||||
ApiException.showError(context, Exception('Erreur lors de la création du membre'));
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur création membre: $e');
|
||||
if (mounted) {
|
||||
// En cas d'exception, ne pas fermer le dialog
|
||||
ApiException.showError(context, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,8 @@ class _AdminDashboardPageState extends State<AdminDashboardPage> with WidgetsBin
|
||||
userRepository: userRepository,
|
||||
amicaleRepository: amicaleRepository,
|
||||
membreRepository: membreRepository,
|
||||
passageRepository: passageRepository,
|
||||
operationRepository: operationRepository,
|
||||
);
|
||||
case _PageType.operations:
|
||||
return const Scaffold(body: Center(child: Text('Page Opérations')));
|
||||
|
||||
Reference in New Issue
Block a user