Files
geo/app/docs/README-APP.md
Pierre 242a90720e feat: Début des évolutions interfaces mobiles v3.2.4
- Préparation de la nouvelle branche pour les évolutions
- Mise à jour de la version vers 3.2.4
- Intégration des modifications en cours

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-04 16:49:29 +02:00

68 KiB
Executable File
Raw Blame History

GEOSECTOR v3.2.4

🚒 Application de gestion des distributions de calendriers par secteurs géographiques pour les amicales de pompiers


🎯 Vue d'ensemble

GEOSECTOR est une solution complète développée en Flutter qui révolutionne la gestion des campagnes de distribution de calendriers pour les amicales de pompiers. L'application combine géolocalisation, gestion multi-rôles et synchronisation en temps réel pour optimiser les tournées et maximiser l'efficacité des équipes.

🏆 Points forts de la v3.2.4

  • Architecture moderne sans Provider, basée sur l'injection de dépendances
  • Réactivité native avec ValueListenableBuilder et Hive
  • Interface adaptative selon les rôles utilisateur et la taille d'écran
  • Performance optimisée avec un ApiService singleton et cache Hive
  • Gestion avancée des permissions multi-niveaux
  • Gestion d'erreurs centralisée avec ApiException
  • Interface utilisateur épurée avec suppression des titres superflus
  • Chat responsive avec layout adaptatif mobile/desktop
  • Système de filtrage centralisé dans PassagesListWidget
  • Intégration Stripe Connect pour les paiements des amicales

📋 Table des matières

  1. Fonctionnalités
  2. Architecture technique
  3. Installation
  4. Modèles de données
  5. Architecture des composants
  6. Gestion des rôles
  7. Interface utilisateur
  8. API et synchronisation
  9. Gestion des erreurs
  10. Cartes et géolocalisation

🚀 Fonctionnalités

🎯 Fonctionnalités métier

Pour les Membres (Rôle 1)

  • Visualisation des secteurs assignés sur carte interactive
  • Suivi GPS en temps réel des tournées
  • Enregistrement des passages avec géolocalisation
  • Gestion des stocks de calendriers
  • Historique des distributions
  • Chat intégré avec l'équipe

Pour les Admins Amicale (Rôle 2)

  • Gestion de leur amicale (informations, coordonnées)
  • Gestion des membres de l'amicale (création, modification, suppression)
  • Attribution des rôles aux membres (Membre/Administrateur)
  • Gestion du statut actif des comptes membres
  • Consultation des statistiques de l'amicale
  • Attribution des secteurs aux membres
  • Suivi des performances équipe

Pour les Super Admins (Rôle 3+)

  • Gestion globale multi-amicales
  • Administration des utilisateurs et permissions
  • Configuration des paramètres système
  • Analytics avancées et reporting
  • Gestion des secteurs géographiques

🔧 Fonctionnalités techniques

  • 🗺️ Cartographie avancée : Flutter Map avec tuiles Mapbox
  • 📍 Géolocalisation précise : Suivi GPS des équipes
  • 💾 Stockage hybride : Cache local Hive + synchronisation cloud avec optimisation des performances
  • 💬 Communication : Chat MQTT en temps réel
  • 🔐 Sécurité : Authentification JWT + gestion fine des permissions
  • 📱 Multi-plateforme : iOS, Android, Web
  • 🌐 Mode hors-ligne : Fonctionnement dégradé sans connexion
  • Gestion d'erreurs robuste : Extraction automatique des messages API
  • 🎨 Interface responsive : Adaptation automatique selon la taille d'écran

🏗️ Architecture technique

Stack technologique

Composant Technologie Version Usage
Framework Flutter 3.32+ Interface multi-plateforme
Langage Dart 3.0+ Logique applicative
Navigation GoRouter 12.1.3 Routing déclaratif
Stockage local Hive 2.2.3 Base de données NoSQL locale
Réactivité ValueListenableBuilder Native Écoute des changements Hive
HTTP Dio 5.4.0 Client HTTP avec intercepteurs
Cartes Flutter Map 6.1.0 Rendu cartographique
Géolocalisation Geolocator 10.1.0 Services de localisation
Chat MQTT5 Client 4.2.0 Messagerie temps réel
UI Material Design 3 Native Composants d'interface
Logging LoggerService Custom Logs conditionnels par env

🏛️ Architecture en couches

graph TD
    A[UI Layer - Widgets] --> B[Repository Layer - Business Logic]
    B --> C[Data Layer - Hive + API]

    A1[ValueListenableBuilder] --> A
    A2[Custom Widgets] --> A
    A3[UserFormDialog] --> A

    B1[UserRepository] --> B
    B2[AmicaleRepository] --> B
    B3[MembreRepository] --> B

    C1[Hive Boxes] --> C
    C2[API Service Singleton] --> C
    C3[ApiException Handler] --> C

📁 Structure du projet

app/
├── lib/
│   ├── core/                          # Couche centrale
│   │   ├── constants/                 # Constantes globales
│   │   │   ├── app_keys.dart         # Clés des Box Hive
│   │   │   └── api_endpoints.dart    # Endpoints API
│   │   ├── data/
│   │   │   └── models/               # Modèles Hive
│   │   │       ├── user_model.dart   # @HiveType(typeId: 3)
│   │   │       ├── amicale_model.dart # @HiveType(typeId: 4)
│   │   │       └── membre_model.dart  # @HiveType(typeId: 5)
│   │   ├── repositories/             # Logique métier
│   │   │   ├── user_repository.dart
│   │   │   ├── amicale_repository.dart
│   │   │   └── membre_repository.dart
│   │   ├── services/                 # Services externes
│   │   │   ├── api_service.dart      # HTTP Singleton
│   │   │   ├── chat_service.dart     # MQTT
│   │   │   └── location_service.dart # GPS
│   │   └── utils/                    # Utilitaires
│   │       ├── validators.dart
│   │       └── formatters.dart
│   ├── presentation/                  # Interface utilisateur
│   │   ├── admin/                    # Pages administrateur
│   │   │   ├── admin_dashboard_page.dart
│   │   │   ├── admin_amicale_page.dart
│   │   │   └── admin_statistics_page.dart
│   │   ├── user/                     # Pages utilisateur
│   │   │   ├── user_dashboard_page.dart
│   │   │   ├── map_page.dart
│   │   │   └── distribution_page.dart
│   │   ├── widgets/                  # Composants réutilisables
│   │   │   ├── tables/
│   │   │   │   ├── amicale_table_widget.dart
│   │   │   │   ├── amicale_row_widget.dart
│   │   │   │   ├── membre_table_widget.dart
│   │   │   │   └── membre_row_widget.dart
│   │   │   ├── forms/
│   │   │   │   ├── amicale_form.dart
│   │   │   │   └── custom_text_field.dart
│   │   │   └── common/
│   │   │       ├── dashboard_layout.dart
│   │   │       └── loading_widget.dart
│   │   └── theme/
│   │       └── app_theme.dart
│   ├── app.dart                      # Configuration app
│   └── main.dart                     # Point d'entrée
├── assets/                           # Ressources statiques
│   ├── images/
│   ├── icons/
│   └── fonts/
├── test/                            # Tests unitaires
├── integration_test/                # Tests d'intégration
└── docs/                           # Documentation


🚀 Installation et configuration

Prérequis système

  • Flutter SDK : 3.32 ou supérieur
  • Dart SDK : 3.0 ou supérieur
  • IDE : Android Studio, VS Code, ou IntelliJ
  • Environnement :
    • Android : SDK 21+ (Android 5.0+)
    • iOS : iOS 12.0+
    • Web : Navigateurs modernes

🔐 Configuration des clés API

Mapbox (Cartographie)

  1. Créer un compte sur Mapbox
  2. Générer un token d'accès
  3. Ajouter le token dans .env

Configuration MQTT (Chat)

  1. Configurer votre broker MQTT
  2. Créer les credentials
  3. Tester la connexion

🗄️ Modèles de données

Registres Hive des adaptateurs

// Modèles principaux
UserModelAdapter()           // typeId: 0
OperationModelAdapter()      // typeId: 1
SectorModelAdapter()         // typeId: 3
PassageModelAdapter()        // typeId: 4
MembreModelAdapter()         // typeId: 5
UserSectorModelAdapter()     // typeId: 6
RegionModelAdapter()         // typeId: 7
ClientModelAdapter()         // typeId: 10
AmicaleModelAdapter()        // typeId: 11

