- 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>
1761 lines
61 KiB
Markdown
Executable File
1761 lines
61 KiB
Markdown
Executable File
# GEOSECTOR v2.1
|
||
|
||
🚒 **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 v2.1
|
||
|
||
- **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
|
||
- **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
|
||
|
||
---
|
||
|
||
## 📋 Table des matières
|
||
|
||
1. [Fonctionnalités](#-fonctionnalités)
|
||
2. [Architecture technique](#️-architecture-technique)
|
||
3. [Installation](#-installation-et-configuration)
|
||
4. [Modèles de données](#️-modèles-de-données)
|
||
5. [Architecture des composants](#-architecture-des-composants)
|
||
6. [Gestion des rôles](#-gestion-des-rôles)
|
||
7. [Interface utilisateur](#-interface-utilisateur)
|
||
8. [API et synchronisation](#-api-et-synchronisation)
|
||
9. [Gestion des erreurs](#-gestion-des-erreurs)
|
||
10. [Cartes et géolocalisation](#️-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
|
||
|
||
```mermaid
|
||
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](https://www.mapbox.com/)
|
||
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
|
||
|
||
```dart
|
||
// 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 v2.1 - Interface épurée et responsive
|
||
|
||
#### **🎯 Simplification des titres de pages**
|
||
|
||
La v2.1 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 v2.0 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
|
||
|
||
```dart
|
||
// 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
|
||
|
||
```mermaid
|
||
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` :
|
||
|
||
```dart
|
||
// ✅ 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**
|
||
|
||
```dart
|
||
// ❌ 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;
|
||
}
|
||
}
|
||
```
|
||
|
||
```dart
|
||
// ✅ 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)**
|
||
|
||
```dart
|
||
// ✅ 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` :
|
||
```dart
|
||
import 'package:geosector_app/core/utils/api_exception.dart';
|
||
```
|
||
|
||
### 🔄 Flux d'erreur corrigé
|
||
|
||
```mermaid
|
||
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 v2.0 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 :
|
||
|
||
```dart
|
||
// 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**
|
||
|
||
```dart
|
||
// 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**
|
||
|
||
```dart
|
||
// 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)**
|
||
|
||
```dart
|
||
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)**
|
||
|
||
```dart
|
||
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 :
|
||
|
||
```dart
|
||
// 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**
|
||
|
||
```dart
|
||
// ✅ 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**
|
||
|
||
```dart
|
||
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**
|
||
|
||
```dart
|
||
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 :
|
||
|
||
```dart
|
||
// 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 :
|
||
|
||
```dart
|
||
// 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 v2.0 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
|
||
|
||
```dart
|
||
// 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
|
||
|
||
```dart
|
||
// 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
|
||
|
||
```dart
|
||
// 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)**
|
||
|
||
```http
|
||
DELETE /users/{id}
|
||
```
|
||
|
||
- Confirmation simple
|
||
- Suppression directe
|
||
- Aucun transfert nécessaire
|
||
|
||
**Cas 2 : Passages existants (suppression avec transfert)**
|
||
|
||
```http
|
||
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)**
|
||
|
||
```dart
|
||
// 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
|
||
|
||
```mermaid
|
||
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
|
||
|
||
```dart
|
||
// É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
|
||
|
||
```mermaid
|
||
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 v2.0 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**
|
||
|
||
```dart
|
||
// 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**
|
||
|
||
```dart
|
||
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 v2.0 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**
|
||
|
||
```dart
|
||
// 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` :
|
||
|
||
```dart
|
||
// ✅ 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 v2.0 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"
|
||
|
||
```mermaid
|
||
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
|
||
- **Filtrage avancé** : Par type, utilisateur, période, avec exclusions possibles
|
||
- **Actions contextuelles** : Modification, suppression, génération de reçus
|
||
|
||
#### 🔄 Flux conditionnel des clics sur passages
|
||
|
||
Le widget implémente un comportement intelligent lors du clic sur un passage :
|
||
|
||
```dart
|
||
// 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 :
|
||
|
||
```dart
|
||
// 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é :
|
||
|
||
```dart
|
||
// 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)**
|
||
|
||
```dart
|
||
// 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)**
|
||
|
||
```dart
|
||
// 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
|
||
|
||
```dart
|
||
// 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 v2.0, 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 :
|
||
|
||
```json
|
||
{
|
||
"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 :
|
||
|
||
```dart
|
||
// 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
|
||
|
||
```mermaid
|
||
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
|
||
|
||
### v2.1 (Janvier 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)
|
||
|
||
#### **Corrections de bugs**
|
||
- ✅ Fix backdrop persistant après fermeture de PassageFormDialog
|
||
- ✅ Fix contexte Navigator pour dialogs (rootNavigator: false)
|
||
- ✅ Fix responsive des titres sur petits écrans
|
||
|
||
### v2.0 (Décembre 2024)
|
||
- 🏗️ Architecture moderne sans Provider
|
||
- 💾 Optimisation cache Hive
|
||
- 🔐 Normes NIST pour les identifiants
|
||
- 📊 Système de logging intelligent
|
||
- 🎯 Pattern Dialog Auto-Gérée
|