Fix: Hive sync et update entité via API REST

- Correction mapping JSON membres (fk_role, chk_active)
- Ajout traitement amicale au login
- Fix callback onSubmit pour sync Hive après update API
This commit is contained in:
d6soft
2025-06-09 18:46:49 +02:00
parent 15a0f2d2be
commit f3f1a9c5e8
56 changed files with 141860 additions and 142386 deletions

View File

@@ -68,10 +68,9 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
}
} catch (e) {
debugPrint('Erreur lors de la récupération de la version: $e');
// Fallback sur la version du AppInfoService si elle existe
if (mounted) {
setState(() {
_appVersion = AppInfoService.fullVersion.split(' ').last; // Extraire juste le numéro
_appVersion = AppInfoService.fullVersion.split(' ').last;
});
}
}
@@ -81,29 +80,24 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
void initState() {
super.initState();
// Animation controller sur 5 secondes (augmenté de 3 à 5 secondes)
// Animation controller
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 5),
);
// Animation de 4x la taille à 1x la taille (augmenté de 3x à 4x)
_scaleAnimation = Tween<double>(
begin: 4.0, // Commencer à 4x la taille
end: 1.0, // Terminer à la taille normale
begin: 4.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeOutBack, // Curve pour un effet de rebond
curve: Curves.easeOutBack,
),
);
// Démarrer l'animation immédiatement
_animationController.forward();
_getAppVersion();
// Simuler le processus d'initialisation
_startInitialization();
}
@@ -114,151 +108,337 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
}
void _startInitialization() async {
// Étape 1: Initialisation des boîtes Hive (0% à 75%)
if (mounted) {
setState(() {
_statusMessage = "Initialisation des données...";
_progress = 0.0;
});
}
// Initialiser toutes les boîtes Hive
await _initializeAllHiveBoxes();
await Future.delayed(const Duration(milliseconds: 500));
// Étape 2: Initialisation des services (75% à 100%)
if (mounted) {
setState(() {
_statusMessage = "Préparation de l'application...";
_progress = 0.75;
});
}
await Future.delayed(const Duration(milliseconds: 500));
// Table rase complète et recréation propre
await _completeReset();
// Finalisation
if (mounted) {
setState(() {
_statusMessage = "Application prête !";
_progress = 1.0;
_isInitializing = false;
_showButtons = true;
_progress = 1.0; // S'assurer que la barre est à 100%
});
}
}
// Méthode pour initialiser toutes les boîtes Hive
Future<void> _initializeAllHiveBoxes() async {
/// RESET COMPLET : Destruction totale et recréation propre
Future<void> _completeReset() async {
try {
debugPrint('Initialisation de toutes les boîtes Hive...');
debugPrint('🧹 RESET COMPLET : Destruction totale des données Hive...');
// Structure pour les boîtes à ouvrir avec leurs noms d'affichage
final boxesToOpen = [
{'name': AppKeys.userBoxName, 'display': 'Préparation utilisateurs'},
{'name': AppKeys.amicaleBoxName, 'display': 'Préparation amicale'},
{'name': AppKeys.clientsBoxName, 'display': 'Préparation clients'},
{'name': AppKeys.regionsBoxName, 'display': 'Préparation régions'},
{'name': AppKeys.operationsBoxName, 'display': 'Préparation opérations'},
{'name': AppKeys.sectorsBoxName, 'display': 'Préparation secteurs'},
{'name': AppKeys.passagesBoxName, 'display': 'Préparation passages'},
{'name': AppKeys.membresBoxName, 'display': 'Préparation membres'},
{'name': AppKeys.userSectorBoxName, 'display': 'Préparation secteurs utilisateurs'},
{'name': AppKeys.settingsBoxName, 'display': 'Préparation paramètres'},
{'name': AppKeys.chatConversationsBoxName, 'display': 'Préparation conversations'},
{'name': AppKeys.chatMessagesBoxName, 'display': 'Préparation messages'},
];
// Étape 1: Sauvegarder les utilisateurs existants (optionnel)
Map<dynamic, UserModel>? existingUsers;
// Calculer l'incrément de progression pour chaque boîte (0.75 / nombre de boîtes)
final progressIncrement = 0.75 / boxesToOpen.length;
double currentProgress = 0.0;
if (mounted) {
setState(() {
_statusMessage = "Sauvegarde des utilisateurs...";
_progress = 0.05;
});
}
// Ouvrir chaque boîte si elle n'est pas déjà ouverte
for (int i = 0; i < boxesToOpen.length; i++) {
final boxName = boxesToOpen[i]['name'] as String;
final displayName = boxesToOpen[i]['display'] as String;
try {
if (Hive.isBoxOpen(AppKeys.userBoxName)) {
final userBox = Hive.box<UserModel>(AppKeys.userBoxName);
existingUsers = Map.from(userBox.toMap());
debugPrint('📦 ${existingUsers.length} utilisateurs sauvegardés');
}
} catch (e) {
debugPrint('⚠️ Erreur sauvegarde utilisateurs: $e');
existingUsers = null;
}
// Mettre à jour la barre de progression et le message
currentProgress = progressIncrement * (i + 1);
// Étape 2: DESTRUCTION RADICALE - Fermer tout ce qui peut être ouvert
if (mounted) {
setState(() {
_statusMessage = "Fermeture de toutes les bases de données...";
_progress = 0.15;
});
}
await _closeAllKnownBoxes();
// Étape 3: DESTRUCTION RADICALE - Supprimer tout Hive du disque
if (mounted) {
setState(() {
_statusMessage = "Suppression complète des anciennes données...";
_progress = 0.25;
});
}
await _nukeHiveCompletely();
// Étape 4: RECRÉATION PROPRE
if (mounted) {
setState(() {
_statusMessage = "Création des nouvelles bases de données...";
_progress = 0.40;
});
}
await _createAllBoxesFresh();
// Étape 5: Restaurer les utilisateurs (optionnel)
if (existingUsers != null && existingUsers.isNotEmpty) {
if (mounted) {
setState(() {
_statusMessage = displayName;
_progress = currentProgress;
_statusMessage = "Restauration des utilisateurs...";
_progress = 0.80;
});
}
if (!Hive.isBoxOpen(boxName)) {
debugPrint('Ouverture de la boîte $boxName ($displayName)...');
// Ouvrir la boîte avec le type approprié
if (boxName == AppKeys.userBoxName) {
await Hive.openBox<UserModel>(boxName);
} else if (boxName == AppKeys.amicaleBoxName) {
await Hive.openBox<AmicaleModel>(boxName);
} else if (boxName == AppKeys.clientsBoxName) {
await Hive.openBox<ClientModel>(boxName);
} else if (boxName == AppKeys.regionsBoxName) {
// Ouvrir la boîte des régions sans type spécifique pour l'instant
// car RegionModelAdapter n'est pas encore enregistré
await Hive.openBox(boxName);
} else if (boxName == AppKeys.operationsBoxName) {
await Hive.openBox<OperationModel>(boxName);
} else if (boxName == AppKeys.sectorsBoxName) {
await Hive.openBox<SectorModel>(boxName);
} else if (boxName == AppKeys.passagesBoxName) {
await Hive.openBox<PassageModel>(boxName);
} else if (boxName == AppKeys.membresBoxName) {
await Hive.openBox<MembreModel>(boxName);
} else if (boxName == AppKeys.userSectorBoxName) {
await Hive.openBox<UserSectorModel>(boxName);
} else if (boxName == AppKeys.chatConversationsBoxName) {
await Hive.openBox<ConversationModel>(boxName);
} else if (boxName == AppKeys.chatMessagesBoxName) {
await Hive.openBox<MessageModel>(boxName);
} else {
await Hive.openBox(boxName);
}
debugPrint('Boîte $boxName ouverte avec succès');
} else {
debugPrint('Boîte $boxName déjà ouverte');
}
// Ajouter une temporisation entre chaque ouverture
await Future.delayed(const Duration(milliseconds: 500));
await _restoreUsers(existingUsers);
}
// Mettre à jour la barre de progression à 0.2 (20%) à la fin
// Étape 6: Vérification finale
if (mounted) {
setState(() {
_statusMessage = 'Toutes les boîtes sont prêtes';
_progress = 0.8;
_statusMessage = "Vérification des bases de données...";
_progress = 0.90;
});
await Future.delayed(const Duration(milliseconds: 500));
}
debugPrint('✅ RESET COMPLET terminé avec succès');
} catch (e) {
debugPrint('❌ Erreur lors du reset complet: $e');
if (mounted) {
setState(() {
_statusMessage = "Préparation de l'application...";
_progress = 0.9;
});
await Future.delayed(const Duration(milliseconds: 500));
}
// Finalisation
if (mounted) {
setState(() {
_statusMessage = "Erreur critique - Redémarrage recommandé";
_progress = 1.0;
_isInitializing = false;
_showButtons = true;
_progress = 1.0;
});
}
debugPrint('Toutes les boîtes Hive sont maintenant ouvertes');
} catch (e) {
debugPrint('Erreur lors de l\'initialisation des boîtes Hive: $e');
}
}
// En cas d'erreur, mettre à jour le message
if (mounted) {
setState(() {
_statusMessage = 'Erreur lors de l\'initialisation des données';
});
/// Ferme toutes les boîtes connues
Future<void> _closeAllKnownBoxes() async {
try {
final allKnownBoxes = [
AppKeys.userBoxName,
AppKeys.amicaleBoxName,
AppKeys.clientsBoxName,
AppKeys.regionsBoxName,
AppKeys.operationsBoxName,
AppKeys.sectorsBoxName,
AppKeys.passagesBoxName,
AppKeys.membresBoxName,
AppKeys.userSectorBoxName,
AppKeys.settingsBoxName,
AppKeys.chatConversationsBoxName,
AppKeys.chatMessagesBoxName,
// Boîtes potentiellement problématiques
'auth', 'locations', 'messages', 'temp'
];
debugPrint('🔒 Fermeture de ${allKnownBoxes.length} boîtes connues...');
for (final boxName in allKnownBoxes) {
try {
if (Hive.isBoxOpen(boxName)) {
await Hive.box(boxName).close();
debugPrint('✅ Boîte $boxName fermée');
}
} catch (e) {
debugPrint('⚠️ Erreur fermeture $boxName: $e');
// Continuer même en cas d'erreur
}
}
await Future.delayed(const Duration(milliseconds: 1000));
} catch (e) {
debugPrint('❌ Erreur fermeture des boîtes: $e');
}
}
/// Suppression RADICALE de tout Hive
Future<void> _nukeHiveCompletely() async {
try {
debugPrint('💥 DESTRUCTION NUCLÉAIRE de Hive...');
if (kIsWeb) {
// En version web, supprimer toutes les boîtes possibles une par une
final allPossibleBoxes = [
AppKeys.userBoxName,
AppKeys.amicaleBoxName,
AppKeys.clientsBoxName,
AppKeys.regionsBoxName,
AppKeys.operationsBoxName,
AppKeys.sectorsBoxName,
AppKeys.passagesBoxName,
AppKeys.membresBoxName,
AppKeys.userSectorBoxName,
AppKeys.settingsBoxName,
AppKeys.chatConversationsBoxName,
AppKeys.chatMessagesBoxName,
// Toutes les boîtes potentiellement corrompues
'auth', 'locations', 'messages', 'temp', 'cache', 'data'
];
for (final boxName in allPossibleBoxes) {
try {
await Hive.deleteBoxFromDisk(boxName);
debugPrint('✅ Boîte $boxName DÉTRUITE');
} catch (e) {
debugPrint('⚠️ Erreur destruction $boxName: $e');
}
}
} else {
// Sur mobile/desktop, destruction totale
try {
await Hive.deleteFromDisk();
debugPrint('✅ Hive COMPLÈTEMENT DÉTRUIT');
} catch (e) {
debugPrint('⚠️ Erreur destruction totale: $e');
// Fallback : supprimer boîte par boîte
await _deleteBoxesOneByOne();
}
}
// Attendre pour s'assurer que tout est détruit
await Future.delayed(const Duration(seconds: 2));
} catch (e) {
debugPrint('❌ Erreur destruction Hive: $e');
}
}
/// Fallback : supprimer les boîtes une par une
Future<void> _deleteBoxesOneByOne() async {
final allBoxes = [
AppKeys.userBoxName,
AppKeys.amicaleBoxName,
AppKeys.clientsBoxName,
AppKeys.regionsBoxName,
AppKeys.operationsBoxName,
AppKeys.sectorsBoxName,
AppKeys.passagesBoxName,
AppKeys.membresBoxName,
AppKeys.userSectorBoxName,
AppKeys.settingsBoxName,
AppKeys.chatConversationsBoxName,
AppKeys.chatMessagesBoxName,
];
for (final boxName in allBoxes) {
try {
await Hive.deleteBoxFromDisk(boxName);
debugPrint('✅ Boîte $boxName supprimée (fallback)');
} catch (e) {
debugPrint('⚠️ Erreur suppression fallback $boxName: $e');
}
}
}
/// Recrée toutes les boîtes VIDES et PROPRES
Future<void> _createAllBoxesFresh() async {
try {
debugPrint('🆕 Création de toutes les boîtes vides...');
final boxesToCreate = [
{'name': AppKeys.userBoxName, 'type': 'UserModel'},
{'name': AppKeys.amicaleBoxName, 'type': 'AmicaleModel'},
{'name': AppKeys.clientsBoxName, 'type': 'ClientModel'},
{'name': AppKeys.regionsBoxName, 'type': 'dynamic'},
{'name': AppKeys.operationsBoxName, 'type': 'OperationModel'},
{'name': AppKeys.sectorsBoxName, 'type': 'SectorModel'},
{'name': AppKeys.passagesBoxName, 'type': 'PassageModel'},
{'name': AppKeys.membresBoxName, 'type': 'MembreModel'},
{'name': AppKeys.userSectorBoxName, 'type': 'UserSectorModel'},
{'name': AppKeys.settingsBoxName, 'type': 'dynamic'},
{'name': AppKeys.chatConversationsBoxName, 'type': 'ConversationModel'},
{'name': AppKeys.chatMessagesBoxName, 'type': 'MessageModel'},
];
final progressIncrement = 0.35 / boxesToCreate.length; // De 0.40 à 0.75
for (int i = 0; i < boxesToCreate.length; i++) {
final boxInfo = boxesToCreate[i];
final boxName = boxInfo['name'] as String;
final boxType = boxInfo['type'] as String;
if (mounted) {
setState(() {
_statusMessage = "Création de $boxName...";
_progress = 0.40 + (progressIncrement * i);
});
}
try {
// Créer la boîte avec le bon type
switch (boxType) {
case 'UserModel':
await Hive.openBox<UserModel>(boxName);
break;
case 'AmicaleModel':
await Hive.openBox<AmicaleModel>(boxName);
break;
case 'ClientModel':
await Hive.openBox<ClientModel>(boxName);
break;
case 'OperationModel':
await Hive.openBox<OperationModel>(boxName);
break;
case 'SectorModel':
await Hive.openBox<SectorModel>(boxName);
break;
case 'PassageModel':
await Hive.openBox<PassageModel>(boxName);
break;
case 'MembreModel':
await Hive.openBox<MembreModel>(boxName);
break;
case 'UserSectorModel':
await Hive.openBox<UserSectorModel>(boxName);
break;
case 'ConversationModel':
await Hive.openBox<ConversationModel>(boxName);
break;
case 'MessageModel':
await Hive.openBox<MessageModel>(boxName);
break;
default:
await Hive.openBox(boxName);
}
debugPrint('✅ Boîte $boxName créée (type: $boxType)');
} catch (e) {
debugPrint('❌ Erreur création $boxName: $e');
// En cas d'erreur, essayer sans type
try {
await Hive.openBox(boxName);
debugPrint('⚠️ Boîte $boxName créée sans type');
} catch (e2) {
debugPrint('❌ Échec total création $boxName: $e2');
}
}
await Future.delayed(const Duration(milliseconds: 200));
}
} catch (e) {
debugPrint('❌ Erreur création des boîtes: $e');
}
}
/// Restaure les utilisateurs sauvegardés
Future<void> _restoreUsers(Map<dynamic, UserModel> users) async {
try {
if (Hive.isBoxOpen(AppKeys.userBoxName)) {
final userBox = Hive.box<UserModel>(AppKeys.userBoxName);
for (final entry in users.entries) {
try {
await userBox.put(entry.key, entry.value);
} catch (e) {
debugPrint('⚠️ Erreur restauration utilisateur ${entry.key}: $e');
}
}
debugPrint('${users.length} utilisateurs restaurés');
}
} catch (e) {
debugPrint('❌ Erreur restauration utilisateurs: $e');
}
}
@@ -295,7 +475,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
children: [
const Spacer(flex: 2),
// Logo avec animation de réduction
// Logo avec animation
AnimatedBuilder(
animation: _scaleAnimation,
builder: (context, child) {
@@ -306,13 +486,13 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
},
child: Image.asset(
'assets/images/logo-geosector-1024.png',
height: 180, // Augmenté de 140 à 180
height: 180,
),
),
const SizedBox(height: 24),
// Titre avec animation fade-in
// Titre
AnimatedOpacity(
opacity: _isInitializing ? 0.9 : 1.0,
duration: const Duration(milliseconds: 500),
@@ -328,7 +508,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
const SizedBox(height: 16),
// Sous-titre avec nouveau slogan
// Sous-titre
AnimatedOpacity(
opacity: _isInitializing ? 0.8 : 1.0,
duration: const Duration(milliseconds: 500),
@@ -356,7 +536,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
valueColor: AlwaysStoppedAnimation<Color>(
theme.colorScheme.primary,
),
minHeight: 10, // Augmenté de 6 à 10
minHeight: 10,
),
),
),
@@ -369,7 +549,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
),
],
// Boutons après l'initialisation
// Boutons (reste identique)
if (_showButtons) ...[
// Bouton Connexion Utilisateur
AnimatedOpacity(
@@ -377,7 +557,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
duration: const Duration(milliseconds: 500),
child: ElevatedButton(
onPressed: () {
context.go('/login/user'); // Utiliser la route spécifique
context.go('/login/user');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
@@ -402,13 +582,13 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
),
const SizedBox(height: 16),
// Bouton Connexion Administrateur
// Bouton Connexion Administrateur
AnimatedOpacity(
opacity: _showButtons ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
child: ElevatedButton(
onPressed: () {
context.go('/login/admin'); // Utiliser la route spécifique
context.go('/login/admin');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
@@ -432,7 +612,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
),
),
const SizedBox(height: 32), // 2 espaces sous le bouton précédent
const SizedBox(height: 32),
// Bouton d'inscription
AnimatedOpacity(
@@ -472,7 +652,6 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
duration: const Duration(milliseconds: 500),
child: TextButton.icon(
onPressed: () {
// Déterminer l'URL du site web en fonction de l'environnement
String webUrl = 'https://geosector.fr';
if (kIsWeb) {
@@ -486,7 +665,6 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
}
}
// Ouvrir l'URL dans une nouvelle fenêtre/onglet
launchUrl(
Uri.parse(webUrl),
mode: LaunchMode.externalApplication,
@@ -514,7 +692,8 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
),
),
),
// Badge de version en bas à droite
// Badge de version
if (_appVersion.isNotEmpty)
Positioned(
bottom: 16,