import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales import 'package:flutter/material.dart'; import 'package:geosector_app/core/theme/app_theme.dart'; import 'package:geosector_app/presentation/widgets/charts/charts.dart'; class UserStatisticsPage extends StatefulWidget { const UserStatisticsPage({super.key}); @override State createState() => _UserStatisticsPageState(); } class _UserStatisticsPageState extends State { // Période sélectionnée String _selectedPeriod = 'Semaine'; // Secteur sélectionné (0 = tous les secteurs) int _selectedSectorId = 0; @override Widget build(BuildContext context) { final theme = Theme.of(context); final size = MediaQuery.of(context).size; final isDesktop = size.width > 900; return Scaffold( backgroundColor: Colors.transparent, body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Statistiques', style: theme.textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.primary, ), ), const SizedBox(height: 16), // Filtres _buildFilters(theme, isDesktop), const SizedBox(height: 24), // Graphiques _buildCharts(theme), const SizedBox(height: 24), // Résumé par type de passage _buildPassageTypeSummary(theme, isDesktop), const SizedBox(height: 24), // Résumé par type de règlement _buildPaymentTypeSummary(theme, isDesktop), ], ), ), ), ); } // Construction des filtres Widget _buildFilters(ThemeData theme, bool isDesktop) { return Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Filtres', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Wrap( spacing: 16, runSpacing: 16, children: [ // Sélection de la période _buildFilterSection( 'Période', ['Jour', 'Semaine', 'Mois', 'Année'], _selectedPeriod, (value) { setState(() { _selectedPeriod = value; }); }, theme, ), // Sélection du secteur (si l'utilisateur a plusieurs secteurs) _buildSectorSelector(context, theme), // Bouton d'application des filtres ElevatedButton.icon( onPressed: () { // Actualiser les statistiques avec les filtres sélectionnés setState(() { // Dans une implémentation réelle, on chargerait ici les données // filtrées par période et secteur }); }, icon: const Icon(Icons.filter_list), label: const Text('Appliquer'), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.accentColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ], ), ], ), ), ); } // Construction du sélecteur de secteur Widget _buildSectorSelector(BuildContext context, ThemeData theme) { // Utiliser l'instance globale définie dans app.dart // Récupérer les secteurs de l'utilisateur final sectors = userRepository.getUserSectors(); // Si l'utilisateur n'a qu'un seul secteur, ne pas afficher le sélecteur if (sectors.length <= 1) { return const SizedBox.shrink(); } // Créer la liste des options avec "Tous" comme première option final List> items = [ const DropdownMenuItem( value: 0, child: Text('Tous les secteurs'), ), ]; // Ajouter les secteurs de l'utilisateur for (final sector in sectors) { items.add( DropdownMenuItem( value: sector.id, child: Text(sector.libelle), ), ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Secteur', style: theme.textTheme.titleSmall, ), const SizedBox(height: 8), Container( constraints: const BoxConstraints(maxWidth: 250), child: DropdownButton( value: _selectedSectorId, isExpanded: true, items: items, onChanged: (value) { if (value != null) { setState(() { _selectedSectorId = value; }); } }, hint: const Text('Sélectionner un secteur'), ), ), ], ); } // Construction d'une section de filtre Widget _buildFilterSection( String title, List options, String selectedValue, Function(String) onChanged, ThemeData theme, ) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: theme.textTheme.titleSmall, ), const SizedBox(height: 8), SegmentedButton( segments: options.map((option) { return ButtonSegment( value: option, label: Text(option), ); }).toList(), selected: {selectedValue}, onSelectionChanged: (Set selection) { onChanged(selection.first); }, style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( (Set states) { if (states.contains(MaterialState.selected)) { return AppTheme.secondaryColor; } return theme.colorScheme.surface; }, ), foregroundColor: MaterialStateProperty.resolveWith( (Set states) { if (states.contains(MaterialState.selected)) { return Colors.white; } return theme.colorScheme.onSurface; }, ), ), ), ], ); } // Construction des graphiques Widget _buildCharts(ThemeData theme) { return Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Passages et règlements par $_selectedPeriod', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 24), SizedBox( height: 300, child: _buildActivityChart(theme), ), ], ), ), ); } // Construction du graphique d'activité Widget _buildActivityChart(ThemeData theme) { // Générer des données fictives pour les passages final now = DateTime.now(); final List> passageData = []; // Récupérer le secteur sélectionné (si applicable) final String sectorLabel = _selectedSectorId == 0 ? 'Tous les secteurs' : userRepository.getSectorById(_selectedSectorId)?.libelle ?? 'Secteur inconnu'; // Déterminer la plage de dates en fonction de la période sélectionnée DateTime startDate; int daysToGenerate; switch (_selectedPeriod) { case 'Jour': startDate = DateTime(now.year, now.month, now.day); daysToGenerate = 1; break; case 'Semaine': // Début de la semaine (lundi) final weekday = now.weekday; startDate = now.subtract(Duration(days: weekday - 1)); daysToGenerate = 7; break; case 'Mois': // Début du mois startDate = DateTime(now.year, now.month, 1); // Calculer le nombre de jours dans le mois final lastDayOfMonth = DateTime(now.year, now.month + 1, 0).day; daysToGenerate = lastDayOfMonth; break; case 'Année': // Début de l'année startDate = DateTime(now.year, 1, 1); daysToGenerate = 365; break; default: startDate = DateTime(now.year, now.month, now.day); daysToGenerate = 7; } // Générer des données pour la période sélectionnée for (int i = 0; i < daysToGenerate; i++) { final date = startDate.add(Duration(days: i)); // Générer des données pour chaque type de passage for (int typeId = 1; typeId <= 6; typeId++) { // Générer un nombre de passages basé sur le jour et le type final count = (typeId == 1 || typeId == 2) ? (2 + (date.day % 6)) // Plus de passages pour les types 1 et 2 : (date.day % 4); // Moins pour les autres types if (count > 0) { passageData.add({ 'date': date.toIso8601String(), 'type_passage': typeId, 'nb': count, }); } } } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Afficher le secteur sélectionné si ce n'est pas "Tous" if (_selectedSectorId != 0) Padding( padding: const EdgeInsets.only(bottom: 16.0), child: Text( 'Secteur: $sectorLabel', style: theme.textTheme.titleSmall?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.primary, ), ), ), ActivityChart( passageData: passageData, periodType: _selectedPeriod, height: 300, ), ], ); } // Construction du résumé par type de passage Widget _buildPassageTypeSummary(ThemeData theme, bool isDesktop) { return PassageSummaryCard( title: 'Répartition par type de passage', titleColor: theme.colorScheme.primary, titleIcon: Icons.pie_chart, height: 300, useValueListenable: true, userId: userRepository.getCurrentUser()?.id, showAllPassages: false, excludePassageTypes: const [2], // Exclure "À finaliser" isDesktop: isDesktop, ); } // Construction du résumé par type de règlement Widget _buildPaymentTypeSummary(ThemeData theme, bool isDesktop) { return PaymentSummaryCard( title: 'Répartition par type de règlement', titleColor: AppTheme.accentColor, titleIcon: Icons.pie_chart, height: 300, useValueListenable: true, userId: userRepository.getCurrentUser()?.id, showAllPayments: false, isDesktop: isDesktop, backgroundIcon: Icons.euro_symbol, backgroundIconColor: Colors.blue, backgroundIconOpacity: 0.05, ); } }