import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:geosector_app/presentation/widgets/charts/passage_data.dart'; import 'package:geosector_app/presentation/widgets/charts/passage_utils.dart'; import 'package:intl/intl.dart'; /// Widget de graphique combiné pour afficher les passages et règlements class CombinedChart extends StatelessWidget { /// Liste des données de passage par type final List> passageData; /// Liste des données de règlement par type final List> paymentData; /// Type de période (Jour, Semaine, Mois, Année) final String periodType; /// Hauteur du graphique final double height; /// Largeur des barres final double barWidth; /// Rayon des points sur les lignes final double dotRadius; /// Épaisseur des lignes final double lineWidth; /// Montant maximum pour l'axe Y des règlements final double? maxYAmount; /// Nombre maximum pour l'axe Y des passages final int? maxYCount; const CombinedChart({ super.key, required this.passageData, required this.paymentData, this.periodType = 'Jour', this.height = 300, this.barWidth = 16, this.dotRadius = 4, this.lineWidth = 3, this.maxYAmount, this.maxYCount, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); // Convertir les données brutes en modèles structurés final passagesByType = PassageUtils.getPassageDataByType(passageData); final paymentsByType = PassageUtils.getPaymentDataByType(paymentData); // Extraire les dates uniques pour l'axe X final List allDates = []; for (final data in passageData) { final DateTime date = data['date'] is DateTime ? data['date'] : DateTime.parse(data['date']); if (!allDates.any((d) => d.year == date.year && d.month == date.month && d.day == date.day)) { allDates.add(date); } } // Trier les dates allDates.sort((a, b) => a.compareTo(b)); // Calculer le maximum pour les axes Y double maxAmount = 0; for (final typeData in paymentsByType) { for (final data in typeData) { if (data.amount > maxAmount) { maxAmount = data.amount; } } } int maxCount = 0; for (final typeData in passagesByType) { for (final data in typeData) { if (data.count > maxCount) { maxCount = data.count; } } } // Utiliser les maximums fournis ou calculés final effectiveMaxYAmount = maxYAmount ?? (maxAmount * 1.2).ceilToDouble(); final effectiveMaxYCount = maxYCount ?? (maxCount * 1.2).ceil(); return SizedBox( height: height, child: BarChart( BarChartData( alignment: BarChartAlignment.spaceAround, maxY: effectiveMaxYCount.toDouble(), barTouchData: BarTouchData( touchTooltipData: BarTouchTooltipData( tooltipPadding: const EdgeInsets.all(8), tooltipMargin: 8, getTooltipItem: (group, groupIndex, rod, rodIndex) { final date = allDates[group.x.toInt()]; final formattedDate = DateFormat('dd/MM').format(date); // Calculer le total des passages pour cette date int totalPassages = 0; for (final typeData in passagesByType) { for (final data in typeData) { if (data.date.year == date.year && data.date.month == date.month && data.date.day == date.day) { totalPassages += data.count; } } } return BarTooltipItem( '$formattedDate: $totalPassages passages', TextStyle( color: theme.colorScheme.onSurface, fontWeight: FontWeight.bold, ), ); }, ), ), titlesData: FlTitlesData( show: true, bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 30, getTitlesWidget: (value, meta) { if (value >= 0 && value < allDates.length) { final date = allDates[value.toInt()]; final formattedDate = PassageUtils.formatDateForChart(date, periodType); return SideTitleWidget( meta: meta, space: 8, child: Text( formattedDate, style: TextStyle( color: theme.colorScheme.onSurface.withOpacity(0.6), fontSize: 10, ), ), ); } return const SizedBox(); }, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { return SideTitleWidget( meta: meta, space: 8, child: Text( value.toInt().toString(), style: TextStyle( color: theme.colorScheme.onSurface.withOpacity(0.6), fontSize: 10, ), ), ); }, reservedSize: 30, ), ), rightTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { // Convertir la valeur de l'axe Y des passages à l'échelle des montants final amountValue = (value / effectiveMaxYCount) * effectiveMaxYAmount; return SideTitleWidget( meta: meta, space: 8, child: Text( '${amountValue.toInt()}€', style: TextStyle( color: theme.colorScheme.onSurface.withOpacity(0.6), fontSize: 10, ), ), ); }, reservedSize: 40, ), ), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), gridData: FlGridData( show: true, getDrawingHorizontalLine: (value) { return FlLine( color: theme.dividerColor.withOpacity(0.2), strokeWidth: 1, ); }, drawVerticalLine: false, ), borderData: FlBorderData(show: false), barGroups: _createBarGroups(allDates, passagesByType), extraLinesData: const ExtraLinesData( horizontalLines: [], verticalLines: [], extraLinesOnTop: true, ), ), swapAnimationDuration: const Duration(milliseconds: 250), ), ); } /// Créer les groupes de barres pour les passages List _createBarGroups( List allDates, List> passagesByType, ) { final List groups = []; for (int i = 0; i < allDates.length; i++) { final date = allDates[i]; // Calculer le total des passages pour cette date int totalPassages = 0; for (final typeData in passagesByType) { for (final data in typeData) { if (data.date.year == date.year && data.date.month == date.month && data.date.day == date.day) { totalPassages += data.count; } } } // Créer un groupe de barres pour cette date groups.add( BarChartGroupData( x: i, barRods: [ BarChartRodData( toY: totalPassages.toDouble(), color: Colors.blue.shade700, width: barWidth, borderRadius: const BorderRadius.only( topLeft: Radius.circular(6), topRight: Radius.circular(6), ), ), ], ), ); } return groups; } } /// Widget de légende pour le graphique combiné class CombinedChartLegend extends StatelessWidget { const CombinedChartLegend({super.key}); @override Widget build(BuildContext context) { return Wrap( spacing: 16, runSpacing: 8, children: [ _buildLegendItem('Passages', Colors.blue.shade700, isBar: true), _buildLegendItem('Espèces', const Color(0xFF4CAF50)), _buildLegendItem('Chèques', const Color(0xFF2196F3)), _buildLegendItem('CB', const Color(0xFFF44336)), ], ); } /// Créer un élément de légende Widget _buildLegendItem(String label, Color color, {bool isBar = false}) { return Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 16, height: 16, decoration: BoxDecoration( color: color, shape: isBar ? BoxShape.rectangle : BoxShape.circle, borderRadius: isBar ? BorderRadius.circular(3) : null, ), ), const SizedBox(width: 4), Text( label, style: const TextStyle(fontSize: 12), ), ], ); } }