import 'package:flutter/material.dart'; import 'package:geosector_app/core/services/api_service.dart'; import 'dart:math' as math; import 'package:hive_flutter/hive_flutter.dart'; import 'package:geosector_app/core/data/models/amicale_model.dart'; import 'package:geosector_app/core/data/models/membre_model.dart'; import 'package:geosector_app/core/data/models/user_model.dart'; import 'package:geosector_app/presentation/widgets/user_form_dialog.dart'; import 'package:geosector_app/core/repositories/user_repository.dart'; import 'package:geosector_app/core/repositories/amicale_repository.dart'; 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'; /// Class pour dessiner les petits points blancs sur le fond class DotsPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.white.withOpacity(0.5) ..style = PaintingStyle.fill; final random = math.Random(42); // Seed fixe pour consistance final numberOfDots = (size.width * size.height) ~/ 1500; for (int i = 0; i < numberOfDots; i++) { final x = random.nextDouble() * size.width; final y = random.nextDouble() * size.height; final radius = 1.0 + random.nextDouble() * 2.0; canvas.drawCircle(Offset(x, y), radius, paint); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } /// Page d'administration de l'amicale et des membres /// Cette page est intégrée dans le tableau de bord administrateur class AdminAmicalePage extends StatefulWidget { final UserRepository userRepository; final AmicaleRepository amicaleRepository; final MembreRepository membreRepository; const AdminAmicalePage({ super.key, required this.userRepository, required this.amicaleRepository, required this.membreRepository, }); @override State createState() => _AdminAmicalePageState(); } class _AdminAmicalePageState extends State { UserModel? _currentUser; String? _errorMessage; @override void initState() { super.initState(); _loadCurrentUser(); } void _loadCurrentUser() { final currentUser = widget.userRepository.getCurrentUser(); debugPrint('🔍 _loadCurrentUser - Utilisateur: ${currentUser?.username} (ID: ${currentUser?.id})'); debugPrint('🔍 _loadCurrentUser - fkEntite: ${currentUser?.fkEntite}'); if (currentUser == null) { setState(() { _errorMessage = 'Utilisateur non connecté'; }); return; } if (currentUser.fkEntite == null) { setState(() { _errorMessage = 'Utilisateur non associé à une amicale'; }); return; } // Vérifier immédiatement si l'amicale existe final amicale = widget.amicaleRepository.getUserAmicale(currentUser.fkEntite!); debugPrint('🔍 Amicale trouvée dans le repository: ${amicale?.name ?? 'null'}'); setState(() { _currentUser = currentUser; _errorMessage = null; }); } void _handleEditMembre(MembreModel membre) { showDialog( context: context, builder: (context) => UserFormDialog( title: 'Modifier le membre', user: membre.toUserModel(), showRoleSelector: true, showActiveCheckbox: true, // Activer la checkbox allowUsernameEdit: true, // Permettre l'édition du username // allowSectNameEdit sera automatiquement true via UserForm availableRoles: const [ RoleOption( value: 1, label: 'Membre', description: 'Peut consulter et distribuer dans ses secteurs', ), RoleOption( value: 2, label: 'Administrateur', description: 'Peut gérer l\'amicale et ses membres', ), ], onSubmit: (updatedUser) async { try { // Convertir le UserModel mis à jour vers MembreModel final updatedMembre = MembreModel.fromUserModel(updatedUser, membre); // Utiliser directement updateMembre qui passe par l'API /users final success = await widget.membreRepository.updateMembre(updatedMembre); if (success && mounted) { Navigator.of(context).pop(); ApiException.showSuccess(context, 'Membre ${updatedMembre.firstName} ${updatedMembre.name} mis à jour'); } else if (!success && mounted) { ApiException.showError(context, Exception('Erreur lors de la mise à jour')); } } catch (e) { debugPrint('❌ Erreur mise à jour membre: $e'); if (mounted) { ApiException.showError(context, e); } } }, ), ); } void _handleDeleteMembre(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.'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Annuler'), ), 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); } } }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: const Text('Supprimer'), ), ], ), ); } void _handleAddMembre() { if (_currentUser?.fkEntite == null) return; // Créer un UserModel vide avec les valeurs par défaut final newUser = UserModel( id: 0, // ID temporaire pour nouveau membre username: '', firstName: '', name: '', sectName: '', phone: '', mobile: '', email: '', fkTitre: 1, // Par défaut M. fkEntite: _currentUser!.fkEntite!, // Association à l'amicale courante role: 1, // Par défaut membre isActive: true, // Par défaut actif createdAt: DateTime.now(), lastSyncedAt: DateTime.now(), ); showDialog( context: context, builder: (context) => UserFormDialog( title: 'Ajouter un nouveau membre', user: newUser, showRoleSelector: true, showActiveCheckbox: true, allowUsernameEdit: true, availableRoles: const [ RoleOption( value: 1, label: 'Membre', description: 'Peut consulter et distribuer dans ses secteurs', ), RoleOption( value: 2, label: 'Administrateur', description: 'Peut gérer l\'amicale et ses membres', ), ], onSubmit: (newUserData) async { try { // Créer un nouveau MembreModel directement final newMembre = MembreModel( id: 0, // L'API assignera un vrai ID username: newUserData.username, firstName: newUserData.firstName, name: newUserData.name, sectName: newUserData.sectName, phone: newUserData.phone, mobile: newUserData.mobile, email: newUserData.email, fkTitre: newUserData.fkTitre, fkEntite: newUserData.fkEntite!, role: newUserData.role, isActive: newUserData.isActive, dateNaissance: newUserData.dateNaissance, dateEmbauche: newUserData.dateEmbauche, createdAt: DateTime.now(), ); final createdMembre = await widget.membreRepository.createMembre(newMembre); if (createdMembre != null && mounted) { Navigator.of(context).pop(); ApiException.showSuccess(context, 'Membre ${createdMembre.firstName} ${createdMembre.name} ajouté avec succès'); } else if (mounted) { ApiException.showError(context, Exception('Erreur lors de la création du membre')); } } catch (e) { debugPrint('❌ Erreur création membre: $e'); if (mounted) { ApiException.showError(context, e); } } }, ), ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Stack( children: [ // Fond dégradé avec petits points blancs Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.white, Colors.red.shade300], ), ), child: CustomPaint( painter: DotsPainter(), child: const SizedBox(width: double.infinity, height: double.infinity), ), ), // Contenu principal Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Titre de la page Text( 'Mon amicale et ses membres', style: theme.textTheme.headlineMedium?.copyWith( color: theme.colorScheme.primary, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 24), // Message d'erreur si présent if (_errorMessage != null) Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: Colors.red.withOpacity(0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.red.withOpacity(0.3)), ), child: Row( children: [ const Icon(Icons.error_outline, color: Colors.red), const SizedBox(width: 12), Expanded( child: Text( _errorMessage!, style: const TextStyle(color: Colors.red), ), ), ], ), ), // Contenu principal avec ValueListenableBuilder if (_currentUser != null && _currentUser!.fkEntite != null) Expanded( child: ValueListenableBuilder>( valueListenable: widget.amicaleRepository.getAmicalesBox().listenable(), builder: (context, amicalesBox, child) { debugPrint('🔍 AmicalesBox - Nombre d\'amicales: ${amicalesBox.length}'); debugPrint('🔍 AmicalesBox - Clés disponibles: ${amicalesBox.keys.toList()}'); debugPrint('🔍 Recherche amicale avec fkEntite: ${_currentUser!.fkEntite}'); final amicale = amicalesBox.get(_currentUser!.fkEntite!); debugPrint('🔍 Amicale récupérée: ${amicale?.name ?? 'AUCUNE'}'); if (amicale == null) { // Ajouter plus d'informations de debug debugPrint('❌ PROBLÈME: Amicale non trouvée'); debugPrint('❌ fkEntite recherché: ${_currentUser!.fkEntite}'); debugPrint('❌ Contenu de la box: ${amicalesBox.values.map((a) => '${a.id}: ${a.name}').join(', ')}'); return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.business_outlined, size: 64, color: theme.colorScheme.primary.withOpacity(0.7), ), const SizedBox(height: 16), Text( 'Amicale non trouvée', style: theme.textTheme.titleLarge, ), const SizedBox(height: 8), Text( 'L\'amicale associée à votre compte n\'existe plus.\nfkEntite: ${_currentUser!.fkEntite}', textAlign: TextAlign.center, style: theme.textTheme.bodyLarge, ), ], ), ); } return ValueListenableBuilder>( valueListenable: widget.membreRepository.getMembresBox().listenable(), builder: (context, membresBox, child) { // Filtrer les membres par amicale // Note: Il faudra ajouter le champ fkEntite au modèle MembreModel final membres = membresBox.values.where((membre) => membre.fkEntite == _currentUser!.fkEntite).toList(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Section Amicale Text( 'Informations de l\'amicale', style: theme.textTheme.titleLarge?.copyWith( color: theme.colorScheme.primary, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), // Tableau Amicale Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: AmicaleTableWidget( amicales: [amicale], onEdit: null, onDelete: null, amicaleRepository: widget.amicaleRepository, userRepository: widget.userRepository, apiService: ApiService.instance, showActionsColumn: false, ), ), const SizedBox(height: 32), // Section Membres Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Membres de l\'amicale (${membres.length})', style: theme.textTheme.titleLarge?.copyWith( color: theme.colorScheme.primary, fontWeight: FontWeight.w600, ), ), ElevatedButton.icon( onPressed: _handleAddMembre, icon: const Icon(Icons.add), label: const Text('Ajouter un membre'), style: ElevatedButton.styleFrom( backgroundColor: theme.colorScheme.primary, foregroundColor: Colors.white, ), ), ], ), const SizedBox(height: 16), // Tableau Membres Expanded( child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: MembreTableWidget( membres: membres, onEdit: _handleEditMembre, onDelete: _handleDeleteMembre, membreRepository: widget.membreRepository, ), ), ), ], ); }, ); }, ), ), // Message si pas d'utilisateur connecté if (_currentUser == null) const Expanded( child: Center( child: CircularProgressIndicator(), ), ), ], ), ), ], ); } }