import 'package:flutter/material.dart'; import 'package:geosector_app/core/data/models/passage_model.dart'; import 'package:geosector_app/core/constants/app_keys.dart'; import 'package:geosector_app/core/utils/api_exception.dart'; import 'package:geosector_app/core/services/current_amicale_service.dart'; import 'package:geosector_app/app.dart'; class PassageMapDialog extends StatelessWidget { final PassageModel passage; final bool isAdmin; final VoidCallback? onDeleted; const PassageMapDialog({ super.key, required this.passage, this.isAdmin = false, this.onDeleted, }); @override Widget build(BuildContext context) { final int type = passage.fkType; // Récupérer le type de passage final String typePassage = AppKeys.typesPassages[type]?['titre'] ?? 'Inconnu'; final Color typeColor = Color(AppKeys.typesPassages[type]?['couleur1'] ?? 0xFF9E9E9E); // Construire l'adresse complète final String adresse = '${passage.numero} ${passage.rueBis} ${passage.rue}'.trim(); // Informations sur l'étage, l'appartement et la résidence (si habitat = 2) String? etageInfo; String? apptInfo; String? residenceInfo; if (passage.fkHabitat == 2) { if (passage.niveau.isNotEmpty) { etageInfo = 'Étage ${passage.niveau}'; } if (passage.appt.isNotEmpty) { apptInfo = 'Appt. ${passage.appt}'; } if (passage.residence.isNotEmpty) { residenceInfo = passage.residence; } } // Formater la date (uniquement si le type n'est pas 2 et si la date existe) String? dateInfo; if (type != 2 && passage.passedAt != null) { final date = passage.passedAt!; dateInfo = '${_formatDate(date)} à ${date.hour}h${date.minute.toString().padLeft(2, '0')}'; } // Récupérer le nom du passage (si le type n'est pas 6 - Maison vide) String? nomInfo; if (type != 6 && passage.name.isNotEmpty) { nomInfo = passage.name; } // Récupérer les informations de règlement si le type est 1 (Effectué) ou 5 (Lot) Widget? reglementInfo; if ((type == 1 || type == 5) && passage.fkTypeReglement > 0) { final int typeReglementId = passage.fkTypeReglement; final String montant = passage.montant; // Récupérer les informations du type de règlement if (AppKeys.typesReglements.containsKey(typeReglementId)) { final Map typeReglement = AppKeys.typesReglements[typeReglementId]!; final String titre = typeReglement['titre'] as String; final Color couleur = Color(typeReglement['couleur'] as int); final IconData iconData = typeReglement['icon_data'] as IconData; reglementInfo = Container( padding: const EdgeInsets.all(8), margin: const EdgeInsets.only(top: 8), decoration: BoxDecoration( color: couleur.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(4), border: Border.all(color: couleur.withValues(alpha: 0.3)), ), child: Row( children: [ Icon(iconData, color: couleur, size: 20), const SizedBox(width: 8), Text('$titre: $montant €', style: TextStyle(color: couleur, fontWeight: FontWeight.bold)), ], ), ); } } // Vérifier si l'utilisateur peut supprimer (admin ou user avec permission) bool canDelete = isAdmin; if (!isAdmin) { try { final amicale = CurrentAmicaleService.instance.currentAmicale; if (amicale != null) { canDelete = amicale.chkUserDeletePass == true; } } catch (e) { debugPrint('Erreur lors de la vérification des permissions: $e'); } } return AlertDialog( title: Row( children: [ Container( width: 10, height: 10, decoration: BoxDecoration( color: typeColor, shape: BoxShape.circle, ), ), const SizedBox(width: 8), Expanded( child: Text( 'Passage #${passage.id}', style: const TextStyle(fontSize: 18), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: typeColor.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(12), ), child: Text( typePassage, style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: typeColor, ), ), ), ], ), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Afficher en premier si le passage n'est pas affecté à un secteur if (passage.fkSector == null) ...[ Container( padding: const EdgeInsets.all(8), margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: Colors.red.withValues(alpha: 0.1), border: Border.all(color: Colors.red, width: 1), borderRadius: BorderRadius.circular(4), ), child: Row( children: [ const Icon(Icons.warning, color: Colors.red, size: 20), const SizedBox(width: 8), const Expanded( child: Text( 'Ce passage n\'est plus affecté à un secteur', style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold), ), ), ], ), ), ], // Adresse _buildInfoRow(Icons.location_on, 'Adresse', adresse.isEmpty ? 'Non renseignée' : adresse), // Résidence if (residenceInfo != null) _buildInfoRow(Icons.apartment, 'Résidence', residenceInfo), // Étage et appartement if (etageInfo != null || apptInfo != null) _buildInfoRow(Icons.stairs, 'Localisation', [etageInfo, apptInfo].where((e) => e != null).join(' - ')), // Date if (dateInfo != null) _buildInfoRow(Icons.calendar_today, 'Date', dateInfo), // Nom if (nomInfo != null) _buildInfoRow(Icons.person, 'Nom', nomInfo), // Ville if (passage.ville.isNotEmpty) _buildInfoRow(Icons.location_city, 'Ville', passage.ville), // Remarque if (passage.remarque.isNotEmpty) _buildInfoRow(Icons.note, 'Remarque', passage.remarque), // Règlement if (reglementInfo != null) reglementInfo, ], ), ), actions: [ // Bouton de suppression si autorisé if (canDelete) TextButton.icon( onPressed: () { Navigator.of(context).pop(); _showDeleteConfirmationDialog(context); }, icon: const Icon(Icons.delete, size: 20), label: const Text('Supprimer'), style: TextButton.styleFrom( foregroundColor: Colors.red, ), ), // Bouton de fermeture TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Fermer'), ), ], ); } // Helper pour construire une ligne d'information Widget _buildInfoRow(IconData icon, String label, String value) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 16, color: Colors.grey[600]), const SizedBox(width: 8), Expanded( child: RichText( text: TextSpan( style: const TextStyle(color: Colors.black87), children: [ TextSpan( text: '$label: ', style: const TextStyle(fontWeight: FontWeight.w600), ), TextSpan(text: value), ], ), ), ), ], ), ); } // Formater une date String _formatDate(DateTime date) { return '${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year}'; } // Afficher le dialog de confirmation de suppression void _showDeleteConfirmationDialog(BuildContext context) { final TextEditingController confirmController = TextEditingController(); final String streetNumber = passage.numero; final String fullAddress = '${passage.numero} ${passage.rueBis} ${passage.rue}'.trim(); showDialog( context: context, barrierDismissible: false, builder: (BuildContext dialogContext) { return AlertDialog( title: const Row( children: [ Icon(Icons.warning, color: Colors.red, size: 28), SizedBox(width: 8), Text('Confirmation de suppression'), ], ), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'ATTENTION : Cette action est irréversible !', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.red, fontSize: 16, ), ), const SizedBox(height: 16), Text( 'Vous êtes sur le point de supprimer définitivement le passage :', style: TextStyle(color: Colors.grey[800]), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey[300]!), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( fullAddress.isEmpty ? 'Adresse inconnue' : fullAddress, style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 14, ), ), if (passage.ville.isNotEmpty) ...[ const SizedBox(height: 4), Text( passage.ville, style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ], ), ), const SizedBox(height: 20), const Text( 'Pour confirmer la suppression, veuillez saisir le numéro de rue de ce passage :', style: TextStyle(fontWeight: FontWeight.w500), ), const SizedBox(height: 12), TextField( controller: confirmController, decoration: InputDecoration( labelText: 'Numéro de rue', hintText: streetNumber.isNotEmpty ? 'Ex: $streetNumber' : 'Saisir le numéro', border: const OutlineInputBorder(), prefixIcon: const Icon(Icons.home), ), keyboardType: TextInputType.text, textCapitalization: TextCapitalization.characters, ), ], ), ), actions: [ TextButton( onPressed: () { confirmController.dispose(); Navigator.of(dialogContext).pop(); }, child: const Text('Annuler'), ), ElevatedButton( onPressed: () async { // Vérifier que le numéro saisi correspond final enteredNumber = confirmController.text.trim(); if (enteredNumber.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Veuillez saisir le numéro de rue'), backgroundColor: Colors.orange, ), ); return; } if (streetNumber.isNotEmpty && enteredNumber.toUpperCase() != streetNumber.toUpperCase()) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Le numéro de rue ne correspond pas'), backgroundColor: Colors.red, ), ); return; } // Fermer le dialog confirmController.dispose(); Navigator.of(dialogContext).pop(); // Effectuer la suppression await _deletePassage(context); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: const Text('Supprimer définitivement'), ), ], ); }, ); } // Supprimer un passage Future _deletePassage(BuildContext context) async { try { // Appeler le repository pour supprimer via l'API final success = await passageRepository.deletePassageViaApi(passage.id); if (success && context.mounted) { ApiException.showSuccess(context, 'Passage supprimé avec succès'); // Appeler le callback si fourni onDeleted?.call(); } else if (context.mounted) { ApiException.showError( context, Exception('Erreur lors de la suppression')); } } catch (e) { debugPrint('Erreur suppression passage: $e'); if (context.mounted) { ApiException.showError(context, e); } } } }