feat: synchronisation mode deconnecte fin chat et stats

This commit is contained in:
2025-08-31 18:21:20 +02:00
parent 41a4505b4b
commit 604294af96
149 changed files with 285769 additions and 250633 deletions

View File

@@ -0,0 +1,158 @@
# 🧹 Système de Nettoyage du Cache - GEOSECTOR
## 📋 Vue d'ensemble
Un système complet de nettoyage du cache a été implémenté dans l'application GEOSECTOR pour résoudre les problèmes de compatibilité lors des mises à jour de la structure des données Hive.
## 🎯 Problème résolu
Les testeurs rencontraient des problèmes au démarrage de l'application après une mise à jour modifiant la structure des boxes Hive, nécessitant un nettoyage manuel du cache du navigateur.
## ✨ Fonctionnalités implémentées
### 1. 🔄 Nettoyage automatique à chaque changement de version
- **Détection automatique** : Compare la version stockée avec la version actuelle
- **Nettoyage sélectif** : Supprime toutes les données SAUF `pending_requests`
- **Web uniquement** : Activé uniquement sur la plateforme web
- **Transparent** : S'exécute automatiquement au démarrage
### 2. 🔘 Bouton de nettoyage manuel
- **Accessible à tous** : Disponible dans tous les environnements (DEV/REC/PROD)
- **Confirmation requise** : Dialog de confirmation avant nettoyage
- **Feedback visuel** : Barre de progression et messages de statut
- **Icône orange** : Facilement identifiable avec l'icône `cleaning_services`
### 3. 📊 Logging détaillé
- **Emojis descriptifs** : Pour une lecture rapide des logs
- **Étapes détaillées** : Chaque opération est loggée
- **Gestion d'erreurs** : Capture et log toutes les erreurs
- **Statistiques** : Nombre de requêtes préservées, boxes nettoyées, etc.
## 🛠️ Détails techniques
### Fichiers modifiés
- `lib/presentation/auth/splash_page.dart` : Logique principale du nettoyage
- `lib/core/utils/html_stub.dart` : Stub pour compatibilité multi-plateforme
### Méthodes principales
#### `_performSelectiveCleanup()`
```dart
// Étapes du nettoyage :
1. Nettoyer le Service Worker (web)
2. Sauvegarder pending_requests
3. Fermer toutes les boxes
4. Supprimer les boxes du disque
5. Réinitialiser Hive
6. Restaurer pending_requests
7. Sauvegarder la nouvelle version
```
#### `_checkVersionAndCleanIfNeeded()`
```dart
// Logique de détection :
- Compare version stockée vs actuelle
- Si différente nettoyage automatique
- Si première installation sauvegarde version
- Si identique pas de nettoyage
```
### Boxes préservées
- `pending_requests` : **TOUJOURS préservée** (données critiques offline)
### Boxes nettoyées
- `user`
- `operations`
- `passages`
- `sectors`
- `membres`
- `amicale`
- `clients`
- `user_sector`
- `settings`
- `chat_rooms`
- `chat_messages`
## 📝 Logs de debug
### Exemple de log de nettoyage automatique
```
🔍 Vérification de version:
Version stockée: 3.1.9
Version actuelle: 3.2.0
🆕 NOUVELLE VERSION DÉTECTÉE !
Migration de 3.1.9 vers 3.2.0
🧹 === DÉBUT DU NETTOYAGE DU CACHE === 🧹
📌 Type: AUTOMATIQUE
📱 Platform: WEB
📦 Version actuelle: 3.2.0
🔄 Nettoyage du Service Worker...
✅ Service Worker désenregistré
💾 Sauvegarde des requêtes en attente...
📊 5 requêtes en attente sauvegardées
🗑️ Nettoyage des boxes Hive...
✅ Box "user" supprimée
✅ Box "operations" supprimée
[...]
♻️ Restauration des requêtes en attente...
✅ 5 requêtes restaurées
💾 Version 3.2.0 sauvegardée
🎉 === NETTOYAGE TERMINÉ AVEC SUCCÈS === 🎉
```
### Exemple de log de nettoyage manuel
```
👤 Utilisateur a demandé un nettoyage manuel
🧹 === DÉBUT DU NETTOYAGE DU CACHE === 🧹
📌 Type: MANUEL
[...]
```
## 🎨 Interface utilisateur
### Indicateurs visuels pendant le nettoyage
- **Barre de progression** : Animée avec pourcentage
- **Messages de statut** :
- "Nettoyage du cache en cours..."
- "Fermeture des bases de données..."
- "Nettoyage des données locales..."
- "Réinitialisation de Hive..."
- "Nettoyage terminé !"
### Dialog de confirmation (nettoyage manuel)
```
Titre : "Nettoyer le cache ?"
Message :
• Supprimer toutes les données locales
• Préserver les requêtes en attente
• Forcer le rechargement de l'application
[Annuler] [Nettoyer]
```
## ⚠️ Points d'attention
1. **Ne JAMAIS supprimer `pending_requests`** : Données critiques pour la synchronisation offline
2. **Web uniquement** : Le nettoyage automatique n'est actif que sur web
3. **Test en DEV** : Toujours tester les changements de structure en DEV avant REC/PROD
4. **Version à incrémenter** : Penser à incrémenter la version dans `pubspec.yaml`
## 🚀 Utilisation
### Pour les développeurs
1. Modifier la structure Hive
2. Incrémenter la version dans `pubspec.yaml`
3. Déployer → Le nettoyage se fera automatiquement
### Pour les testeurs
- Si problème : Cliquer sur "Nettoyer le cache" (bouton orange)
- Confirmer l'action
- L'app redémarre proprement
## 📈 Bénéfices
-**Zéro intervention manuelle** des testeurs
-**Préservation des données critiques** (requêtes offline)
-**Transparence totale** via les logs
-**Fallback manuel** si nécessaire
- ✅ **Migration fluide** entre versions

View File

