import 'package:flutter/material.dart'; import '../../../theme/etm_tokens.dart'; import '../models/dashboard_models.dart'; class HeosDecisionCard extends StatefulWidget { const HeosDecisionCard({super.key}); @override State createState() => _HeosDecisionCardState(); } class _HeosDecisionCardState extends State with SingleTickerProviderStateMixin { late final AnimationController _pulseCtrl; late final Animation _pulseAnim; final _decision = HeosDecision.demo(); @override void initState() { super.initState(); _pulseCtrl = AnimationController( vsync: this, duration: const Duration(seconds: 2), )..repeat(reverse: true); _pulseAnim = Tween(begin: 0.25, end: 0.7) .animate(CurvedAnimation(parent: _pulseCtrl, curve: Curves.easeInOut)); } @override void dispose() { _pulseCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final eco = EtmTokens.ecoOf(context); final ink = EtmTokens.inkOf(context); return Container( decoration: BoxDecoration( color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radiusCard), boxShadow: EtmTokens.cardShadowOf(context), border: Border.all(color: eco.withValues(alpha: 0.25), width: 1), ), padding: const EdgeInsets.all(18), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── En-tête Row( children: [ Container( width: 34, height: 34, decoration: BoxDecoration( color: eco.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(EtmTokens.radiusCtrl), ), child: Icon(Icons.auto_awesome_rounded, color: eco, size: 18), ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Héos pilote en ce moment', style: EtmTokens.sans( size: 14, weight: FontWeight.w600, color: ink, )), Text( 'optimise sur 24 h · maj ${_fmtTime(_decision.updatedAt)}', style: EtmTokens.mono( size: 10, color: EtmTokens.mutedOf(context), weight: FontWeight.w400, ), ), ], ), ), AnimatedBuilder( animation: _pulseAnim, builder: (context, _) => Container( width: 10, height: 10, decoration: BoxDecoration( shape: BoxShape.circle, color: eco.withValues(alpha: _pulseAnim.value), boxShadow: [ BoxShadow( color: eco.withValues(alpha: _pulseAnim.value * 0.6), blurRadius: 6, spreadRadius: 2, ), ], ), ), ), ], ), const SizedBox(height: 14), // ── Résumé avec highlight _HighlightedSummary( text: _decision.summary, highlight: _decision.highlight, highlightColor: eco, textColor: EtmTokens.mutedOf(context), ), const SizedBox(height: 14), // ── Hairline Divider(height: 1, color: EtmTokens.lineOf(context)), // ── Actions ...List.generate(_decision.actions.length, (i) { final action = _decision.actions[i]; final isLast = i == _decision.actions.length - 1; return Column( children: [ _ActionRow(action: action, eco: eco, context: context), if (!isLast) Divider(height: 1, color: EtmTokens.lineOf(context)), ], ); }), const SizedBox(height: 12), // ── Pied GestureDetector( onTap: () {}, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Voir le plan de la journée', style: EtmTokens.sans( size: 13, weight: FontWeight.w600, color: EtmTokens.brand), ), const SizedBox(width: 4), Icon(Icons.arrow_forward_ios_rounded, size: 12, color: EtmTokens.brand), ], ), ), ], ), ); } String _fmtTime(DateTime dt) => '${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}'; } // ── Highlighted summary ─────────────────────────────────────────────────────── class _HighlightedSummary extends StatelessWidget { final String text; final String highlight; final Color highlightColor; final Color textColor; const _HighlightedSummary({ required this.text, required this.highlight, required this.highlightColor, required this.textColor, }); @override Widget build(BuildContext context) { final idx = text.indexOf(highlight); if (idx < 0) { return Text(text, style: EtmTokens.sans(size: 13, color: textColor, height: 1.5)); } return Text.rich( TextSpan(children: [ TextSpan( text: text.substring(0, idx), style: EtmTokens.sans(size: 13, color: textColor, height: 1.5), ), WidgetSpan( child: Container( padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 1), decoration: BoxDecoration( color: highlightColor.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(4), ), child: Text( highlight, style: EtmTokens.sans( size: 13, color: highlightColor, weight: FontWeight.w600, height: 1.5, ), ), ), ), TextSpan( text: text.substring(idx + highlight.length), style: EtmTokens.sans(size: 13, color: textColor, height: 1.5), ), ]), ); } } // ── Action row ──────────────────────────────────────────────────────────────── class _ActionRow extends StatelessWidget { final HeosAction action; final Color eco; final BuildContext context; const _ActionRow({ required this.action, required this.eco, required this.context, }); @override Widget build(BuildContext _) { final icon = _iconFor(action.iconKey); return Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 32, height: 32, decoration: BoxDecoration( color: eco.withValues(alpha: 0.10), borderRadius: BorderRadius.circular(EtmTokens.radiusCtrl), ), child: Icon(icon, color: eco, size: 17), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(action.title, style: EtmTokens.sans( size: 13, weight: FontWeight.w600, color: EtmTokens.inkOf(context))), const SizedBox(height: 2), Text(action.why, style: EtmTokens.sans( size: 11, color: EtmTokens.mutedOf(context))), ], ), ), const SizedBox(width: 8), Text( '${action.powerKw.toStringAsFixed(1)} kW', style: EtmTokens.mono(size: 13, color: eco), ), ], ), ); } IconData _iconFor(String key) => switch (key) { 'water' => Icons.water_drop_outlined, 'ev' => Icons.ev_station_rounded, 'battery' => Icons.battery_charging_full_rounded, 'heat' => Icons.heat_pump_outlined, _ => Icons.bolt_rounded, }; }