import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:intl/intl.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/rendering.dart'; import 'dart:ui' as ui; import 'dart:typed_data'; import 'package:tugas_akhir_supabase/utils/pdf_generator.dart'; import 'dart:io'; import 'package:tugas_akhir_supabase/screens/panen/analisis_chart_screen.dart'; import 'package:tugas_akhir_supabase/core/theme/app_colors.dart'; class HarvestResultScreen extends StatefulWidget { final String userId; final Map? harvestData; final Map? scheduleData; const HarvestResultScreen({ super.key, required this.userId, this.harvestData, this.scheduleData, }); @override State createState() => _HarvestResultScreenState(); } class _HarvestResultScreenState extends State { final supabase = Supabase.instance.client; final currency = NumberFormat.currency(locale: 'id_ID', symbol: 'Rp '); // Tab index int _selectedTabIndex = 0; // Data fields from the previous analysis double? _produktivitasPerHektar; double? _totalBiayaProduksi; double? _pendapatanKotor; double? _keuntunganBersih; double? _rasioKeuntungan; String? _statusPanen; // Data from harvestData Map? get _harvestData => widget.harvestData; Map? get _selectedSchedule => widget.scheduleData; // GlobalKey for capturing chart view as image final GlobalKey _chartKey = GlobalKey(); @override void initState() { super.initState(); // Gunakan Future.microtask untuk menghindari setState selama build Future.microtask(() => _loadData()); } void _loadData() { try { if (widget.harvestData != null) { final data = widget.harvestData!; setState(() { _produktivitasPerHektar = data['productivity']; _totalBiayaProduksi = data['cost']; _pendapatanKotor = data['income']; _keuntunganBersih = data['profit']; _rasioKeuntungan = data['profit_margin']?.toDouble(); _statusPanen = data['status']; }); // Debug untuk memastikan data konsisten debugPrint('=== HASIL SCREEN DATA VALIDATION ==='); debugPrint('Cost: $_totalBiayaProduksi'); debugPrint('Income: $_pendapatanKotor'); debugPrint('Profit: $_keuntunganBersih'); debugPrint('Profit Margin: $_rasioKeuntungan%'); debugPrint('Status: $_statusPanen'); } } catch (e) { debugPrint('Error loading harvest data: $e'); // Handle error gracefully setState(() { _produktivitasPerHektar = 0; _totalBiayaProduksi = 0; _pendapatanKotor = 0; _keuntunganBersih = 0; _rasioKeuntungan = 0; _statusPanen = 'Tidak diketahui'; }); } } @override Widget build(BuildContext context) { return GestureDetector( // Hapus unfocus otomatis yang menyebabkan masalah keyboard // onTap: () => FocusScope.of(context).unfocus(), child: Scaffold( body: SafeArea(child: _buildBody()), floatingActionButton: FloatingActionButton( onPressed: () { // Hapus unfocus yang mungkin menyebabkan masalah keyboard // FocusScope.of(context).unfocus(); // Small delay to ensure UI is responsive Future.delayed(const Duration(milliseconds: 50), () { if (mounted) { _exportToPdf(); } }); }, backgroundColor: Colors.green.shade700, tooltip: 'Ekspor PDF', child: const Icon(Icons.picture_as_pdf), ), ), ); } Widget _buildBody() { return DefaultTabController( length: 3, child: Scaffold( appBar: AppBar( title: const Text('Analisis Panen'), backgroundColor: AppColors.primary, foregroundColor: Colors.white, elevation: 0, actions: [ IconButton( icon: const Icon(Icons.refresh), tooltip: 'Refresh Data', onPressed: () { setState(() { // Reset data _produktivitasPerHektar = null; _totalBiayaProduksi = null; _pendapatanKotor = null; _keuntunganBersih = null; _rasioKeuntungan = null; _statusPanen = null; }); // Reload data _loadData(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Data berhasil diperbarui')), ); }, ), IconButton( icon: const Icon(Icons.help_outline), onPressed: () { // Hapus unfocus yang mungkin menyebabkan masalah keyboard // FocusScope.of(context).unfocus(); // Add small delay to ensure UI responsiveness Future.delayed(const Duration(milliseconds: 50), () { if (mounted) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Tentang Analisis Panen'), content: const Text( 'Analisis panen mengukur produktivitas, efisiensi biaya, dan profitabilitas tanaman Anda. ' 'Status "Baik" menunjukkan produktivitas dan keuntungan optimal.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Tutup'), ), ], ), ); } }); }, ), ], bottom: TabBar( indicatorColor: Colors.white, labelColor: Colors.white, unselectedLabelColor: Colors.white60, isScrollable: true, onTap: (index) { setState(() { _selectedTabIndex = index; }); }, tabs: const [ Tab(icon: Icon(Icons.analytics, size: 20), text: 'Ringkasan'), Tab(icon: Icon(Icons.pie_chart, size: 20), text: 'Grafik'), Tab(icon: Icon(Icons.assessment, size: 20), text: 'Detail'), ], ), ), body: Column( children: [ // Status header - More compact and modern Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), color: Colors.white, child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: _getStatusColor(_statusPanen), shape: BoxShape.circle, ), child: Icon( _getStatusIcon(_statusPanen), color: Colors.white, size: 20, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Status: $_statusPanen', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: _getStatusColor(_statusPanen), ), ), Text( _getStatusDescription(_statusPanen), style: TextStyle( color: Colors.grey.shade800, fontSize: 12, ), ), ], ), ), ], ), ), // TabBarView wrapped in Expanded to avoid overflow Expanded( child: TabBarView( children: [ _buildSummaryTab(), _buildChartTab(), _buildDetailTab(), ], ), ), ], ), ), ); } Widget _buildSummaryTab() { return ListView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), children: [ // Top metrics in a grid - Consistent sizing GridView.count( crossAxisCount: 2, mainAxisSpacing: 8, crossAxisSpacing: 8, childAspectRatio: 1.8, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ _buildMetricCard( 'Produktivitas', '${_produktivitasPerHektar?.toStringAsFixed(2) ?? "0"} kilogram/ha', Icons.park_outlined, Colors.green.shade700, ), _buildMetricCard( 'Keuntungan', currency.format(_keuntunganBersih ?? 0), Icons.show_chart, Colors.blue.shade700, ), _buildMetricCard( 'R/C Ratio', _getRcRatio().toStringAsFixed(2), Icons.analytics, Colors.orange.shade700, ), _buildMetricCard( 'Pendapatan', currency.format(_pendapatanKotor ?? 0), Icons.attach_money, Colors.purple.shade700, ), ], ), const SizedBox(height: 12), // Income vs Cost - Modern Chart Card( elevation: 2, color: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Pendapatan vs Biaya', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), // Income and Cost comparison Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 10, height: 10, decoration: BoxDecoration( color: Colors.green, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 6), const Text( 'Pendapatan', style: TextStyle(fontSize: 12), ), ], ), const SizedBox(height: 4), FittedBox( fit: BoxFit.scaleDown, child: Text( currency.format(_pendapatanKotor ?? 0), style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 15, ), ), ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 10, height: 10, decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 6), const Text( 'Biaya', style: TextStyle(fontSize: 12), ), ], ), const SizedBox(height: 4), FittedBox( fit: BoxFit.scaleDown, child: Text( currency.format(_totalBiayaProduksi ?? 0), style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 15, ), ), ), ], ), ), ], ), const SizedBox(height: 12), // Bar Chart - Simplified SizedBox( height: 140, child: BarChart( BarChartData( alignment: BarChartAlignment.center, groupsSpace: 40, maxY: (_pendapatanKotor ?? 0) > (_totalBiayaProduksi ?? 0) ? (_pendapatanKotor ?? 0) * 1.2 : (_totalBiayaProduksi ?? 0) * 1.2, titlesData: FlTitlesData( show: true, bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 24, getTitlesWidget: (value, meta) { String text = ''; if (value == 0) text = 'Pendapatan'; if (value == 1) text = 'Biaya'; return Padding( padding: const EdgeInsets.only(top: 6.0), child: Text( text, style: const TextStyle(fontSize: 10), ), ); }, ), ), leftTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), topTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), borderData: FlBorderData(show: false), gridData: FlGridData(show: false), barGroups: [ BarChartGroupData( x: 0, barRods: [ BarChartRodData( toY: _pendapatanKotor ?? 0, color: Colors.green, width: 20, borderRadius: const BorderRadius.vertical( top: Radius.circular(6), ), ), ], ), BarChartGroupData( x: 1, barRods: [ BarChartRodData( toY: _totalBiayaProduksi ?? 0, color: Colors.red, width: 20, borderRadius: const BorderRadius.vertical( top: Radius.circular(6), ), ), ], ), ], ), ), ), ], ), ), ), const SizedBox(height: 12), // Recommendation card - More concise Card( elevation: 2, color: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(Icons.lightbulb, color: Colors.amber, size: 18), SizedBox(width: 6), Text( 'Rekomendasi', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 8), Text( _getRecommendation(_statusPanen), style: const TextStyle(fontSize: 13), ), ], ), ), ), const SizedBox(height: 16), // Action buttons in a row - Cleaner Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: () { Navigator.pop(context); }, icon: const Icon(Icons.arrow_back, size: 16), label: const Text('Kembali', style: TextStyle(fontSize: 13)), style: OutlinedButton.styleFrom( foregroundColor: Colors.green.shade700, padding: const EdgeInsets.symmetric(vertical: 10), side: BorderSide(color: Colors.green.shade700), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton.icon( onPressed: () { // Dismiss keyboard before action FocusScope.of(context).unfocus(); _exportToPdf(); }, icon: const Icon(Icons.download, size: 16), label: const Text( 'Unduh Laporan', style: TextStyle(fontSize: 13), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade700, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 10), ), ), ), ], ), ], ); } Widget _buildChartTab() { return RepaintBoundary( key: _chartKey, child: HarvestAnalysisChart( userId: widget.userId, scheduleData: _selectedSchedule, harvestData: _harvestData, isManualInput: _selectedSchedule == null, ), ); } Widget _buildDetailTab() { return ListView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), children: [ // Cost breakdown card Card( elevation: 2, color: Colors.white, margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.money_off, color: Colors.red.shade700, size: 16), const SizedBox(width: 6), const Text( 'Rincian Biaya Produksi', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 12), // Pie chart and legend in a row SizedBox( height: 160, child: Row( children: [ // Pie chart Expanded( flex: 3, child: PieChart( PieChartData( sectionsSpace: 2, centerSpaceRadius: 25, sections: _getCostPieSections(), ), ), ), // Legend Expanded( flex: 2, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLegendItem('Bibit', Colors.green.shade800), _buildLegendItem('Pupuk', Colors.brown.shade600), _buildLegendItem( 'Pestisida', Colors.purple.shade700, ), _buildLegendItem( 'Tenaga Kerja', Colors.blue.shade700, ), _buildLegendItem('Irigasi', Colors.cyan.shade700), ], ), ), ], ), ), const Divider(height: 20), // Cost items in a more compact list _buildCostItem( 'Bibit', _harvestData?['seed_cost'] ?? 0, Colors.green.shade800, ), _buildCostItem( 'Pupuk', _harvestData?['fertilizer_cost'] ?? 0, Colors.brown.shade600, ), _buildCostItem( 'Pestisida', _harvestData?['pesticide_cost'] ?? 0, Colors.purple.shade700, ), _buildCostItem( 'Tenaga Kerja', _harvestData?['labor_cost'] ?? 0, Colors.blue.shade700, ), _buildCostItem( 'Irigasi', _harvestData?['irrigation_cost'] ?? 0, Colors.cyan.shade700, ), const Divider(height: 20), // Total cost Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Total Biaya', style: TextStyle(fontWeight: FontWeight.bold), ), Text( currency.format(_totalBiayaProduksi ?? 0), style: const TextStyle(fontWeight: FontWeight.bold), ), ], ), ], ), ), ), // Financial ratios card Card( elevation: 2, color: Colors.white, margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.analytics, color: AppColors.primary, size: 16), const SizedBox(width: 6), const Text( 'Analisis Kelayakan Usaha Tani', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 10), // Rasio-rasio keuangan _buildRatioItem( 'R/C Ratio', _getRcRatio(), 'Pendapatan/Biaya', 1.0, 1.5, _getRcRatioColor(_getRcRatio()), ), const SizedBox(height: 12), _buildRatioItem( 'B/C Ratio', _getBcRatio(), 'Keuntungan/Biaya', 0.0, 1.0, _getBcRatioColor(_getBcRatio()), ), const SizedBox(height: 12), _buildRatioItem( 'Profit Margin', _getProfitMargin(), '%', 0.0, 15.0, _getProfitMarginColor(_getProfitMargin()), ), const SizedBox(height: 16), // Penjelasan Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(6), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Keterangan:', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12, color: Colors.blue.shade800, ), ), const SizedBox(height: 4), Text( '• R/C Ratio > 1: Usaha tani layak secara ekonomi\n' '• B/C Ratio > 0: Usaha tani menguntungkan\n' '• Profit Margin: Persentase keuntungan dari pendapatan', style: TextStyle( fontSize: 11, color: Colors.blue.shade900, ), ), ], ), ), ], ), ), ), // Productivity analysis card Card( elevation: 2, color: Colors.white, margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.insights, color: AppColors.primary, size: 16), const SizedBox(width: 6), const Text( 'Analisis Produktivitas', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 10), // Row 1: Luas Lahan & Total Panen Row( children: [ Expanded( child: _buildSimpleInfoItem( 'Plot', '${widget.scheduleData?['plot'] ?? "Tidak diketahui"}', ), ), const SizedBox(width: 8), Expanded( child: _buildSimpleInfoItem( 'Total Panen', '${_harvestData?['quantity']?.toString() ?? "0"} kilogram', ), ), ], ), const SizedBox(height: 6), // Row 2: Produktivitas & Harga Jual Row( children: [ Expanded( child: _buildSimpleInfoItem( 'Produktivitas', '${_produktivitasPerHektar?.toStringAsFixed(2) ?? "0"} kilogram/ha', isHighlighted: true, ), ), const SizedBox(width: 8), Expanded( child: _buildSimpleInfoItem( 'Harga Jual', '${currency.format((_harvestData?['income'] ?? 0) / ((_harvestData?['quantity'] ?? 1) * 100))}/kg', ), ), ], ), const SizedBox(height: 6), // Row 3: Pendapatan & Keuntungan Row( children: [ Expanded( child: _buildSimpleInfoItem( 'Pendapatan', currency.format(_pendapatanKotor ?? 0), ), ), const SizedBox(width: 8), Expanded( child: _buildSimpleInfoItem( 'Keuntungan', currency.format(_keuntunganBersih ?? 0), isHighlighted: true, ), ), ], ), const SizedBox(height: 6), // Benchmark visualization Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Benchmark Panen', style: TextStyle( fontWeight: FontWeight.w500, color: Colors.grey.shade800, fontSize: 13, ), ), const SizedBox(height: 8), _buildBenchmarkItem( 'Produktivitas', _produktivitasPerHektar ?? 0, 3000.0, 'kilogram/ha', 5000.0, ), const SizedBox(height: 10), _buildBenchmarkItem( 'R/C Ratio', _getRcRatio(), 1.0, '', 1.5, ), ], ), ], ), ), ), ], ); } Widget _buildSimpleInfoItem( String label, String value, { bool isHighlighted = false, Color? valueColor, }) { return Container( width: double.infinity, decoration: BoxDecoration( color: isHighlighted ? AppColors.lightGreen : Colors.grey.shade50, borderRadius: BorderRadius.circular(6), ), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( label, style: TextStyle(fontSize: 10, color: Colors.grey.shade700), maxLines: 1, overflow: TextOverflow.ellipsis, ), FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text( value, style: TextStyle( fontWeight: FontWeight.bold, color: valueColor, fontSize: 11, ), maxLines: 1, ), ), ], ), ); } Widget _buildMetricCard( String title, String value, IconData icon, Color color, ) { return Card( elevation: 2, color: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(icon, color: color, size: 16), const SizedBox(width: 4), Expanded( child: Text( title, style: TextStyle( color: color, fontWeight: FontWeight.w500, fontSize: 12, ), overflow: TextOverflow.ellipsis, ), ), ], ), FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text( value, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), ), ], ), ), ); } Widget _buildCostItem(String title, double value, Color iconColor) { return Padding( padding: const EdgeInsets.symmetric(vertical: 3.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: iconColor, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 6), Text(title, style: const TextStyle(fontSize: 12)), ], ), Text(currency.format(value), style: const TextStyle(fontSize: 12)), ], ), ); } Widget _buildLegendItem(String title, Color color) { return Padding( padding: const EdgeInsets.only(bottom: 4.0), child: Row( children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 4), Expanded( child: Text( title, style: const TextStyle(fontSize: 10), overflow: TextOverflow.ellipsis, ), ), ], ), ); } Widget _buildBenchmarkItem( String label, double value, double benchmark, String unit, double excellent, ) { final double percentage = value / excellent * 100; Color progressColor; if (value >= excellent) { progressColor = AppColors.primary; } else if (value >= benchmark) { progressColor = Colors.orange.shade600; } else { progressColor = Colors.red.shade600; } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: TextStyle(fontSize: 12, color: Colors.grey.shade700), ), Text( unit == 'ton/ha' ? '${value.toStringAsFixed(2)} kilogram/ha' : '${value.toStringAsFixed(2)} $unit', style: TextStyle( fontWeight: FontWeight.bold, color: progressColor, fontSize: 12, ), ), ], ), const SizedBox(height: 6), Stack( children: [ Container( height: 5, decoration: BoxDecoration( color: Colors.grey.shade200, borderRadius: BorderRadius.circular(3), ), ), Container( height: 5, width: ((MediaQuery.of(context).size.width - 70) * percentage / 100) .clamp(0.0, double.infinity), decoration: BoxDecoration( color: progressColor, borderRadius: BorderRadius.circular(3), ), ), ], ), const SizedBox(height: 3), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Min: 0', style: TextStyle(fontSize: 9, color: Colors.grey.shade600), ), Text( 'Target: $benchmark', style: TextStyle(fontSize: 9, color: Colors.orange.shade600), ), Text( 'Optimal: $excellent', style: TextStyle(fontSize: 9, color: Colors.green.shade600), ), ], ), ], ); } List _getCostPieSections() { final total = _totalBiayaProduksi ?? 1; // avoid division by zero final seedCost = _harvestData?['seed_cost'] ?? 0; final fertilizerCost = _harvestData?['fertilizer_cost'] ?? 0; final pesticideCost = _harvestData?['pesticide_cost'] ?? 0; final laborCost = _harvestData?['labor_cost'] ?? 0; final irrigationCost = _harvestData?['irrigation_cost'] ?? 0; return [ if (seedCost > 0) PieChartSectionData( value: seedCost, title: '${((seedCost / total) * 100).toStringAsFixed(0)}%', color: AppColors.primary, radius: 45, titleStyle: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 10, ), ), if (fertilizerCost > 0) PieChartSectionData( value: fertilizerCost, title: '${((fertilizerCost / total) * 100).toStringAsFixed(0)}%', color: Colors.brown.shade600, radius: 45, titleStyle: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 10, ), ), if (pesticideCost > 0) PieChartSectionData( value: pesticideCost, title: '${((pesticideCost / total) * 100).toStringAsFixed(0)}%', color: Colors.purple.shade700, radius: 45, titleStyle: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 10, ), ), if (laborCost > 0) PieChartSectionData( value: laborCost, title: '${((laborCost / total) * 100).toStringAsFixed(0)}%', color: Colors.blue.shade700, radius: 45, titleStyle: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 10, ), ), if (irrigationCost > 0) PieChartSectionData( value: irrigationCost, title: '${((irrigationCost / total) * 100).toStringAsFixed(0)}%', color: Colors.cyan.shade700, radius: 45, titleStyle: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 10, ), ), ]; } IconData _getStatusIcon(String? status) { switch (status) { case 'Baik': return Icons.check_circle; case 'Cukup': return Icons.thumbs_up_down; case 'Kurang': return Icons.warning; default: return Icons.help_outline; } } Color _getStatusColor(String? status) { switch (status) { case 'Baik': return Colors.green.shade600; case 'Cukup': return Colors.orange.shade600; case 'Kurang': return Colors.red.shade600; default: return Colors.grey; } } String _getStatusDescription(String? status) { final rcRatio = _getRcRatio(); switch (status) { case 'Baik': if (rcRatio >= 1.5) { return 'R/C Ratio ${rcRatio.toStringAsFixed(2)} - Usaha tani sangat layak secara ekonomi'; } else { return 'Produktivitas dan efisiensi usaha tani optimal'; } case 'Cukup': if (rcRatio >= 1.0) { return 'R/C Ratio ${rcRatio.toStringAsFixed(2)} - Usaha tani cukup layak secara ekonomi'; } else { return 'Produktivitas baik namun efisiensi biaya perlu ditingkatkan'; } case 'Kurang': if (rcRatio < 1.0) { return 'R/C Ratio ${rcRatio.toStringAsFixed(2)} - Usaha tani tidak layak secara ekonomi'; } else { return 'Produktivitas dan profitabilitas perlu ditingkatkan'; } default: return ''; } } String _getRecommendation(String? status) { // Ambil R/C Ratio untuk analisis lebih spesifik final rcRatio = _getRcRatio(); switch (status) { case 'Baik': if (rcRatio >= 2.0) { return 'Usaha tani sangat layak dan menguntungkan (R/C Ratio ${rcRatio.toStringAsFixed(2)}). Pertahankan praktik pertanian yang sudah baik dan pertimbangkan untuk memperluas area tanam atau meningkatkan produksi.'; } else { return 'Pertahankan praktik pertanian yang sudah baik. Tingkatkan efisiensi biaya untuk meningkatkan R/C Ratio. Pertimbangkan untuk mencoba varietas unggulan untuk produktivitas lebih tinggi.'; } case 'Cukup': if (rcRatio < 1.2) { return 'Usaha tani cukup layak (R/C Ratio ${rcRatio.toStringAsFixed(2)}) namun berisiko. Tingkatkan efisiensi biaya produksi, terutama pada komponen biaya terbesar untuk meningkatkan keuntungan.'; } else { return 'Fokus pada peningkatan produktivitas, karena R/C Ratio sudah cukup baik (${rcRatio.toStringAsFixed(2)}). Optimalkan penggunaan input dan teknik budidaya untuk hasil panen lebih banyak.'; } case 'Kurang': if (rcRatio < 1.0) { return 'Usaha tani tidak layak secara ekonomi (R/C Ratio ${rcRatio.toStringAsFixed(2)} < 1). Evaluasi ulang seluruh struktur biaya dan teknik budidaya. Pertimbangkan untuk beralih ke komoditas lain yang lebih sesuai.'; } else { return 'Evaluasi ulang teknik budidaya yang diterapkan untuk meningkatkan produktivitas. Pastikan pemilihan varietas yang tepat, perbaiki teknik pemupukan, dan kendalikan hama penyakit secara terpadu.'; } default: return 'Belum dapat memberikan rekomendasi spesifik.'; } } // Fungsi untuk mengekspor data ke PDF Future _exportToPdf() async { try { // Show loading indicator showDialog( context: context, barrierDismissible: false, builder: (context) => const Center(child: CircularProgressIndicator()), ); // Capture chart view as image if available Uint8List? chartImageBytes; if (_selectedTabIndex == 1) { // Switch to chart tab if not already on it setState(() { _selectedTabIndex = 1; }); // Wait for the UI to update await Future.delayed(const Duration(milliseconds: 300)); // Try to capture the chart try { chartImageBytes = await _captureChartAsImage(); } catch (e) { debugPrint('Failed to capture chart image: $e'); } } // Get daily logs data for more comprehensive report List>? dailyLogs; if (widget.scheduleData != null) { try { final scheduleId = widget.scheduleData!['id']; final res = await supabase .from('daily_logs') .select() .eq('schedule_id', scheduleId) .order('date', ascending: true); if (res.isNotEmpty) { dailyLogs = List>.from(res); } } catch (e) { debugPrint('Error fetching daily logs for PDF: $e'); } } // Generate PDF using the HarvestPdfGenerator final pdfGenerator = HarvestPdfGenerator(); final pdfFile = await pdfGenerator.generatePdf( title: 'Laporan Analisis Panen', harvestData: _harvestData ?? {}, scheduleData: widget.scheduleData, dailyLogs: dailyLogs, chartImageBytes: chartImageBytes, ); // Close loading dialog if (!context.mounted) return; Navigator.pop(context); // Show success dialog with options showDialog( context: context, builder: (context) => AlertDialog( title: const Text('PDF Berhasil Dibuat'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Laporan PDF analisis panen telah berhasil dibuat.', ), const SizedBox(height: 8), Text( 'Lokasi: ${pdfFile.path}', style: const TextStyle( fontSize: 12, fontStyle: FontStyle.italic, ), ), ], ), ), actions: [ TextButton( onPressed: () { Navigator.pop(context); }, child: const Text('Tutup'), ), TextButton( onPressed: () { Navigator.pop(context); _sharePdf(pdfFile); }, child: const Text('Bagikan'), ), TextButton( onPressed: () { Navigator.pop(context); _openPdf(pdfFile); }, child: const Text('Buka'), ), ], ), ); } catch (e) { // Close loading dialog if open if (context.mounted) { Navigator.pop(context); } // Show error message ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal membuat PDF: ${e.toString()}'), backgroundColor: Colors.red, ), ); } } // Function to capture chart as image Future _captureChartAsImage() async { try { // Find the RenderRepaintBoundary object associated with the key RenderRepaintBoundary? boundary = _chartKey.currentContext?.findRenderObject() as RenderRepaintBoundary?; if (boundary == null) { debugPrint('Could not find chart boundary'); return null; } // Capture the image ui.Image image = await boundary.toImage(pixelRatio: 3.0); ByteData? byteData = await image.toByteData( format: ui.ImageByteFormat.png, ); if (byteData == null) { debugPrint('Failed to convert image to bytes'); return null; } return byteData.buffer.asUint8List(); } catch (e) { debugPrint('Error capturing chart image: $e'); return null; } } // Helper function to open the PDF Future _openPdf(File file) async { try { final pdfGenerator = HarvestPdfGenerator(); await pdfGenerator.openPdf(file); } catch (e) { if (!context.mounted) return; // If opening fails, show dialog with options showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Gagal Membuka PDF'), content: const Text( 'Tidak dapat membuka file PDF secara langsung. ' 'Silakan bagikan file untuk dibuka dengan aplikasi lain.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Tutup'), ), TextButton( onPressed: () { Navigator.pop(context); _sharePdf(file); }, child: const Text('Bagikan'), ), ], ), ); } } // Helper function to share the PDF Future _sharePdf(File file) async { try { final pdfGenerator = HarvestPdfGenerator(); await pdfGenerator.sharePdf(file); } catch (e) { if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Gagal membagikan PDF: ${e.toString()}'), backgroundColor: Colors.red, ), ); } } // Helper method untuk mendapatkan R/C Ratio double _getRcRatio() { final cost = _totalBiayaProduksi ?? 1.0; final income = _pendapatanKotor ?? 0.0; return cost > 0 ? income / cost : 0.0; } // Helper method untuk mendapatkan B/C Ratio double _getBcRatio() { final cost = _totalBiayaProduksi ?? 1.0; final profit = _keuntunganBersih ?? 0.0; return cost > 0 ? profit / cost : 0.0; } // Helper method untuk mendapatkan Profit Margin double _getProfitMargin() { final income = _pendapatanKotor ?? 1.0; final profit = _keuntunganBersih ?? 0.0; return income > 0 ? (profit / income) * 100 : 0.0; } // Warna untuk R/C Ratio Color _getRcRatioColor(double value) { if (value >= 1.5) return Colors.green.shade600; if (value >= 1.0) return Colors.orange.shade600; return Colors.red.shade600; } // Warna untuk B/C Ratio Color _getBcRatioColor(double value) { if (value >= 1.0) return Colors.green.shade600; if (value >= 0.0) return Colors.orange.shade600; return Colors.red.shade600; } // Warna untuk Profit Margin Color _getProfitMarginColor(double value) { if (value >= 15.0) return Colors.green.shade600; if (value >= 0.0) return Colors.orange.shade600; return Colors.red.shade600; } // Widget untuk menampilkan item ratio Widget _buildRatioItem( String label, double value, String unit, double minThreshold, double goodThreshold, Color valueColor, ) { return Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.grey.shade50, borderRadius: BorderRadius.circular(6), border: Border.all(color: valueColor.withOpacity(0.3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: TextStyle(fontSize: 13, fontWeight: FontWeight.w500), ), Text( '${value.toStringAsFixed(2)}${unit.isNotEmpty ? ' $unit' : ''}', style: TextStyle( fontSize: 13, fontWeight: FontWeight.bold, color: valueColor, ), ), ], ), const SizedBox(height: 6), Stack( children: [ Container( height: 4, decoration: BoxDecoration( color: Colors.grey.shade200, borderRadius: BorderRadius.circular(2), ), ), Container( height: 4, width: value <= 0 ? 0 : (value > goodThreshold * 2 ? 1.0 : value / (goodThreshold * 2)) * MediaQuery.of(context).size.width * 0.7, decoration: BoxDecoration( color: valueColor, borderRadius: BorderRadius.circular(2), ), ), ], ), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( minThreshold.toStringAsFixed(1), style: TextStyle(fontSize: 10, color: Colors.grey.shade600), ), Text( goodThreshold.toStringAsFixed(1), style: TextStyle(fontSize: 10, color: Colors.grey.shade600), ), ], ), ], ), ); } }