// Modèles de chat
ConversationModelAdapter()   // typeId: 20
MessageModelAdapter()        // typeId: 21
ParticipantModelAdapter()    // typeId: 22
AnonymousUserModelAdapter() // typeId: 23
AudienceTargetModelAdapter() // typeId: 24
NotificationSettingsAdapter() // typeId: 25

Clarification importante : UserModel vs MembreModel vs UserSectorModel

⚠️ ATTENTION : Il existe une distinction cruciale entre ces trois modèles :

UserModel (Box: users)

  • Représente uniquement l'utilisateur courant connecté (current user)
  • Stocké dans la box Hive users qui ne contient qu'un seul enregistrement
  • Utilisé pour l'authentification et la session de l'utilisateur actuel
  • Ne pas confondre avec les membres de l'amicale

MembreModel (Box: membres)

  • Représente tous les membres d'une amicale
  • Stocké dans la box Hive membres qui contient plusieurs enregistrements
  • Utilisé pour la gestion des équipes et l'attribution aux secteurs
  • Chaque membre a son propre ID unique

UserSectorModel (Box: user_sector)

  • Représente l'association entre un membre et un secteur
  • ⚠️ IMPORTANT : Le champ id dans UserSectorModel correspond à l'ID du membre (MembreModel.id), PAS à l'ID de l'utilisateur (UserModel.id)
  • Permet de savoir quels membres sont affectés à quels secteurs
  • Nom trompeur : devrait s'appeler "MemberSectorModel" pour éviter la confusion

Compatibilité entre modèles

  • UserModel ↔ MembreModel : Conversion bidirectionnelle via toUserModel() et fromUserModel()
  • Synchronisation : Maintien de la cohérence entre les deux représentations
  • Champs spécialisés : Préservation des données spécifiques à chaque modèle

🎨 Interface utilisateur

📱 Améliorations v3.x - Interface épurée et responsive

🎯 Simplification des titres de pages

La v3.1.0 a apporté une refonte majeure de l'interface pour maximiser l'espace utile et améliorer l'expérience utilisateur sur tous les écrans :

Pages avec titres supprimés :

  • user_history_page.dart : Historique des passages
  • user_statistics_page.dart : Statistiques
  • user_map_page.dart : Carte des passages
  • admin_history_page.dart : Historique admin
  • admin_statistics_page.dart : Statistiques admin
  • chat_communication_page.dart : Interface de chat

Pages avec titres conservés mais optimisés :

  • user_dashboard_home_page.dart : Titre responsive (taille réduite de 28 à 20)
  • admin_dashboard_home_page.dart : Titre réduit (de headlineSmall à titleLarge) + suppression icône refresh

💬 Chat responsive adaptatif

Le module de chat (rooms_page_embedded.dart) s'adapte automatiquement à la taille d'écran :

Desktop (>900px) :

  • Layout horizontal : Rooms à gauche (300px), Messages à droite
  • Navigation fluide entre les conversations

Mobile (<900px) :

  • Layout vertical : Rooms en haut (30% hauteur), Messages en bas
  • Hauteur adaptative avec contraintes (200-350px)
  • Optimisation pour les écrans tactiles

🗺️ Carte avec filtres intégrés

La carte des passages (user_map_page.dart) a été repensée :

  • Carte plein écran : Utilisation maximale de l'espace disponible
  • Filtres en overlay : 6 pastilles colorées en bas à gauche
  • Design minimaliste :
    • Pastille vive = filtre actif
    • Pastille semi-transparente (alpha 0.3) = filtre inactif
    • Sans labels pour économiser l'espace
    • Container blanc arrondi avec ombre pour regrouper les pastilles

Architecture des composants

UserFormDialog - Modale unifiée Réutilisabilité : Même widget pour "Mon Compte" et "Gestion des Membres" Personnalisation contextuelle : Sélecteur de rôle (Membre/Administrateur) Checkbox statut actif/inactif Édition du nom d'utilisateur selon le contexte Gestion du nom de tournée (sectName) Interface responsive : Adaptation automatique selon la largeur d'écran UserForm - Formulaire intelligent Layout adaptatif :

900px : Champs groupés en lignes (username+email, prénom+nom, téléphones, dates) ≤ 900px : Champs empilés verticalement Validation conditionnelle : Au moins nom OU nom de tournée requis Champs dynamiques : Affichage selon les permissions et le contexte Indicateurs visuels : Points rouges sur les champs obligatoires Tableaux interactifs AmicaleTableWidget : Gestion des amicales avec édition inline MembreTableWidget : Gestion des membres avec actions contextuelles Alternance de couleurs : Amélioration de la lisibilité Clic sur ligne : Ouverture directe du formulaire d'édition

🔗 API et synchronisation

Principe "API First"

Flow de mise à jour

Validation API : Tentative de mise à jour sur le serveur Succès → Sauvegarde locale avec isSynced: true Erreur → Aucune modification locale + affichage de l'erreur

Avantages

Cohérence des données : Local toujours synchronisé avec le serveur Gestion d'erreurs propre : Pas de conflits entre données locales et distantes UX claire : Feedback immédiat sur les erreurs de validation

ApiService Singleton

  • Thread-safe : Initialisation sécurisée avec verrous
  • Auto-configuration : Détection automatique de l'environnement (DEV/REC/PROD)
  • Gestion de session : Headers d'authentification automatiques
  • Retry logic : Nouvelles tentatives pour les erreurs réseau

⚠️ Gestion des erreurs

🎯 Système ApiException intelligent

GEOSECTOR v3.x utilise un système centralisé de gestion des messages qui s'adapte automatiquement au contexte d'affichage pour garantir une visibilité optimale des notifications utilisateur.

🧠 Détection automatique de contexte

L'ApiException détecte intelligemment si elle est appelée depuis une Dialog et adapte l'affichage :

  • 📱 Contexte normal : SnackBar standard en bas d'écran
  • 💬 Contexte Dialog : Overlay SnackBar positionné au-dessus de la Dialog estompée
  • 🌐 Mobile/Web : Adaptation automatique selon la plateforme
  • 🎨 Cohérence visuelle : Couleurs, icônes et comportements unifiés
// Même API partout - détection intelligente du contexte
void _handleValidation() {
  if (formInvalid) {
    // Dans une Dialog : overlay au-dessus, Dialog reste ouverte
    ApiException.showError(context, Exception("Champs requis manquants"));
    return;
  }
  
  // Succès : fermer Dialog puis afficher confirmation
  Navigator.pop(context);
  ApiException.showSuccess(context, "Données sauvegardées");
}

Avantages de l'approche unifiée

Aspect Avant Avec ApiException
Visibilité SnackBar masqué par Dialog Overlay visible au-dessus
Consistance Messages dispersés API unifiée dans toute l'app
Maintenance Code répétitif Système centralisé
UX Frustrant (messages cachés) Fluide et prévisible

Architecture centralisée

sequenceDiagram
    participant UI as dashboard_app_bar.dart
    participant UR as user_repository.dart
    participant AS as api_service.dart
    participant API as API Server
    participant AE as ApiException
    participant EU as ErrorUtils

    Note over UI: Utilisateur clique "Enregistrer"
    UI->>UR: updateUser(updatedUser)

    Note over UR: Tente la mise à jour
    UR->>AS: updateUser(user)

    Note over AS: Appel HTTP PUT
    AS->>API: PUT /users/123 {email: "test@test.com"}

    alt Succès API
        API-->>AS: 200 OK {user data}
        AS-->>UR: UserModel (mis à jour)
        UR-->>UI: UserModel (succès)
        UI->>EU: showSuccessSnackBar()
        Note over UI: ✅ "Profil mis à jour"

    else Erreur API (ex: email déjà utilisé)
        API-->>AS: 409 Conflict {"message": "Cet email est déjà utilisé"}

        Note over AS: Conversion en ApiException
        AS->>AE: ApiException.fromDioException(dioError)
        AE-->>AS: ApiException("Cet email est déjà utilisé")
        AS-->>UR: throw ApiException
        UR-->>UI: throw ApiException

        Note over UI: Gestion de l'erreur
        UI->>EU: showErrorSnackBar(context, exception)
        EU->>AE: extractErrorMessage(exception)
        AE-->>EU: "Cet email est déjà utilisé"
        Note over UI: ❌ "Erreur: Cet email est déjà utilisé"
        Note over UI: Dialog reste ouvert

    else Erreur réseau
        API-->>AS: Network Error / Timeout
        AS->>AE: ApiException.fromDioException(networkError)
        AE-->>AS: ApiException("Problème de connexion réseau")
        AS-->>UR: throw ApiException
        UR-->>UI: throw ApiException
        UI->>EU: showErrorSnackBar(context, exception)
        Note over UI: ❌ "Problème de connexion réseau"
    end

