From a1214bc1167d1d7e92a93a7e2af30aae4fc05dcf Mon Sep 17 00:00:00 2001 From: DahoudG <41957584+DahoudG@users.noreply.github.com> Date: Fri, 19 Sep 2025 18:34:04 +0000 Subject: [PATCH] Version stable - Authentifiaction Ok + Dashboard SuperAdmin --- test_superadmin_role.py | 208 ++++ .../auth/services/keycloak_role_mapper.dart | 112 +- .../super_admin_dashboard.dart | 960 +++++++++++------- .../widgets/dashboard_activity_tile.dart | 14 +- 4 files changed, 899 insertions(+), 395 deletions(-) create mode 100644 test_superadmin_role.py diff --git a/test_superadmin_role.py b/test_superadmin_role.py new file mode 100644 index 0000000..1bb277b --- /dev/null +++ b/test_superadmin_role.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 +""" +Test du mapping du rôle SUPER_ADMINISTRATEUR +""" + +import requests +import json +import base64 + +def test_superadmin_role(): + base_url = "http://localhost:8180" + + print("🧪 Test du rôle SUPER_ADMINISTRATEUR") + print("=" * 60) + print() + + # Test avec superadmin@unionflow.com + email_username = "superadmin@unionflow.com" + password = "SuperAdmin123!" + + print(f"🔐 Authentification de {email_username}") + + data = { + "username": email_username, + "password": password, + "grant_type": "password", + "client_id": "unionflow-mobile" + } + + try: + response = requests.post( + f"{base_url}/realms/unionflow/protocol/openid-connect/token", + data=data, + headers={"Content-Type": "application/x-www-form-urlencoded"} + ) + + print(f"📊 Status: {response.status_code}") + + if response.status_code == 200: + token_data = response.json() + if "access_token" in token_data: + print("✅ AUTHENTIFICATION RÉUSSIE !") + + # Décoder le token pour voir les rôles + access_token = token_data['access_token'] + + # Décoder le payload du JWT + token_parts = access_token.split('.') + if len(token_parts) >= 2: + # Ajouter du padding si nécessaire + payload = token_parts[1] + payload += '=' * (4 - len(payload) % 4) + decoded = base64.b64decode(payload) + token_info = json.loads(decoded) + + print() + print("🎫 INFORMATIONS DU TOKEN :") + print(f" 👤 Utilisateur: {token_info.get('preferred_username', 'N/A')}") + print(f" 📧 Email: {token_info.get('email', 'N/A')}") + print(f" 👨‍💼 Nom: {token_info.get('name', 'N/A')}") + + # Extraire les rôles + roles = [] + if 'realm_access' in token_info and 'roles' in token_info['realm_access']: + all_roles = token_info['realm_access']['roles'] + # Filtrer les rôles système + roles = [r for r in all_roles if not r.startswith('default-') and r not in ['offline_access', 'uma_authorization']] + + print() + print("🎭 RÔLES KEYCLOAK EXTRAITS :") + for role in roles: + if role == 'SUPER_ADMINISTRATEUR': + print(f" ✅ {role} (SUPER ADMIN - ACCÈS COMPLET)") + else: + print(f" ✓ {role}") + + print() + print("🎯 MAPPING VERS L'APPLICATION MOBILE :") + + if 'SUPER_ADMINISTRATEUR' in roles: + print(" ✅ Rôle mappé: UserRole.superAdmin") + print(" ✅ Dashboard: SuperAdminDashboard") + print(" ✅ Navigation: Command Center") + print(" ✅ Permissions: ACCÈS COMPLET SYSTÈME") + print() + print("🚀 FONCTIONNALITÉS DISPONIBLES :") + print(" • Vue globale multi-organisations") + print(" • Métriques système en temps réel") + print(" • Outils d'administration avancés") + print(" • Monitoring et analytics") + print(" • Gestion des utilisateurs globale") + print(" • Configuration système") + print(" • Sécurité et audit") + print(" • Sauvegarde et maintenance") + print() + print("📱 ONGLETS DU DASHBOARD :") + print(" 1. Vue Globale - Métriques système") + print(" 2. Organisations - Gestion multi-org") + print(" 3. Système - Monitoring technique") + print(" 4. Analytics - Rapports avancés") + + return True + else: + print(" ❌ Rôle SUPER_ADMINISTRATEUR non trouvé") + print(" ⚠️ L'utilisateur sera mappé comme visiteur") + return False + + else: + print("❌ Token manquant dans la réponse") + else: + print("❌ Authentification échouée") + print(f"Response: {response.text}") + + return False + + except Exception as e: + print(f"❌ Erreur: {e}") + return False + +def test_all_roles(): + """Teste tous les comptes avec leurs rôles""" + base_url = "http://localhost:8180" + + print() + print("=" * 60) + print("🧪 TEST DE TOUS LES RÔLES UNIONFLOW") + print("=" * 60) + print() + + users = [ + ("superadmin@unionflow.com", "SuperAdmin123!", "SUPER_ADMINISTRATEUR"), + ("marie.active@unionflow.com", "Marie123!", "MEMBRE_ACTIF"), + ("jean.simple@unionflow.com", "Jean123!", "MEMBRE_SIMPLE"), + ("tech.lead@unionflow.com", "TechLead123!", "RESPONSABLE_TECHNIQUE"), + ("rh.manager@unionflow.com", "RhManager123!", "RESPONSABLE_MEMBRES"), + ] + + for email, password, expected_role in users: + print(f"🔐 Test de {email} (attendu: {expected_role})") + + data = { + "username": email, + "password": password, + "grant_type": "password", + "client_id": "unionflow-mobile" + } + + try: + response = requests.post( + f"{base_url}/realms/unionflow/protocol/openid-connect/token", + data=data, + headers={"Content-Type": "application/x-www-form-urlencoded"} + ) + + if response.status_code == 200: + token_data = response.json() + access_token = token_data['access_token'] + + # Décoder le token + token_parts = access_token.split('.') + payload = token_parts[1] + payload += '=' * (4 - len(payload) % 4) + decoded = base64.b64decode(payload) + token_info = json.loads(decoded) + + # Extraire les rôles + roles = [] + if 'realm_access' in token_info and 'roles' in token_info['realm_access']: + all_roles = token_info['realm_access']['roles'] + roles = [r for r in all_roles if not r.startswith('default-') and r not in ['offline_access', 'uma_authorization']] + + if expected_role in roles: + print(f" ✅ Rôle {expected_role} trouvé") + else: + print(f" ❌ Rôle {expected_role} manquant. Rôles trouvés: {roles}") + + else: + print(f" ❌ Authentification échouée: {response.status_code}") + + except Exception as e: + print(f" ❌ Erreur: {e}") + + print() + +if __name__ == "__main__": + success = test_superadmin_role() + + if success: + print() + print("=" * 60) + print("🎉 SUPER ADMINISTRATEUR CONFIGURÉ AVEC SUCCÈS !") + print("=" * 60) + print() + print("📱 VOTRE APPLICATION MOBILE AFFICHERA :") + print(" • Dashboard Super Admin complet") + print(" • Navigation Command Center") + print(" • Toutes les fonctionnalités administratives") + print() + print("🚀 Testez maintenant sur votre Samsung avec :") + print(" Username: superadmin@unionflow.com") + print(" Password: SuperAdmin123!") + else: + print() + print("⚠️ Problème de configuration détecté") + print("Vérifiez que le rôle SUPER_ADMINISTRATEUR est bien assigné") + + # Test de tous les rôles + test_all_roles() diff --git a/unionflow-mobile-apps/lib/core/auth/services/keycloak_role_mapper.dart b/unionflow-mobile-apps/lib/core/auth/services/keycloak_role_mapper.dart index 06602d9..38f4c25 100644 --- a/unionflow-mobile-apps/lib/core/auth/services/keycloak_role_mapper.dart +++ b/unionflow-mobile-apps/lib/core/auth/services/keycloak_role_mapper.dart @@ -11,22 +11,28 @@ class KeycloakRoleMapper { /// Mapping des rôles Keycloak vers UserRole static const Map _keycloakToUserRole = { // Rôles administratifs + 'SUPER_ADMINISTRATEUR': UserRole.superAdmin, 'ADMIN': UserRole.superAdmin, + 'ADMINISTRATEUR_ORGANISATION': UserRole.orgAdmin, 'PRESIDENT': UserRole.orgAdmin, - + // Rôles de gestion + 'RESPONSABLE_TECHNIQUE': UserRole.moderator, + 'RESPONSABLE_MEMBRES': UserRole.moderator, 'TRESORIER': UserRole.moderator, 'SECRETAIRE': UserRole.moderator, 'GESTIONNAIRE_MEMBRE': UserRole.moderator, 'ORGANISATEUR_EVENEMENT': UserRole.moderator, - + // Rôles membres + 'MEMBRE_ACTIF': UserRole.activeMember, + 'MEMBRE_SIMPLE': UserRole.simpleMember, 'MEMBRE': UserRole.activeMember, }; /// Mapping des rôles Keycloak vers permissions spécifiques static const Map> _keycloakToPermissions = { - 'ADMIN': [ + 'SUPER_ADMINISTRATEUR': [ // Permissions Super Admin - Accès total PermissionMatrix.SYSTEM_ADMIN, PermissionMatrix.SYSTEM_CONFIG, @@ -46,7 +52,41 @@ class KeycloakRoleMapper { PermissionMatrix.REPORTS_GENERATE, PermissionMatrix.DASHBOARD_ANALYTICS, ], - + 'ADMIN': [ + // Permissions Super Admin - Accès total (compatibilité) + PermissionMatrix.SYSTEM_ADMIN, + PermissionMatrix.SYSTEM_CONFIG, + PermissionMatrix.SYSTEM_SECURITY, + PermissionMatrix.ORG_CREATE, + PermissionMatrix.ORG_DELETE, + PermissionMatrix.ORG_CONFIG, + PermissionMatrix.MEMBERS_VIEW_ALL, + PermissionMatrix.MEMBERS_EDIT_ALL, + PermissionMatrix.MEMBERS_DELETE_ALL, + PermissionMatrix.FINANCES_VIEW_ALL, + PermissionMatrix.FINANCES_EDIT_ALL, + PermissionMatrix.EVENTS_VIEW_ALL, + PermissionMatrix.EVENTS_EDIT_ALL, + PermissionMatrix.SOLIDARITY_VIEW_ALL, + PermissionMatrix.SOLIDARITY_EDIT_ALL, + PermissionMatrix.REPORTS_GENERATE, + PermissionMatrix.DASHBOARD_ANALYTICS, + ], + 'ADMINISTRATEUR_ORGANISATION': [ + // Permissions Admin Organisation + PermissionMatrix.ORG_CONFIG, + PermissionMatrix.MEMBERS_VIEW_ALL, + PermissionMatrix.MEMBERS_EDIT_ALL, + PermissionMatrix.FINANCES_VIEW_ALL, + PermissionMatrix.FINANCES_EDIT_ALL, + PermissionMatrix.EVENTS_VIEW_ALL, + PermissionMatrix.EVENTS_EDIT_ALL, + PermissionMatrix.SOLIDARITY_VIEW_ALL, + PermissionMatrix.SOLIDARITY_EDIT_ALL, + PermissionMatrix.REPORTS_GENERATE, + PermissionMatrix.DASHBOARD_ANALYTICS, + ], + 'PRESIDENT': [ // Permissions Président - Gestion organisation PermissionMatrix.ORG_CONFIG, @@ -62,7 +102,32 @@ class KeycloakRoleMapper { PermissionMatrix.DASHBOARD_ANALYTICS, PermissionMatrix.COMM_SEND_ALL, ], - + + 'RESPONSABLE_TECHNIQUE': [ + // Permissions Responsable Technique + PermissionMatrix.SYSTEM_MONITORING, + PermissionMatrix.SYSTEM_MAINTENANCE, + PermissionMatrix.MEMBERS_VIEW_ALL, + PermissionMatrix.MEMBERS_EDIT_BASIC, + PermissionMatrix.EVENTS_VIEW_ALL, + PermissionMatrix.EVENTS_EDIT_ALL, + PermissionMatrix.DASHBOARD_VIEW, + PermissionMatrix.REPORTS_GENERATE, + ], + + 'RESPONSABLE_MEMBRES': [ + // Permissions Responsable Membres + PermissionMatrix.MEMBERS_VIEW_ALL, + PermissionMatrix.MEMBERS_EDIT_ALL, + PermissionMatrix.MEMBERS_DELETE_ALL, + PermissionMatrix.EVENTS_VIEW_ALL, + PermissionMatrix.EVENTS_EDIT_ALL, + PermissionMatrix.SOLIDARITY_VIEW_ALL, + PermissionMatrix.SOLIDARITY_EDIT_ALL, + PermissionMatrix.DASHBOARD_VIEW, + PermissionMatrix.REPORTS_GENERATE, + ], + 'TRESORIER': [ // Permissions Trésorier - Focus finances PermissionMatrix.FINANCES_VIEW_ALL, @@ -106,9 +171,36 @@ class KeycloakRoleMapper { PermissionMatrix.DASHBOARD_VIEW, PermissionMatrix.COMM_SEND_MEMBERS, ], - + + 'MEMBRE_ACTIF': [ + // Permissions Membre Actif + PermissionMatrix.MEMBERS_VIEW_OWN, + PermissionMatrix.MEMBERS_EDIT_OWN, + PermissionMatrix.EVENTS_VIEW_ALL, + PermissionMatrix.EVENTS_PARTICIPATE, + PermissionMatrix.EVENTS_CREATE, + PermissionMatrix.SOLIDARITY_VIEW_ALL, + PermissionMatrix.SOLIDARITY_PARTICIPATE, + PermissionMatrix.SOLIDARITY_CREATE, + PermissionMatrix.FINANCES_VIEW_OWN, + PermissionMatrix.DASHBOARD_VIEW, + PermissionMatrix.COMM_SEND_MEMBERS, + ], + + 'MEMBRE_SIMPLE': [ + // Permissions Membre Simple + PermissionMatrix.MEMBERS_VIEW_OWN, + PermissionMatrix.MEMBERS_EDIT_OWN, + PermissionMatrix.EVENTS_VIEW_PUBLIC, + PermissionMatrix.EVENTS_PARTICIPATE, + PermissionMatrix.SOLIDARITY_VIEW_PUBLIC, + PermissionMatrix.SOLIDARITY_PARTICIPATE, + PermissionMatrix.FINANCES_VIEW_OWN, + PermissionMatrix.DASHBOARD_VIEW, + ], + 'MEMBRE': [ - // Permissions Membre Standard + // Permissions Membre Standard (compatibilité) PermissionMatrix.MEMBERS_VIEW_OWN, PermissionMatrix.MEMBERS_EDIT_OWN, PermissionMatrix.EVENTS_VIEW_PUBLIC, @@ -124,12 +216,18 @@ class KeycloakRoleMapper { static UserRole mapToUserRole(List keycloakRoles) { // Priorité des rôles (du plus élevé au plus bas) const List rolePriority = [ + 'SUPER_ADMINISTRATEUR', 'ADMIN', + 'ADMINISTRATEUR_ORGANISATION', 'PRESIDENT', + 'RESPONSABLE_TECHNIQUE', + 'RESPONSABLE_MEMBRES', 'TRESORIER', 'SECRETAIRE', 'GESTIONNAIRE_MEMBRE', 'ORGANISATEUR_EVENEMENT', + 'MEMBRE_ACTIF', + 'MEMBRE_SIMPLE', 'MEMBRE', ]; diff --git a/unionflow-mobile-apps/lib/features/dashboard/presentation/pages/role_dashboards/super_admin_dashboard.dart b/unionflow-mobile-apps/lib/features/dashboard/presentation/pages/role_dashboards/super_admin_dashboard.dart index 2a4759d..f90156d 100644 --- a/unionflow-mobile-apps/lib/features/dashboard/presentation/pages/role_dashboards/super_admin_dashboard.dart +++ b/unionflow-mobile-apps/lib/features/dashboard/presentation/pages/role_dashboards/super_admin_dashboard.dart @@ -1,19 +1,7 @@ -/// Dashboard Super Administrateur - Command Center Ultra-Sophistiqué -/// Vue globale multi-organisations avec métriques système avancées -library super_admin_dashboard; - import 'package:flutter/material.dart'; -import '../../../../../core/design_system/tokens/tokens.dart'; -import '../../widgets/widgets.dart'; -/// Dashboard Command Center pour Super Administrateur -/// -/// Fonctionnalités exclusives : -/// - Vue globale multi-organisations -/// - Métriques système en temps réel -/// - Outils d'administration avancés -/// - Monitoring et analytics -/// - Gestion des utilisateurs globale +/// Dashboard pour le Super Administrateur +/// Interface mobile optimisée avec drawer de navigation class SuperAdminDashboard extends StatefulWidget { const SuperAdminDashboard({super.key}); @@ -21,217 +9,370 @@ class SuperAdminDashboard extends StatefulWidget { State createState() => _SuperAdminDashboardState(); } -class _SuperAdminDashboardState extends State - with TickerProviderStateMixin { - - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(length: 4, vsync: this); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } +class _SuperAdminDashboardState extends State { + int _selectedIndex = 0; + + final List _sectionTitles = [ + 'Vue Globale', + 'Organisations', + 'Utilisateurs', + 'Système', + 'Analytics', + 'Sécurité', + 'Configuration', + ]; @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: ColorTokens.surface, - body: CustomScrollView( - slivers: [ - // App Bar avec gradient Super Admin - SliverAppBar( - expandedHeight: 200, - floating: false, - pinned: true, - backgroundColor: const Color(0xFF6C5CE7), // Violet Super Admin - flexibleSpace: FlexibleSpaceBar( - title: const Text( - 'Command Center', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + backgroundColor: const Color(0xFFF8F9FA), + appBar: AppBar( + title: Text( + 'Command Center - ${_sectionTitles[_selectedIndex]}', + style: const TextStyle( + color: Color(0xFF6C5CE7), + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + backgroundColor: Colors.white, + elevation: 2, + centerTitle: false, + ), + drawer: _buildDrawer(context), + body: _buildSelectedContent(), + ); + } + + /// Drawer de navigation mobile + Widget _buildDrawer(BuildContext context) { + return Drawer( + child: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFF6C5CE7), + Color(0xFF5A4FCF), + Color(0xFF4834D4), + ], + ), + ), + child: SafeArea( + child: Column( + children: [ + // Profil utilisateur + _buildUserProfile(), + const SizedBox(height: 16), + + // Menu de navigation + Expanded( + child: _buildNavigationMenu(), ), - background: Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [ - Color(0xFF6C5CE7), // Violet principal - Color(0xFF5A4FCF), // Violet plus foncé - Color(0xFF4834D4), // Violet profond - ], - ), + + // Actions du bas + _buildBottomActions(), + ], + ), + ), + ), + ); + } + + /// Profil utilisateur dans la sidebar + Widget _buildUserProfile() { + return Container( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + // Photo de profil + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.2), + borderRadius: BorderRadius.circular(30), + border: Border.all( + color: Colors.white.withOpacity(0.3), + width: 2, + ), + ), + child: const Icon( + Icons.admin_panel_settings, + color: Colors.white, + size: 30, + ), + ), + + const SizedBox(height: 8), + + // Nom et email + const Text( + 'Super Admin', + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + const Text( + 'superadmin@unionflow.com', + style: TextStyle( + color: Colors.white70, + fontSize: 11, + ), + ), + + const SizedBox(height: 8), + + // Badge de rôle + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.2), + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: Colors.white.withOpacity(0.3), + ), + ), + child: const Text( + 'SUPER_ADMINISTRATEUR', + style: TextStyle( + color: Colors.white, + fontSize: 9, + fontWeight: FontWeight.w600, + letterSpacing: 0.5, + ), + ), + ), + ], + ), + ); + } + + /// Menu de navigation dans la sidebar + Widget _buildNavigationMenu() { + return ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 12), + itemCount: _sectionTitles.length, + itemBuilder: (context, index) { + final isSelected = _selectedIndex == index; + return Container( + margin: const EdgeInsets.only(bottom: 4), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.of(context).pop(); // Fermer le drawer + setState(() { + _selectedIndex = index; + }); + }, + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, ), - child: Stack( + decoration: BoxDecoration( + color: isSelected + ? Colors.white.withOpacity(0.2) + : Colors.transparent, + borderRadius: BorderRadius.circular(8), + border: isSelected + ? Border.all(color: Colors.white.withOpacity(0.3)) + : null, + ), + child: Row( children: [ - // Motif géométrique sophistiqué - Positioned.fill( - child: CustomPaint( - painter: _GeometricPatternPainter(), + Icon( + _getSectionIcon(index), + color: Colors.white, + size: 20, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + _sectionTitles[index], + style: TextStyle( + color: Colors.white, + fontWeight: isSelected + ? FontWeight.w600 + : FontWeight.normal, + ), ), ), - // Contenu de l'en-tête - Positioned( - bottom: 60, - left: 20, - right: 20, - child: Row( - children: [ - Container( - width: 60, - height: 60, - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.2), - borderRadius: BorderRadius.circular(30), - border: Border.all( - color: Colors.white.withOpacity(0.3), - width: 2, - ), - ), - child: const Icon( - Icons.admin_panel_settings, - color: Colors.white, - size: 30, - ), - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Super Administrateur', - style: TypographyTokens.headlineSmall.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - Text( - 'Contrôle total du système', - style: TypographyTokens.bodyMedium.copyWith( - color: Colors.white.withOpacity(0.9), - ), - ), - ], - ), - ), - ], + if (isSelected) + const Icon( + Icons.chevron_right, + color: Colors.white, + size: 16, ), - ), ], ), ), ), - bottom: TabBar( - controller: _tabController, - indicatorColor: Colors.white, - labelColor: Colors.white, - unselectedLabelColor: Colors.white.withOpacity(0.7), - tabs: const [ - Tab(text: 'Vue Globale'), - Tab(text: 'Organisations'), - Tab(text: 'Système'), - Tab(text: 'Analytics'), - ], + ), + ); + }, + ); + } + + /// Actions du bas + Widget _buildBottomActions() { + return Container( + padding: const EdgeInsets.all(16), + child: SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: () { + Navigator.of(context).pop(); + // TODO: Implémenter la déconnexion + }, + icon: const Icon(Icons.logout, size: 16), + label: const Text('Déconnexion'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white.withOpacity(0.2), + foregroundColor: Colors.white, + elevation: 0, + padding: const EdgeInsets.symmetric(vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Colors.white.withOpacity(0.3), + ), ), ), - - // Contenu des onglets - SliverFillRemaining( - child: TabBarView( - controller: _tabController, - children: [ - _buildGlobalOverviewTab(), - _buildOrganizationsTab(), - _buildSystemTab(), - _buildAnalyticsTab(), - ], - ), - ), - ], + ), ), ); } - /// Onglet Vue Globale - Widget _buildGlobalOverviewTab() { + /// Contenu de la section sélectionnée + Widget _buildSelectedContent() { + switch (_selectedIndex) { + case 0: + return _buildGlobalOverviewContent(); + case 1: + return _buildOrganizationsContent(); + case 2: + return _buildUsersContent(); + case 3: + return _buildSystemContent(); + case 4: + return _buildAnalyticsContent(); + case 5: + return _buildSecurityContent(); + case 6: + return _buildConfigurationContent(); + default: + return _buildGlobalOverviewContent(); + } + } + + /// Icône pour chaque section + IconData _getSectionIcon(int index) { + switch (index) { + case 0: return Icons.dashboard; + case 1: return Icons.business; + case 2: return Icons.people; + case 3: return Icons.computer; + case 4: return Icons.analytics; + case 5: return Icons.security; + case 6: return Icons.settings; + default: return Icons.dashboard; + } + } + + /// Vue Globale - Métriques système simplifiées + Widget _buildGlobalOverviewContent() { return SingleChildScrollView( - padding: const EdgeInsets.all(SpacingTokens.md), + padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Métriques globales - _buildGlobalMetricsSection(), - const SizedBox(height: SpacingTokens.xl), - - // Alertes système - _buildSystemAlertsSection(), - const SizedBox(height: SpacingTokens.xl), - - // Activité récente globale - _buildGlobalActivitySection(), + // KPIs système en temps réel + _buildSimpleKPIsSection(), + const SizedBox(height: 16), + + // Performance serveur + _buildSimpleServerSection(), + const SizedBox(height: 16), + + // Alertes importantes + _buildSimpleAlertsSection(), + const SizedBox(height: 16), + + // Activité récente + _buildSimpleActivitySection(), ], ), ); } - /// Section métriques globales - Widget _buildGlobalMetricsSection() { + /// Section KPIs simplifiée + Widget _buildSimpleKPIsSection() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - 'Métriques Globales', - style: TypographyTokens.headlineMedium.copyWith( + const Text( + 'Métriques Système', + style: TextStyle( fontWeight: FontWeight.bold, + color: Color(0xFF6C5CE7), + fontSize: 20, ), ), - const SizedBox(height: SpacingTokens.md), - - // Grille de métriques système - GridView.count( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - crossAxisCount: 2, - crossAxisSpacing: SpacingTokens.md, - mainAxisSpacing: SpacingTokens.md, - childAspectRatio: 1.4, + const SizedBox(height: 12), + Row( children: [ - _buildSystemMetricCard( - 'Organisations', - '247', - '+12 ce mois', - Icons.business, - const Color(0xFF0984E3), + Expanded( + child: _buildSimpleKPICard( + 'Organisations', + '247', + '+12 ce mois', + Icons.business, + const Color(0xFF0984E3), + ), ), - _buildSystemMetricCard( - 'Utilisateurs', - '15,847', - '+1,234 ce mois', - Icons.people, - const Color(0xFF00B894), + const SizedBox(width: 8), + Expanded( + child: _buildSimpleKPICard( + 'Utilisateurs', + '15,847', + '+1,234 ce mois', + Icons.people, + const Color(0xFF00B894), + ), ), - _buildSystemMetricCard( - 'Uptime', - '99.97%', - '30 derniers jours', - Icons.trending_up, - const Color(0xFF00CEC9), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: _buildSimpleKPICard( + 'Uptime', + '99.97%', + '30 derniers jours', + Icons.trending_up, + const Color(0xFF00CEC9), + ), ), - _buildSystemMetricCard( - 'Performance', - '1.2s', - 'Temps de réponse', - Icons.speed, - const Color(0xFFE17055), + const SizedBox(width: 8), + Expanded( + child: _buildSimpleKPICard( + 'Temps Réponse', + '1.2s', + 'Moyenne 24h', + Icons.speed, + const Color(0xFFE17055), + ), ), ], ), @@ -239,8 +380,8 @@ class _SuperAdminDashboardState extends State ); } - /// Carte de métrique système - Widget _buildSystemMetricCard( + /// Carte KPI simplifiée + Widget _buildSimpleKPICard( String title, String value, String subtitle, @@ -248,14 +389,14 @@ class _SuperAdminDashboardState extends State Color color, ) { return Container( - padding: const EdgeInsets.all(SpacingTokens.md), + padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.white, - borderRadius: BorderRadius.circular(RadiusTokens.md), + borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), - blurRadius: 10, + blurRadius: 8, offset: const Offset(0, 2), ), ], @@ -265,41 +406,32 @@ class _SuperAdminDashboardState extends State children: [ Row( children: [ - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: color.withOpacity(0.1), - borderRadius: BorderRadius.circular(20), - ), - child: Icon(icon, color: color, size: 20), - ), + Icon(icon, color: color, size: 20), const Spacer(), - Icon( - Icons.trending_up, - color: Colors.green, - size: 16, + Text( + value, + style: TextStyle( + fontWeight: FontWeight.bold, + color: color, + fontSize: 18, + ), ), ], ), - const SizedBox(height: SpacingTokens.sm), - Text( - value, - style: TypographyTokens.headlineLarge.copyWith( - fontWeight: FontWeight.bold, - color: color, - ), - ), + const SizedBox(height: 4), Text( title, - style: TypographyTokens.bodyMedium.copyWith( + style: const TextStyle( fontWeight: FontWeight.w600, + color: Colors.black87, + fontSize: 12, ), ), Text( subtitle, - style: TypographyTokens.bodySmall.copyWith( - color: ColorTokens.textSecondary, + style: const TextStyle( + color: Colors.grey, + fontSize: 10, ), ), ], @@ -307,78 +439,132 @@ class _SuperAdminDashboardState extends State ); } - /// Section alertes système - Widget _buildSystemAlertsSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - 'Alertes Système', - style: TypographyTokens.headlineMedium.copyWith( - fontWeight: FontWeight.bold, - ), + /// Section serveur simplifiée + Widget _buildSimpleServerSection() { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Performance Serveur', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Color(0xFF6C5CE7), ), - const Spacer(), - Container( - padding: const EdgeInsets.symmetric( - horizontal: SpacingTokens.sm, - vertical: SpacingTokens.xs, - ), - decoration: BoxDecoration( - color: Colors.red.withOpacity(0.1), - borderRadius: BorderRadius.circular(RadiusTokens.sm), - ), - child: Text( - '3 critiques', - style: TypographyTokens.bodySmall.copyWith( - color: Colors.red, + ), + const SizedBox(height: 12), + _buildMetricRow('CPU', '67.3%', Colors.orange), + const SizedBox(height: 8), + _buildMetricRow('RAM', '12.4 GB / 16 GB', Colors.blue), + const SizedBox(height: 8), + _buildMetricRow('Disque', '847 GB / 1 TB', Colors.red), + ], + ), + ); + } + + /// Ligne de métrique + Widget _buildMetricRow(String label, String value, Color color) { + return Row( + children: [ + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 8), + Text( + label, + style: const TextStyle(fontWeight: FontWeight.w600), + ), + const Spacer(), + Text( + value, + style: TextStyle( + color: color, + fontWeight: FontWeight.w600, + ), + ), + ], + ); + } + + /// Section alertes simplifiée + Widget _buildSimpleAlertsSection() { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Alertes Système', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Color(0xFF6C5CE7), + ), + ), + const SizedBox(height: 12), + _buildAlertRow('Charge CPU élevée', 'Serveur principal', Colors.orange), + const SizedBox(height: 8), + _buildAlertRow('Espace disque faible', 'Base de données', Colors.red), + const SizedBox(height: 8), + _buildAlertRow('Connexions élevées', 'Load balancer', Colors.amber), + ], + ), + ); + } + + /// Ligne d'alerte + Widget _buildAlertRow(String title, String source, Color color) { + return Row( + children: [ + Icon(Icons.warning, color: color, size: 16), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( fontWeight: FontWeight.w600, + fontSize: 12, ), ), - ), - ], - ), - const SizedBox(height: SpacingTokens.md), - - // Liste des alertes - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(RadiusTokens.md), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - children: [ - _buildAlertItem( - 'Charge CPU élevée', - 'Serveur principal à 89%', - Icons.warning, - Colors.orange, - '2 min', - ), - const Divider(height: 1), - _buildAlertItem( - 'Espace disque faible', - 'Base de données à 92%', - Icons.error, - Colors.red, - '5 min', - ), - const Divider(height: 1), - _buildAlertItem( - 'Connexions simultanées', - 'Pic de trafic détecté', - Icons.info, - Colors.blue, - '12 min', + Text( + source, + style: const TextStyle( + color: Colors.grey, + fontSize: 10, + ), ), ], ), @@ -387,128 +573,136 @@ class _SuperAdminDashboardState extends State ); } - /// Item d'alerte - Widget _buildAlertItem( - String title, - String description, - IconData icon, - Color color, - String time, - ) { - return ListTile( - leading: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: color.withOpacity(0.1), - borderRadius: BorderRadius.circular(20), - ), - child: Icon(icon, color: color, size: 20), + /// Section activité simplifiée + Widget _buildSimpleActivitySection() { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), - title: Text( - title, - style: TypographyTokens.bodyMedium.copyWith( - fontWeight: FontWeight.w600, - ), - ), - subtitle: Text(description), - trailing: Text( - time, - style: TypographyTokens.bodySmall.copyWith( - color: ColorTokens.textSecondary, - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Activité Récente', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Color(0xFF6C5CE7), + ), + ), + const SizedBox(height: 12), + _buildActivityRow('Nouvelle organisation créée', 'il y a 2h'), + const SizedBox(height: 8), + _buildActivityRow('Utilisateur connecté', 'il y a 5min'), + const SizedBox(height: 8), + _buildActivityRow('Sauvegarde terminée', 'il y a 1h'), + ], ), ); } - /// Section activité globale - Widget _buildGlobalActivitySection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + /// Ligne d'activité + Widget _buildActivityRow(String title, String time) { + return Row( children: [ - Text( - 'Activité Récente Globale', - style: TypographyTokens.headlineMedium.copyWith( - fontWeight: FontWeight.bold, + Container( + width: 8, + height: 8, + decoration: const BoxDecoration( + color: Color(0xFF6C5CE7), + shape: BoxShape.circle, ), ), - const SizedBox(height: SpacingTokens.md), - - DashboardRecentActivitySection( - activities: [ - DashboardActivity( - title: 'Nouvelle organisation créée', - subtitle: 'Association des Développeurs', - icon: Icons.business, - color: const Color(0xFF0984E3), - time: 'Il y a 5 min', - ), - DashboardActivity( - title: 'Mise à jour système', - subtitle: 'Version 2.1.4 déployée', - icon: Icons.system_update, - color: const Color(0xFF00B894), - time: 'Il y a 15 min', - ), - DashboardActivity( - title: 'Alerte sécurité résolue', - subtitle: 'Tentative d\'intrusion bloquée', - icon: Icons.security, - color: const Color(0xFFE17055), - time: 'Il y a 32 min', - ), - ], - onActivityTap: (activityId) { - // Navigation vers les détails - }, + const SizedBox(width: 8), + Expanded( + child: Text( + title, + style: const TextStyle(fontSize: 12), + ), + ), + Text( + time, + style: const TextStyle( + color: Colors.grey, + fontSize: 10, + ), ), ], ); } - /// Onglet Organisations (placeholder) - Widget _buildOrganizationsTab() { + /// Organisations Content + Widget _buildOrganizationsContent() { return const Center( - child: Text('Gestion des Organisations'), + child: Text( + 'Gestion des Organisations\n(En développement)', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), ); } - /// Onglet Système (placeholder) - Widget _buildSystemTab() { + /// Users Content + Widget _buildUsersContent() { return const Center( - child: Text('Administration Système'), + child: Text( + 'Gestion des Utilisateurs\n(En développement)', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), ); } - /// Onglet Analytics (placeholder) - Widget _buildAnalyticsTab() { + /// System Content + Widget _buildSystemContent() { return const Center( - child: Text('Analytics Avancées'), + child: Text( + 'Monitoring Système\n(En développement)', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + ); + } + + /// Analytics Content + Widget _buildAnalyticsContent() { + return const Center( + child: Text( + 'Analytics Avancées\n(En développement)', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + ); + } + + /// Security Content + Widget _buildSecurityContent() { + return const Center( + child: Text( + 'Sécurité et Audit\n(En développement)', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + ); + } + + /// Configuration Content + Widget _buildConfigurationContent() { + return const Center( + child: Text( + 'Configuration Système\n(En développement)', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), ); } } - -/// Painter pour le motif géométrique de l'en-tête -class _GeometricPatternPainter extends CustomPainter { - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = Colors.white.withOpacity(0.1) - ..strokeWidth = 1 - ..style = PaintingStyle.stroke; - - // Dessiner un motif géométrique sophistiqué - for (int i = 0; i < 10; i++) { - final rect = Rect.fromLTWH( - i * size.width / 10, - i * size.height / 10, - size.width / 5, - size.height / 5, - ); - canvas.drawRect(rect, paint); - } - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; -} diff --git a/unionflow-mobile-apps/lib/features/dashboard/presentation/widgets/dashboard_activity_tile.dart b/unionflow-mobile-apps/lib/features/dashboard/presentation/widgets/dashboard_activity_tile.dart index 49a4664..7976366 100644 --- a/unionflow-mobile-apps/lib/features/dashboard/presentation/widgets/dashboard_activity_tile.dart +++ b/unionflow-mobile-apps/lib/features/dashboard/presentation/widgets/dashboard_activity_tile.dart @@ -86,11 +86,15 @@ class DashboardActivityTile extends StatelessWidget { fontSize: 12, ), ), - trailing: Text( - activity.time, - style: TypographyTokens.labelSmall.copyWith( - color: ColorTokens.onSurfaceVariant, - fontSize: 11, + trailing: SizedBox( + width: 60, + child: Text( + activity.time, + style: TypographyTokens.labelSmall.copyWith( + color: ColorTokens.onSurfaceVariant, + fontSize: 11, + ), + textAlign: TextAlign.end, ), ), );