feat: Release version 3.1.4 - Mode terrain et génération PDF

 Nouvelles fonctionnalités:
- Ajout du mode terrain pour utilisation mobile hors connexion
- Génération automatique de reçus PDF avec template personnalisé
- Révision complète du système de cartes avec amélioration des performances

🔧 Améliorations techniques:
- Refactoring du module chat avec architecture simplifiée
- Optimisation du système de sécurité NIST SP 800-63B
- Amélioration de la gestion des secteurs géographiques
- Support UTF-8 étendu pour les noms d'utilisateurs

📱 Application mobile:
- Nouveau mode terrain dans user_field_mode_page
- Interface utilisateur adaptative pour conditions difficiles
- Synchronisation offline améliorée

🗺️ Cartographie:
- Optimisation des performances MapBox
- Meilleure gestion des tuiles hors ligne
- Amélioration de l'affichage des secteurs

📄 Documentation:
- Ajout guide Android (ANDROID-GUIDE.md)
- Documentation sécurité API (API-SECURITY.md)
- Guide module chat (CHAT_MODULE.md)

🐛 Corrections:
- Résolution des erreurs 400 lors de la création d'utilisateurs
- Correction de la validation des noms d'utilisateurs
- Fix des problèmes de synchronisation chat

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-19 19:38:03 +02:00
parent c1f23c4345
commit 5ab03751e1
1823 changed files with 272663 additions and 198438 deletions

View File