Composants de gestion d'erreurs

ApiException

Extraction intelligente : Messages spécifiques depuis la réponse API Codes HTTP standardisés : Mapping automatique des erreurs communes Types d'erreurs : Classification (validation, authentification, réseau, conflit) Méthodes d'affichage : showError() et showSuccess() intégrées

Responsabilités par couche

ApiService : Conversion des erreurs Dio en ApiException Repository : Propagation transparente des erreurs Interface : Affichage utilisateur via ApiException.showError()

Messages d'erreurs contextuels

409 Conflict : "Cet email est déjà utilisé par un autre utilisateur" 400 Bad Request : "Données invalides" 401 Unauthorized : "Non autorisé : veuillez vous reconnecter" 500 Server Error : "Erreur serveur interne" Network Errors : "Problème de connexion réseau" Timeout : "Délai d'attente dépassé"

🔧 Pattern de gestion des erreurs API dans les Repositories

🎯 Problème à résoudre

Les messages d'erreur spécifiques de l'API (comme "Cet email est déjà utilisé") n'étaient pas affichés à l'utilisateur. À la place, un message générique "Erreur inattendue" apparaissait.

📝 Modifications requises

1. ApiService - Conversion automatique des DioException

Toutes les méthodes HTTP génériques doivent convertir les DioException en ApiException :

// ✅ CORRECT - ApiService avec conversion automatique
Future<Response> put(String path, {dynamic data}) async {
  try {
    return await _dio.put(path, data: data);
  } on DioException catch (e) {
    throw ApiException.fromDioException(e); // ← Extraction automatique du message
  } catch (e) {
    if (e is ApiException) rethrow;
    throw ApiException('Erreur inattendue lors de la requête PUT', originalError: e);
  }
}

Appliquer le même pattern pour post(), get(), delete().

2. Repository - Simplification de la gestion des erreurs

// ❌ INCORRECT - Code inutile qui ne sera jamais exécuté
Future<bool> updateMembre(MembreModel membre) async {
  try {
    final response = await ApiService.instance.put('/users/${membre.id}', data: data);
    
    if (response.statusCode == 200) {
      await saveMembreBox(membre);
      return true;
    }
    
    // ⚠️ CE CODE NE SERA JAMAIS ATTEINT car Dio lance une exception pour les codes d'erreur
    if (response.data != null && response.data is Map<String, dynamic>) {
      final responseData = response.data as Map<String, dynamic>;
      if (responseData['status'] == 'error') {
        throw Exception(responseData['message']);
      }
    }
    return false;
  } catch (e) {
    rethrow;
  }
}
// ✅ CORRECT - Code simplifié et fonctionnel
Future<bool> updateMembre(MembreModel membre) async {
  try {
    final response = await ApiService.instance.put('/users/${membre.id}', data: data);
    
    // Si on arrive ici, c'est que la requête a réussi (200)
    await saveMembreBox(membre);
    return true;
    
  } catch (e) {
    // L'ApiException contient déjà le message extrait de l'API
    rethrow; // Propager l'exception pour affichage
  }
}

3. Amélioration des logs (avec LoggerService)

// ✅ CORRECT - Logs propres sans détails techniques
catch (e) {
  // Ne pas logger les détails techniques de DioException
  if (e is ApiException) {
    LoggerService.error('Erreur lors de la mise à jour: ${e.message}');
  } else {
    LoggerService.error('Erreur lors de la mise à jour');
  }
  rethrow;
}

N'oubliez pas d'importer ApiException :

import 'package:geosector_app/core/utils/api_exception.dart';

🔄 Flux d'erreur corrigé

sequenceDiagram
    participant API as API Server
    participant Dio as Dio Client  
    participant AS as ApiService
    participant AE as ApiException
    participant R as Repository
    participant UI as Interface

    UI->>R: updateData()
    R->>AS: put('/endpoint')
    AS->>Dio: HTTP PUT
    Dio->>API: Request
    
    alt Code d'erreur (409, 400, etc.)
        API-->>Dio: Error + JSON body
        Note over API: {"status": "error",<br/>"message": "Message spécifique"}
        
        Dio-->>AS: DioException
        AS->>AE: fromDioException()
        Note over AE: Extrait message<br/>depuis response.data
        AE-->>AS: ApiException("Message spécifique")
        AS-->>R: throw ApiException
        R-->>UI: throw ApiException
        UI->>UI: showError("Message spécifique")
    else Succès (200, 201)
        API-->>Dio: Success
        Dio-->>AS: Response
        AS-->>R: Response
        R->>R: Sauvegarde Hive
        R-->>UI: return true
    end

Checklist de migration

Pour chaque repository :

  • Vérifier que l'ApiService convertit les DioException en ApiException
  • Simplifier le code : supprimer les vérifications de statut après l'appel API
  • Propager les exceptions avec rethrow
  • Améliorer les logs pour ne pas afficher les détails techniques
  • Importer ApiException si nécessaire
  • Tester avec une erreur 409 pour vérifier l'affichage du message

📊 Résultat attendu

Avant Après
"Erreur inattendue" "Cet email est déjà utilisé par un autre utilisateur"
Logs avec stack trace Dio Message d'erreur simple et clair
Code complexe avec vérifications inutiles Code simplifié et maintenable

Cette approche garantit que tous les messages d'erreur de l'API sont correctement affichés à l'utilisateur, améliorant ainsi l'expérience utilisateur et facilitant le débogage.

📝 Service de Logging Intelligent

🎯 Vue d'ensemble

GEOSECTOR v3.x implémente un LoggerService centralisé qui désactive automatiquement les logs de debug en production, optimisant ainsi les performances et la sécurité tout en facilitant le développement.

🔍 Détection automatique de l'environnement

Le LoggerService détecte automatiquement l'environnement d'exécution :

// Détection basée sur l'URL pour le web
if (currentUrl.contains('dapp.geosector.fr'))  DEV
if (currentUrl.contains('rapp.geosector.fr'))  REC
Sinon  PROD

// Pour mobile/desktop : utilise kReleaseMode de Flutter

Comportement par environnement :

Environnement Logs Debug Logs Erreur Stack Traces
DEV Activés Activés Complètes
REC Activés Activés Complètes
PROD Désactivés Activés Masquées

🛠️ API du LoggerService

Méthodes principales

// Remplacement direct de debugPrint
LoggerService.log('Message simple');

// Logs catégorisés avec emojis automatiques
LoggerService.info('Information');           // 
LoggerService.success('Opération réussie');  // ✅
LoggerService.warning('Attention');          // ⚠️
LoggerService.error('Erreur', exception);    // ❌ (toujours affiché)
LoggerService.debug('Debug', emoji: '🔧');   // 🔧

// Logs spécialisés
LoggerService.api('Requête API envoyée');    // 🔗
LoggerService.database('Box Hive ouverte');  // 💾
LoggerService.navigation('Route: /admin');   // 🧭
LoggerService.performance('Temps: 45ms');    // ⏱️

Fonctionnalités avancées

// Logs groupés pour améliorer la lisibilité
LoggerService.group('Traitement utilisateur', [
  'Validation des données',
  'Appel API',
  'Sauvegarde locale',
  'Notification envoyée'
]);
// Affiche :
// ┌─ Traitement utilisateur
// ├─ Validation des données
// ├─ Appel API
// ├─ Sauvegarde locale
// └─ Notification envoyée

// Log JSON formaté
LoggerService.json('Payload API', {
  'id': 123,
  'name': 'Test',
  'active': true
});
// Affiche :
// 📋 Payload API:
//    id: 123
//    name: Test
//    active: true

