feat: WebSocket temps réel + Finance Workflow + corrections

- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics)
  * Backend: KafkaEventProducer, KafkaEventConsumer
  * Mobile: WebSocketService (reconnection, heartbeat, typed events)
  * DashboardBloc: Auto-refresh depuis WebSocket events

- Finance Workflow: approbations + budgets (backend + mobile)
  * Backend: entities, services, resources, migrations Flyway V6
  * Mobile: features finance_workflow complète avec BLoC

- Corrections DI: interfaces IRepository partout
  * IProfileRepository, IOrganizationRepository, IMembreRepository
  * GetIt configuré avec @injectable

- Spec-Kit: constitution + templates mis à jour
  * .specify/memory/constitution.md enrichie
  * Templates agent, plan, spec, tasks, checklist

- Nettoyage: fichiers temporaires supprimés

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 02:12:17 +00:00
parent bbc409de9d
commit e8ad874015
635 changed files with 58160 additions and 20674 deletions

View File

@@ -5,6 +5,7 @@ library feedback_page;
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:dio/dio.dart';
import '../../../../core/utils/logger.dart';
import '../../../../shared/design_system/unionflow_design_system.dart';
class FeedbackPage extends StatefulWidget {
@@ -54,7 +55,8 @@ class _FeedbackPageState extends State<FeedbackPage> {
_messageController.clear();
_showSnackBar('Merci pour votre retour !');
}
} catch (_) {
} catch (e, st) {
AppLogger.error('FeedbackPage: envoi feedback échoué', error: e, stackTrace: st);
if (mounted) {
_showSnackBar('Envoi échoué. Réessayez plus tard.', isError: true);
}

View File

@@ -23,14 +23,16 @@ class _LanguageSettingsPageState extends State<LanguageSettingsPage> {
];
@override
void initState() {
super.initState();
void didChangeDependencies() {
super.didChangeDependencies();
_syncFromProvider();
}
void _syncFromProvider() {
final lp = context.read<LocaleProvider>();
setState(() => _selectedLanguage = lp.currentLanguageName);
if (lp.currentLanguageName != _selectedLanguage) {
setState(() => _selectedLanguage = lp.currentLanguageName);
}
}
Future<void> _changeLanguage(String languageName, String code) async {

View File

@@ -4,6 +4,7 @@ library privacy_settings_page;
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../shared/design_system/unionflow_design_system.dart';
class PrivacySettingsPage extends StatefulWidget {
@@ -278,7 +279,13 @@ class _PrivacySettingsPageState extends State<PrivacySettingsPage> {
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
onPressed: () async {
Navigator.of(context).pop();
final uri = Uri.parse('mailto:support@unionflow.com?subject=Demande de suppression de compte');
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
},
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Text('Contacter l\'administrateur', style: TextStyle(color: Colors.white)),
),
@@ -377,7 +384,9 @@ class _PrivacySettingsPageState extends State<PrivacySettingsPage> {
Switch(
value: value,
onChanged: onChanged,
activeColor: ColorTokens.primary,
activeTrackColor: ColorTokens.primary,
thumbColor: WidgetStateProperty.resolveWith((states) =>
states.contains(WidgetState.selected) ? Colors.white : null),
),
],
),

View File

@@ -1,5 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../shared/design_system/unionflow_design_system.dart';
import '../../../../core/di/injection_container.dart';
import '../../../../features/authentication/presentation/bloc/auth_bloc.dart';
import '../../../../features/authentication/data/models/user_role.dart';
import '../../data/models/system_metrics_model.dart';
import '../bloc/system_settings_bloc.dart';
import '../bloc/system_settings_event.dart';
import '../bloc/system_settings_state.dart';
/// Page Paramètres Système - UnionFlow Mobile
///
@@ -15,7 +23,10 @@ class SystemSettingsPage extends StatefulWidget {
class _SystemSettingsPageState extends State<SystemSettingsPage>
with TickerProviderStateMixin {
late TabController _tabController;
// Métriques système en temps réel
SystemMetricsModel? _metrics;
// États des paramètres système
bool _maintenanceMode = false;
bool _debugMode = false;
@@ -25,11 +36,11 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
bool _sslEnforced = true;
bool _apiLoggingEnabled = false;
bool _performanceMonitoring = true;
String _selectedLogLevel = 'INFO';
String _selectedBackupFrequency = 'Quotidien';
String _selectedCacheStrategy = 'Intelligent';
final List<String> _logLevels = ['DEBUG', 'INFO', 'WARN', 'ERROR'];
final List<String> _backupFrequencies = ['Temps réel', 'Horaire', 'Quotidien', 'Hebdomadaire'];
final List<String> _cacheStrategies = ['Agressif', 'Intelligent', 'Conservateur', 'Désactivé'];
@@ -49,31 +60,112 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF8F9FA),
body: Column(
children: [
// Header harmonisé
_buildHeader(),
// Onglets
_buildTabBar(),
// Contenu des onglets
Expanded(
child: TabBarView(
controller: _tabController,
return BlocBuilder<AuthBloc, AuthState>(
builder: (context, authState) {
// Accès réservé aux super administrateurs (configuration système globale)
if (authState is! AuthAuthenticated || authState.effectiveRole != UserRole.superAdmin) {
return Scaffold(
backgroundColor: const Color(0xFFF8F9FA),
appBar: AppBar(
title: const Text('Paramètres Système'),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
tooltip: 'Retour',
),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.lock_outline, size: 64, color: ColorTokens.onSurfaceVariant.withOpacity(0.5)),
const SizedBox(height: 16),
Text(
'Accès réservé',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: ColorTokens.onSurface,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Les paramètres système sont réservés aux administrateurs plateforme.',
style: TextStyle(
fontSize: 14,
color: ColorTokens.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
],
),
),
),
);
}
return BlocProvider(
create: (_) => sl<SystemSettingsBloc>()
..add(LoadSystemConfig())
..add(LoadSystemMetrics()),
child: BlocConsumer<SystemSettingsBloc, SystemSettingsState>(
listener: (context, state) {
if (state is SystemMetricsLoaded) {
setState(() {
_metrics = state.metrics;
});
} else if (state is SystemSettingsSuccess) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: ColorTokens.success,
behavior: SnackBarBehavior.floating,
),
);
} else if (state is SystemSettingsError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.error),
backgroundColor: ColorTokens.error,
behavior: SnackBarBehavior.floating,
),
);
}
},
builder: (context, state) {
return Scaffold(
backgroundColor: const Color(0xFFF8F9FA),
body: Column(
children: [
_buildGeneralTab(),
_buildSecurityTab(),
_buildPerformanceTab(),
_buildMaintenanceTab(),
_buildMonitoringTab(),
// Header harmonisé
_buildHeader(),
// Onglets
_buildTabBar(),
// Contenu des onglets
Expanded(
child: TabBarView(
controller: _tabController,
children: [
_buildGeneralTab(),
_buildSecurityTab(),
_buildPerformanceTab(),
_buildMaintenanceTab(),
_buildMonitoringTab(),
],
),
),
],
),
),
],
);
},
),
);
},
);
}
@@ -179,7 +271,7 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
Expanded(
child: _buildSystemIndicator(
'Statut',
_maintenanceMode ? 'Maintenance' : 'Opérationnel',
_metrics?.systemStatus ?? (_maintenanceMode ? 'MAINTENANCE' : 'OPERATIONAL'),
_maintenanceMode ? Icons.build : Icons.check_circle,
_maintenanceMode ? Colors.orange : Colors.green,
),
@@ -188,7 +280,7 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
Expanded(
child: _buildSystemIndicator(
'Charge CPU',
'23%',
_metrics != null ? '${_metrics!.cpuUsagePercent?.toStringAsFixed(0) ?? '0'}%' : '-',
Icons.memory,
Colors.blue,
),
@@ -197,7 +289,7 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
Expanded(
child: _buildSystemIndicator(
'Utilisateurs',
'1,247',
_metrics?.activeUsersCount?.toString() ?? '-',
Icons.people,
Colors.purple,
),
@@ -361,7 +453,9 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
),
_buildActionSetting(
'Vider le cache système',
'Supprimer tous les fichiers temporaires (2.3 GB)',
_metrics != null
? 'Supprimer tous les fichiers temporaires (${_metrics!.totalCacheSizeFormatted ?? "0 B"})'
: 'Supprimer tous les fichiers temporaires',
Icons.delete_sweep,
const Color(0xFFE17055),
() => _clearSystemCache(),
@@ -384,9 +478,9 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
'Paramètres de connectivité',
Icons.network_check,
[
_buildInfoSetting('Serveur API', 'https://api.unionflow.com'),
_buildInfoSetting('Serveur d\'authentification', 'https://auth.unionflow.com'),
_buildInfoSetting('CDN Assets', 'https://cdn.unionflow.com'),
_buildInfoSetting('Serveur API', _metrics?.apiBaseUrl ?? 'Non configuré'),
_buildInfoSetting('Serveur d\'authentification', _metrics?.authServerUrl ?? 'Non configuré'),
_buildInfoSetting('CDN Assets', _metrics?.cdnUrl ?? 'Non configuré'),
_buildActionSetting(
'Tester la connectivité',
'Vérifier la connexion aux services',
@@ -453,8 +547,14 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
'Gestion des accès utilisateurs',
Icons.login,
[
_buildInfoSetting('Sessions actives', '1,247 utilisateurs connectés'),
_buildInfoSetting('Tentatives échouées', '23 dans les dernières 24h'),
_buildInfoSetting(
'Sessions actives',
_metrics != null ? '${_metrics!.activeSessionsCount ?? 0} sessions actives' : 'Chargement...',
),
_buildInfoSetting(
'Tentatives échouées',
_metrics != null ? '${_metrics!.failedLoginAttempts24h ?? 0} dans les dernières 24h' : 'Chargement...',
),
_buildActionSetting(
'Forcer la déconnexion globale',
'Déconnecter tous les utilisateurs',
@@ -562,10 +662,30 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
'État actuel du système',
Icons.speed,
[
_buildMetricItem('CPU', '23%', Icons.memory, Colors.blue),
_buildMetricItem('RAM', '67%', Icons.storage, Colors.green),
_buildMetricItem('Disque', '45%', Icons.storage, Colors.orange),
_buildMetricItem('Réseau', '12 MB/s', Icons.network_check, Colors.purple),
_buildMetricItem(
'CPU',
_metrics != null ? '${_metrics!.cpuUsagePercent?.toStringAsFixed(1) ?? '0'}%' : '-',
Icons.memory,
Colors.blue,
),
_buildMetricItem(
'RAM',
_metrics != null ? '${_metrics!.memoryUsagePercent?.toStringAsFixed(1) ?? '0'}%' : '-',
Icons.storage,
Colors.green,
),
_buildMetricItem(
'Disque',
_metrics != null ? '${_metrics!.diskUsagePercent?.toStringAsFixed(1) ?? '0'}%' : '-',
Icons.storage,
Colors.orange,
),
_buildMetricItem(
'Réseau',
_metrics?.networkInFormatted ?? '0 B/s',
Icons.network_check,
Colors.purple,
),
],
),
@@ -662,8 +782,14 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
'Opérations de maintenance',
Icons.build,
[
_buildInfoSetting('Dernière maintenance', '15/12/2024 à 02:30'),
_buildInfoSetting('Prochaine maintenance', '22/12/2024 à 02:00'),
_buildInfoSetting(
'Dernière maintenance',
_metrics?.lastMaintenance ?? 'Aucune maintenance récente',
),
_buildInfoSetting(
'Prochaine maintenance',
_metrics?.nextScheduledMaintenance ?? 'Non planifiée',
),
_buildActionSetting(
'Planifier une maintenance',
'Programmer une fenêtre de maintenance',
@@ -689,8 +815,14 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
'Gestion des versions',
Icons.system_update,
[
_buildInfoSetting('Version actuelle', 'UnionFlow Server 2.1.0'),
_buildInfoSetting('Dernière vérification', 'Il y a 2 heures'),
_buildInfoSetting(
'Version actuelle',
_metrics != null ? 'UnionFlow Server ${_metrics!.applicationVersion ?? "N/A"}' : 'Chargement...',
),
_buildInfoSetting(
'Uptime',
_metrics?.uptimeFormatted ?? 'Chargement...',
),
_buildActionSetting(
'Vérifier les mises à jour',
'Rechercher les nouvelles versions',
@@ -763,10 +895,26 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
'Journaux d\'activité',
Icons.article,
[
_buildLogItem('Erreurs critiques', '3', Colors.red),
_buildLogItem('Avertissements', '27', Colors.orange),
_buildLogItem('Informations', '1,247', Colors.blue),
_buildLogItem('Debug', '5,892', Colors.grey),
_buildLogItem(
'Erreurs critiques',
_metrics?.criticalErrorsCount?.toString() ?? '0',
Colors.red,
),
_buildLogItem(
'Avertissements',
_metrics?.warningsCount?.toString() ?? '0',
Colors.orange,
),
_buildLogItem(
'Informations',
_metrics?.infoLogsCount?.toString() ?? '0',
Colors.blue,
),
_buildLogItem(
'Debug',
_metrics?.debugLogsCount?.toString() ?? '0',
Colors.grey,
),
_buildActionSetting(
'Voir tous les logs',
'Ouvrir la console de logs complète',
@@ -792,10 +940,22 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
'Métriques d\'activité',
Icons.bar_chart,
[
_buildStatItem('Utilisateurs actifs (24h)', '1,247'),
_buildStatItem('Requêtes API (1h)', '45,892'),
_buildStatItem('Données transférées', '2.3 GB'),
_buildStatItem('Temps de réponse moyen', '127ms'),
_buildStatItem(
'Utilisateurs actifs (24h)',
_metrics?.activeUsersCount?.toString() ?? '0',
),
_buildStatItem(
'Requêtes API (1h)',
_metrics?.apiRequestsLastHour?.toString() ?? '0',
),
_buildStatItem(
'Mémoire utilisée',
_metrics?.usedMemoryFormatted ?? '0 B',
),
_buildStatItem(
'Temps de réponse moyen',
_metrics != null ? '${_metrics!.averageResponseTimeMs?.toStringAsFixed(0) ?? "0"}ms' : '0ms',
),
_buildActionSetting(
'Rapport détaillé',
'Générer un rapport complet d\'utilisation',
@@ -1396,9 +1556,15 @@ class _SystemSettingsPageState extends State<SystemSettingsPage>
}
// Actions générales
void _clearSystemCache() => _showSuccessSnackBar('Cache système vidé (2.3 GB libérés)');
void _clearSystemCache() {
context.read<SystemSettingsBloc>().add(ClearCache());
}
void _optimizeDatabase() => _showSuccessSnackBar('Base de données optimisée');
void _testConnectivity() => _showSuccessSnackBar('Connectivité OK - Tous les services répondent');
void _testConnectivity() {
context.read<SystemSettingsBloc>().add(TestDatabaseConnection());
}
// Actions de sécurité
void _regenerateApiKeys() => _showWarningDialog('Régénérer les clés API', 'Cette action invalidera toutes les clés existantes.');