import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import '../models/chart_data_point.dart'; class HistoryGrafikCard extends StatelessWidget { final List dataPoints; final VoidCallback? onBookmarkTap; const HistoryGrafikCard({ super.key, required this.dataPoints, this.onBookmarkTap, }); @override Widget build(BuildContext context) { if (dataPoints.isEmpty) { return Container( padding: const EdgeInsets.all(32), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: Center( child: Column( children: [ Icon(Icons.show_chart, size: 48, color: Colors.grey[400]), const SizedBox(height: 12), Text( 'Belum ada data history', style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), ], ), ), ); } final hours = dataPoints.map((p) => p.hour.toDouble()).toList(); final drops = dataPoints.map((p) => p.dropsPerMinute.toDouble()).toList(); final minX = hours.reduce((a, b) => a < b ? a : b); final maxX = hours.reduce((a, b) => a > b ? a : b); final minY = (drops.reduce((a, b) => a < b ? a : b) - 5.0).clamp( 0.0, double.infinity, ); final maxY = drops.reduce((a, b) => a > b ? a : b) + 5.0; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.1), spreadRadius: 0, blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFFE3F2FD), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.insert_chart_outlined, size: 20, color: Color(0xFF2196F3), ), ), const SizedBox(width: 12), const Text( 'Grafik Infus Pasien', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), ), ], ), const SizedBox(height: 20), SizedBox( height: 180, child: LineChart( LineChartData( gridData: FlGridData( show: true, drawVerticalLine: false, horizontalInterval: (maxY - minY) / 4, getDrawingHorizontalLine: (value) { return FlLine(color: Colors.grey[200]!, strokeWidth: 1); }, ), titlesData: FlTitlesData( show: true, rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 30, interval: dataPoints.length > 5 ? 2.0 : 1.0, getTitlesWidget: (double value, TitleMeta meta) { return Padding( padding: const EdgeInsets.only(top: 8.0), child: Text( '${value.toInt()}:00', style: TextStyle( color: Colors.grey[600], fontWeight: FontWeight.w500, fontSize: 10, ), ), ); }, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, interval: (maxY - minY) / 4, reservedSize: 35, getTitlesWidget: (double value, TitleMeta meta) { return Text( '${value.toInt()}', style: TextStyle( color: Colors.grey[600], fontWeight: FontWeight.w500, fontSize: 11, ), ); }, ), ), ), borderData: FlBorderData( show: true, border: Border( left: BorderSide(color: Colors.grey[300]!), bottom: BorderSide(color: Colors.grey[300]!), ), ), minX: minX, maxX: maxX, minY: minY, maxY: maxY, lineBarsData: [ LineChartBarData( spots: dataPoints .map( (point) => FlSpot( point.hour.toDouble(), point.dropsPerMinute.toDouble(), ), ) .toList(), isCurved: true, gradient: const LinearGradient( colors: [Color(0xFF2196F3), Color(0xFF1976D2)], ), barWidth: 3, isStrokeCapRound: true, dotData: FlDotData( show: true, getDotPainter: (spot, percent, barData, index) { return FlDotCirclePainter( radius: 4, color: Colors.white, strokeWidth: 2, strokeColor: const Color(0xFF2196F3), ); }, ), belowBarData: BarAreaData( show: true, gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ const Color(0xFF2196F3).withOpacity(0.3), const Color(0xFF2196F3).withOpacity(0.0), ], ), ), ), ], lineTouchData: LineTouchData( enabled: true, touchTooltipData: LineTouchTooltipData( getTooltipColor: (touchedSpot) => const Color(0xFF2196F3), tooltipBorder: const BorderSide( color: Color(0xFF1976D2), width: 1, ), tooltipPadding: const EdgeInsets.all(8), tooltipMargin: 8, getTooltipItems: (List touchedBarSpots) { return touchedBarSpots.map((barSpot) { return LineTooltipItem( '${barSpot.y.toInt()} tpm\n${barSpot.x.toInt()}:00', const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12, ), ); }).toList(); }, ), ), ), ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 12, height: 3, decoration: BoxDecoration( color: const Color(0xFF2196F3), borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 6), Text( 'Tetes per Menit', style: TextStyle( fontSize: 11, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), ], ), ], ), ); } }