// Log conditionnel
LoggerService.conditional(
  'Debug spécifique',
  condition: user.role > 2
);

🔄 Migration depuis debugPrint

Avant (debugPrint partout)

debugPrint('🔄 Début de la création d\'un nouveau membre');
debugPrint('📤 Données envoyées à l\'API: $data');
debugPrint('❌ Erreur: $e');

Après (LoggerService)

LoggerService.info('Début de la création d\'un nouveau membre');
LoggerService.api('Données envoyées à l\'API: $data');
LoggerService.error('Erreur lors de la création', e);

📋 Utilisation avec les extensions

Pour une syntaxe encore plus concise, utilisez les extensions String :

// Import nécessaire
import 'package:geosector_app/core/services/logger_service.dart';

// Utilisation directe sur les strings
'Connexion réussie'.logSuccess();
'Erreur de validation'.logError(exception);
'Requête GET /users'.logApi();
'Box membres ouverte'.logDatabase();

🎯 Bonnes pratiques

1. Catégorisation des logs

// ✅ BON - Log catégorisé et clair
LoggerService.api('POST /users - Création membre');
LoggerService.database('Sauvegarde dans Box membres');

// ❌ MAUVAIS - Log générique sans contexte
debugPrint('Traitement en cours...');

2. Gestion des erreurs

try {
  await operation();
  LoggerService.success('Opération terminée');
} catch (e, stackTrace) {
  // Les erreurs sont TOUJOURS loggées, même en PROD
  LoggerService.error('Échec de l\'opération', e, stackTrace);
}

3. Logs de performance

final stopwatch = Stopwatch()..start();
await heavyOperation();
stopwatch.stop();
LoggerService.performance('Opération lourde: ${stopwatch.elapsedMilliseconds}ms');

🔒 Sécurité et performances

Avantages en production

  1. Sécurité : Aucune information sensible exposée dans la console
  2. Performance : Pas d'appels inutiles à debugPrint
  3. Taille : Bundle JavaScript plus léger (tree shaking)
  4. Professionnalisme : Console propre pour les utilisateurs finaux

Conservation des logs d'erreur

Les erreurs restent visibles en production pour faciliter le support :

// Toujours affiché, même en PROD
LoggerService.error('Erreur critique détectée', error);
// Mais sans la stack trace complète qui pourrait révéler
// des détails d'implémentation

📊 Impact sur le codebase

Métrique Avant Après
Logs en PROD Tous visibles Erreurs uniquement
Performance web debugPrint actifs Désactivés automatiquement
Maintenance debugPrint dispersés Service centralisé
Lisibilité Emojis manuels Catégorisation automatique

🚀 Configuration et initialisation

Le LoggerService fonctionne automatiquement sans configuration :

// main.dart - Aucune initialisation requise
void main() async {
  // LoggerService détecte automatiquement l'environnement
  // via ApiService.getCurrentEnvironment()
  
  runApp(GeosectorApp());
}

// Utilisation immédiate dans n'importe quel fichier
LoggerService.info('Application démarrée');

Cette architecture garantit un système de logging professionnel, sécurisé et performant, adapté aux besoins de développement tout en protégeant la production. 📝

🔐 Gestion des identifiants - Normes NIST SP 800-63B

🎯 Vue d'ensemble

GEOSECTOR v3.x implémente les normes NIST SP 800-63B pour la gestion des identifiants (usernames et passwords), offrant une sécurité renforcée tout en améliorant l'expérience utilisateur avec des règles plus flexibles et modernes.

📋 Conformité NIST SP 800-63B

Exigence NIST Implémentation Statut
Longueur minimale : 8 caractères MIN = 8 caractères CONFORME
Longueur maximale : 64 caractères minimum MAX = 64 caractères CONFORME
Accepter TOUS les caractères ASCII imprimables Aucune restriction sur les caractères CONFORME
Accepter les espaces Espaces acceptés (début, milieu, fin) CONFORME
Accepter Unicode (émojis, accents, etc.) Support UTF-8 complet CONFORME
Vérifier contre les mots de passe compromis API Have I Been Pwned avec k-anonymity CONFORME
Pas d'obligation de composition Pas d'erreur si manque majuscules/chiffres/spéciaux CONFORME
Pas de changement périodique forcé Aucune expiration automatique CONFORME
Permettre les phrases de passe "Mon chat Félix a 3 ans!" accepté CONFORME

🔑 Règles pour les identifiants

