import 'package:flutter/material.dart'; import '../../../../shared/widgets/common/unified_card.dart'; import '../../../../shared/theme/design_system.dart'; import '../../../../core/utils/formatters.dart'; import '../../domain/entities/analytics_data.dart'; /// Widget de carte KPI utilisant le design system unifié class KPICardWidget extends StatelessWidget { const KPICardWidget({ super.key, required this.analyticsData, this.onTap, this.showTrend = true, this.showDetails = false, this.compact = false, }); final AnalyticsData analyticsData; final VoidCallback? onTap; final bool showTrend; final bool showDetails; final bool compact; @override Widget build(BuildContext context) { return UnifiedCard( variant: UnifiedCardVariant.elevated, onTap: onTap, child: Padding( padding: EdgeInsets.all( compact ? DesignSystem.spacing12 : DesignSystem.spacing16, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // En-tête avec icône et titre Row( children: [ Container( padding: const EdgeInsets.all(DesignSystem.spacing8), decoration: BoxDecoration( color: _getCouleurMetrique().withOpacity(0.1), borderRadius: BorderRadius.circular(DesignSystem.radius8), ), child: Icon( _getIconeMetrique(), color: _getCouleurMetrique(), size: compact ? 20 : 24, ), ), const SizedBox(width: DesignSystem.spacing12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( analyticsData.libelleAffichage, style: compact ? DesignSystem.textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w600, ) : DesignSystem.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), if (!compact && analyticsData.description != null) Padding( padding: const EdgeInsets.only( top: DesignSystem.spacing4, ), child: Text( analyticsData.description!, style: DesignSystem.textTheme.bodySmall?.copyWith( color: DesignSystem.textSecondaryColor, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ), // Indicateur de fiabilité if (showDetails) _buildIndicateurFiabilite(), ], ), SizedBox(height: compact ? DesignSystem.spacing8 : DesignSystem.spacing16), // Valeur principale Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( child: Text( analyticsData.valeurFormatee, style: compact ? DesignSystem.textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, color: _getCouleurMetrique(), ) : DesignSystem.textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, color: _getCouleurMetrique(), ), ), ), // Évolution if (showTrend && analyticsData.pourcentageEvolution != null) _buildEvolution(), ], ), // Détails supplémentaires if (showDetails) ...[ const SizedBox(height: DesignSystem.spacing12), _buildDetails(), ], // Période et dernière mise à jour if (!compact) ...[ const SizedBox(height: DesignSystem.spacing12), _buildInfosPeriode(), ], ], ), ), ); } /// Widget d'évolution avec icône et pourcentage Widget _buildEvolution() { final evolution = analyticsData.pourcentageEvolution!; final isPositive = evolution > 0; final isNegative = evolution < 0; Color couleur; IconData icone; if (isPositive) { couleur = DesignSystem.successColor; icone = Icons.trending_up; } else if (isNegative) { couleur = DesignSystem.errorColor; icone = Icons.trending_down; } else { couleur = DesignSystem.warningColor; icone = Icons.trending_flat; } return Container( padding: const EdgeInsets.symmetric( horizontal: DesignSystem.spacing8, vertical: DesignSystem.spacing4, ), decoration: BoxDecoration( color: couleur.withOpacity(0.1), borderRadius: BorderRadius.circular(DesignSystem.radius12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( icone, size: 16, color: couleur, ), const SizedBox(width: DesignSystem.spacing4), Text( analyticsData.evolutionFormatee, style: DesignSystem.textTheme.bodySmall?.copyWith( color: couleur, fontWeight: FontWeight.w600, ), ), ], ), ); } /// Widget d'indicateur de fiabilité Widget _buildIndicateurFiabilite() { final fiabilite = analyticsData.indicateurFiabilite; Color couleur; if (fiabilite >= 90) { couleur = DesignSystem.successColor; } else if (fiabilite >= 70) { couleur = DesignSystem.warningColor; } else { couleur = DesignSystem.errorColor; } return Container( padding: const EdgeInsets.symmetric( horizontal: DesignSystem.spacing6, vertical: DesignSystem.spacing2, ), decoration: BoxDecoration( color: couleur.withOpacity(0.1), borderRadius: BorderRadius.circular(DesignSystem.radius8), border: Border.all( color: couleur.withOpacity(0.3), width: 1, ), ), child: Text( '${fiabilite.toStringAsFixed(0)}%', style: DesignSystem.textTheme.bodySmall?.copyWith( color: couleur, fontWeight: FontWeight.w600, ), ), ); } /// Widget des détails supplémentaires Widget _buildDetails() { return Column( children: [ // Valeur précédente if (analyticsData.valeurPrecedente != null) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Période précédente', style: DesignSystem.textTheme.bodySmall?.copyWith( color: DesignSystem.textSecondaryColor, ), ), Text( _formaterValeur(analyticsData.valeurPrecedente!), style: DesignSystem.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w500, ), ), ], ), const SizedBox(height: DesignSystem.spacing4), // Éléments analysés if (analyticsData.nombreElementsAnalyses != null) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Éléments analysés', style: DesignSystem.textTheme.bodySmall?.copyWith( color: DesignSystem.textSecondaryColor, ), ), Text( analyticsData.nombreElementsAnalyses.toString(), style: DesignSystem.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w500, ), ), ], ), const SizedBox(height: DesignSystem.spacing4), // Temps de calcul if (analyticsData.tempsCalculMs != null) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Temps de calcul', style: DesignSystem.textTheme.bodySmall?.copyWith( color: DesignSystem.textSecondaryColor, ), ), Text( '${analyticsData.tempsCalculMs}ms', style: DesignSystem.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w500, ), ), ], ), ], ); } /// Widget des informations de période Widget _buildInfosPeriode() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( analyticsData.periodeAnalyse.libelle, style: DesignSystem.textTheme.bodySmall?.copyWith( color: DesignSystem.textSecondaryColor, ), ), ), Text( 'Mis à jour ${AppFormatters.formatDateRelative(analyticsData.dateCalcul)}', style: DesignSystem.textTheme.bodySmall?.copyWith( color: DesignSystem.textSecondaryColor, ), ), ], ); } /// Obtient la couleur de la métrique Color _getCouleurMetrique() { return Color(int.parse( analyticsData.couleur.replaceFirst('#', '0xFF'), )); } /// Obtient l'icône de la métrique IconData _getIconeMetrique() { switch (analyticsData.icone) { case 'people': return Icons.people; case 'attach_money': return Icons.attach_money; case 'event': return Icons.event; case 'favorite': return Icons.favorite; case 'trending_up': return Icons.trending_up; case 'business': return Icons.business; case 'settings': return Icons.settings; default: return Icons.analytics; } } /// Formate une valeur selon le type de métrique String _formaterValeur(double valeur) { switch (analyticsData.typeMetrique.typeValeur) { case 'amount': return '${valeur.toStringAsFixed(0)} ${analyticsData.unite}'; case 'percentage': return '${valeur.toStringAsFixed(1)}${analyticsData.unite}'; case 'average': return valeur.toStringAsFixed(1); default: return valeur.toStringAsFixed(0); } } }