import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:tugas_akhir_supabase/screens/panen/analisis_hasil_screen.dart'; import 'dart:math' as math; class AnalisisInputScreen extends StatefulWidget { final String userId; final Map? scheduleData; const AnalisisInputScreen({ super.key, required this.userId, this.scheduleData, }); @override _AnalisisInputScreenState createState() => _AnalisisInputScreenState(); } class _AnalisisInputScreenState extends State { final _formKey = GlobalKey(); bool _isLoading = false; // Form controllers final _areaController = TextEditingController(); final _quantityController = TextEditingController(); final _seedCostController = TextEditingController(); final _fertilizerCostController = TextEditingController(); final _pesticideCostController = TextEditingController(); final _laborCostController = TextEditingController(); final _irrigationCostController = TextEditingController(); final _pricePerKgController = TextEditingController(); // Tambahan variabel sesuai pedoman pertanian Indonesia final _landPreparationCostController = TextEditingController(); // Biaya persiapan lahan final _toolsEquipmentCostController = TextEditingController(); // Biaya alat dan peralatan final _transportationCostController = TextEditingController(); // Biaya transportasi final _postHarvestCostController = TextEditingController(); // Biaya pasca panen final _otherCostController = TextEditingController(); // Biaya lain-lain // Dropdown controllers String _selectedWeatherCondition = 'Normal'; String _selectedIrrigationType = 'Irigasi Teknis'; String _selectedSoilType = 'Lempung'; String _selectedFertilizerType = 'NPK'; // Lists untuk dropdown final List _weatherConditionOptions = [ 'Normal', 'Kekeringan', 'Banjir', 'Curah Hujan Tinggi', ]; final List _irrigationTypeOptions = [ 'Irigasi Teknis', 'Irigasi Setengah Teknis', 'Irigasi Sederhana', 'Tadah Hujan', ]; final List _soilTypeOptions = [ 'Lempung', 'Pasir', 'Liat', 'Lempung Berpasir', 'Liat Berpasir', ]; final List _fertilizerTypeOptions = [ 'NPK', 'Urea', 'TSP/SP-36', 'KCL', 'Organik', 'Campuran', ]; // Selected schedule String? _selectedScheduleId; Map? _selectedSchedule; List> _schedules = []; Map> _fieldsData = {}; // Cache untuk data lahan bool _isManualMode = false; @override void initState() { super.initState(); debugPrint('AnalisisInputScreen initState with userId: ${widget.userId}'); debugPrint('Schedule data provided: ${widget.scheduleData}'); _fetchSchedules(); // Set default values if schedule data is provided if (widget.scheduleData != null) { _selectedScheduleId = widget.scheduleData!['id']; _isManualMode = false; debugPrint( 'Setting selected schedule ID from props: $_selectedScheduleId', ); } else { _isManualMode = true; debugPrint('No schedule data provided, using manual mode'); _setDefaultValues(); } } void _setDefaultValues() { // For manual mode, we can set either empty fields or default values if (_isManualMode) { // Untuk mode manual, isi dengan nilai default yang realistis // seperti yang diminta dosen agar esensi "otomatis" tetap terjaga // Nilai default untuk luas lahan - 1000 m² (10 are) _areaController.text = '1000'; // Nilai default untuk hasil panen - 500 kg (asumsi produktivitas rata-rata) _quantityController.text = '500'; // Nilai default untuk biaya produksi langsung _seedCostController.text = '300000'; _fertilizerCostController.text = '450000'; _pesticideCostController.text = '250000'; _irrigationCostController.text = '200000'; // Nilai default untuk biaya produksi tidak langsung _laborCostController.text = '800000'; _landPreparationCostController.text = '300000'; _toolsEquipmentCostController.text = '200000'; _transportationCostController.text = '150000'; _postHarvestCostController.text = '100000'; _otherCostController.text = '50000'; // Default harga jual per kg (rata-rata harga gabah) _pricePerKgController.text = '4500'; // Reset dropdown ke default _selectedWeatherCondition = 'Normal'; _selectedIrrigationType = 'Irigasi Teknis'; _selectedSoilType = 'Lempung'; _selectedFertilizerType = 'NPK'; } } @override void dispose() { _areaController.dispose(); _quantityController.dispose(); _seedCostController.dispose(); _fertilizerCostController.dispose(); _pesticideCostController.dispose(); _laborCostController.dispose(); _irrigationCostController.dispose(); _pricePerKgController.dispose(); // Dispose controller tambahan _landPreparationCostController.dispose(); _toolsEquipmentCostController.dispose(); _transportationCostController.dispose(); _postHarvestCostController.dispose(); _otherCostController.dispose(); super.dispose(); } Future _fetchSchedules() async { if (widget.userId.isEmpty) return; setState(() => _isLoading = true); try { debugPrint('Fetching schedules for user: ${widget.userId}'); // Fetch crop schedules with more complete data final response = await Supabase.instance.client .from('crop_schedules') .select(''' id, crop_name, field_id, plot, start_date, end_date, seed_cost, fertilizer_cost, pesticide_cost, irrigation_cost, expected_yield, land_preparation_cost, tools_equipment_cost, transportation_cost, post_harvest_cost, other_cost, weather_condition, irrigation_type, soil_type, fertilizer_type, labor_cost, area_size, status, created_at, user_id ''') .eq('user_id', widget.userId) .order('created_at', ascending: false); debugPrint( 'Fetched ${response.length} schedules for user ${widget.userId}', ); // Preload fields data for better performance await _preloadFieldsData(); if (mounted) { setState(() { _schedules = List>.from(response); debugPrint('Schedules loaded: ${_schedules.length}'); // Jika ada jadwal yang diberikan melalui widget.scheduleData, pilih itu if (widget.scheduleData != null && widget.scheduleData!['id'] != null) { _selectedScheduleId = widget.scheduleData!['id']; _isManualMode = false; debugPrint('Selected schedule from props: $_selectedScheduleId'); } // Jika tidak ada jadwal yang dipilih tapi ada jadwal tersedia, pilih yang pertama else if (_schedules.isNotEmpty && _selectedScheduleId == null) { _selectedScheduleId = _schedules.first['id']; _isManualMode = false; debugPrint('Selected first schedule: $_selectedScheduleId'); } else if (_isManualMode) { _setDefaultValues(); } }); // Call update methods outside setState to avoid issues if (widget.scheduleData != null && widget.scheduleData!['id'] != null) { await _updateFormFieldsFromSelectedSchedule(); } else if (_schedules.isNotEmpty && _selectedScheduleId != null && !_isManualMode) { await _updateFormFieldsFromSelectedSchedule(); } } } catch (e) { debugPrint('Error fetching schedules: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Error mengambil data jadwal: ${e.toString()}'), backgroundColor: Colors.red, ), ); } } finally { if (mounted) { setState(() => _isLoading = false); } } } // Preload fields data untuk mempercepat akses Future _preloadFieldsData() async { try { final fieldsResponse = await Supabase.instance.client .from('fields') .select('id, name, area_size, area_unit, region, location') .eq('user_id', widget.userId); _fieldsData = {}; for (var field in fieldsResponse) { _fieldsData[field['id']] = Map.from(field); } debugPrint('Preloaded ${_fieldsData.length} fields data'); } catch (e) { debugPrint('Error preloading fields data: $e'); } } Future _updateFormFieldsFromSelectedSchedule() async { if (_isManualMode || _selectedScheduleId == null || _schedules.isEmpty) { _setDefaultValues(); return; } try { // Find the selected schedule from the schedules list _selectedSchedule = _schedules.firstWhere( (schedule) => schedule['id'] == _selectedScheduleId, orElse: () => {}, ); if (_selectedSchedule == null || _selectedSchedule!.isEmpty) { debugPrint('Selected schedule not found in schedules list'); _setDefaultValues(); return; } debugPrint( 'Updating form fields from selected schedule: $_selectedSchedule', ); // Update form fields with data from the selected schedule _seedCostController.text = (_selectedSchedule!['seed_cost'] ?? 0).toString(); _fertilizerCostController.text = (_selectedSchedule!['fertilizer_cost'] ?? 0).toString(); _pesticideCostController.text = (_selectedSchedule!['pesticide_cost'] ?? 0).toString(); _irrigationCostController.text = (_selectedSchedule!['irrigation_cost'] ?? 0).toString(); // Update form fields untuk kolom baru _landPreparationCostController.text = (_selectedSchedule!['land_preparation_cost'] ?? 0).toString(); _toolsEquipmentCostController.text = (_selectedSchedule!['tools_equipment_cost'] ?? 0).toString(); _transportationCostController.text = (_selectedSchedule!['transportation_cost'] ?? 0).toString(); _postHarvestCostController.text = (_selectedSchedule!['post_harvest_cost'] ?? 0).toString(); _otherCostController.text = (_selectedSchedule!['other_cost'] ?? 0).toString(); // Update dropdown values jika tersedia _selectedWeatherCondition = _selectedSchedule!['weather_condition'] ?? 'Normal'; _selectedIrrigationType = _selectedSchedule!['irrigation_type'] ?? 'Irigasi Teknis'; _selectedSoilType = _selectedSchedule!['soil_type'] ?? 'Lempung'; _selectedFertilizerType = _selectedSchedule!['fertilizer_type'] ?? 'NPK'; // Mengisi semua field secara otomatis untuk mempertahankan esensi "otomatis" // Mengambil data plot dari jadwal String plotName = _selectedSchedule!['plot']?.toString() ?? ''; double plotArea = 0; // Cek apakah ada area_size yang sudah disimpan di jadwal if (_selectedSchedule!.containsKey('area_size') && _selectedSchedule!['area_size'] != null) { // Gunakan area yang sudah ada di jadwal try { plotArea = double.parse(_selectedSchedule!['area_size'].toString()); debugPrint('Using area_size directly from schedule: $plotArea m²'); } catch (e) { debugPrint('Error parsing area_size from schedule: $e'); } } // Jika tidak ada area di jadwal, coba ambil dari field_id menggunakan cache else if (_selectedSchedule!.containsKey('field_id') && _selectedSchedule!['field_id'] != null) { final fieldId = _selectedSchedule!['field_id']; // Cek apakah data field sudah ada di cache if (_fieldsData.containsKey(fieldId)) { final fieldData = _fieldsData[fieldId]!; if (fieldData.containsKey('area_size') && fieldData['area_size'] != null) { try { plotArea = double.parse(fieldData['area_size'].toString()); debugPrint('Retrieved area from fields cache: $plotArea m²'); } catch (e) { debugPrint('Error parsing area_size from fields cache: $e'); } } } else { // Jika tidak ada di cache, ambil dari database try { final fieldResponse = await Supabase.instance.client .from('fields') .select('area_size, area_unit') .eq('id', fieldId) .single(); if (fieldResponse.containsKey('area_size') && fieldResponse['area_size'] != null) { plotArea = double.parse(fieldResponse['area_size'].toString()); debugPrint('Retrieved area_size from fields table: $plotArea m²'); } } catch (e) { debugPrint('Error fetching field area_size: $e'); } } } // Mengisi luas lahan dari data yang ditemukan atau default _areaController.text = plotArea > 0 ? plotArea.toString() : '1000'; // Mengisi jumlah produksi dari expected_yield atau estimasi double expectedYield = 0; if (_selectedSchedule!.containsKey('expected_yield') && _selectedSchedule!['expected_yield'] != null) { try { expectedYield = double.parse( _selectedSchedule!['expected_yield'].toString(), ); } catch (e) { debugPrint('Error parsing expected_yield: $e'); } } if (expectedYield > 0) { _quantityController.text = expectedYield.toString(); } else { // Estimasi berdasarkan jenis tanaman dan luas String cropName = _selectedSchedule!['crop_name']?.toString().toLowerCase() ?? ''; double area = double.tryParse(_areaController.text) ?? 1000; double estimatedYield = 0; // Estimasi hasil panen berdasarkan jenis tanaman (kg/ha) if (cropName.contains('padi')) { estimatedYield = area * 5.5 / 10000; // Rata-rata 5.5 ton/ha } else if (cropName.contains('jagung')) { estimatedYield = area * 5.2 / 10000; // Rata-rata 5.2 ton/ha } else if (cropName.contains('kedelai')) { estimatedYield = area * 1.5 / 10000; // Rata-rata 1.5 ton/ha } else if (cropName.contains('bawang')) { estimatedYield = area * 9.5 / 10000; // Rata-rata 9.5 ton/ha } else { estimatedYield = area * 4.0 / 10000; // Default 4 ton/ha } _quantityController.text = estimatedYield.toStringAsFixed(0); } // Mengisi biaya tenaga kerja dari data atau estimasi double laborCost = 0; if (_selectedSchedule!.containsKey('labor_cost') && _selectedSchedule!['labor_cost'] != null) { try { laborCost = double.parse(_selectedSchedule!['labor_cost'].toString()); } catch (e) { debugPrint('Error parsing labor_cost: $e'); } } _laborCostController.text = laborCost > 0 ? laborCost.toString() : '300000'; // Mengisi harga jual per kg berdasarkan jenis tanaman String cropName = _selectedSchedule!['crop_name']?.toString().toLowerCase() ?? ''; double pricePerKg = 0; // Harga pasar rata-rata (Rp/kg) berdasarkan jenis tanaman if (cropName.contains('padi')) { pricePerKg = 4500; // Harga GKP per kg } else if (cropName.contains('jagung')) { pricePerKg = 4200; // Harga jagung pipil per kg } else if (cropName.contains('kedelai')) { pricePerKg = 9000; // Harga kedelai per kg } else if (cropName.contains('bawang')) { pricePerKg = 25000; // Harga bawang merah per kg } else if (cropName.contains('kopi')) { pricePerKg = 35000; // Harga kopi per kg } else { pricePerKg = 5000; // Default harga per kg } _pricePerKgController.text = pricePerKg.toString(); debugPrint('Auto-filled all fields for schedule: $_selectedScheduleId'); debugPrint('Area: ${_areaController.text} m²'); debugPrint('Quantity: ${_quantityController.text} kg'); debugPrint('Price per kg: ${_pricePerKgController.text}'); debugPrint('Seed cost: ${_seedCostController.text}'); debugPrint('Fertilizer cost: ${_fertilizerCostController.text}'); debugPrint('Pesticide cost: ${_pesticideCostController.text}'); debugPrint('Irrigation cost: ${_irrigationCostController.text}'); debugPrint('Labor cost: ${_laborCostController.text}'); } catch (e) { debugPrint('Error updating form fields from selected schedule: $e'); _setDefaultValues(); } } Future _analyzeHarvest() async { if (!_formKey.currentState!.validate()) return; setState(() => _isLoading = true); try { // Parse input values final double area = double.tryParse(_areaController.text) ?? 0; final double quantity = double.tryParse(_quantityController.text) ?? 0; final double seedCost = double.tryParse(_seedCostController.text) ?? 0; final double fertilizerCost = double.tryParse(_fertilizerCostController.text) ?? 0; final double pesticideCost = double.tryParse(_pesticideCostController.text) ?? 0; final double laborCost = double.tryParse(_laborCostController.text) ?? 0; final double irrigationCost = double.tryParse(_irrigationCostController.text) ?? 0; final double pricePerKg = double.tryParse(_pricePerKgController.text) ?? 0; // Parse biaya tambahan final double landPreparationCost = double.tryParse(_landPreparationCostController.text) ?? 0; final double toolsEquipmentCost = double.tryParse(_toolsEquipmentCostController.text) ?? 0; final double transportationCost = double.tryParse(_transportationCostController.text) ?? 0; final double postHarvestCost = double.tryParse(_postHarvestCostController.text) ?? 0; final double otherCost = double.tryParse(_otherCostController.text) ?? 0; // Berikan waktu untuk UI update await Future.delayed(const Duration(milliseconds: 100)); // Calculate productivity (kilogram/ha) final double productivityPerHa = area > 0 ? (quantity / area) * 10000 : 0; // Calculate total cost (termasuk biaya tambahan) final double directCost = seedCost + fertilizerCost + pesticideCost + irrigationCost; // Biaya langsung final double indirectCost = laborCost + landPreparationCost + toolsEquipmentCost + transportationCost + postHarvestCost + otherCost; // Biaya tidak langsung final double totalCost = directCost + indirectCost; // Calculate income (quantity in kilogram) final double income = quantity * pricePerKg; // Calculate profit final double profit = income - totalCost; // Calculate profit margin final double profitMargin = income > 0 ? (profit / income) * 100 : 0; // Calculate R/C ratio (Revenue Cost Ratio) - Standar analisis usaha tani Indonesia final double rcRatio = totalCost > 0 ? income / totalCost : 0; // Calculate B/C ratio (Benefit Cost Ratio) - Standar analisis usaha tani Indonesia final double bcRatio = totalCost > 0 ? profit / totalCost : 0; // Calculate BEP Price (Break Even Point harga) - Standar analisis usaha tani Indonesia final double bepPrice = quantity > 0 ? totalCost / quantity : 0; // Calculate BEP Production (Break Even Point produksi) - Standar analisis usaha tani Indonesia final double bepProduction = pricePerKg > 0 ? totalCost / pricePerKg : 0; // Calculate ROI (Return on Investment) final double roi = totalCost > 0 ? (profit / totalCost) * 100 : 0; // Calculate production cost per kg - Biaya pokok produksi per kg final double productionCostPerKg = quantity > 0 ? totalCost / quantity : 0; // Determine status based on productivity and profit margin // Menggunakan standar Kementan dan Kemenristekdikti untuk usahatani String status; if (rcRatio >= 2.0) { status = 'Sangat Layak'; } else if (rcRatio >= 1.5) { status = 'Layak'; } else if (rcRatio >= 1.0) { status = 'Cukup Layak'; } else { status = 'Tidak Layak'; } // Prepare harvest data final Map harvestData = { 'user_id': widget.userId, 'schedule_id': _selectedScheduleId, 'area': area, 'quantity': quantity, 'productivity': productivityPerHa, 'seed_cost': seedCost, 'fertilizer_cost': fertilizerCost, 'pesticide_cost': pesticideCost, 'labor_cost': laborCost, 'irrigation_cost': irrigationCost, 'land_preparation_cost': landPreparationCost, 'tools_equipment_cost': toolsEquipmentCost, 'transportation_cost': transportationCost, 'post_harvest_cost': postHarvestCost, 'other_cost': otherCost, 'direct_cost': directCost, 'indirect_cost': indirectCost, 'cost': totalCost, 'total_cost': totalCost, 'price_per_kg': pricePerKg, 'income': income, 'profit': profit, 'profit_margin': profitMargin, 'rc_ratio': rcRatio, 'bc_ratio': bcRatio, 'bep_price': bepPrice, 'bep_production': bepProduction, 'roi': roi, 'production_cost_per_kg': productionCostPerKg, 'status': status, 'weather_condition': _selectedWeatherCondition, 'irrigation_type': _selectedIrrigationType, 'soil_type': _selectedSoilType, 'fertilizer_type': _selectedFertilizerType, 'harvest_date': DateTime.now().toIso8601String(), }; // Tambahkan informasi jadwal tanam jika ada if (!_isManualMode && _selectedSchedule != null) { // Tambahkan informasi penting dari jadwal tanam harvestData['crop_name'] = _selectedSchedule!['crop_name']; harvestData['field_id'] = _selectedSchedule!['field_id']; harvestData['plot'] = _selectedSchedule!['plot']; harvestData['start_date'] = _selectedSchedule!['start_date']; harvestData['end_date'] = _selectedSchedule!['end_date']; // Tambahkan informasi lahan jika tersedia if (_selectedSchedule!['field_id'] != null && _fieldsData.containsKey(_selectedSchedule!['field_id'])) { final fieldData = _fieldsData[_selectedSchedule!['field_id']]!; harvestData['field_name'] = fieldData['name']; harvestData['field_location'] = fieldData['location']; harvestData['field_region'] = fieldData['region']; } } // Simpan hasil analisis ke database jika pengguna tidak dalam mode manual if (!_isManualMode && _selectedScheduleId != null) { try { // Simpan hasil analisis ke tabel harvest_analysis final analysisResponse = await Supabase.instance.client .from('harvest_analysis') .insert({ 'user_id': widget.userId, 'schedule_id': _selectedScheduleId, 'area': area, 'quantity': quantity, 'productivity': productivityPerHa, 'total_cost': totalCost, 'income': income, 'profit': profit, 'profit_margin': profitMargin, 'rc_ratio': rcRatio, 'bc_ratio': bcRatio, 'status': status, 'created_at': DateTime.now().toIso8601String(), }) .select() .single(); debugPrint( 'Hasil analisis berhasil disimpan: ${analysisResponse['id']}', ); harvestData['analysis_id'] = analysisResponse['id']; } catch (e) { debugPrint('Error menyimpan hasil analisis: $e'); } } // Berikan waktu untuk UI update sebelum navigasi await Future.delayed(const Duration(milliseconds: 100)); // Navigate to result screen if (!mounted) return; debugPrint('=== HARVEST DATA YANG DIKIRIM KE HASIL ANALISIS ==='); harvestData.forEach((k, v) => debugPrint('$k: $v')); Navigator.push( context, MaterialPageRoute( builder: (context) => HarvestResultScreen( userId: widget.userId, harvestData: harvestData, scheduleData: _selectedSchedule, ), ), ); } catch (e) { debugPrint('Error analyzing harvest: $e'); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Error: ${e.toString().substring(0, math.min(e.toString().length, 100))}', ), backgroundColor: Colors.red, ), ); } finally { if (mounted) { setState(() => _isLoading = false); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Analisis Hasil Panen'), backgroundColor: const Color(0xFF056839), foregroundColor: Colors.white, actions: [ IconButton( icon: const Icon(Icons.refresh), tooltip: 'Refresh Data', onPressed: () { setState(() => _isLoading = true); _fetchSchedules().then((_) { setState(() => _isLoading = false); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Data jadwal berhasil diperbarui'), ), ); }); }, ), ], ), body: _isLoading ? const Center(child: CircularProgressIndicator()) : _buildForm(), ); } Widget _buildForm() { return Form( key: _formKey, child: ListView( padding: const EdgeInsets.all(16.0), children: [ // Data Tanaman section _buildSectionTitle('Data Tanaman'), const SizedBox(height: 16), // Jadwal Tanam dropdown _buildScheduleDropdown(), const SizedBox(height: 16), // Luas Lahan field _buildTextField( controller: _areaController, label: 'Luas Lahan (m²)', icon: Icons.landscape, keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) { return 'Masukkan luas lahan'; } return null; }, ), const SizedBox(height: 16), // Total Panen field _buildTextField( controller: _quantityController, label: 'Total Panen (kilogram)', icon: Icons.shopping_basket, keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) { return 'Masukkan total panen'; } return null; }, ), const SizedBox(height: 24), // Informasi Kondisi Tanam _buildSectionTitle('Informasi Kondisi Tanam'), const SizedBox(height: 16), // Dropdown Kondisi Cuaca _buildDropdown( label: 'Kondisi Cuaca', icon: Icons.wb_sunny, value: _selectedWeatherCondition, items: _weatherConditionOptions.map((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { _selectedWeatherCondition = newValue; }); } }, ), const SizedBox(height: 16), // Dropdown Jenis Irigasi _buildDropdown( label: 'Jenis Irigasi', icon: Icons.water, value: _selectedIrrigationType, items: _irrigationTypeOptions.map((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { _selectedIrrigationType = newValue; }); } }, ), const SizedBox(height: 16), // Dropdown Jenis Tanah _buildDropdown( label: 'Jenis Tanah', icon: Icons.grass, value: _selectedSoilType, items: _soilTypeOptions.map((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { _selectedSoilType = newValue; }); } }, ), const SizedBox(height: 16), // Dropdown Jenis Pupuk Utama _buildDropdown( label: 'Jenis Pupuk Utama', icon: Icons.eco, value: _selectedFertilizerType, items: _fertilizerTypeOptions.map((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { _selectedFertilizerType = newValue; }); } }, ), const SizedBox(height: 24), // Biaya Produksi section _buildSectionTitle('Biaya Produksi Langsung'), const SizedBox(height: 16), // Biaya Bibit field _buildTextField( controller: _seedCostController, label: 'Biaya Bibit (Rp)', icon: Icons.spa, keyboardType: TextInputType.number, prefixText: 'Rp ', validator: (value) { if (value == null || value.isEmpty) { return 'Masukkan biaya bibit'; } return null; }, ), const SizedBox(height: 16), // Biaya Pupuk field _buildTextField( controller: _fertilizerCostController, label: 'Biaya Pupuk (Rp)', icon: Icons.eco, keyboardType: TextInputType.number, prefixText: 'Rp ', validator: (value) { if (value == null || value.isEmpty) { return 'Masukkan biaya pupuk'; } return null; }, ), const SizedBox(height: 16), // Biaya Pestisida field _buildTextField( controller: _pesticideCostController, label: 'Biaya Pestisida (Rp)', icon: Icons.bug_report, keyboardType: TextInputType.number, prefixText: 'Rp ', validator: (value) { if (value == null || value.isEmpty) { return 'Masukkan biaya pestisida'; } return null; }, ), const SizedBox(height: 16), // Biaya Irigasi field _buildTextField( controller: _irrigationCostController, label: 'Biaya Irigasi (Rp)', icon: Icons.water_drop, keyboardType: TextInputType.number, prefixText: 'Rp ', validator: (value) { if (value == null || value.isEmpty) { return 'Masukkan biaya irigasi'; } return null; }, ), const SizedBox(height: 24), // Biaya Tidak Langsung section _buildSectionTitle('Biaya Produksi Tidak Langsung'), const SizedBox(height: 16), // Biaya Tenaga Kerja field _buildTextField( controller: _laborCostController, label: 'Biaya Tenaga Kerja (Rp)', icon: Icons.people, keyboardType: TextInputType.number, prefixText: 'Rp ', validator: (value) { if (value == null || value.isEmpty) { return 'Masukkan biaya tenaga kerja'; } return null; }, ), const SizedBox(height: 16), // Biaya Persiapan Lahan _buildTextField( controller: _landPreparationCostController, label: 'Biaya Persiapan Lahan (Rp)', icon: Icons.agriculture, keyboardType: TextInputType.number, prefixText: 'Rp ', ), const SizedBox(height: 16), // Biaya Alat dan Peralatan _buildTextField( controller: _toolsEquipmentCostController, label: 'Biaya Alat & Peralatan (Rp)', icon: Icons.build, keyboardType: TextInputType.number, prefixText: 'Rp ', ), const SizedBox(height: 16), // Biaya Transportasi _buildTextField( controller: _transportationCostController, label: 'Biaya Transportasi (Rp)', icon: Icons.local_shipping, keyboardType: TextInputType.number, prefixText: 'Rp ', ), const SizedBox(height: 16), // Biaya Pasca Panen _buildTextField( controller: _postHarvestCostController, label: 'Biaya Pasca Panen (Rp)', icon: Icons.inventory_2, keyboardType: TextInputType.number, prefixText: 'Rp ', ), const SizedBox(height: 16), // Biaya Lain-lain _buildTextField( controller: _otherCostController, label: 'Biaya Lain-lain (Rp)', icon: Icons.more_horiz, keyboardType: TextInputType.number, prefixText: 'Rp ', ), const SizedBox(height: 24), // Harga Jual section _buildSectionTitle('Harga Jual'), const SizedBox(height: 16), // Harga Jual per Kg field _buildTextField( controller: _pricePerKgController, label: 'Harga Jual per Kg (Rp)', icon: Icons.attach_money, keyboardType: TextInputType.number, prefixText: 'Rp ', validator: (value) { if (value == null || value.isEmpty) { return 'Masukkan harga jual per kg'; } return null; }, ), const SizedBox(height: 32), // Analyze button SizedBox( height: 50, child: ElevatedButton( onPressed: _isLoading ? null : _analyzeHarvest, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF056839), foregroundColor: Colors.white, textStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), child: _isLoading ? const CircularProgressIndicator(color: Colors.white) : const Text('ANALISIS HASIL PANEN'), ), ), const SizedBox(height: 24), ], ), ); } Widget _buildSectionTitle(String title) { return Text( title, style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.w600, color: const Color(0xFF056839), ), ); } Widget _buildTextField({ required TextEditingController controller, required String label, required IconData icon, TextInputType keyboardType = TextInputType.text, String? Function(String?)? validator, String? prefixText, }) { return TextFormField( controller: controller, keyboardType: keyboardType, validator: validator, decoration: InputDecoration( labelText: label, prefixIcon: Icon(icon, color: const Color(0xFF056839)), prefixText: prefixText, border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFF056839), width: 2), ), ), inputFormatters: keyboardType == TextInputType.number ? [FilteringTextInputFormatter.digitsOnly] : null, ); } Widget _buildScheduleDropdown() { return InkWell( onTap: () => _showScheduleSelector(), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade400), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(Icons.calendar_today, color: const Color(0xFF056839)), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Pilih Jadwal Tanam', style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), const SizedBox(height: 4), Text( _getSelectedScheduleText(), style: const TextStyle(fontSize: 16), ), ], ), ), const Icon(Icons.arrow_drop_down), ], ), ), ); } String _getSelectedScheduleText() { if (_isManualMode) { return 'Manual'; } if (_selectedScheduleId != null) { try { final selectedSchedule = _schedules.firstWhere( (s) => s['id'] == _selectedScheduleId, orElse: () => {'crop_name': 'Jadwal tidak ditemukan'}, ); return selectedSchedule['crop_name'] ?? 'Jadwal tidak ditemukan'; } catch (e) { debugPrint('Error finding selected schedule: $e'); return 'Jadwal tidak ditemukan'; } } return 'Pilih Jadwal Tanam'; } void _showScheduleSelector() { try { showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return Container( height: MediaQuery.of(context).size.height * 0.7, padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Pilih Jadwal Tanam', style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, ), ), IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context), ), ], ), const SizedBox(height: 8), Text( 'Jadwal yang tersedia: ${_schedules.length}', style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey[600], ), ), const SizedBox(height: 16), Expanded( child: _schedules.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.calendar_today, size: 64, color: Colors.grey[400], ), const SizedBox(height: 16), Text( 'Belum ada jadwal tanam', style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w500, color: Colors.grey[600], ), ), const SizedBox(height: 8), Text( 'Gunakan mode manual untuk saat ini', style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey[500], ), ), ], ), ) : ListView.builder( itemCount: _schedules.length + 1, // +1 for Manual option itemBuilder: (context, index) { if (index == 0) { // Manual option return Card( elevation: _isManualMode ? 2 : 0, color: _isManualMode ? const Color(0xFFE8F5E9) : null, margin: const EdgeInsets.only(bottom: 8), child: ListTile( leading: Container( width: 40, height: 40, decoration: BoxDecoration( color: const Color( 0xFF056839, ).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.add, color: Color(0xFF056839), ), ), title: const Text('Input Manual'), subtitle: const Text( 'Masukkan data secara manual', ), trailing: _isManualMode ? const Icon( Icons.check_circle, color: Color(0xFF056839), ) : null, onTap: () { try { setState(() { _selectedScheduleId = null; _selectedSchedule = null; _isManualMode = true; }); Navigator.pop(context); // Use setState again to ensure UI updates properly setState(() { _setDefaultValues(); }); ScaffoldMessenger.of( context, ).showSnackBar( const SnackBar( content: Text( 'Mode manual dipilih. Semua field diisi dengan nilai default.', ), duration: Duration(seconds: 2), ), ); debugPrint('Selected manual mode'); } catch (e) { debugPrint( 'Error selecting manual mode: $e', ); } }, ), ); } else { // Schedule options final schedule = _schedules[index - 1]; final isSelected = !_isManualMode && _selectedScheduleId == schedule['id']; // Format dates if available String dateInfo = ''; if (schedule['start_date'] != null && schedule['end_date'] != null) { try { final startDate = DateTime.parse( schedule['start_date'], ); final endDate = DateTime.parse( schedule['end_date'], ); dateInfo = '${startDate.day}/${startDate.month}/${startDate.year} - ${endDate.day}/${endDate.month}/${endDate.year}'; } catch (e) { dateInfo = 'Tanggal tidak valid'; debugPrint('Error parsing dates: $e'); } } // Ambil informasi lahan jika tersedia String fieldInfo = ''; if (schedule['field_id'] != null && _fieldsData.containsKey( schedule['field_id'], )) { final fieldData = _fieldsData[schedule['field_id']]!; fieldInfo = fieldData['name'] ?? ''; } // Ambil informasi luas lahan String areaInfo = ''; if (schedule['area_size'] != null && schedule['area_size'] > 0) { areaInfo = '${schedule['area_size']} m²'; } else if (schedule['field_id'] != null && _fieldsData.containsKey( schedule['field_id'], ) && _fieldsData[schedule['field_id']]!['area_size'] != null) { areaInfo = '${_fieldsData[schedule['field_id']]!['area_size']} m²'; } return Card( elevation: isSelected ? 2 : 0, color: isSelected ? const Color(0xFFE8F5E9) : null, margin: const EdgeInsets.only(bottom: 8), child: ListTile( leading: Container( width: 40, height: 40, decoration: BoxDecoration( color: const Color( 0xFF056839, ).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.eco, color: Color(0xFF056839), ), ), title: Text( schedule['crop_name'] ?? 'Tanaman', ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (fieldInfo.isNotEmpty) Text( 'Lahan: $fieldInfo', style: const TextStyle( fontSize: 12, ), ), Text( 'Plot: ${schedule['plot'] ?? '-'}${areaInfo.isNotEmpty ? ' • $areaInfo' : ''}', style: const TextStyle(fontSize: 12), ), if (dateInfo.isNotEmpty) Text( 'Periode: $dateInfo', style: const TextStyle( fontSize: 12, ), ), ], ), isThreeLine: true, trailing: isSelected ? const Icon( Icons.check_circle, color: Color(0xFF056839), ) : null, onTap: () async { try { setState(() { _selectedScheduleId = schedule['id']; _selectedSchedule = schedule; _isManualMode = false; }); Navigator.pop(context); // Show loading indicator ScaffoldMessenger.of( context, ).showSnackBar( const SnackBar( content: Text( 'Mengisi data otomatis...', ), duration: Duration(seconds: 1), ), ); // Update fields with await since it's async now await _updateFormFieldsFromSelectedSchedule(); // Show success message after fields are updated if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar( const SnackBar( content: Text( 'Data jadwal berhasil diisi otomatis', ), duration: Duration(seconds: 2), ), ); } debugPrint( 'Selected schedule: ${schedule['id']} - ${schedule['crop_name']}', ); } catch (e) { debugPrint( 'Error selecting schedule: $e', ); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar( SnackBar( content: Text( 'Error: ${e.toString()}', ), backgroundColor: Colors.red, ), ); } } }, ), ); } }, ), ), ], ), ); }, ); } catch (e) { debugPrint('Error showing schedule selector: $e'); // Fallback to simple dialog if bottom sheet fails showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Pilih Jadwal Tanam'), content: const Text( 'Terjadi kesalahan saat menampilkan jadwal. Silakan coba lagi nanti.', ), actions: [ TextButton( onPressed: () { Navigator.pop(context); setState(() { _isManualMode = true; _selectedScheduleId = null; _selectedSchedule = null; _setDefaultValues(); }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( 'Mode manual dipilih. Semua field diisi dengan nilai default.', ), duration: Duration(seconds: 2), ), ); }, child: const Text('Gunakan Mode Manual'), ), ], ), ); } } // Dropdown builder helper Widget _buildDropdown({ required String label, required IconData icon, required String value, required List> items, required void Function(String?) onChanged, }) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade400), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(icon, color: const Color(0xFF056839)), const SizedBox(width: 12), Expanded( child: DropdownButtonHideUnderline( child: DropdownButton( value: value, icon: const Icon(Icons.arrow_drop_down), isExpanded: true, style: const TextStyle(color: Colors.black, fontSize: 16), onChanged: onChanged, items: items, hint: Text(label), ), ), ), ], ), ); } }