/// Dashboard Adaptatif Principal - Orchestrateur Intelligent /// Sélectionne et affiche le dashboard approprié selon le rôle utilisateur library adaptive_dashboard_page; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../core/auth/bloc/auth_bloc.dart'; import '../../../../core/auth/models/user_role.dart'; import '../../../../core/widgets/adaptive_widget.dart'; import 'role_dashboards/super_admin_dashboard.dart'; import 'role_dashboards/org_admin_dashboard.dart'; import 'role_dashboards/moderator_dashboard.dart'; import 'role_dashboards/active_member_dashboard.dart'; import 'role_dashboards/simple_member_dashboard.dart'; import 'role_dashboards/visitor_dashboard.dart'; /// Page Dashboard Adaptatif - Le cœur du système morphique /// /// Cette page utilise l'AdaptiveWidget pour afficher automatiquement /// le dashboard approprié selon le rôle de l'utilisateur connecté. /// /// Fonctionnalités : /// - Morphing automatique entre les dashboards /// - Animations fluides lors des changements de rôle /// - Gestion des états de chargement et d'erreur /// - Fallback gracieux pour les rôles non supportés class AdaptiveDashboardPage extends StatefulWidget { const AdaptiveDashboardPage({super.key}); @override State createState() => _AdaptiveDashboardPageState(); } class _AdaptiveDashboardPageState extends State with TickerProviderStateMixin { /// Contrôleur d'animation pour les transitions late AnimationController _transitionController; /// Animation de fade pour les transitions late Animation _fadeAnimation; @override void initState() { super.initState(); _initializeAnimations(); } @override void dispose() { _transitionController.dispose(); super.dispose(); } /// Initialise les animations de transition void _initializeAnimations() { _transitionController = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, ); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _transitionController, curve: Curves.easeInOutCubic, )); // Démarrer l'animation initiale _transitionController.forward(); } @override Widget build(BuildContext context) { return Scaffold( body: BlocListener( listener: (context, state) { // Déclencher l'animation lors des changements d'état if (state is AuthAuthenticated) { _transitionController.reset(); _transitionController.forward(); } }, child: AnimatedBuilder( animation: _fadeAnimation, builder: (context, child) { return Opacity( opacity: _fadeAnimation.value, child: _buildAdaptiveDashboard(), ); }, ), ), ); } /// Construit le dashboard adaptatif selon le rôle Widget _buildAdaptiveDashboard() { return AdaptiveWidget( // Mapping des rôles vers leurs dashboards spécifiques roleWidgets: { UserRole.superAdmin: () => const SuperAdminDashboard(), UserRole.orgAdmin: () => const OrgAdminDashboard(), UserRole.moderator: () => const ModeratorDashboard(), UserRole.activeMember: () => const ActiveMemberDashboard(), UserRole.simpleMember: () => const SimpleMemberDashboard(), UserRole.visitor: () => const VisitorDashboard(), }, // Permissions requises pour accéder au dashboard requiredPermissions: const [ 'dashboard.view.own', ], // Widget affiché si les permissions sont insuffisantes fallbackWidget: _buildUnauthorizedDashboard(), // Widget affiché pendant le chargement loadingWidget: _buildLoadingDashboard(), // Configuration des animations enableMorphing: true, morphingDuration: const Duration(milliseconds: 800), animationCurve: Curves.easeInOutCubic, // Audit trail activé auditLog: true, ); } /// Dashboard affiché en cas d'accès non autorisé Widget _buildUnauthorizedDashboard() { return Scaffold( body: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFFF8F9FA), Color(0xFFE9ECEF), ], ), ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Icône d'accès refusé Container( width: 120, height: 120, decoration: BoxDecoration( color: Colors.red.withOpacity(0.1), borderRadius: BorderRadius.circular(60), ), child: const Icon( Icons.lock_outline, size: 60, color: Colors.red, ), ), const SizedBox(height: 32), // Titre Text( 'Accès Non Autorisé', style: Theme.of(context).textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, color: Colors.red, ), ), const SizedBox(height: 16), // Description Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( 'Vous n\'avez pas les permissions nécessaires pour accéder au dashboard. Veuillez contacter un administrateur.', textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Colors.grey[600], ), ), ), const SizedBox(height: 32), // Bouton de contact ElevatedButton.icon( onPressed: () => _onContactSupport(), icon: const Icon(Icons.support_agent), label: const Text('Contacter le Support'), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), ), ), ], ), ), ), ); } /// Dashboard affiché pendant le chargement Widget _buildLoadingDashboard() { return Scaffold( body: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFF6C5CE7), Color(0xFF5A4FCF), ], ), ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Logo animé TweenAnimationBuilder( tween: Tween(begin: 0.0, end: 1.0), duration: const Duration(seconds: 2), builder: (context, value, child) { return Transform.rotate( angle: value * 2 * 3.14159, child: Container( width: 80, height: 80, decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(40), border: Border.all( color: Colors.white.withOpacity(0.3), width: 2, ), ), child: const Icon( Icons.dashboard, color: Colors.white, size: 40, ), ), ); }, ), const SizedBox(height: 32), // Titre Text( 'UnionFlow', style: Theme.of(context).textTheme.headlineLarge?.copyWith( color: Colors.white, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), // Indicateur de chargement const SizedBox( width: 40, height: 40, child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white), strokeWidth: 3, ), ), const SizedBox(height: 16), // Message de chargement Text( 'Préparation de votre dashboard...', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Colors.white.withOpacity(0.9), ), ), ], ), ), ), ); } /// Gère le contact avec le support void _onContactSupport() { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Contacter le Support'), content: const Text( 'Pour obtenir de l\'aide, veuillez envoyer un email à :\n\nsupport@unionflow.com\n\nOu appelez le :\n+33 1 23 45 67 89', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Fermer'), ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); // Ici, on pourrait ouvrir l'app email ou téléphone }, child: const Text('Envoyer Email'), ), ], ), ); } } /// Extension pour faciliter la navigation vers le dashboard adaptatif extension AdaptiveDashboardNavigation on BuildContext { /// Navigue vers le dashboard adaptatif void navigateToAdaptiveDashboard() { Navigator.of(this).pushReplacement( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const AdaptiveDashboardPage(), transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition( opacity: animation, child: SlideTransition( position: Tween( begin: const Offset(0.0, 0.1), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeInOutCubic, )), child: child, ), ); }, transitionDuration: const Duration(milliseconds: 600), ), ); } } /// Mixin pour les dashboards qui ont besoin de fonctionnalités communes mixin DashboardMixin on State { /// Affiche une notification de succès void showSuccessNotification(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ const Icon(Icons.check_circle, color: Colors.white), const SizedBox(width: 8), Expanded(child: Text(message)), ], ), backgroundColor: Colors.green, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ); } /// Affiche une notification d'erreur void showErrorNotification(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ const Icon(Icons.error, color: Colors.white), const SizedBox(width: 8), Expanded(child: Text(message)), ], ), backgroundColor: Colors.red, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ); } /// Affiche une boîte de dialogue de confirmation Future showConfirmationDialog(String title, String message) async { final result = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(title), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('Annuler'), ), ElevatedButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('Confirmer'), ), ], ), ); return result ?? false; } }