import 'package:flutter/material.dart'; import '../models/energy_data.dart'; import '../services/nymea_service.dart'; import '../theme/app_theme.dart'; class EVChargingCard extends StatelessWidget { final EnergyData data; final NymeaService service; const EVChargingCard({ super.key, required this.data, required this.service, }); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Recharge du véhicule', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: AppTheme.textDark, ), ), Text( _statusLabel(data.chargingMode), style: const TextStyle( fontSize: 13, color: AppTheme.textLight, ), ), ], ), IconButton( icon: const Icon(Icons.settings_outlined), onPressed: () => _showSettings(context), color: AppTheme.textLight, ), ], ), const SizedBox(height: 16), // Mode buttons Row( children: [ _ModeButton( label: 'PV', icon: Icons.wb_sunny_rounded, color: AppTheme.pvGreen, isSelected: data.chargingMode == ChargingMode.pv, onTap: () => service.setChargingMode(ChargingMode.pv), ), const SizedBox(width: 8), _ModeButton( label: 'Min + PV', icon: Icons.bolt, color: AppTheme.minPvBlue, isSelected: data.chargingMode == ChargingMode.minPv, onTap: () => service.setChargingMode(ChargingMode.minPv), ), const SizedBox(width: 8), _ModeButton( label: 'Boost', icon: Icons.rocket_launch_rounded, color: AppTheme.boostRed, isSelected: data.chargingMode == ChargingMode.boost, onTap: () => service.setChargingMode(ChargingMode.boost), ), ], ), const SizedBox(height: 16), // Stats _InfoRow( label: 'Puissance borne', value: '${data.chargingPower.toStringAsFixed(1)} kW', ), const SizedBox(height: 6), _InfoRow( label: 'Source', value: '${data.solarSourcePercent.toStringAsFixed(0)}% solaire', valueColor: AppTheme.pvGreen, ), const SizedBox(height: 6), _InfoRow( label: 'Limite réseau', value: data.gridLimitOk ? 'OK ✓' : 'Dépassée !', valueColor: data.gridLimitOk ? AppTheme.primaryGreen : AppTheme.boostRed, ), // Charging indicator const SizedBox(height: 12), if (data.chargingMode != ChargingMode.pv || data.pvPower > 0) _ChargingIndicator( mode: data.chargingMode, pvPower: data.pvPower, chargingPower: data.chargingPower, ), ], ), ), ); } String _statusLabel(ChargingMode mode) { switch (mode) { case ChargingMode.pv: return 'Surplus PV disponible'; case ChargingMode.minPv: return 'Minimum + surplus PV'; case ChargingMode.boost: return 'Charge rapide (réseau)'; } } void _showSettings(BuildContext context) { showModalBottomSheet( context: context, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (_) => const _ChargingSettingsSheet(), ); } } class _ModeButton extends StatelessWidget { final String label; final IconData icon; final Color color; final bool isSelected; final VoidCallback onTap; const _ModeButton({ required this.label, required this.icon, required this.color, required this.isSelected, required this.onTap, }); @override Widget build(BuildContext context) { return Expanded( child: GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: isSelected ? color : color.withValues(alpha:0.1), borderRadius: BorderRadius.circular(30), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: isSelected ? Colors.white : color, size: 16), const SizedBox(width: 4), Text( label, style: TextStyle( color: isSelected ? Colors.white : color, fontWeight: FontWeight.bold, fontSize: 13, ), ), ], ), ), ), ); } } class _InfoRow extends StatelessWidget { final String label; final String value; final Color? valueColor; const _InfoRow({required this.label, required this.value, this.valueColor}); @override Widget build(BuildContext context) { return Row( children: [ Text(label, style: const TextStyle(fontSize: 13, color: AppTheme.textLight)), const Text(' : ', style: TextStyle(color: AppTheme.textLight)), Text( value, style: TextStyle( fontSize: 13, fontWeight: FontWeight.bold, color: valueColor ?? AppTheme.textDark, ), ), ], ); } } class _ChargingIndicator extends StatelessWidget { final ChargingMode mode; final double pvPower; final double chargingPower; const _ChargingIndicator({ required this.mode, required this.pvPower, required this.chargingPower, }); @override Widget build(BuildContext context) { final pvPercent = (pvPower / (chargingPower * 1000)).clamp(0.0, 1.0); final gridPercent = mode == ChargingMode.boost ? 1.0 : (1 - pvPercent).clamp(0.0, 1.0); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Répartition charge', style: TextStyle(fontSize: 12, color: AppTheme.textLight)), const SizedBox(height: 6), ClipRRect( borderRadius: BorderRadius.circular(8), child: SizedBox( height: 10, child: Row( children: [ Flexible( flex: (pvPercent * 100).toInt(), child: Container(color: AppTheme.solarYellow), ), Flexible( flex: (gridPercent * 100).toInt(), child: Container(color: Colors.orange.shade300), ), ], ), ), ), const SizedBox(height: 4), Row( children: [ _legend(AppTheme.solarYellow, 'Solaire'), const SizedBox(width: 12), _legend(Colors.orange.shade300, 'Réseau'), ], ), ], ); } Widget _legend(Color color, String label) => Row( children: [ Container(width: 10, height: 10, color: color), const SizedBox(width: 4), Text(label, style: const TextStyle(fontSize: 11, color: AppTheme.textLight)), ], ); } class _ChargingSettingsSheet extends StatefulWidget { const _ChargingSettingsSheet(); @override State<_ChargingSettingsSheet> createState() => _ChargingSettingsSheetState(); } class _ChargingSettingsSheetState extends State<_ChargingSettingsSheet> { double _minPower = 6; double _maxPower = 16; bool _scheduleEnabled = false; TimeOfDay _startTime = const TimeOfDay(hour: 22, minute: 0); TimeOfDay _endTime = const TimeOfDay(hour: 6, minute: 0); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Paramètres de charge', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 20), const Text('Courant minimum (A)', style: TextStyle(fontWeight: FontWeight.w500)), Slider( value: _minPower, min: 6, max: 16, divisions: 10, label: '${_minPower.toStringAsFixed(0)} A', onChanged: (v) => setState(() => _minPower = v), activeColor: AppTheme.primaryGreen, ), const Text('Courant maximum (A)', style: TextStyle(fontWeight: FontWeight.w500)), Slider( value: _maxPower, min: 6, max: 32, divisions: 26, label: '${_maxPower.toStringAsFixed(0)} A', onChanged: (v) => setState(() => _maxPower = v), activeColor: AppTheme.primaryGreen, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Programmation horaire', style: TextStyle(fontWeight: FontWeight.w500)), Switch( value: _scheduleEnabled, onChanged: (v) => setState(() => _scheduleEnabled = v), activeThumbColor: AppTheme.primaryGreen, ), ], ), if (_scheduleEnabled) ...[ Row( children: [ Expanded( child: _TimePicker( label: 'Début', time: _startTime, onChanged: (t) => setState(() => _startTime = t), ), ), const SizedBox(width: 16), Expanded( child: _TimePicker( label: 'Fin', time: _endTime, onChanged: (t) => setState(() => _endTime = t), ), ), ], ), ], const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.pop(context), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryGreen, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), ), child: const Text('Enregistrer'), ), ), ], ), ); } } class _TimePicker extends StatelessWidget { final String label; final TimeOfDay time; final ValueChanged onChanged; const _TimePicker({ required this.label, required this.time, required this.onChanged, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: () async { final picked = await showTimePicker(context: context, initialTime: time); if (picked != null) onChanged(picked); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ const Icon(Icons.access_time, size: 16, color: AppTheme.textLight), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle( fontSize: 11, color: AppTheme.textLight)), Text( '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}', style: const TextStyle( fontWeight: FontWeight.bold, color: AppTheme.textDark), ), ], ), ], ), ), ); } }