import 'package:flutter/material.dart'; import 'package:geosector_app/core/theme/app_theme.dart'; import 'package:geosector_app/presentation/widgets/charts/charts.dart'; import 'dart:math' as math; /// Class pour dessiner les petits points blancs sur le fond class DotsPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.white.withOpacity(0.5) ..style = PaintingStyle.fill; final random = math.Random(42); // Seed fixe pour consistance final numberOfDots = (size.width * size.height) ~/ 1500; for (int i = 0; i < numberOfDots; i++) { final x = random.nextDouble() * size.width; final y = random.nextDouble() * size.height; final radius = 1.0 + random.nextDouble() * 2.0; canvas.drawCircle(Offset(x, y), radius, paint); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class AdminStatisticsPage extends StatefulWidget { const AdminStatisticsPage({Key? key}) : super(key: key); @override State createState() => _AdminStatisticsPageState(); } class _AdminStatisticsPageState extends State { // Filtres String _selectedPeriod = 'Jour'; String _selectedFilterType = 'Secteur'; String _selectedSector = 'Tous'; String _selectedUser = 'Tous'; int _daysToShow = 15; // Liste des périodes et types de filtre final List _periods = ['Jour', 'Semaine', 'Mois', 'Année']; final List _filterTypes = ['Secteur', 'Membre']; // Données simulées pour les secteurs et membres (à remplacer par des données réelles) final List _sectors = [ 'Tous', 'Secteur Nord', 'Secteur Sud', 'Secteur Est', 'Secteur Ouest' ]; final List _members = [ 'Tous', 'Jean Dupont', 'Marie Martin', 'Pierre Legrand', 'Sophie Petit', 'Lucas Moreau' ]; @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final isDesktop = screenWidth > 800; return Stack( children: [ // Fond dégradé avec petits points blancs Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.white, Colors.red.shade300], ), ), child: CustomPaint( painter: DotsPainter(), child: Container(width: double.infinity, height: double.infinity), ), ), // Contenu de la page SingleChildScrollView( padding: const EdgeInsets.all(AppTheme.spacingL), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Titre et description Text( 'Analyse des statistiques', style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: AppTheme.spacingS), Text( 'Visualisez les statistiques de passages et de collecte pour votre amicale.', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey[600], ), ), const SizedBox(height: AppTheme.spacingL), // Filtres Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), color: Colors.white, // Fond opaque child: Padding( padding: const EdgeInsets.all(AppTheme.spacingM), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Filtres', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: AppTheme.spacingM), isDesktop ? Row( children: [ Expanded(child: _buildPeriodDropdown()), const SizedBox(width: AppTheme.spacingM), Expanded(child: _buildDaysDropdown()), const SizedBox(width: AppTheme.spacingM), Expanded(child: _buildFilterTypeDropdown()), const SizedBox(width: AppTheme.spacingM), Expanded(child: _buildFilterDropdown()), ], ) : Column( children: [ _buildPeriodDropdown(), const SizedBox(height: AppTheme.spacingM), _buildDaysDropdown(), const SizedBox(height: AppTheme.spacingM), _buildFilterTypeDropdown(), const SizedBox(height: AppTheme.spacingM), _buildFilterDropdown(), ], ), ], ), ), ), const SizedBox(height: AppTheme.spacingL), // Graphique d'activité principal Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), color: Colors.white, // Fond opaque child: Padding( padding: const EdgeInsets.all(AppTheme.spacingM), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Évolution des passages', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: AppTheme.spacingM), ActivityChart( height: 350, showAllPassages: true, title: '', daysToShow: _daysToShow, periodType: _selectedPeriod, userId: _selectedUser != 'Tous' ? _getUserIdFromName(_selectedUser) : null, // Si on filtre par secteur, on devrait passer l'ID du secteur ), ], ), ), ), const SizedBox(height: AppTheme.spacingL), // Graphiques de répartition isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: _buildChartCard( 'Répartition par type de passage', PassageSummaryCard( title: '', titleColor: AppTheme.primaryColor, titleIcon: Icons.pie_chart, height: 300, useValueListenable: true, showAllPassages: true, excludePassageTypes: const [ 2 ], // Exclure "À finaliser" userId: _selectedUser != 'Tous' ? _getUserIdFromName(_selectedUser) : null, isDesktop: MediaQuery.of(context).size.width > 800, ), ), ), const SizedBox(width: AppTheme.spacingM), Expanded( child: _buildChartCard( 'Répartition par mode de paiement', const PaymentPieChart( payments: [ PaymentData( typeId: 1, amount: 1500.0, color: const Color(0xFFFFC107), icon: Icons.toll, title: 'Espèce', ), PaymentData( typeId: 2, amount: 2500.0, color: const Color(0xFF8BC34A), icon: Icons.wallet, title: 'Chèque', ), PaymentData( typeId: 3, amount: 1000.0, color: const Color(0xFF00B0FF), icon: Icons.credit_card, title: 'CB', ), ], size: 300, ), ), ), ], ) : Column( children: [ _buildChartCard( 'Répartition par type de passage', PassageSummaryCard( title: '', titleColor: AppTheme.primaryColor, titleIcon: Icons.pie_chart, height: 300, useValueListenable: true, showAllPassages: true, excludePassageTypes: const [ 2 ], // Exclure "À finaliser" userId: _selectedUser != 'Tous' ? _getUserIdFromName(_selectedUser) : null, isDesktop: MediaQuery.of(context).size.width > 800, ), ), const SizedBox(height: AppTheme.spacingM), _buildChartCard( 'Répartition par mode de paiement', const PaymentPieChart( payments: [ PaymentData( typeId: 1, amount: 1500.0, color: const Color(0xFFFFC107), icon: Icons.toll, title: 'Espèce', ), PaymentData( typeId: 2, amount: 2500.0, color: const Color(0xFF8BC34A), icon: Icons.wallet, title: 'Chèque', ), PaymentData( typeId: 3, amount: 1000.0, color: const Color(0xFF00B0FF), icon: Icons.credit_card, title: 'CB', ), ], size: 300, ), ), ], ), const SizedBox(height: AppTheme.spacingL), // Graphique combiné (si disponible) _buildChartCard( 'Comparaison passages/montants', const SizedBox( height: 350, child: Center( child: Text('Graphique combiné à implémenter'), ), ), ), const SizedBox(height: AppTheme.spacingL), // Actions Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), color: Colors.white, // Fond opaque child: Padding( padding: const EdgeInsets.all(AppTheme.spacingM), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Actions', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: AppTheme.spacingM), Wrap( spacing: AppTheme.spacingM, runSpacing: AppTheme.spacingM, children: [ ElevatedButton.icon( onPressed: () { // Exporter les statistiques }, icon: const Icon(Icons.file_download), label: const Text('Exporter les statistiques'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryColor, foregroundColor: Colors.white, ), ), ElevatedButton.icon( onPressed: () { // Imprimer les statistiques }, icon: const Icon(Icons.print), label: const Text('Imprimer'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.secondaryColor, foregroundColor: Colors.white, ), ), ElevatedButton.icon( onPressed: () { // Partager les statistiques }, icon: const Icon(Icons.share), label: const Text('Partager'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.accentColor, foregroundColor: Colors.white, ), ), ], ), ], ), ), ), ], ), ), ], ); } // Dropdown pour la période Widget _buildPeriodDropdown() { return InputDecorator( decoration: InputDecoration( labelText: 'Période', border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), contentPadding: const EdgeInsets.symmetric( horizontal: AppTheme.spacingM, vertical: AppTheme.spacingS, ), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: _selectedPeriod, isDense: true, isExpanded: true, items: _periods.map((String period) { return DropdownMenuItem( value: period, child: Text(period), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { _selectedPeriod = newValue; }); } }, ), ), ); } // Dropdown pour le nombre de jours Widget _buildDaysDropdown() { return InputDecorator( decoration: InputDecoration( labelText: 'Nombre de jours', border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), contentPadding: const EdgeInsets.symmetric( horizontal: AppTheme.spacingM, vertical: AppTheme.spacingS, ), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: _daysToShow, isDense: true, isExpanded: true, items: [7, 15, 30, 60, 90, 180, 365].map((int days) { return DropdownMenuItem( value: days, child: Text('$days jours'), ); }).toList(), onChanged: (int? newValue) { if (newValue != null) { setState(() { _daysToShow = newValue; }); } }, ), ), ); } // Dropdown pour le type de filtre Widget _buildFilterTypeDropdown() { return InputDecorator( decoration: InputDecoration( labelText: 'Filtrer par', border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), contentPadding: const EdgeInsets.symmetric( horizontal: AppTheme.spacingM, vertical: AppTheme.spacingS, ), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: _selectedFilterType, isDense: true, isExpanded: true, items: _filterTypes.map((String type) { return DropdownMenuItem( value: type, child: Text(type), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { _selectedFilterType = newValue; // Réinitialiser les filtres spécifiques _selectedSector = 'Tous'; _selectedUser = 'Tous'; }); } }, ), ), ); } // Dropdown pour le filtre spécifique (secteur ou membre) Widget _buildFilterDropdown() { final List items = _selectedFilterType == 'Secteur' ? _sectors : _members; final String value = _selectedFilterType == 'Secteur' ? _selectedSector : _selectedUser; return InputDecorator( decoration: InputDecoration( labelText: _selectedFilterType, border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), contentPadding: const EdgeInsets.symmetric( horizontal: AppTheme.spacingM, vertical: AppTheme.spacingS, ), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: value, isDense: true, isExpanded: true, items: items.map((String item) { return DropdownMenuItem( value: item, child: Text(item), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { if (_selectedFilterType == 'Secteur') { _selectedSector = newValue; } else { _selectedUser = newValue; } }); } }, ), ), ); } // Widget pour envelopper un graphique dans une carte Widget _buildChartCard(String title, Widget chart) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium), ), color: Colors.white, // Fond opaque child: Padding( padding: const EdgeInsets.all(AppTheme.spacingM), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: AppTheme.spacingM), chart, ], ), ), ); } // Méthode utilitaire pour obtenir l'ID utilisateur à partir de son nom int? _getUserIdFromName(String name) { // Dans un cas réel, cela nécessiterait une requête au repository // Pour l'exemple, on utilise une correspondance simple if (name == 'Jean Dupont') return 1; if (name == 'Marie Martin') return 2; if (name == 'Pierre Legrand') return 3; if (name == 'Sophie Petit') return 4; if (name == 'Lucas Moreau') return 5; return null; } }