feat: Livraison version 3.0.6
- Amélioration de la gestion des entités et des utilisateurs - Mise à jour des modèles Amicale et Client avec champs supplémentaires - Ajout du service de logging et amélioration du chargement UI - Refactoring des formulaires utilisateur et amicale - Intégration de file_picker et image_picker pour la gestion des fichiers - Amélioration de la gestion des membres et de leur suppression - Optimisation des performances de l'API - Mise à jour de la documentation technique 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -96,6 +96,7 @@ GEOSECTOR est une solution complète développée en Flutter qui révolutionne l
|
||||
| **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
|
||||
|
||||
@@ -423,6 +424,355 @@ Interface : Affichage utilisateur via ApiException.showError()
|
||||
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 rôles
|
||||
|
||||
### Hiérarchie des permissions
|
||||
|
||||
Reference in New Issue
Block a user