import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:hive_flutter/hive_flutter.dart'; import 'package:geosector_app/core/data/models/sector_model.dart'; import 'package:geosector_app/core/services/current_user_service.dart'; import 'package:geosector_app/presentation/widgets/sector_distribution_card.dart'; import 'package:geosector_app/presentation/widgets/charts/charts.dart'; import 'package:geosector_app/presentation/widgets/members_board_passages.dart'; import 'package:geosector_app/core/constants/app_keys.dart'; import 'package:geosector_app/core/theme/app_theme.dart'; import 'package:geosector_app/presentation/widgets/app_scaffold.dart'; /// Widget de contenu du tableau de bord unifié (sans scaffold) class HomeContent extends StatefulWidget { const HomeContent({super.key}); @override State createState() => _HomeContentState(); } class _HomeContentState extends State { // Détection du rôle late final bool isAdmin; late final int currentUserId; @override void initState() { super.initState(); // Déterminer le rôle de l'utilisateur et le mode d'affichage final currentUser = userRepository.getCurrentUser(); isAdmin = CurrentUserService.instance.shouldShowAdminUI; currentUserId = currentUser?.id ?? 0; } @override Widget build(BuildContext context) { debugPrint('Building HomeContent (isAdmin: $isAdmin)'); final screenWidth = MediaQuery.of(context).size.width; final isDesktop = screenWidth > 800; // Récupérer l'opération en cours final currentOperation = userRepository.getCurrentOperation(); // Titre dynamique avec l'ID et le nom de l'opération final String title = currentOperation != null ? 'Opération #${currentOperation.id} ${currentOperation.name}' : 'Opération'; // Retourner seulement le contenu (sans scaffold) return SingleChildScrollView( padding: EdgeInsets.symmetric( horizontal: isDesktop ? AppTheme.spacingL : AppTheme.spacingS, vertical: AppTheme.spacingL, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Titre Text( title, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: AppTheme.spacingM), // LIGNE 1 : Graphiques de répartition (type de passage et mode de paiement) isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: _buildPassageTypeCard(context), ), const SizedBox(width: AppTheme.spacingM), Expanded( child: _buildPaymentTypeCard(context), ), ], ) : Column( children: [ _buildPassageTypeCard(context), const SizedBox(height: AppTheme.spacingM), _buildPaymentTypeCard(context), ], ), const SizedBox(height: AppTheme.spacingL), // Tableau détaillé des membres - uniquement pour admin sur Web if (isAdmin && kIsWeb) ...[ const MembersBoardPassages( height: 700, ), const SizedBox(height: AppTheme.spacingL), ], // LIGNE 2 : Carte de répartition par secteur // Le widget filtre automatiquement selon le rôle de l'utilisateur ValueListenableBuilder>( valueListenable: Hive.box(AppKeys.sectorsBoxName).listenable(), builder: (context, Box box, child) { // Filtrer les secteurs pour les users int sectorCount; if (isAdmin) { sectorCount = box.values.length; } else { final userSectors = userRepository.getUserSectors(); sectorCount = userSectors.length; } return SectorDistributionCard( title: '$sectorCount secteur${sectorCount > 1 ? 's' : ''}', height: 500, ); }, ), const SizedBox(height: AppTheme.spacingL), // LIGNE 3 : Graphique d'activité Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), boxShadow: AppTheme.cardShadow, ), child: ActivityChart( height: 350, showAllPassages: isAdmin, // Admin voit tout, user voit tous les passages de ses secteurs title: isAdmin ? 'Passages réalisés par jour (15 derniers jours)' : 'Passages de mes secteurs par jour (15 derniers jours)', daysToShow: 15, ), ), const SizedBox(height: AppTheme.spacingL), // Actions rapides - uniquement pour admin sur le web if (isAdmin && kIsWeb) ...[ Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), boxShadow: AppTheme.cardShadow, ), padding: const EdgeInsets.all(AppTheme.spacingM), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Actions sur cette opération', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, color: AppTheme.primaryColor, ), ), const SizedBox(height: AppTheme.spacingM), Wrap( spacing: AppTheme.spacingM, runSpacing: AppTheme.spacingM, children: [ _buildActionButton( context, 'Exporter les données', Icons.file_download_outlined, AppTheme.primaryColor, () {}, ), _buildActionButton( context, 'Gérer les secteurs', Icons.map_outlined, AppTheme.accentColor, () {}, ), ], ), ], ), ), ], ], ), ); } // Construit la carte de répartition par type de passage Widget _buildPassageTypeCard(BuildContext context) { return PassageSummaryCard( title: isAdmin ? 'Passages' : 'Passages de mes secteurs', titleColor: AppTheme.primaryColor, titleIcon: Icons.route, height: 300, useValueListenable: true, showAllPassages: isAdmin, // Admin voit tout, user voit tous les passages de ses secteurs userId: null, // Pas de filtre par userId, on filtre par secteurs assignés excludePassageTypes: const [], // Afficher tous les types de passages customTotalDisplay: (total) => '$total passage${total > 1 ? 's' : ''}', isDesktop: MediaQuery.of(context).size.width > 800, backgroundIcon: Icons.route, backgroundIconColor: AppTheme.primaryColor, backgroundIconOpacity: 0.07, backgroundIconSize: 180, ); } // Construit la carte de répartition par mode de paiement Widget _buildPaymentTypeCard(BuildContext context) { return PaymentSummaryCard( title: isAdmin ? 'Règlements' : 'Mes règlements', titleColor: AppTheme.buttonSuccessColor, titleIcon: Icons.euro, height: 300, useValueListenable: true, showAllPayments: isAdmin, // Admin voit tout, user voit uniquement ses règlements (fkUser) userId: null, // Le filtre fkUser est géré automatiquement dans PaymentSummaryCard customTotalDisplay: (total) => '${total.toStringAsFixed(2)} €', isDesktop: MediaQuery.of(context).size.width > 800, backgroundIcon: Icons.euro, backgroundIconColor: AppTheme.primaryColor, backgroundIconOpacity: 0.07, backgroundIconSize: 180, ); } Widget _buildActionButton( BuildContext context, String label, IconData icon, Color color, VoidCallback onPressed, ) { return ElevatedButton.icon( onPressed: onPressed, icon: Icon(icon), label: Text(label), style: ElevatedButton.styleFrom( backgroundColor: color, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: AppTheme.spacingL, vertical: AppTheme.spacingM, ), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), ), ); } } /// Page autonome du tableau de bord unifié utilisant AppScaffold class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { // Utiliser le mode d'affichage pour déterminer l'UI final isAdmin = CurrentUserService.instance.shouldShowAdminUI; return AppScaffold( key: ValueKey('home_scaffold_${isAdmin ? 'admin' : 'user'}'), selectedIndex: 0, // Dashboard/Home est toujours l'index 0 pageTitle: 'Tableau de bord', body: const HomeContent(), ); } }