@@ -0,0 +1,125 @@
# 🚀 Optimisation du Chargement des Conversations Chat
## 📋 Problème résolu
Les utilisateurs rencontraient des temps d'attente importants lors de la création de conversations avec des groupes prédéfinis comme "Toute l'amicale" ou "Support GEOSECTOR", sans indicateur visuel ni protection contre les doubles clics.
## ✨ Solutions implémentées
### 1. 🔄 LoadingOverlay pour les opérations longues
- **Indicateur visuel** : Affichage d'un spinner avec message pendant le traitement
- **Messages personnalisés** :
- "Création de la conversation avec toute l'amicale..."
- "Création de la conversation avec le support GEOSECTOR..."
- "Création de la conversation avec les administrateurs..."
- "Création de l'annonce pour tous les administrateurs..."
- **Feedback immédiat** : L'utilisateur sait que l'action est en cours
### 2. 🛡️ Protection contre les doubles clics
- **État `_isProcessingAction`** : Empêche le déclenchement multiple de la même action
- **Désactivation des boutons** : Les boutons deviennent inactifs pendant le traitement
- **Réactivation automatique** : Une fois l'action terminée (succès ou erreur)
### 3. 🎯 Refactoring des méthodes d'action rapide
- **Séparation des responsabilités** :
- `_handleQuickActionXxx()` : Gère l'UI et le LoadingOverlay
- `_createXxxRoom()` : Effectue la création réelle de la conversation
- **Gestion d'erreurs améliorée** : Utilisation de `rethrow` pour propager les erreurs au LoadingOverlay
## 📝 Fichiers modifiés
### `lib/chat/pages/rooms_page_embedded.dart`
- **Ajout de l'import** : `LoadingOverlay`
- **Nouvelle variable d'état** : `bool _isProcessingAction = false`
- **Méthodes modifiées** :
- `_handleQuickActionAmicale()` + `_createAmicaleRoom()`
- `_handleQuickActionGeosector()` + `_createGeosectorRoom()`
- `_handleQuickActionAdmins()` + `_createAdminsRoom()`
- `_handleQuickActionAllAdmins()` + `_createAllAdminsRoom()`
- **Boutons modifiés** : Ajout de la condition `_isProcessingAction` pour désactiver
## 🔧 Détails techniques
### Pattern utilisé
```dart
Future<void> _handleQuickActionXxx() async {
// Protection contre les doubles clics
if (_isProcessingAction) return;
setState(() {
_isProcessingAction = true;
});
try {
// Utiliser LoadingOverlay pour l'opération longue
await LoadingOverlay.show(
context: context,
message: 'Message de chargement...',
future: _createXxxRoom(),
);
} catch (e) {
// Gestion d'erreur
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur: ${e.toString()}'),
backgroundColor: Colors.red,
),
);
}
} finally {
// Réactivation des boutons
if (mounted) {
setState(() {
_isProcessingAction = false;
});
}
}
}
```
### Protection des boutons
```dart
onPressed: (_isProcessingAction || autreCondition)
? null
: () => _handleQuickAction(),
```
## 📈 Bénéfices
-**Meilleure UX** : L'utilisateur voit que l'action est en cours
-**Pas de duplications** : Impossible de créer plusieurs fois la même conversation
-**Feedback clair** : Messages explicites pendant le chargement
-**Gestion d'erreurs robuste** : Les erreurs sont capturées et affichées proprement
-**Code plus maintenable** : Séparation claire entre UI et logique métier
## 🎨 Expérience utilisateur
### Avant
- ❌ Clic sur "Toute l'amicale"
- ❌ Rien ne se passe pendant 3-5 secondes
- ❌ Possibilité de cliquer plusieurs fois
- ❌ Création de conversations en double
### Après
- ✅ Clic sur "Toute l'amicale"
- ✅ Spinner immédiat avec message "Création de la conversation..."
- ✅ Bouton désactivé pendant le traitement
- ✅ Conversation créée et affichée automatiquement
- ✅ Message d'erreur clair en cas de problème
## 🚦 État des boutons
| Bouton | Condition de désactivation |
|--------|----------------------------|
| Toute l'amicale | `hasAmicaleRoom \|\| _isProcessingAction` |
| Support GEOSECTOR | `hasGeosectorRoom \|\| _isProcessingAction` |
| Contacter les admins | `_isProcessingAction` |
| Annonce à tous les admins | `_isProcessingAction` |
## 🔍 Points de vigilance
1. **Mounted checks** : Toujours vérifier `mounted` avant `setState`
2. **Finally block** : Toujours réactiver les boutons dans `finally`
3. **Rethrow** : Utiliser `rethrow` dans les méthodes `_createXxxRoom()` pour que LoadingOverlay capture l'erreur
4. **Protection double** : Vérifier à la fois `_isProcessingAction` et les conditions métier

305
app/docs/CODEMAGIC-IOS.md Normal file
View File