@@ -0,0 +1,238 @@
import 'package:flutter/services.dart';
import 'package:yaml/yaml.dart';
/// Classe pour charger et gérer la configuration du chat depuis chat_config.yaml
class ChatConfigLoader {
static ChatConfigLoader? _instance;
static ChatConfigLoader get instance => _instance ??= ChatConfigLoader._();
Map<String, dynamic>? _config;
ChatConfigLoader._();
/// Charger la configuration depuis le fichier YAML
Future<void> loadConfig() async {
try {
// Charger le fichier YAML
final yamlString = await rootBundle.loadString('lib/chat/chat_config.yaml');
// Vérifier que le contenu n'est pas vide
if (yamlString.isEmpty) {
print('Fichier de configuration chat vide, utilisation de la configuration par défaut');
_config = _getDefaultConfig();
return;
}
// Parser le YAML
dynamic yamlMap;
try {
yamlMap = loadYaml(yamlString);
} catch (parseError) {
print('Erreur de parsing YAML (utilisation de la config par défaut): $parseError');
print('Contenu YAML problématique (premiers 500 caractères): ${yamlString.substring(0, yamlString.length > 500 ? 500 : yamlString.length)}');
_config = _getDefaultConfig();
return;
}
// Convertir en Map<String, dynamic>
_config = _convertYamlToMap(yamlMap);
print('Configuration chat chargée avec succès');
} catch (e) {
print('Erreur lors du chargement de la configuration chat: $e');
// Utiliser une configuration par défaut en cas d'erreur
_config = _getDefaultConfig();
}
}
/// Convertir YamlMap en Map standard
dynamic _convertYamlToMap(dynamic yamlData) {
if (yamlData is YamlMap) {
final map = <String, dynamic>{};
yamlData.forEach((key, value) {
map[key.toString()] = _convertYamlToMap(value);
});
return map;
} else if (yamlData is YamlList) {
return yamlData.map((item) => _convertYamlToMap(item)).toList();
} else {
return yamlData;
}
}
/// Obtenir les permissions pour un rôle
Map<String, dynamic> getPermissionsForRole(int role) {
if (_config == null) {
return {};
}
final permissions = _config!['chat_permissions'] as Map<String, dynamic>?;
if (permissions == null) {
return {};
}
return permissions['role_$role'] as Map<String, dynamic>? ?? {};
}
/// Vérifier si un utilisateur peut envoyer un message à un autre
bool canSendMessageTo({
required int senderRole,
required int recipientRole,
int? senderEntite,
int? recipientEntite,
}) {
final permissions = getPermissionsForRole(senderRole);
final canMessageWith = permissions['can_message_with'] as List<dynamic>?;
if (canMessageWith == null) {
return false;
}
for (final rule in canMessageWith) {
if (rule['role'] == recipientRole) {
final condition = rule['condition'] as String?;
switch (condition) {
case 'same_entite':
return senderEntite != null &&
recipientEntite != null &&
senderEntite == recipientEntite;
case 'all':
return true;
default:
return false;
}
}
}
return false;
}
/// Obtenir les destinataires possibles pour un rôle
List<Map<String, dynamic>> getPossibleRecipientsConfig(int role) {
final permissions = getPermissionsForRole(role);
final canMessageWith = permissions['can_message_with'] as List<dynamic>?;
if (canMessageWith == null) {
return [];
}
return canMessageWith.map((rule) {
return {
'role': rule['role'],
'condition': rule['condition'],
'description': rule['description'],
'allow_selection': rule['allow_selection'] ?? false,
'allow_broadcast': rule['allow_broadcast'] ?? false,
};
}).toList();
}
/// Obtenir le nom d'un rôle
String getRoleName(int role) {
final permissions = getPermissionsForRole(role);
return permissions['name'] as String? ?? 'Utilisateur';
}
/// Obtenir la description d'un rôle
String getRoleDescription(int role) {
final permissions = getPermissionsForRole(role);
return permissions['description'] as String? ?? '';
}
/// Obtenir le texte d'aide pour un rôle
String getHelpText(int role) {
final permissions = getPermissionsForRole(role);
return permissions['help_text'] as String? ?? '';
}
/// Vérifier si un rôle peut créer des groupes
bool canCreateGroup(int role) {
final permissions = getPermissionsForRole(role);
return permissions['can_create_group'] as bool? ?? false;
}
/// Vérifier si un rôle peut faire du broadcast
bool canBroadcast(int role) {
final permissions = getPermissionsForRole(role);
return permissions['can_broadcast'] as bool? ?? false;
}
/// Obtenir la configuration UI
Map<String, dynamic> getUIConfig() {
return _config?['ui_config'] as Map<String, dynamic>? ?? {};
}
/// Obtenir les messages de l'interface
Map<String, dynamic> getUIMessages() {
final uiConfig = getUIConfig();
return uiConfig['messages'] as Map<String, dynamic>? ?? {};
}
/// Obtenir la couleur d'un rôle
String getRoleColor(int role) {
final uiConfig = getUIConfig();
final roleColors = uiConfig['role_colors'] as Map<String, dynamic>?;
return roleColors?[role.toString()] as String? ?? '#64748B';
}
/// Obtenir les informations du module
Map<String, dynamic> getModuleInfo() {
return _config?['module_info'] as Map<String, dynamic>? ?? {
'version': '1.0.0',
'name': 'Chat Module Light',
'description': 'Module de chat autonome et portable pour GEOSECTOR'
};
}
/// Obtenir la version du module
String getModuleVersion() {
final moduleInfo = getModuleInfo();
return moduleInfo['version'] as String? ?? '1.0.0';
}
/// Configuration par défaut si le fichier YAML n'est pas trouvé
Map<String, dynamic> _getDefaultConfig() {
return {
'chat_permissions': {
'role_1': {
'name': 'Membre',
'can_message_with': [
{'role': 1, 'condition': 'same_entite'},
{'role': 2, 'condition': 'same_entite'},
],
'can_create_group': false,
'can_broadcast': false,
'help_text': 'Vous pouvez discuter avec les membres de votre amicale',
},
'role_2': {
'name': 'Admin Amicale',
'can_message_with': [
{'role': 1, 'condition': 'same_entite'},
{'role': 2, 'condition': 'same_entite'},
{'role': 9, 'condition': 'all'},
],
'can_create_group': true,
'can_broadcast': false,
'help_text': 'Vous pouvez discuter avec les membres et les super admins',
},
'role_9': {
'name': 'Super Admin',
'can_message_with': [
{'role': 2, 'condition': 'all', 'allow_selection': true, 'allow_broadcast': true},
],
'can_create_group': true,
'can_broadcast': true,
'help_text': 'Vous pouvez envoyer des messages aux administrateurs d\'amicale',
},
},
'ui_config': {
'show_role_badge': true,
'enable_autocomplete': true,
'messages': {
'no_permission': 'Vous n\'avez pas la permission',
'search_placeholder': 'Rechercher...',
},
},
};
}
}