Username (Nom d'utilisateur)

  • Longueur : 8 à 64 caractères
  • Caractères acceptés : TOUS (lettres, chiffres, espaces, accents, symboles, emojis)
  • Exemples valides :
    • jean dupont 75
    • Marie-Claire.2024
    • Pompier Paris 🚒
    • utilisateur@amicale

Password (Mot de passe)

  • Longueur : 8 à 64 caractères
  • Aucune règle de composition obligatoire (plus besoin de majuscules/minuscules/chiffres/spéciaux)
  • Phrases de passe recommandées pour une meilleure mémorisation
  • Exemples valides :
    • Mon chat Félix a 3 ans!
    • J'aime les pizzas du vendredi soir
    • Le camion rouge part à 8h30
    • ☀️ Soleil brillant sur Paris ☀️

🎲 Générateurs intelligents

Générateur de username

Crée des noms d'utilisateur uniques basés sur :

  • Nom/prénom de la personne
  • Code postal et ville de l'amicale
  • Numéro aléatoire pour l'unicité
  • Peut inclure des espaces et séparateurs (., -, _)

Générateur de phrases de passe

Génère des phrases de passe mémorables en français :

  • Phrases naturelles et faciles à retenir
  • Combinaisons variées (sujets, verbes, compléments)
  • Ajout optionnel de caractères spéciaux ou emojis
  • Exemples générés :
    • Le chien Max danse dans le jardin!
    • Mon vélo rouge vole 42 fois en été
    • Luna a 7 ans!☀️

⚠️ Points importants

  1. Pas de trim() : Les espaces en début/fin sont préservés et font partie de l'identifiant
  2. Pas de vérification password == username : Sur demande du client, cette règle a été retirée
  3. Validation côté API : L'API vérifie les mots de passe contre la base Have I Been Pwned
  4. Rétrocompatibilité : Les anciens identifiants restent valides

🔄 Impact sur l'expérience utilisateur

Aspect Avant Après NIST
Complexité Règles strictes difficiles à mémoriser Liberté totale, phrases naturelles
Longueur password 12-16 caractères obligatoires 8-64 caractères flexibles
Caractères spéciaux Obligatoires Optionnels
Mémorisation Mots de passe complexes oubliés Phrases personnelles mémorables
Sécurité Règles arbitraires Vérification contre bases de données compromises

🎯 Gestion des rôles

Hiérarchie des permissions

Membre (Rôle 1) : Consultation et distribution dans ses secteurs Admin Amicale (Rôle 2) : Gestion complète de son amicale et ses membres Super Admin (Rôle 3+) : Administration globale multi-amicales

Fonctionnalités par rôle

Admin Amicale - Gestion des membres Création : Nouveaux membres avec attribution de rôle Modification : Informations personnelles, rôle, statut actif Suppression : Avec confirmation obligatoire Validation : Contrôle d'unicité email/username par l'API

Interface adaptative

Sélecteur de rôle : Visible uniquement pour les admins Checkbox statut actif : Contrôle d'accès aux comptes Édition contextuelle : Champs modifiables selon les permissions Actions conditionnelles : Boutons disponibles selon le niveau d'autorisation

👥 Gestion des membres (Admin Amicale)

🎯 Vue d'ensemble

La gestion des membres est une fonctionnalité clé pour les Admins Amicale (Rôle 2) qui permet une administration complète des équipes au sein de leur amicale. Cette interface centralise toutes les opérations liées aux membres avec une approche sécurisée et intuitive.

📱 Interface AdminAmicalePage

L'interface principale admin_amicale_page.dart offre une vue d'ensemble complète :

  • Informations de l'amicale : Affichage des détails de l'amicale courante
  • Liste des membres : Tableau interactif avec actions contextuelles
  • Ajout de membres : Bouton d'action pour créer de nouveaux comptes
  • Opération courante : Indication de l'opération active en cours

Fonctionnalités principales

🆕 Création de nouveaux membres

// Workflow de création
1. Clic sur "Ajouter un membre"
2. Ouverture du UserFormDialog
3. Formulaire vierge avec valeurs par défaut
4. Sélection du rôle (Membre/Administrateur)
5. Configuration du statut actif
6. Validation et création via API
7. Attribution automatique à l'amicale courante

Champs obligatoires :

  • Email (unique dans le système)
  • Au moins un nom (nom de famille OU nom de tournée)
  • Rôle dans l'amicale

Champs optionnels :

  • Nom d'utilisateur (éditable pour les admins)
  • Prénom, téléphones, dates
  • Nom de tournée (pour identification terrain)

✏️ Modification des membres existants

// Actions disponibles
- Clic sur une ligne  Ouverture du formulaire d'édition
- Modification de tous les champs personnels
- Changement de rôle (Membre  Administrateur)
- Activation/Désactivation du compte
- Gestion du nom de tournée

Workflow de modification :

  1. Sélection du membre dans le tableau
  2. Ouverture automatique du UserFormDialog
  3. Formulaire pré-rempli avec données existantes
  4. Modification des champs souhaités
  5. Validation et mise à jour via API
  6. Synchronisation automatique avec Hive

🗑️ Suppression intelligente des membres

La suppression des membres intègre une logique métier avancée pour préserver l'intégrité des données :

🔍 Vérification des passages
// Algorithme de vérification
1. Récupération de opération courante
2. Analyse des passages du membre pour cette opération
3. Classification : passages réalisés vs passages à finaliser
4. Comptage total des passages actifs
📊 Scénarios de suppression

Cas 1 : Aucun passage (suppression simple)

DELETE /users/{id}
  • Confirmation simple
  • Suppression directe
  • Aucun transfert nécessaire

Cas 2 : Passages existants (suppression avec transfert)

DELETE /users/{id}?transfer_to={destinataire}&operation_id={operation}
  • Dialog d'avertissement avec détails
  • Sélection obligatoire d'un membre destinataire
  • Transfert automatique de tous les passages
  • Préservation de l'historique

Cas 3 : Alternative recommandée (désactivation)

// Mise à jour du statut
membre.copyWith(isActive: false)
  • Préservation complète des données
  • Blocage de la connexion
  • Maintien de l'historique
  • Réactivation possible ultérieurement

🔐 Sécurité et permissions

Contrôles d'accès

  • Isolation par amicale : Admins limités à leur amicale
  • Vérification des rôles : Validation côté client et serveur
  • Opération courante : Filtrage par contexte d'opération
  • Validation API : Contrôles d'unicité et cohérence

Gestion des erreurs

graph TD
    A[Action utilisateur] --> B[Validation locale]
    B --> C[Appel API]
    C --> D{Succès ?}
    D -->|Oui| E[Mise à jour Hive]
    D -->|Non| F[Affichage erreur]
    E --> G[Notification succès]
    F --> H[Dialog reste ouvert]

🎨 Interface utilisateur

Tableaux interactifs

MembreTableWidget - Composant principal :

  • Colonnes : ID, Prénom, Nom, Email, Rôle, Statut
  • Actions : Modification, Suppression
  • Alternance de couleurs pour lisibilité
  • Tri et navigation intuitifs

MembreRowWidget - Ligne individuelle :

  • Clic pour édition rapide
  • Boutons d'action contextuels
  • Indicateurs visuels de statut
  • Tooltip informatifs

Formulaires adaptatifs

UserFormDialog - Modale réutilisable :

  • Layout responsive (>900px vs mobile)
  • Validation en temps réel
  • Gestion des erreurs inline
  • Sauvegarde avec feedback

📡 Synchronisation et réactivité

Architecture ValueListenableBuilder

// Écoute automatique des changements
ValueListenableBuilder<Box<MembreModel>>(
  valueListenable: membreRepository.getMembresBox().listenable(),
  builder: (context, membresBox, child) {
    // Interface mise à jour automatiquement
    final membres = membresBox.values
        .where((m) => m.fkEntite == currentUser.fkEntite)
        .toList();

    return MembreTableWidget(membres: membres);
  },
)

Principe "API First"

  1. Validation API : Tentative sur serveur en priorité
  2. Succès → Sauvegarde locale + mise à jour interface
  3. Erreur → Affichage message + maintien état local
  4. Cohérence : Données locales toujours synchronisées

🔄 Workflow complet

sequenceDiagram
    participant A as Admin
    participant UI as Interface
    participant R as Repository
    participant API as Serveur
    participant H as Hive

    A->>UI: Action membre
    UI->>R: Appel repository
    R->>API: Requête HTTP
    API-->>R: Réponse

    alt Succès
        R->>H: Sauvegarde locale
        H-->>UI: Notification changement
        UI-->>A: Interface mise à jour
    else Erreur
        R-->>UI: Exception
        UI-->>A: Message d'erreur
    end

🎯 Bonnes pratiques

Pour les administrateurs

  1. Vérification avant suppression : Toujours examiner les passages
  2. Préférer la désactivation : Éviter la perte de données
  3. Noms de tournée : Utiliser des identifiants terrain clairs
  4. Rôles appropriés : Limiter les administrateurs aux besoins

Gestion des erreurs courantes

Erreur Cause Solution
Email déjà utilisé Duplication Choisir un autre email
Membre avec passages Données liées Transférer ou désactiver
Aucune opération active Configuration Vérifier les opérations
Accès refusé Permissions Vérifier le rôle admin

Cette architecture garantit une gestion des membres robuste, sécurisée et intuitive, optimisant l'efficacité administrative tout en préservant l'intégrité des données opérationnelles. 👥

🗺️ Cartes et géolocalisation

🎯 Architecture cartographique

Flutter Map : Rendu cartographique haute performance Providers de tuiles : Solution hybride Mapbox/OpenStreetMap Géolocalisation temps réel : Suivi GPS des équipes Secteurs géographiques : Visualisation et attribution dynamique Passages géolocalisés : Enregistrement précis des distributions

🌍 Configuration des tuiles de carte

GEOSECTOR v3.x utilise une stratégie différenciée pour l'affichage des tuiles de carte selon la plateforme :

Configuration actuelle

Plateforme Provider URL Template Raison
Web Mapbox https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/ Token compatible avec l'API styles
Mobile OpenStreetMap https://tile.openstreetmap.org/{z}/{x}/{y}.png Gratuit, sans restriction de token

Pourquoi cette approche ?

  1. Problème de permissions Mapbox : Les tokens Mapbox actuels (DEV/REC/PROD) n'ont pas les permissions pour l'API styles/v1 qui retourne une erreur 403 sur mobile
  2. Solution pragmatique : OpenStreetMap fonctionne parfaitement sans token et offre une qualité de carte équivalente
  3. Facilité de maintenance : Pas besoin de gérer des tokens différents selon les environnements

💾 Système de cache des tuiles

Le cache fonctionne pour les deux providers (Mapbox et OpenStreetMap) :

Configuration du cache

// Dans mapbox_map.dart
CachedTileProvider(
  store: FileCacheStore(cachePath),
  maxStale: Duration(days: 30), // Tuiles valides 30 jours
)

Caches séparés par provider

  • Web (Mapbox) : Cache dans MapboxTileCache/
  • Mobile (OSM) : Cache dans OSMTileCache/

Avantages du cache

Mode hors ligne : Les zones visitées restent disponibles sans connexion Performance : Chargement instantané des tuiles en cache Économie de données : Pas de re-téléchargement inutile Fiabilité : Fonctionne même avec une connexion instable

🔧 Widget MapboxMap centralisé

Le widget MapboxMap (lib/presentation/widgets/mapbox_map.dart) centralise toute la logique cartographique :

Paramètres principaux

MapboxMap(
  initialPosition: LatLng(48.1173, -1.6778), // Rennes
  initialZoom: 13.0,
  markers: [...],           // Marqueurs (passages, etc.)
  polygons: [...],          // Polygones (secteurs)
  useOpenStreetMap: !kIsWeb, // OSM sur mobile, Mapbox sur web
  showControls: true,       // Boutons zoom/localisation
)

Utilisation dans l'application

  • admin_map_page.dart : Carte d'administration avec édition de secteurs
  • user_map_page.dart : Carte utilisateur avec visualisation des passages
  • user_field_mode_page.dart : Mode terrain avec GPS et boussole

🔄 Migration future vers Mapbox complet

Si vous obtenez un token Mapbox avec les permissions appropriées :

  1. Retirer le paramètre useOpenStreetMap: !kIsWeb dans les pages
  2. Le widget détectera automatiquement et utilisera Mapbox partout
  3. Le cache continuera de fonctionner avec le nouveau provider

📱 Mode terrain (Field Mode)

Le mode terrain offre des fonctionnalités avancées pour les équipes sur le terrain :

  • Indicateurs GPS : Qualité du signal (GPS/Réseau) avec actualisation 5s
  • Mode boussole : Orientation de la carte selon le magnétomètre
  • Markers optimisés : Affichage simplifié (première lettre de rueBis)
  • Liste triée : Passages organisés par distance
  • Cercles de distance : Visualisation des zones de proximité

🔄 Synchronisation et réactivité

Hive + ValueListenableBuilder

Réactivité native : Mise à jour automatique de l'interface Performance optimisée : Pas de Provider, injection directe Écoute sélective : Réactivité fine par Box Hive Cohérence des données : Synchronisation bidirectionnelle User/Membre

Services Singleton

CurrentUserService : Gestion de l'utilisateur connecté CurrentAmicaleService : Amicale de l'utilisateur actuel ApiService : Communication centralisée avec l'API DataLoadingService : Orchestration du chargement des données

🚀 Optimisation des performances Hive

📈 Gestion des Box Hive avec cache

GEOSECTOR v3.x implémente une stratégie de cache avancée pour les Box Hive afin d'éliminer les goulots d'étranglement de performance lors d'opérations haute fréquence.

🎯 Problème résolu

Avant l'optimisation, l'application effectuait jusqu'à 848 vérifications Hive.isBoxOpen() par chargement de page, causant des ralentissements significatifs lors du rendu des listes et du filtrage des données.

💡 Solution : Cache lazy des Box

// Pattern implémenté dans tous les repositories
class OptimizedRepository {
  Box<ModelType>? _cachedBox;
  
  Box<ModelType> get _modelBox {
    if (_cachedBox == null) {
      if (!Hive.isBoxOpen(AppKeys.boxName)) {
        throw Exception('Box non ouverte');
      }
      _cachedBox = Hive.box<ModelType>(AppKeys.boxName);
      debugPrint('Repository: Box mise en cache');
    }
    return _cachedBox!;
  }

  void _resetCache() {
    _cachedBox = null;
  }
}

🔄 Gestion du cache et réactivité

Point critique : Le cache doit être réinitialisé après toute modification pour garantir le bon fonctionnement de ValueListenableBuilder :

// ✅ OBLIGATOIRE après modification
Future<void> saveData(ModelType data) async {
  await _modelBox.put(data.id, data);
  _resetCache(); // ← Crucial pour la réactivité UI
  notifyListeners();
}

// ✅ OBLIGATOIRE après suppression
Future<void> deleteData(int id) async {
  await _modelBox.delete(id);
  _resetCache(); // ← Crucial pour la réactivité UI
  notifyListeners();
}

// ✅ OBLIGATOIRE après vidage ou traitement API
Future<void> processApiData(List<dynamic> data) async {
  await _modelBox.clear();
  // ... traitement des données
  _resetCache(); // ← Crucial pour la réactivité UI
  notifyListeners();
}

📊 Impact performance

Métrique Avant optimisation Après optimisation
Vérifications box 848 par page 1 par session
Temps de rendu 200-500ms <50ms
Filtrage liste Lent (check répétés) Instantané
Mémoire Overhead minimal Impact négligeable

🏗️ Repositories optimisés

L'optimisation est implémentée dans tous les repositories critiques :

  • SectorRepository : Gestion des secteurs géographiques
  • PassageRepository : Suivi des distributions
  • MembreRepository : Gestion des équipes
  • OperationRepository : Campagnes et opérations
  • AmicaleRepository : Organisations

🎯 Règles d'implémentation

  1. Cache systématique : Tous les repositories fréquemment utilisés
  2. Reset obligatoire : Après toute opération de modification
  3. Getter lazy : Accès différé à la box uniquement si nécessaire
  4. Debug logging : Traçabilité du cache en développement
  5. Cohérence : Pattern appliqué uniformément

Cette architecture garantit une application performante, maintenable et évolutive avec une excellente expérience utilisateur. 🚀

🎨 Architecture des Dialogs Auto-Gérées

🎯 Principe de conception

GEOSECTOR v3.x implémente une architecture simplifiée des dialogs qui élimine la complexité des callbacks asynchrones et garantit une gestion robuste des formulaires modaux.

🏗️ Pattern "Dialog Auto-Gérée"

graph TD
    A[Page Parente] -->|Injection Repository| B[Dialog Auto-Gérée]
    B -->|Appel Direct| C[Repository]
    C -->|API Call| D[Serveur]
    D -->|Réponse| C
    C -->|Sauvegarde| E[Hive Local]
    B -->|Auto-Fermeture| A
    B -->|Callback Simple| A
    E -->|ValueListenableBuilder| A

📱 Widgets de passages et navigation

🎯 PassagesListWidget

Le widget PassagesListWidget est le composant central pour l'affichage et la gestion des passages dans toute l'application. Il offre une expérience utilisateur cohérente avec des fonctionnalités adaptatives selon le contexte.

Fonctionnalités principales

  • Affichage adaptatif : Liste complète ou tableau de bord avec fond transparent
  • Flux conditionnel de clic : Comportement intelligent selon le type de passage
  • Bouton de création intégré : Bouton "+" vert dans l'en-tête pour ajouter des passages
  • Système de filtrage centralisé : Tous les filtres intégrés et configurables
  • Actions contextuelles : Modification, suppression, génération de reçus

🔧 Système de filtrage centralisé (v3.2.2)

Depuis la v3.2.2, PassagesListWidget intègre tous les filtres de manière configurable :

PassagesListWidget(
  // Données
  passages: formattedPassages,
  
  // Configuration des filtres
  showFilters: true,           // Active le système de filtrage
  showSearch: true,            // Barre de recherche
  showTypeFilter: true,        // Filtre par type de passage
  showPaymentFilter: true,     // Filtre par mode de paiement
  showSectorFilter: true,      // Filtre par secteur géographique
  showUserFilter: true,        // Filtre par membre (admin uniquement)
  showPeriodFilter: true,      // Filtre par période temporelle
  
  // Données pour les filtres
  sectors: _sectors,           // Liste des secteurs disponibles
  members: users,              // Liste des membres (pour admin)
  
  // Valeurs initiales
  initialSectorId: selectedSectorId,
  initialUserId: selectedUserId,
  initialPeriod: 'Toutes',
  dateRange: selectedDateRange,
  
  // Callback de synchronisation
  onFiltersChanged: (filters) {
    setState(() {
      // Synchronisation avec l'état parent
      selectedSectorId = filters['sectorId'];
      selectedPeriod = filters['period'];
      // ...
    });
  },
)

Avantages de la centralisation :

  • Code unique : Plus de duplication entre les pages
  • Configuration flexible : Chaque page active uniquement les filtres pertinents
  • Interface cohérente : Même expérience utilisateur partout
  • Maintenance simplifiée : Modifications centralisées
  • Responsive automatique : Adaptation desktop/mobile gérée par le widget

🔄 Flux conditionnel des clics sur passages

Le widget implémente un comportement intelligent lors du clic sur un passage :

// Logique de gestion des clics
void _handlePassageClick(Map<String, dynamic> passage) {
  final int passageType = passage['type'] as int? ?? 1;
  
  if (passageType == 2) {
    // Type 2 (À finaliser) : Ouverture directe du formulaire d'édition
    _showEditDialog(context, passageModel);
  } else {
    // Autres types : Affichage des détails avec option de modification
    _showDetailsDialogWithEditOption(context, passage, passageModel);
  }
}

Comportements par type de passage :

  • Type 1 (Réalisé) : Affiche les détails complets avec option "Modifier"
  • Type 2 (À finaliser) : Ouvre directement le formulaire d'édition pour finalisation rapide
  • Type 3 (Absent) : Affiche les détails avec options limitées
  • Type 4 (Refusé) : Affiche les détails en lecture seule

🎨 Dialog de détails amélioré

La boîte de dialogue des détails a été repensée pour une meilleure lisibilité :

  • Organisation par sections : Client, Passage, Lieu avec icônes distinctives
  • Badges colorés : Visualisation rapide du type et statut
  • Formatage intelligent : Dates, montants et informations structurées
  • Actions contextuelles : Boutons adaptés selon les permissions

Bouton de création contextuel

Le widget intègre un bouton "+" vert flottant dans l'en-tête pour créer de nouveaux passages :

// Paramètres pour activer le bouton de création
PassagesListWidget(
  showAddButton: true,  // Active le bouton "+"
  onAddPassage: () async {
    // Logique de création avec PassageFormDialog
    await _showPassageFormDialog(context);
  },
)

Pages avec bouton de création activé :

  • user_field_mode_page.dart : Mode terrain pour création rapide
  • user_history_page.dart : Historique avec ajout possible
  • admin_history_page.dart : Gestion administrative complète

🎯 DashboardAppBar - Évolution de l'interface

Suppression du bouton "Nouveau passage"

Le bouton global "Nouveau passage" a été définitivement retiré de la barre d'application (DashboardAppBar) pour privilégier une approche contextuelle :

Avant :

  • Bouton toujours visible dans l'AppBar
  • Création de passage possible depuis n'importe quelle page
  • Confusion possible sur le contexte de création

Après :

  • Boutons "+" contextuels dans les pages appropriées
  • Création limitée aux contextes pertinents
  • Interface épurée et plus intuitive

🎨 Architecture simplifiée

La suppression du bouton global a permis de :

  • Nettoyer les dépendances (passage_form_dialog.dart, app_keys.dart)
  • Simplifier les paramètres de DashboardLayout
  • Réduire la complexité de navigation
  • Améliorer la cohérence UX

🎯 Mode tableau de bord

Pour les pages de tableau de bord, le PassagesListWidget s'adapte automatiquement :

🏠 Page d'accueil utilisateur

Dans user_dashboard_home_page.dart, l'affichage est optimisé :

// Configuration pour le tableau de bord
SizedBox(
  height: 450,  // Hauteur fixe pour éviter l'overflow
  child: PassagesListWidget(
    passages: recentPassages,
    showFilters: false,      // Pas de filtres sur le dashboard
    showSearch: false,       // Pas de recherche
    maxPassages: 20,         // Limite aux 20 plus récents
    transparentBackground: true,  // Fond transparent pour intégration
  ),
)

Améliorations du dashboard :

  • Suppression de la Card wrapper pour un design épuré
  • Fond transparent pour intégration harmonieuse
  • En-tête coloré maintenu pour la lisibilité
  • Limite augmentée à 20 passages récents (au lieu de 10)

Composants de l'architecture

1. Page Parente (ex: AdminOperationsPage)

// Ouverture simplifiée de dialog
void _showEditOperationDialog(OperationModel op) {
  showDialog(
    context: context,
    builder: (dialogContext) => OperationFormDialog(
      operation: op,
      operationRepository: widget.operationRepository, // ← Injection directe
      onSuccess: () {
        if (mounted) setState(() {}); // ← Simple rafraîchissement
      },
    ),
  );
}

2. Dialog Auto-Gérée (ex: OperationFormDialog)

// Gestion complète dans la dialog
void _handleSubmit() async {
  try {
    // Appel direct du repository
    final success = await widget.operationRepository.saveOperationFromModel(operationData);

    if (success && mounted) {
      // Délai pour synchronisation Hive
      Future.delayed(const Duration(milliseconds: 200), () {
        Navigator.of(context).pop(); // ← Auto-fermeture
        widget.onSuccess?.call(); // ← Notification simple
        ApiException.showSuccess(context, "Opération sauvegardée");
      });
    }
  } catch (e) {
    ApiException.showError(context, e); // ← Erreur sans fermeture
  }
}

🎯 Avantages de cette approche

Aspect Avant (Complexe) Après (Simplifié)
Callbacks Asynchrones complexes Simple onSuccess: () => setState()
Fermeture Gérée par le parent Auto-fermeture dans la dialog
Gestion d'erreurs Dispersée Centralisée dans la dialog
Synchronisation Problématique Délai de 200ms pour Hive
Maintenance Code dispersé Logique unifiée

🔧 Responsabilités claires

Page Parente

  • Ouverture des dialogs avec injection de dépendances
  • Rafraîchissement de l'interface via setState()
  • Gestion des tableaux et listes intégrés

Dialog Auto-Gérée

  • Validation et soumission du formulaire
  • Appel direct des repositories
  • Auto-fermeture en cas de succès
  • Gestion des erreurs sans fermeture

Repository

  • Logique métier (création vs modification)
  • Appels API appropriés
  • Sauvegarde locale dans Hive
  • Propagation des exceptions

🚀 Exemple d'implémentation

// 1. Page parente - Code minimal
void _showCreateDialog() {
  showDialog(
    context: context,
    builder: (context) => OperationFormDialog(
      title: 'Créer une opération',
      operationRepository: widget.operationRepository,
      onSuccess: () => setState(() {}),
    ),
  );
}

// 2. Dialog - Auto-gestion complète
class OperationFormDialog extends StatefulWidget {
  final OperationRepository operationRepository;
  final VoidCallback? onSuccess;

  // Gestion interne : validation, API, fermeture, erreurs
}

// 3. Repository - Logique métier pure
Future<bool> saveOperationFromModel(OperationModel operation) async {
  if (operation.id == 0) {
    return await createOperation(...); // POST
  } else {
    return await updateOperation(...); // PUT
  }
}

Résultats obtenus

  • 🎯 Simplicité : Code plus lisible et maintenable
  • 🔒 Robustesse : Gestion d'erreurs centralisée
  • Performance : Synchronisation optimisée avec Hive
  • 🎨 UX : Fermeture automatique et messages appropriés
  • 🔧 Maintenance : Architecture cohérente et prévisible

Cette approche "Dialog Auto-Gérée" constitue un pattern architectural clé de GEOSECTOR v3.x, garantissant une expérience utilisateur fluide et un code maintenable. 🎉

Fonction de création d'une opération

Fonction de création d'une opération

🔄 Traitement des données complexes

Lors de la création d'une opération via OperationRepository.createOperation(), l'API retourne en réponse 201 Created ou 200 OK un payload complexe contenant 4 groupes de données essentiels :

{
  "operations": [...],    // 3 dernières opérations dont la nouvelle active
  "secteurs": [...],      // Secteurs de la nouvelle opération active
  "passages": [...],      // Passages de la nouvelle opération active
  "users_sectors": [...]  // Associations user-secteurs de la nouvelle opération
}

📦 Groupes de données attendus

🎯 Groupe operations

Contient les 3 dernières opérations de l'amicale, incluant celle qui vient d'être créée et qui devient automatiquement active.

🗺️ Groupe secteurs

Contient tous les secteurs géographiques associés à la dernière opération créée et active.

📍 Groupe passages

Contient l'ensemble des passages générés pour cette dernière opération créée et active.

👥 Groupe users_sectors

Contient les associations utilisateur-secteurs définissant les attributions pour cette dernière opération créée et active.

⚙️ Traitement automatique des données

Ces 4 groupes sont traités de manière identique au processus de connexion utilisateur, en utilisant le DataLoadingService avec la logique suivante :

// Dans OperationRepository._processCreationResponse()
await _processOperationCreationData(responseData);

Future<void> _processOperationCreationData(Map<String, dynamic> data) async {
  // 1. Vidage des Box Hive concernées (comme au login)
  await _clearRelatedBoxes();

  // 2. Traitement des 4 groupes via DataLoadingService
  if (data.containsKey('operations')) {
    await DataLoadingService.instance.processOperationsFromApi(data['operations']);
  }

  if (data.containsKey('secteurs')) {
    await DataLoadingService.instance.processSectorsFromApi(data['secteurs']);
  }

  if (data.containsKey('passages')) {
    await DataLoadingService.instance.processPassagesFromApi(data['passages']);
  }

  if (data.containsKey('users_sectors')) {
    await DataLoadingService.instance.processUserSectorsFromApi(data['users_sectors']);
  }
}

Future<void> _clearRelatedBoxes() async {
  // Vidage des Box respectives avant rechargement
  final operationsBox = HiveService.instance.getTypedBox<OperationModel>(AppKeys.operationsBoxName);
  final sectorsBox = HiveService.instance.getTypedBox<SectorModel>(AppKeys.sectorsBoxName);
  final passagesBox = HiveService.instance.getTypedBox<PassageModel>(AppKeys.passagesBoxName);
  final userSectorsBox = HiveService.instance.getTypedBox<UserSectorModel>(AppKeys.userSectorBoxName);

  await operationsBox.clear();
  await sectorsBox.clear();
  await passagesBox.clear();
  await userSectorsBox.clear();
}

🔄 Flux de synchronisation

sequenceDiagram
    participant UI as Interface
    participant OR as OperationRepository
    participant API as Serveur API
    participant HS as HiveService
    participant DLS as DataLoadingService
    participant VLB as ValueListenableBuilder

    UI->>OR: createOperation(newOperation)
    OR->>API: POST /operations
    API-->>OR: 201 Created + 4 groupes

    Note over OR: Traitement des données complexes
    OR->>HS: clear() sur 4 Box concernées
    OR->>DLS: processOperationsFromApi()
    OR->>DLS: processSectorsFromApi()
    OR->>DLS: processPassagesFromApi()
    OR->>DLS: processUserSectorsFromApi()

    Note over DLS: Sauvegarde dans Hive
    DLS->>HS: put() dans Box typées
    HS-->>VLB: Notifications de changement
    VLB-->>UI: Mise à jour automatique

Avantages de cette approche

  • Cohérence totale : Données locales parfaitement synchronisées avec le serveur
  • Performance optimisée : Un seul appel API pour toutes les données nécessaires
  • Réactivité immédiate : Interface mise à jour automatiquement via ValueListenableBuilder
  • Logique centralisée : Réutilisation du DataLoadingService existant
  • Gestion d'erreurs : Rollback automatique en cas d'échec du traitement

Cette architecture garantit une synchronisation robuste et performante lors de la création d'opérations, en maintenant la cohérence des données tout en optimisant l'expérience utilisateur. 🚀


📝 Changelog

v3.2.4 (04 Septembre 2025)

Optimisations et corrections

  • 🐛 Correction du PassagesListWidget dans user_dashboard_home_page
    • Suppression de la hauteur fixe pour éviter le rectangle gris
    • Affichage des 20 derniers passages sans scrolling interne
    • Utilisation du scrolling de la page principale uniquement
  • 🧹 Nettoyage du code
    • Suppression des méthodes non utilisées (_formatDate, _buildSimplePassageCard, _showPassageDetails)
    • Amélioration de la structure des blocs if/else selon les bonnes pratiques
    • Suppression des imports inutiles (intl)
  • Qualité du code
    • Flutter analyze : 0 erreur, 0 warning sur tous les fichiers modifiés
    • Réduction globale de 65% des issues (de 493 à 171)

v3.2.3 (03 Septembre 2025)

Mise à jour des interfaces mobiles

  • 📱 Améliorations de l'interface utilisateur
    • Optimisation des layouts pour mobiles et tablettes
    • Amélioration de la réactivité des composants
  • 🔧 Corrections de bugs mineurs
    • Résolution des problèmes d'affichage sur certains appareils
    • Amélioration des performances de rendu

v3.2.2 (02 Septembre 2025)

Centralisation des filtres dans PassagesListWidget

  • 🎯 Refactoring majeur du système de filtrage
    • Tous les filtres déplacés dans PassagesListWidget (recherche, type, paiement, secteur, membre, période)
    • Configuration flexible via paramètres booléens (showTypeFilter, showPaymentFilter, showSectorFilter, etc.)
    • Suppression du code de filtrage dupliqué dans les pages parentes
  • 🔧 Nouveaux filtres avancés
    • Filtre par secteur avec liste déroulante
    • Filtre par membre pour les pages admin
    • Filtre par période avec options prédéfinies (24h, 48h, 7 jours, 15 jours, mois)
    • Support des plages de dates personnalisées avec DateRangePicker
  • 📱 Interface responsive des filtres
    • Desktop : Filtres compacts sur 2 lignes maximum
    • Mobile : Filtres empilés verticalement pour une meilleure ergonomie
    • Recherche toujours en premier pour une accessibilité optimale
  • 🔄 Synchronisation des filtres
    • Callback onFiltersChanged pour synchroniser l'état avec les pages parentes
    • Notification automatique des changements de filtres
    • Persistance des sélections entre les navigations
  • 📊 Pages simplifiées
    • admin_history_page.dart : Suppression de 400+ lignes de code de filtrage dupliqué
    • user_history_page.dart : Simplification avec activation sélective des filtres pertinents
    • Maintenance facilitée avec une logique unique centralisée
  • 🔧 Correction des layouts
    • admin_history_page.dart : Utilisation d'Expanded au lieu de hauteur fixe (85%)
    • Liste des passages s'étend maintenant jusqu'en bas de l'écran sur mobile
  • 📝 Amélioration des labels de filtres
    • "Tous les types" au lieu de "Tous"
    • "Tous les règlements" au lieu de "Tous"
    • "Toutes les périodes" au lieu de "Tous"
    • Meilleure clarté pour l'utilisateur

v3.2.1 (31 Août 2025)

Build et déploiement

  • 🚀 Publication sur Google Play Store
    • Build AAB (Android App Bundle) pour distribution optimisée
    • Configuration des signatures et keystores
    • Optimisation de la taille de l'application
  • 🔧 Corrections de bugs critiques
    • Fix des problèmes de compilation
    • Résolution des dépendances obsolètes
    • Amélioration de la stabilité générale

v3.2.0 (30 Août 2025)

Intégration Stripe Connect

  • 💳 Système de paiement pour les amicales
    • Configuration Stripe Connect pour les comptes des amicales
    • Interface de gestion des paiements
    • Suivi des transactions et règlements
  • 🏗️ Architecture de paiement
    • Intégration API Stripe
    • Gestion sécurisée des tokens
    • Workflow de paiement complet

v3.1.0 (Juillet 2025)

Interface utilisateur

  • 🎨 Suppression des titres de pages pour maximiser l'espace utile
    • Pages utilisateur : historique, statistiques, carte
    • Pages admin : historique, statistiques
    • Module de chat
  • 📱 Chat responsive avec layout adaptatif
    • Desktop : disposition horizontale rooms/messages
    • Mobile : disposition verticale avec hauteur adaptative
  • 🗺️ Carte optimisée
    • Mode plein écran
    • Filtres en pastilles colorées overlay (bas gauche)
    • Design minimaliste sans labels
  • 📏 Titres responsive sur dashboards
    • Tailles adaptées aux petits écrans
    • Suppression des éléments superflus (icône refresh)

v3.0.0 (Juin 2025)

Refonte architecturale majeure

  • 🏗️ Architecture moderne sans Provider
    • Migration vers l'injection de dépendances
    • Repositories singleton avec instances globales
    • Suppression complète de Provider/Bloc
  • 💾 Optimisation cache Hive
    • Stratégie de cache pour éliminer les vérifications répétées
    • Performance améliorée de 99% sur les opérations de liste
    • Gestion intelligente du cache avec reset après modifications
  • 🔐 Normes NIST SP 800-63B pour les identifiants
    • Support des phrases de passe
    • Acceptation de tous les caractères Unicode
    • Vérification contre les bases de mots de passe compromis
  • 📊 Système de logging intelligent
    • LoggerService centralisé avec détection d'environnement
    • Logs désactivés automatiquement en production
    • Catégorisation avec emojis automatiques
  • 🎯 Pattern Dialog Auto-Gérée
    • Dialogs responsables de leur propre cycle de vie
    • Auto-fermeture sur succès
    • Gestion d'erreurs centralisée dans la dialog

v2.x (2024 - Début 2025)

Versions de développement initial

  • Base de l'architecture Flutter
  • Mise en place des modèles Hive
  • Intégration des cartes Mapbox/OpenStreetMap
  • Système de chat MQTT
  • Gestion des rôles et permissions