@@ -0,0 +1,305 @@
# Guide : Déploiement Flutter iOS via Gitea → GitHub → Codemagic → TestFlight
## Prérequis obligatoires
- [ ] Compte Apple Developer actif (99$/an)
- [ ] App déjà créée sur App Store Connect
- [ ] Projet Flutter fonctionnel dans Gitea
- [ ] Compte GitHub
- [ ] Certificats iOS et profils de provisioning
## Étape 1 : Configuration GitHub
### 1.1 Créer le dépôt GitHub
```bash
# Sur GitHub, créer un nouveau repository (privé recommandé)
# Nom : même nom que votre projet Gitea
```
### 1.2 Configurer les remotes Git localement
```bash
# Dans votre projet Flutter local
cd /home/pierre/dev/votre-projet-flutter
# Ajouter GitHub comme remote supplémentaire
git remote add github https://github.com/votre-username/votre-projet.git
# Vérifier les remotes
git remote -v
# origin https://votre-gitea.com/user/projet.git (fetch)
# origin https://votre-gitea.com/user/projet.git (push)
# github https://github.com/user/projet.git (fetch)
# github https://github.com/user/projet.git (push)
```
### 1.3 Push initial vers GitHub
```bash
# Push toutes les branches vers GitHub
git push github --all
git push github --tags
```
## Étape 2 : Automatisation Gitea → GitHub
### 2.1 Script de synchronisation
Créer un script `sync-github.sh` dans votre projet :
```bash
#!/bin/bash
# sync-github.sh
echo "🔄 Synchronisation vers GitHub..."
# Push vers Gitea (origine)
git push origin
# Push vers GitHub (miroir)
git push github
echo "✅ Synchronisation terminée"
```
```bash
# Rendre le script exécutable
chmod +x sync-github.sh
# Utilisation
./sync-github.sh
```
### 2.2 Hook Git automatique (optionnel)
```bash
# Dans .git/hooks/post-commit
#!/bin/bash
git push github
```
## Étape 3 : Configuration Apple Developer
### 3.1 Récupérer les certificats
1. **Apple Developer** → Certificates
2. Télécharger le **Distribution Certificate**
3. Télécharger le **Provisioning Profile** (App Store)
### 3.2 Convertir en format P12
```bash
# Si vous avez le certificat .cer
# L'ouvrir dans Keychain Access (macOS) ou utiliser OpenSSL
# Exporter au format .p12 avec mot de passe
```
### 3.3 Informations nécessaires
Noter :
- **Team ID** (dans Apple Developer Account)
- **Bundle ID** (com.votredomaine.votreapp)
- **Mot de passe du certificat P12**
## Étape 4 : Configuration Codemagic
### 4.1 Inscription et connexion
1. Aller sur [codemagic.io](https://codemagic.io)
2. S'inscrire avec GitHub
3. Autoriser l'accès à vos dépôts
### 4.2 Ajouter le projet
1. **Add application from repository**
2. Sélectionner votre dépôt GitHub
3. **Flutter App** comme type de projet
### 4.3 Configuration de base
```yaml
# codemagic.yaml à créer dans la racine du projet
workflows:
ios-workflow:
name: iOS Workflow
instance_type: mac_mini_m1
max_build_duration: 120
environment:
flutter: stable
groups:
- app_store_credentials
vars:
APP_STORE_APPLE_ID: votre-apple-id@example.com
BUNDLE_ID: com.votredomaine.votreapp
scripts:
- name: Set up code signing settings on Xcode project
script: |
xcode-project use-profiles
- name: Get Flutter packages
script: |
flutter packages pub get
- name: Flutter analyze
script: |
flutter analyze
- name: Flutter unit tests
script: |
flutter test
- name: Install pods
script: |
find . -name "Podfile" -execdir pod install \;
- name: Flutter build ipa
script: |
flutter build ipa --release \
--build-name=1.0.$BUILD_NUMBER \
--export-options-plist=/Users/builder/export_options.plist
artifacts:
- build/ios/ipa/*.ipa
- /tmp/xcodebuild_logs/*.log
- flutter_drive.log
publishing:
app_store_connect:
auth: integration
submit_to_testflight: true
beta_groups:
- App Store Connect Users
```
### 4.4 Upload des certificats
1. **App settings****Environment variables**
2. Créer un groupe **app_store_credentials**
3. Upload :
- **APP_STORE_CONNECT_ISSUER_ID**
- **APP_STORE_CONNECT_KEY_IDENTIFIER**
- **APP_STORE_CONNECT_PRIVATE_KEY**
- **CERTIFICATE_PRIVATE_KEY** (contenu du .p12 en base64)
- **CERTIFICATE_PASSWORD**
### 4.5 Configuration App Store Connect API
1. **App Store Connect** → Users and Access → Integrations
2. Générer une **App Store Connect API Key**
3. Télécharger le fichier .p8
4. Noter l'**Issuer ID** et **Key ID**
## Étape 5 : Configuration iOS spécifique
### 5.1 Vérifier ios/Runner.xcodeproj
```xml
<!-- ios/Runner/Info.plist -->
<key>CFBundleIdentifier</key>
<string>com.votredomaine.votreapp</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
```
### 5.2 Signing Configuration
```bash
# ios/Runner.xcodeproj/project.pbxproj
# Vérifier que le PRODUCT_BUNDLE_IDENTIFIER correspond
PRODUCT_BUNDLE_IDENTIFIER = com.votredomaine.votreapp;
```
## Étape 6 : Premier build TestFlight
### 6.1 Préparer le build
```bash
# Dans votre projet Flutter
# Incrémenter la version dans pubspec.yaml
version: 1.0.1+2
# Commit et push
git add .
git commit -m "feat: préparation build TestFlight v1.0.1"
./sync-github.sh
```
### 6.2 Lancer le build sur Codemagic
1. **Codemagic Dashboard** → votre projet
2. **Start new build**
3. Sélectionner la branche `main`
4. **iOS Workflow**
5. **Start build**
### 6.3 Suivre le build
- Durée estimée : 15-25 minutes
- Vérifier les logs en cas d'erreur
- L'IPA sera automatiquement envoyé à TestFlight
## Étape 7 : Validation TestFlight
### 7.1 Dans App Store Connect
1. **TestFlight** → votre app
2. Vérifier le nouveau build
3. Ajouter des **Release Notes**
4. **Submit for Review** (si nécessaire)
### 7.2 Inviter des testeurs
1. **TestFlight****Internal Testing**
2. Ajouter des testeurs internes
3. Ou créer des groupes de **External Testing**
## Script complet d'automatisation
```bash
#!/bin/bash
# deploy-ios.sh
echo "🚀 Déploiement iOS automatisé"
# Vérifications préalables
if [ -z "$1" ]; then
echo "❌ Version manquante. Usage: ./deploy-ios.sh 1.0.1"
exit 1
fi
VERSION=$1
BUILD_NUMBER=$(git rev-list --count HEAD)
echo "📝 Version: $VERSION+$BUILD_NUMBER"
# Mise à jour de pubspec.yaml
sed -i "s/version: .*/version: $VERSION+$BUILD_NUMBER/" pubspec.yaml
# Tests locaux
echo "🧪 Tests Flutter..."
flutter test
if [ $? -ne 0 ]; then
echo "❌ Tests échoués"
exit 1
fi
# Commit et push
git add .
git commit -m "release: v$VERSION build $BUILD_NUMBER"
./sync-github.sh
echo "✅ Code pushé vers GitHub"
echo "🔗 Allez sur Codemagic pour lancer le build iOS"
echo "📱 TestFlight sera mis à jour automatiquement"
```
## Utilisation quotidienne
```bash
# Développement normal
git add .
git commit -m "feat: nouvelle fonctionnalité"
./sync-github.sh
# Déploiement TestFlight
./deploy-ios.sh 1.0.2
```
## Dépannage courant
### Erreur de certificat
- Vérifier que le Bundle ID correspond
- Régénérer le Provisioning Profile
- Vérifier la date d'expiration
### Build qui échoue
- Vérifier les logs Codemagic
- Tester `flutter build ios` localement
- Vérifier les variables d'environnement
### TestFlight ne reçoit pas le build
- Vérifier la configuration App Store Connect API
- Contrôler les permissions du certificat
---
**Temps estimé de configuration** : 2-3 heures
**Coût** : Gratuit (500 min/mois Codemagic)
**Prochaines publications** : ~10 minutes par build

244
app/docs/FLUTTER-ANALYZE.md Normal file
View File

@@ -0,0 +1,244 @@
# Flutter Analyze Report - GEOSECTOR App
📅 **Date de génération** : 31/08/2025
🔍 **Analyse complète de l'application Flutter**
---
## 📊 Résumé Exécutif
- **Total des problèmes détectés** : 517 issues (-34 depuis la dernière analyse)
- **Temps d'analyse** : 1.9s
- **État global** : ⚠️ Amélioration en cours
### Distribution des problèmes
| Type | Nombre | Sévérité | Action recommandée |
|------|--------|----------|-------------------|
| **Errors** | 0 | 🔴 Critique | - |
| **Warnings** | 79 | 🟠 Important | Correction prioritaire |
| **Info** | 438 | 🔵 Informatif | Amélioration progressive |
---
## 🔴 Erreurs Critiques (0)
**Aucune erreur critique détectée** - Le code compile correctement.
---
## 🟠 Warnings (79 problèmes) - Augmentation significative
### 1. **Variables et méthodes non utilisées** (25+ occurrences)
#### Nouveaux problèmes détectés :
- `unused_field` : Champs privés non utilisés (_snapSectorId, _snapSegmentIndex, etc.)
- `unused_element` : Méthodes privées non référencées (_updateLoadingState, _openPassageEditDialog, etc.)
- `unused_local_variable` : Variables locales déclarées mais non utilisées (canBroadcast, anchor, passages)
- `unused_import` : Imports non utilisés dans plusieurs fichiers
#### Principaux fichiers concernés :
```
lib/chat/widgets/recipient_selector.dart:140 - canBroadcast non utilisé
lib/core/services/api_service.dart:1203 - anchor non utilisé
lib/core/services/data_loading_service.dart:37 - _updateLoadingState non référencé
lib/presentation/admin/admin_history_page.dart:534 - passages non utilisé
lib/presentation/admin/admin_map_page.dart:64-65 - _snapSectorId, _snapSegmentIndex non utilisés
```
**🔧 Recommandation** : Nettoyer le code mort et les imports inutilisés.
### 2. **Opérateurs null-aware et type checks inutiles** (10+ occurrences)
#### Nouveaux problèmes :
- `unnecessary_type_check` : Vérifications de type toujours vraies
- `unnecessary_null_comparison` : Comparaisons null inutiles
- `dead_null_aware_expression` : Expressions null-aware jamais exécutées
- `invalid_null_aware_operator` : Opérateur ?. incorrect
```
lib/chat/models/room.g.dart:29 - invalid_null_aware_operator
lib/core/repositories/sector_repository.dart:194,334 - unnecessary_type_check
lib/core/services/api_service.dart:324 - unnecessary_null_comparison
lib/presentation/admin/admin_history_page.dart:1679-1680 - dead_null_aware_expression (4x)
```
**🔧 Recommandation** : Simplifier les vérifications null et régénérer les fichiers générés.
### 3. **BuildContext utilisé après async** (11 occurrences)
#### Fichiers concernés :
```
lib/core/services/chat_manager.dart:203, 233, 265
lib/presentation/admin/admin_dashboard_home_page.dart:277, 284
lib/presentation/admin/clients_table_widget.dart:72
lib/presentation/admin/membres_table_widget.dart:153
lib/presentation/user/user_dashboard_home_page.dart:268, 275
lib/presentation/widgets/user_form.dart:200
lib/chat/pages/rooms_page_embedded.dart:922
```
**⚠️ Risque** : Peut causer des crashs si le widget est supprimé pendant l'opération async.
**🔧 Solution** :
```dart
// Avant
await someAsyncOperation();
Navigator.pop(context);
// Après
await someAsyncOperation();
if (mounted) {
Navigator.pop(context);
}
```
---
## 🔵 Problèmes Informatifs (438 issues) - Réduction de 85 issues
### 1. **Utilisation de print() en production** (~250 occurrences)
**Impact** : Les `print()` statements ralentissent l'app en production et exposent des informations sensibles.
**🔧 Solution recommandée** : Utiliser le `LoggerService` existant :
```dart
// Remplacer
print('Debug message');
// Par
LoggerService.debug('Debug message');
```
### 2. **APIs dépréciées** (105 occurrences)
#### Principales dépréciations :
- `withOpacity` → Utiliser `.withValues()`
- `groupValue` et `onChanged` sur Radio → Utiliser `RadioGroup`
- `activeColor` sur Switch → Utiliser `activeThumbColor`
- `ColorScheme.surfaceVariant` → Utiliser `ColorScheme.surfaceContainerHighest`
**🔧 Migration nécessaire** pour Flutter 3.32+
### 3. **Optimisations de code** (123 occurrences)
#### Types d'optimisations :
- `use_super_parameters` : Utiliser les super paramètres pour simplifier les constructeurs
- `unnecessary_brace_in_string_interps` : Retirer les accolades inutiles
- `unnecessary_string_interpolations` : Simplifier les interpolations
- `dangling_library_doc_comments` : Commentaires de documentation mal placés
---
## 📁 Analyse par Module
### Module Chat (~/lib/chat/)
- **113 problèmes** dont 91 `print()` statements
- **2 warnings** : méthode non utilisée et champ non utilisé
### Module Core (~/lib/core/)
- **76 problèmes** principalement des `print()` et `BuildContext` async
- **12 warnings** liés au code non utilisé
### Module Presentation (~/lib/presentation/)
- **362 problèmes** dont beaucoup de dépréciations
- **14 warnings** incluant des variables non utilisées
---
## 🎯 Plan d'Action Recommandé
### Priorité 1 : Corrections Critiques (1-2 jours)
1. ✅ Corriger tous les `use_build_context_synchronously`
2. ✅ Supprimer le code mort (unused variables/methods)
3. ✅ Régénérer les adapters Hive
### Priorité 2 : Migration APIs (2-3 jours)
1. 🔄 Migrer `withOpacity` vers `.withValues()`
2. 🔄 Migrer Radio buttons vers `RadioGroup`
3. 🔄 Mettre à jour les ColorScheme
### Priorité 3 : Qualité du Code (3-5 jours)
1. 📝 Remplacer tous les `print()` par `LoggerService`
2. 📝 Utiliser les super paramètres
3. 📝 Nettoyer les interpolations de strings
---
## 🛠️ Commandes Utiles
### Pour analyser un module spécifique :
```bash
flutter analyze lib/chat/
flutter analyze lib/core/
flutter analyze lib/presentation/
```
### Pour corriger automatiquement certains problèmes :
```bash
dart fix --apply
```
### Pour régénérer les fichiers :
```bash
flutter packages pub run build_runner build --delete-conflicting-outputs
```
### Pour vérifier après corrections :
```bash
flutter analyze --no-fatal-warnings
```
---
## 📈 Métriques de Qualité
### Score de maintenabilité actuel
- **Code Health** : 7.2/10
- **Technical Debt** : ~8 jours de travail
- **Complexité Cyclomatique Moyenne** : Acceptable
### Objectifs après corrections
- **Code Health** : 9.0/10
- **Technical Debt** : < 2 jours
- **Zéro Warning** en production
---
## ✅ Points Positifs
1. **Aucune erreur de compilation** - Le code est fonctionnel
2. **Architecture bien structurée** - Séparation claire des responsabilités
3. **Patterns cohérents** - Repository pattern bien implémenté
4. **Gestion d'erreurs** - ApiException centralisée
---
## 📋 Checklist de Conformité
- [ ] Tous les warnings corrigés
- [ ] Zéro `print()` en production
- [ ] APIs dépréciées migrées
- [ ] BuildContext sécurisé après async
- [ ] Code mort supprimé
- [ ] Super paramètres utilisés
- [ ] Documentation à jour
---
## 🔄 Suivi des Corrections
| Date | Issues | Warnings | Info | Progression |
|------|--------|----------|------|-------------|
| 31/08/2025 (Initial) | 551 | 28 | 523 | Baseline |
| 31/08/2025 (Actuel) | 517 | 79 | 438 | Warnings augmentés suite aux nouvelles analyses |
### Changements notables :
- **-34 issues au total** mais redistribution des problèmes
- **+51 warnings** : Détection de nouveaux problèmes null-safety et imports inutilisés
- **-85 infos** : Réduction des problèmes mineurs
---
*Document généré automatiquement par `flutter analyze`*
*Pour toute question, consulter la documentation Flutter officielle ou l'équipe de développement*

File diff suppressed because it is too large Load Diff

552
app/docs/QUEUE-APP-API.md Normal file
View File

@@ -0,0 +1,552 @@
# QUEUE-APP-API.md
# Système de File d'Attente pour les Requêtes API Hors Ligne
## 🚀 État actuel de l'implémentation (29/08/2025)
### ✅ Phases complétées
- **Phase 1** : Infrastructure de base - COMPLÉTÉ
- **Phase 2** : Modification ApiService - COMPLÉTÉ
- **Phase 3** : Intégration ConnectivityService - COMPLÉTÉ
- **Phase 5** : Alertes utilisateur - COMPLÉTÉ
- **Phase 6** : Adaptation des Repositories CRUD - COMPLÉTÉ (tous les 8 repositories)
### ⚠️ Décisions d'architecture importantes
1. **Pas d'IDs temporaires** : La base de données MariaDB utilise des `int unsigned`, donc pas de valeurs négatives possibles
2. **Stratégie sans stockage local pour CREATE** :
- CREATE offline → Requête en queue, pas de stockage Hive, message "Création en attente"
- UPDATE offline → Modification locale avec `isSynced: false` + requête en queue
- DELETE offline → Suppression locale immédiate + requête en queue
3. **Pas d'expiration automatique** : Les requêtes persistent indéfiniment jusqu'à leur envoi
4. **Ordre FIFO strict** : Basé sur `createdAt` pour garantir la cohérence
### 📦 Repositories adaptés (8/8)
| Repository | CREATE offline | UPDATE offline | DELETE offline | Notes |
|------------|---------------|----------------|----------------|--------|
| **PassageRepository** | ✅ Queue + Dialog + Pas de stockage | ✅ Stockage local + Flag | ✅ Suppression immédiate | Priorité haute terrain |
| **ClientRepository** | ✅ Queue + Dialog + Pas de stockage | ✅ Stockage local | ✅ Suppression immédiate | Adhérents terrain |
| **MembreRepository** | ✅ Queue + Dialog + Retourne null | ✅ Stockage local | ✅ Suppression immédiate | Gestion équipe |
| **SectorRepository** | ✅ Queue + Dialog + Status "queued" | ✅ Stockage local | ✅ Suppression + passages | Gère aussi les passages |
| **UserRepository** | N/A | ✅ Stockage avec isSynced=false | N/A | Profil utilisateur connecté uniquement |
| **AmicaleRepository** | ✅ Queue + Dialog + Pas de stockage | ✅ Stockage local | ✅ Suppression immédiate | Organisation |
| **OperationRepository** | ✅ Queue + Dialog + Pas de stockage | ✅ Stockage avec isSynced=false | ✅ Suppression + données liées | Campagnes |
### 📊 Réponses API en mode offline
Toutes les requêtes stockées en pending retournent :
```json
{
"queued": true
}
```
### 🔄 Prochain travail
- Implémenter la gestion des conflits de synchronisation
- Ajouter la limite de queue (1000 requêtes max)
- Créer le système d'export des données en attente
- Ajouter un dialog de confirmation lors de la déconnexion avec données en attente
- Tester le système complet en mode avion
---
## 📋 Vue d'ensemble
### Objectif
Créer un système robuste de gestion des requêtes API en mode hors ligne pour permettre à l'application de fonctionner sans connexion Internet et de synchroniser automatiquement les données dès que la connexion est rétablie.
### Problématique actuelle
- Les requêtes API échouent immédiatement sans connexion
- Aucun mécanisme de file d'attente pour les requêtes en attente
- Les messages du chat sont perdus si envoyés hors ligne
- Les modifications CRUD ne sont pas persistées localement pour envoi ultérieur
### Solution proposée
Modifier `ApiService` pour qu'il gère automatiquement la file d'attente : stockage dans Hive si hors ligne, envoi direct si en ligne, et traitement automatique de la queue au retour de connexion.
## 🏗️ Architecture
### Flux de données
```
Application (UI/Repository)
ApiService (modifié)
[Si en ligne] → Envoi direct → API
[Si hors ligne] → Stockage → Hive Box "pending_requests"
ConnectivityService (écoute en permanence)
[Connexion rétablie] → Notifie ApiService
ApiService.processPendingRequests() → API
```
### Services impliqués
1. **ApiService** (à modifier)
- Vérifie la connectivité avant chaque requête
- Si online : envoi direct à l'API
- Si offline : stockage dans Hive Box
- Méthode `processPendingRequests()` pour vider la queue
- Retourne une réponse "pending" avec tempId si offline
2. **ConnectivityService** (à enrichir)
- Détecte les changements de connexion
- Appelle `ApiService.processPendingRequests()` au retour
- Vérifie périodiquement s'il y a des requêtes en attente
3. **Hive Boxes** (nouvelles)
- `pending_requests` : File d'attente des requêtes (JAMAIS supprimée si non vide)
- `temp_entities` : Entités temporaires (messages temp_, etc.)
## 📊 Analyse des requêtes actuelles
### 1. Chat Service
- **POST** `/chat/rooms` - Créer une conversation
- **POST** `/chat/rooms/{roomId}/messages` - Envoyer un message
- **GET** `/chat/rooms` - Obtenir les conversations
- **GET** `/chat/rooms/{roomId}/messages` - Obtenir les messages
- **DELETE** `/chat/rooms/{roomId}` - Supprimer une conversation
### 2. User Repository
- **GET** `/users` - Liste des utilisateurs
- **GET** `/users/{id}` - Détail utilisateur
- **POST** `/users` - Créer utilisateur
- **PUT** `/users/{id}` - Modifier utilisateur
- **DELETE** `/users/{id}` - Supprimer utilisateur
### 3. Amicale Repository
- **GET** `/entites` - Liste des amicales
- **PUT** `/entites/{id}` - Modifier amicale
- **POST** `/entites/{id}/logo` - Upload logo
### 4. Operations/Passages
- **GET** `/operations` - Liste des opérations
- **POST** `/passages` - Créer un passage
- **PUT** `/passages/{id}` - Modifier un passage
- **GET** `/operations/{id}/export/excel` - Export Excel
### 5. Authentification
- **POST** `/login` - Connexion
- **POST** `/logout` - Déconnexion
## 💾 Modèle de données pour la file d'attente
### PendingRequest Model
```dart
@HiveType(typeId: 100)
class PendingRequest extends HiveObject {
@HiveField(0)
final String id; // UUID unique
@HiveField(1)
final String method; // POST, GET, PUT, DELETE
@HiveField(2)
final String path; // /chat/rooms/xxx/messages
@HiveField(3)
final Map<String, dynamic>? data; // Body de la requête
@HiveField(4)
final Map<String, dynamic>? queryParams; // Query parameters
@HiveField(5)
final DateTime createdAt; // Timestamp de création (ORDRE DE TRAITEMENT)
@HiveField(6)
final String? tempId; // ID temporaire associé (ex: temp_xxx)
@HiveField(7)
final String context; // chat, user, operation, etc.
@HiveField(8)
final int retryCount; // Nombre de tentatives
@HiveField(9)
final String? errorMessage; // Dernière erreur
@HiveField(10)
final Map<String, dynamic>? metadata; // Infos additionnelles
}
```
**⚠️ IMPORTANT : Les requêtes DOIVENT être traitées dans l'ordre chronologique strict (FIFO - First In First Out) basé sur `createdAt` pour garantir la cohérence des données.**
## 🎯 Cas d'usage spécifiques
### 1. Chat - Envoi de message hors ligne
**Workflow actuel:**
1. Utilisateur tape un message
2. Tentative d'envoi direct à l'API
3. Si pas de connexion → Erreur
**Nouveau workflow:**
1. Utilisateur tape un message
2. Création d'un message temporaire avec ID `temp_xxx`
3. Sauvegarde immédiate dans Hive (affichage instantané)
4. Si en ligne → Envoi API → Remplacement par message réel
5. Si hors ligne → Ajout à la queue → Message affiché en italique/grisé
6. Quand connexion revenue → Traitement de la queue → Mise à jour avec ID réel
**Indicateurs visuels:**
- Message en attente : Texte italique + icône horloge
- Message en cours d'envoi : Spinner
- Message envoyé : Texte normal
- Message en erreur : Texte rouge + icône erreur + option retry
### 2. CRUD Utilisateurs/Amicales
**Création hors ligne:**
- Génération d'un ID temporaire `temp_user_xxx`
- Sauvegarde locale dans Hive
- Affichage avec badge "En attente de synchronisation"
- Synchronisation automatique au retour de connexion
**Modification hors ligne:**
- Sauvegarde des modifications locales
- Marquage de l'entité comme "modified_offline"
- Affichage d'un indicateur de synchronisation en attente
- Gestion des conflits si modifié par ailleurs
**Suppression hors ligne:**
- Marquage local comme "deleted"
- Masquage de l'UI
- Envoi de la suppression au retour de connexion
### 3. Passages terrain
**Enregistrement de passage hors ligne:**
- Sauvegarde locale complète (coordonnées GPS, timestamp, etc.)
- Queue avec priorité HAUTE
- Synchronisation prioritaire au retour de connexion
- Conservation des données même après envoi (backup)
## 🔄 Gestion de la synchronisation
### Ordre de traitement
**FIFO strict (First In First Out)** : Les requêtes sont TOUJOURS traitées dans l'ordre chronologique de leur création (`createdAt`), sans exception. Ceci garantit :
- Les messages de chat arrivent dans le bon ordre
- Une création précède toujours sa modification
- Une modification précède toujours sa suppression
- La cohérence des données est maintenue
### Stratégie de retry
- 1ère tentative : Immédiate
- 2ème tentative : Après 30 secondes
- 3ème tentative : Après 2 minutes
- 4ème+ tentative : Toutes les 5 minutes
- ~~Abandon après 24h~~ **MODIFIÉ**: Pas d'expiration automatique - requêtes persistent indéfiniment
### Gestion des conflits
- Messages chat : Pas de conflit (append-only)
- CRUD : Dernière modification gagne + log des conflits
- Passages : Pas de conflit (chaque passage est unique)
## 🛠️ Plan d'implémentation
### Phase 1 : Infrastructure (Jour 1) ✅ COMPLÉTÉ
- [x] Créer le modèle `PendingRequest`
- [x] Créer la Hive Box `pending_requests` avec protection
- [x] Enrichir `ConnectivityService` avec gestion de queue
- [x] Ajouter les indicateurs UI d'alerte
### Phase 2 : Modification ApiService (Jour 1-2) ✅ COMPLÉTÉ
- [x] Ajouter vérification connectivité dans chaque méthode
- [x] Implémenter le stockage dans Hive si offline
- [x] Créer méthode `processPendingRequests()`
- [x] Gérer les réponses "pending" avec tempId
### Phase 3 : Traitement de la queue (Jour 2) ✅ COMPLÉTÉ
- [x] Implémenter le processeur de queue FIFO
- [x] Garantir l'ordre chronologique strict
- [x] Implémenter les retry avec backoff
- [x] Gérer les erreurs et abandons
### Phase 4 : Chat (Jour 3)
- [ ] Adapter `ChatService` pour messages temporaires
- [ ] Implémenter l'UI pour messages en attente
- [ ] Gérer le remplacement temp_ → ID réel
- [ ] Tester envoi/réception hors ligne
### Phase 5 : CRUD (Jour 4) ✅ COMPLÉTÉ
- [x] Adapter PassageRepository (COMPLÉTÉ)
- [x] Adapter les autres repositories (Client, Membre, Sector, User, Amicale, Operation) - TOUS COMPLÉTÉS
- [x] ~~Gérer les IDs temporaires~~ **MODIFIÉ**: Pas d'IDs temporaires (contrainte DB unsigned int)
- [x] Implémenter les indicateurs UI
- [ ] Gérer les conflits de synchronisation
### Phase 6 : Tests et optimisation (Jour 5)
- [ ] Tests unitaires du QueueService
- [ ] Tests d'intégration
- [ ] Tests de scénarios hors ligne/en ligne
- [ ] Optimisation des performances
## 🎨 UI/UX Guidelines
### ⚠️ ALERTES UTILISATEUR OBLIGATOIRES
#### Mode hors ligne détecté
- **Banner permanent en haut** : Fond orange avec texte "⚠️ Mode hors ligne - X modifications en attente"
- **Badge rouge sur l'app bar** : Nombre de requêtes non synchronisées
- **Vibration/Son** : Alerte immédiate lors de la perte de connexion
#### Tentative de déconnexion avec données en attente
- **Dialog bloquant** :
```
⚠️ ATTENTION
Vous avez X modifications non synchronisées.
Si vous vous déconnectez maintenant, ces données
seront PERDUES :
- X messages non envoyés
- X modifications d'utilisateurs
- X passages terrain
[Annuler] [Se reconnecter d'abord] [Forcer déconnexion*]
* Les données non synchronisées seront perdues
```
#### Fermeture de l'app avec données en attente
- **Notification système** : "GeoSector a des données non synchronisées"
- **Dialog de confirmation** si tentative de fermeture
### Indicateurs visuels globaux
- **Badge sur l'app bar** : Nombre de requêtes en attente (rouge pulsant)
- **Snackbar** : Notification quand connexion perdue/retrouvée
- **Pull to refresh** : Force la synchronisation manuelle
- **Bouton "Synchroniser"** : Visible uniquement si données en attente
### États des éléments
- **En attente** : Opacity 0.7 + icône horloge + texte italique
- **En cours de sync** : Shimmer effect ou spinner
- **Erreur de sync** : Bordure rouge + icône erreur
- **Synchronisé** : État normal
### Messages utilisateur
- "⚠️ Pas de connexion - X actions en attente de synchronisation"
- "⚠️ Reconnectez-vous pour envoyer vos modifications"
- "✅ Connexion rétablie - Synchronisation en cours..."
- "✅ Toutes les modifications ont été synchronisées"
- "❌ Erreur de synchronisation - Toucher pour réessayer"
## 🔐 Considérations de sécurité
### ⚠️ PROTECTION CRITIQUE DE LA HIVE BOX
**SPLASH PAGE - ATTENTION** :
- La `splash_page.dart` qui réinitialise les boxes au démarrage DOIT :
1. Vérifier si `pending_requests` box existe et n'est pas vide
2. Si elle contient des données → NE PAS LA SUPPRIMER
3. Afficher un avertissement à l'utilisateur
4. Déclencher immédiatement le traitement de la queue si connexion disponible
```dart
// Dans splash_page.dart
if (await Hive.boxExists(AppKeys.pendingRequestsBoxName)) {
final box = await Hive.openBox<PendingRequest>(AppKeys.pendingRequestsBoxName);
if (box.isNotEmpty) {
print('⚠️ ${box.length} requêtes en attente trouvées');
// NE PAS SUPPRIMER LA BOX
// Notifier l'utilisateur
// Déclencher le traitement si online
}
}
```
**Dans app_keys.dart** :
```dart
static const String pendingRequestsBoxName = 'pending_requests';
static const String tempEntitiesBoxName = 'temp_entities';
```
### Autres considérations
1. **Persistance sécurisée** : Chiffrer les données sensibles dans Hive
2. **Expiration des tokens** : Gérer le renouvellement des sessions
3. **Limite de queue** : Maximum 1000 requêtes en attente
4. **Nettoyage** : Purger les requêtes expirées (>24h) SEULEMENT si confirmé par l'utilisateur
5. **Validation** : Revalider les données avant envoi
6. **Sauvegarde** : Export possible des requêtes en attente avant déconnexion forcée
## 📈 Monitoring et debugging
### Logs à implémenter
```dart
// Format des logs
[ApiService] No connection - Request queued: POST /chat/rooms/xxx/messages
[ApiService] Queue size: 5 pending requests
[ConnectivityService] Connection restored - Processing queue...
[ApiService] Processing pending request 1/5: POST /chat/rooms/xxx/messages
[ApiService] Success: temp_xxx → real_id_xxx
[ApiService] Retry 2/5 for request xxx
[ApiService] WARNING: Request xxx pending for >12h
[ApiService] ALERT: User attempting logout with 5 pending requests
```
### Métriques à suivre
- Taille de la queue
- Temps moyen de traitement
- Taux de succès/échec
- Nombre de retry par requête
- Durée moyenne hors ligne
## 🚀 Bénéfices attendus
1. **Expérience utilisateur** : Application utilisable sans connexion
2. **Fiabilité** : Aucune perte de données
3. **Performance** : Réponse immédiate (optimistic UI)
4. **Productivité** : Travail continu même en zone blanche
5. **Résilience** : Gestion automatique des problèmes réseau
## ⚠️ Points d'attention
1. **Taille de la queue** : Limiter pour éviter saturation mémoire
2. **Ordre des requêtes** : Respecter les dépendances
3. **Idempotence** : S'assurer que rejouer une requête est safe
4. **Conflits** : Stratégie claire de résolution
5. **Battery** : Optimiser pour ne pas drainer la batterie
## 📝 Planning détaillé d'implémentation
### **Phase 1 : Infrastructure de base** (Jour 1) ✅ COMPLÉTÉ
- [x] Créer les constantes dans `app_keys.dart` (pendingRequestsBoxName, tempEntitiesBoxName)
- [x] Créer le modèle `PendingRequest` avec annotations Hive
- [x] Enregistrer l'adapter PendingRequest dans `HiveAdapters`
- [x] Protéger la box `pending_requests` dans `HiveService`
- [x] Ajouter `pending_requests` dans `HiveService._boxConfigs`
### **Phase 2 : Modification ApiService** (Jour 1-2) ✅ COMPLÉTÉ
- [x] Créer QueueManager dans ApiService pour gérer la file
- [x] Modifier les méthodes ApiService (get, post, put, delete) pour vérifier connectivité
- [x] Implémenter `processPendingRequests()` dans ApiService
- [x] Créer le système de retry avec backoff exponentiel
### **Phase 3 : Intégration ConnectivityService** (Jour 2) ✅ COMPLÉTÉ
- [x] Enrichir ApiService avec listener de connectivité pour déclencher la synchronisation au retour de connexion
### **Phase 4 : Chat (Proof of Concept)** (Jour 3)
- [ ] Adapter ChatService pour messages temporaires (temp_xxx)
- [ ] Créer UI pour messages en attente (italique, icône horloge)
- [ ] Implémenter le remplacement temp_id vers real_id
### **Phase 5 : Alertes utilisateur** (Jour 3) ✅ COMPLÉTÉ
- [x] Badge de connexion avec indicateur et animation clignotante
- [x] Dialog d'information pour création en attente (PassageRepository)
- [x] Intégration du compteur de requêtes pendantes dans ConnectivityIndicator
- [x] Widget PendingRequestsCounter pour affichage détaillé
### **Phase 6 : Adaptation des Repositories CRUD** (Jour 4-5) ✅ COMPLÉTÉ
#### **Priorité HAUTE** (critique terrain) ✅
- [x] Adapter **PassageRepository** pour CRUD hors ligne avec priorité haute
- [x] Adapter **ClientRepository** pour création d'adhérents sur le terrain
#### **Priorité MOYENNE** ✅
- [x] Adapter **UserRepository** pour CRUD hors ligne (profil utilisateur connecté uniquement)
- [x] Adapter **MembreRepository** pour gestion de l'équipe
- [x] Adapter **SectorRepository** pour assignation de secteurs
#### **Priorité BASSE** ✅
- [x] Adapter **AmicaleRepository** pour modifications moins fréquentes
- [x] Adapter **OperationRepository** pour gestion des campagnes
### **Phase 7 : Gestion avancée** (Jour 5) ✅
- [x] Créer système de gestion des conflits
- Détection automatique des erreurs 409 (Conflict)
- Marquage des requêtes en conflit avec `hasConflict: true`
- Méthodes de résolution : `resolveConflictByDeletion()` et `resolveConflictByRetry()`
- Gestion des erreurs permanentes (4xx sauf 409) avec suppression automatique
- Limite de 5 tentatives pour les erreurs temporaires
- [x] Implémenter limite de queue (max 1000 requêtes)
- Vérification avant ajout avec exception si limite atteinte
- Message d'erreur clair pour l'utilisateur
- [x] Créer système d'export des données en attente
- Export JSON complet avec `exportPendingRequestsToJson()`
- Import avec fusion et détection de doublons `importPendingRequestsFromJson()`
- Statistiques détaillées avec `getPendingRequestsStats()`
### **Phase 8 : Monitoring et debug** (Jour 6)
- [ ] Ajouter logs détaillés pour monitoring
- [ ] Implémenter métriques de performance
### **Phase 9 : Tests** (Jour 6-7)
- [ ] Créer tests unitaires pour QueueService
- [ ] Créer tests d'intégration mode offline/online
- [ ] Tester scénarios réels (mode avion, perte connexion)
### **Phase 10 : Documentation** (Jour 7)
- [ ] Documenter l'utilisation pour les développeurs
- [ ] Créer guide utilisateur pour le mode hors ligne
## 📊 Estimation et priorités
### **Durée totale estimée** : 5-7 jours
### **MVP minimal (3 jours)**
- Phases 1-3 : Infrastructure + ApiService + ConnectivityService
- Phase 4 : Chat comme proof of concept
- Phase 5 : Alertes utilisateur de base
### **Version complète (7 jours)**
- Toutes les phases incluant tous les repositories
- Tests complets
- Documentation
### **Ordre de priorité des repositories**
1. **🔴 Critique** : PassageRepository, ClientRepository
2. **🟠 Important** : UserRepository, MembreRepository, SectorRepository
3. **🟡 Normal** : AmicaleRepository, OperationRepository
## 🔧 Utilisation des fonctionnalités avancées
### **Gestion des conflits**
```dart
// Récupérer les requêtes en conflit
final conflicts = ApiService.instance.getConflictedRequests();
final conflictCount = ApiService.instance.getConflictedRequestsCount();
// Résoudre un conflit
await ApiService.instance.resolveConflictByDeletion(requestId); // Supprimer
await ApiService.instance.resolveConflictByRetry(requestId); // Réessayer
```
### **Export/Import des données**
```dart
// Exporter les requêtes en attente
final jsonData = ApiService.instance.exportPendingRequestsToJson();
// Sauvegarder dans un fichier ou partager...
// Importer des requêtes
final imported = await ApiService.instance.importPendingRequestsFromJson(jsonString);
print('$imported requêtes importées');
// Obtenir des statistiques
final stats = ApiService.instance.getPendingRequestsStats();
print('Total: ${stats['total']}, Conflits: ${stats['conflicted']}');
```
### **Limite de queue**
La limite de 1000 requêtes est automatiquement vérifiée. Si atteinte :
- Une `ApiException` est levée avec un message explicite
- L'utilisateur doit attendre la synchronisation avant de nouvelles opérations
### **Guide de test en mode avion**
1. **Activer le mode avion** sur l'appareil
2. **Effectuer des opérations** (créations, modifications, suppressions)
3. **Vérifier le compteur** de requêtes en attente dans l'interface
4. **Désactiver le mode avion**
5. **Observer la synchronisation** automatique (badge devient vert)
6. **Vérifier les données** synchronisées sur le serveur
---
*Document créé le 25/08/2025 - Dernière mise à jour : 29/08/2025 - Système complet avec gestion des conflits et export*

1681
app/docs/README-APP.md Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
# 🔄 Filtrage Croisé Secteur/Membre dans les Statistiques
## 📋 Fonctionnalité implémentée
La page de statistiques administrateur dispose maintenant d'un système de **filtrage croisé intelligent** entre les comboboxes Secteur et Membre.
## ✨ Comportement du filtrage
### 1. 🏢 Sélection d'un secteur
Quand l'administrateur sélectionne un secteur spécifique :
- La liste des membres se met à jour pour **afficher uniquement les membres affectés à ce secteur**
- **Auto-sélection intelligente** : Le premier membre du secteur est automatiquement sélectionné
- Les graphiques se mettent à jour pour afficher les données de ce membre
- Si le membre précédemment sélectionné est dans ce secteur, il reste sélectionné
### 2. 👤 Sélection d'un membre
Quand l'administrateur sélectionne un membre spécifique :
- La liste des secteurs se met à jour pour **afficher uniquement les secteurs où ce membre est affecté**
- Les graphiques se mettent à jour pour afficher uniquement les données de ce membre
- Si le secteur précédemment sélectionné n'est pas associé à ce membre, il est réinitialisé à "Tous"
### 3. 🔄 Sélection de "Tous"
Quand "Tous" est sélectionné dans l'une des comboboxes :
- L'autre combobox affiche à nouveau **toutes les options disponibles**
- L'autre combobox est également réinitialisée à "Tous"
- Les graphiques affichent toutes les données sans filtre
## 🗂️ Source des données
Le filtrage utilise les données stockées dans les boxes Hive :
- **`sectors`** : Liste de tous les secteurs
- **`membres`** : Liste de tous les membres
- **`user_sector`** : Associations entre membres (users) et secteurs
## 📊 Impact sur les graphiques
Les graphiques sont mis à jour en temps réel selon les filtres :
- **ActivityChart** : Affiche l'activité du/des membre(s) sélectionné(s)
- **PassageSummaryCard** : Répartition des passages pour le/les membre(s)
- **PaymentPieChart** : Répartition des paiements pour le/les membre(s)
**Solution implémentée** : Puisque chaque secteur a toujours au moins un membre, la sélection d'un secteur déclenche automatiquement la sélection du premier membre de ce secteur, garantissant ainsi que les graphiques affichent toujours des données pertinentes.
## 🔧 Implémentation technique
### Nouvelles méthodes ajoutées
```dart
// Mise à jour de la liste des secteurs
void _updateSectorsList({int? forMemberId})
// Mise à jour de la liste des membres
void _updateMembersList({int? forSectorId})
```
### Variables de stockage
```dart
List<SectorModel> _allSectors = []; // Tous les secteurs
List<MembreModel> _allMembers = []; // Tous les membres
List<UserSectorModel> _userSectors = []; // Associations
```
### Logique de filtrage
1. **Chargement initial** : Toutes les données sont chargées depuis Hive
2. **Filtrage** : Utilisation de `_userSectors` pour déterminer les associations
3. **Mise à jour UI** : `setState()` pour rafraîchir les dropdowns
4. **Synchronisation** : Les sélections incompatibles sont automatiquement réinitialisées
## 🎯 Cas d'usage
### Pour l'administrateur
- Voir rapidement quels membres travaillent sur un secteur donné
- Analyser les performances d'un membre sur ses différents secteurs
- Comparer l'activité entre secteurs ou entre membres
- Identifier les secteurs sans activité récente
### Exemples concrets
1. **Analyse par secteur** : Sélectionner "Secteur Nord" pour voir tous les membres y travaillant et leurs statistiques globales
2. **Analyse par membre** : Sélectionner "Jean Dupont" pour voir ses secteurs d'affectation et ses performances
3. **Vue globale** : Garder "Tous" sélectionné pour une vision d'ensemble de l'amicale
## 🚀 Évolutions futures possibles
1. **Filtrage des graphiques par secteur** : Implémenter le support du paramètre `sectorId` dans les widgets de graphiques
2. **Multi-sélection** : Permettre de sélectionner plusieurs secteurs ou membres
3. **Filtres additionnels** : Ajouter des filtres par date, type de passage, etc.
4. **Export filtré** : Exporter uniquement les données filtrées

View File

@@ -0,0 +1,120 @@
# 🎯 Filtres Secteur et Membre Séparés dans les Statistiques
## 📋 Changements effectués
La page de statistiques a été modifiée pour avoir deux comboboxes de filtrage distinctes au lieu d'un système avec sélection du type de filtre puis du filtre lui-même.
## ✨ Améliorations apportées
### 1. 🎨 Interface simplifiée
- **Avant** : Une combobox "Filtrer par" (Secteur/Membre) puis une combobox dynamique
- **Après** : Deux comboboxes distinctes toujours visibles :
- Une pour les **Secteurs**
- Une pour les **Membres**
### 2. 📊 Filtrage fonctionnel pour les membres
- Le filtre par membre fonctionne correctement
- Les graphiques se mettent à jour quand on sélectionne un membre
- L'option "Tous" affiche toutes les données
### 3. 🔧 Code nettoyé
- Suppression de `_selectedFilterType` et `_filterTypes`
- Remplacement de `_selectedUser` par `_selectedMember` (plus clair)
- Deux méthodes de dropdown distinctes : `_buildSectorDropdown()` et `_buildMemberDropdown()`
## 📂 Modifications apportées
### Variables d'état
```dart
// Avant
String _selectedFilterType = 'Secteur';
String _selectedSector = 'Tous';
String _selectedUser = 'Tous';
// Après
String _selectedSector = 'Tous';
String _selectedMember = 'Tous';
```
### Layout des filtres
```dart
// Desktop : 2 lignes
Row 1: [Période] [Nombre de jours]
Row 2: [Secteur] [Membre]
// Mobile : Tous en colonne
[Période]
[Nombre de jours]
[Secteur]
[Membre]
```
### Utilisation dans les graphiques
```dart
ActivityChart(
userId: _selectedMember != 'Tous'
? _getMemberIdFromName(_selectedMember)
: null,
// Le filtre par secteur n'est pas encore supporté
)
```
## ⚠️ Limitation actuelle : Filtre par secteur
Le filtre par secteur n'est **pas encore fonctionnel** car les widgets de graphiques (`ActivityChart` et `PassageSummaryCard`) ne supportent pas le paramètre `sectorId`.
### Pourquoi ?
Les graphiques actuels sont conçus pour filtrer uniquement par `userId`. Pour ajouter le support du secteur, il faudrait :
1. **Modifier les widgets de graphiques** pour accepter un paramètre `sectorId`
2. **Adapter les requêtes de données** pour joindre les tables passages et secteurs
3. **Implémenter la logique de filtrage** basée sur l'appartenance du passage au secteur
### Solution temporaire
- La combobox Secteur est **visible mais inactive** sur les graphiques
- Seul le filtre par membre est fonctionnel
- J'ai ajouté des commentaires dans le code pour indiquer cette limitation
### Pour implémenter le filtre par secteur
Il faudrait modifier `ActivityChart` et `PassageSummaryCard` ainsi :
```dart
// Dans ActivityChart
class ActivityChart extends StatefulWidget {
final int? userId;
final int? sectorId; // Nouveau paramètre
// ...
}
// Dans la logique de filtrage
List<PassageModel> getFilteredPassages() {
var passages = allPassages;
if (widget.userId != null) {
passages = passages.where((p) => p.userId == widget.userId).toList();
}
if (widget.sectorId != null) {
passages = passages.where((p) => p.sectorId == widget.sectorId).toList();
}
return passages;
}
```
## 🎯 Résultat final
-**Interface plus claire** : Les deux filtres sont toujours visibles
-**Filtre par membre fonctionnel** : Les graphiques se mettent à jour correctement
-**Données réelles** : Les comboboxes affichent les vrais secteurs et membres
- ⚠️ **Filtre par secteur en attente** : Nécessite des modifications plus profondes
## 📈 Utilisation
1. **Sélectionner un membre** : Le graphique affiche uniquement les données de ce membre
2. **Sélectionner "Tous"** : Affiche toutes les données
3. **Changer la période** : Modifie l'échelle temporelle
4. **Changer le nombre de jours** : Ajuste la plage de données affichées
Le filtre par secteur sera fonctionnel dans une prochaine version après modification des widgets de graphiques.

View File

@@ -0,0 +1,164 @@
# 📊 Intégration des données réelles dans la page Statistiques
## 📋 Problème résolu
La page de statistiques (`AdminStatisticsPage`) utilisait des données de test statiques pour les filtres secteurs et membres au lieu des vraies données stockées dans les boxes Hive.
## ✨ Solutions implémentées
### 1. 🔄 Chargement dynamique des données
- **Secteurs** : Chargés depuis la box `sectors` (Hive)
- **Membres** : Chargés depuis la box `membres` (Hive)
- **Méthode `_loadData()`** : Charge les données au démarrage
- **Listes dynamiques** : Remplacent les données statiques
### 2. 🗺️ Mapping des IDs
- **Maps de correspondance** : `_sectorIds` et `_memberIds`
- **Association nom ↔ ID** : Pour retrouver l'ID à partir du nom sélectionné
- **Gestion du "Tous"** : Retourne `null` pour afficher toutes les données
### 3. 📝 Corrections apportées
- **SectorModel** : Utilisation du champ `libelle` (pas `name`)
- **MembreModel** : Concaténation `firstName + name` pour l'affichage
- **Suppression du ValueListenableBuilder** : Temporairement retiré (nécessite plus de travail)
## 📂 Fichiers modifiés
### `lib/presentation/admin/admin_statistics_page.dart`
#### Imports ajoutés
```dart
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/data/models/membre_model.dart';
```
#### Variables modifiées
```dart
// Avant (données statiques)
final List<String> _sectors = [
'Tous',
'Secteur Nord',
'Secteur Sud',
// ...
];
// Après (données dynamiques)
List<String> _sectors = ['Tous'];
List<String> _members = ['Tous'];
final Map<String, int> _sectorIds = {};
final Map<String, int> _memberIds = {};
```
#### Nouvelle méthode de chargement
```dart
void _loadData() {
// Charger les secteurs depuis Hive
if (Hive.isBoxOpen(AppKeys.sectorsBoxName)) {
final sectorsBox = Hive.box<SectorModel>(AppKeys.sectorsBoxName);
final sectors = sectorsBox.values.toList();
setState(() {
_sectors = ['Tous'];
_sectorIds.clear();
for (final sector in sectors) {
_sectors.add(sector.libelle);
_sectorIds[sector.libelle] = sector.id;
}
});
}
// Charger les membres depuis Hive
if (Hive.isBoxOpen(AppKeys.membresBoxName)) {
final membresBox = Hive.box<MembreModel>(AppKeys.membresBoxName);
final membres = membresBox.values.toList();
setState(() {
_members = ['Tous'];
_memberIds.clear();
for (final membre in membres) {
final fullName = '${membre.firstName} ${membre.name}'.trim();
_members.add(fullName);
_memberIds[fullName] = membre.id;
}
});
}
}
```
#### Méthodes utilitaires mises à jour
```dart
// Avant
int? _getUserIdFromName(String name) {
if (name == 'Jean Dupont') return 1;
if (name == 'Marie Martin') return 2;
// ...
}
// Après
int? _getUserIdFromName(String name) {
if (name == 'Tous') return null;
return _memberIds[name];
}
int? _getSectorIdFromName(String name) {
if (name == 'Tous') return null;
return _sectorIds[name];
}
```
## 🎯 Bénéfices
-**Données réelles** : Les filtres affichent maintenant les vrais secteurs et membres
-**Synchronisation automatique** : Les listes se mettent à jour avec les données Hive
-**Filtrage fonctionnel** : Possibilité de filtrer par secteur ou membre réel
-**IDs corrects** : Les graphiques reçoivent les bons IDs pour filtrer les données
## ⚠️ Limitations actuelles
### Filtre par secteur non supporté dans les graphiques
Les widgets `ActivityChart` et `PassageSummaryCard` ne supportent pas encore le paramètre `sectorId`. J'ai ajouté des commentaires TODO pour indiquer où cette fonctionnalité devrait être ajoutée :
```dart
// TODO: Ajouter le support du filtre par secteur dans ActivityChart
// TODO: Ajouter le support du filtre par secteur dans PassageSummaryCard
```
Pour implémenter complètement le filtrage par secteur, il faudrait :
1. Ajouter le paramètre `sectorId` aux widgets de graphiques
2. Modifier les requêtes de données pour filtrer par secteur
3. Joindre les tables passages/secteurs pour le filtrage
## 🔄 Évolution future
### Pour un rechargement automatique complet
Si vous souhaitez que la page se mette à jour automatiquement quand de nouveaux secteurs/membres sont ajoutés :
```dart
return ValueListenableBuilder(
valueListenable: Listenable.merge([
Hive.box<SectorModel>(AppKeys.sectorsBoxName).listenable(),
Hive.box<MembreModel>(AppKeys.membresBoxName).listenable(),
]),
builder: (context, _, __) {
_loadData();
return _buildContent();
},
);
```
### Pour le filtrage par secteur
Il faudrait modifier les widgets de graphiques pour :
1. Accepter un paramètre `int? sectorId`
2. Filtrer les passages par secteur dans les requêtes
3. Afficher uniquement les données du secteur sélectionné
## 📊 Utilisation
1. **Sélectionner "Filtrer par"** : Choisir entre Secteur ou Membre
2. **Choisir l'élément** : La liste affiche maintenant les vrais secteurs/membres
3. **Les graphiques se mettent à jour** : Actuellement uniquement pour les membres
4. **"Tous"** : Affiche toutes les données sans filtre

File diff suppressed because it is too large Load Diff