feat: Mise à jour des interfaces mobiles v3.2.3

- Amélioration des interfaces utilisateur sur mobile
- Optimisation de la responsivité des composants Flutter
- Mise à jour des widgets de chat et communication
- Amélioration des formulaires et tableaux
- Ajout de nouveaux composants pour l'administration
- Optimisation des thèmes et styles visuels

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-02 20:35:40 +02:00
parent 08f4bff358
commit 43d4cd66e1
2133 changed files with 237004 additions and 173303 deletions

View File

@@ -1,7 +1,6 @@
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/repositories/passage_repository.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';
@@ -10,24 +9,27 @@ 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);
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();
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;
@@ -49,7 +51,8 @@ class PassageMapDialog extends StatelessWidget {
String? dateInfo;
if (type != 2 && passage.passedAt != null) {
final date = passage.passedAt!;
dateInfo = '${_formatDate(date)} à ${date.hour}h${date.minute.toString().padLeft(2, '0')}';
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)
@@ -66,7 +69,8 @@ class PassageMapDialog extends StatelessWidget {
// Récupérer les informations du type de règlement
if (AppKeys.typesReglements.containsKey(typeReglementId)) {
final Map<String, dynamic> typeReglement = AppKeys.typesReglements[typeReglementId]!;
final Map<String, dynamic> 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;
@@ -75,22 +79,23 @@ class PassageMapDialog extends StatelessWidget {
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.only(top: 8),
decoration: BoxDecoration(
color: couleur.withOpacity(0.1),
color: couleur.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: couleur.withOpacity(0.3)),
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)),
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) {
@@ -125,7 +130,7 @@ class PassageMapDialog extends StatelessWidget {
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: typeColor.withOpacity(0.2),
color: typeColor.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(12),
),
child: Text(
@@ -150,7 +155,7 @@ class PassageMapDialog extends StatelessWidget {
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
color: Colors.red.withValues(alpha: 0.1),
border: Border.all(color: Colors.red, width: 1),
borderRadius: BorderRadius.circular(4),
),
@@ -161,42 +166,43 @@ class PassageMapDialog extends StatelessWidget {
const Expanded(
child: Text(
'Ce passage n\'est plus affecté à un secteur',
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
style: TextStyle(
color: Colors.red, fontWeight: FontWeight.bold),
),
),
],
),
),
],
// Adresse
_buildInfoRow(Icons.location_on, 'Adresse', adresse.isEmpty ? 'Non renseignée' : 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(' - ')),
_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),
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,
],
@@ -224,7 +230,7 @@ class PassageMapDialog extends StatelessWidget {
],
);
}
// Helper pour construire une ligne d'information
Widget _buildInfoRow(IconData icon, String label, String value) {
return Padding(
@@ -252,18 +258,19 @@ class PassageMapDialog extends StatelessWidget {
),
);
}
// 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();
final String streetNumber = passage.numero;
final String fullAddress =
'${passage.numero} ${passage.rueBis} ${passage.rue}'.trim();
showDialog(
context: context,
barrierDismissible: false,
@@ -335,7 +342,9 @@ class PassageMapDialog extends StatelessWidget {
controller: confirmController,
decoration: InputDecoration(
labelText: 'Numéro de rue',
hintText: streetNumber.isNotEmpty ? 'Ex: $streetNumber' : 'Saisir le numéro',
hintText: streetNumber.isNotEmpty
? 'Ex: $streetNumber'
: 'Saisir le numéro',
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.home),
),
@@ -366,8 +375,9 @@ class PassageMapDialog extends StatelessWidget {
);
return;
}
if (streetNumber.isNotEmpty && enteredNumber.toUpperCase() != streetNumber.toUpperCase()) {
if (streetNumber.isNotEmpty &&
enteredNumber.toUpperCase() != streetNumber.toUpperCase()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Le numéro de rue ne correspond pas'),
@@ -376,11 +386,11 @@ class PassageMapDialog extends StatelessWidget {
);
return;
}
// Fermer le dialog
confirmController.dispose();
Navigator.of(dialogContext).pop();
// Effectuer la suppression
await _deletePassage(context);
},
@@ -395,20 +405,21 @@ class PassageMapDialog extends StatelessWidget {
},
);
}
// Supprimer un passage
Future<void> _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'));
ApiException.showError(
context, Exception('Erreur lors de la suppression'));
}
} catch (e) {
debugPrint('Erreur suppression passage: $e');
@@ -417,4 +428,4 @@ class PassageMapDialog extends StatelessWidget {
}
}
}
}
}