import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import '../models/panen_model.dart'; import '../providers/panen_provider.dart'; import '../services/excel_export_service.dart'; class RiwayatPage extends StatefulWidget { const RiwayatPage({super.key}); @override State createState() => _RiwayatPageState(); } class _RiwayatPageState extends State { DateTime? _selectedStartDate; DateTime? _selectedEndDate; String _selectedKandang = 'semua'; DateTime? _lastRefreshAt; @override void initState() { super.initState(); _selectedStartDate = DateTime.now().subtract(const Duration(days: 7)); _selectedEndDate = DateTime.now(); _lastRefreshAt = DateTime.now(); } @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.orange.shade100, Colors.amber.shade100, Colors.greenAccent.shade100, ], ), ), child: SafeArea( child: Column( children: [ // Filter Section Container( color: Colors.white, padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Filter Tanggal', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey.shade800, ), ), const SizedBox(height: 12), Row( children: [ Expanded( child: InkWell( onTap: _selectStartDate, 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.calendar_today, size: 18), const SizedBox(width: 8), Text( _selectedStartDate != null ? DateFormat( 'dd/MM/yyyy', ).format(_selectedStartDate!) : 'Mulai', style: const TextStyle(fontSize: 12), ), ], ), ), ), ), const SizedBox(width: 8), Text( 'sd', style: TextStyle(color: Colors.grey.shade600), ), const SizedBox(width: 8), Expanded( child: InkWell( onTap: _selectEndDate, 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.calendar_today, size: 18), const SizedBox(width: 8), Text( _selectedEndDate != null ? DateFormat( 'dd/MM/yyyy', ).format(_selectedEndDate!) : 'Akhir', style: const TextStyle(fontSize: 12), ), ], ), ), ), ), ], ), const SizedBox(height: 12), Text( 'Filter Kandang', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey.shade800, ), ), const SizedBox(height: 8), DropdownButtonFormField( value: _selectedKandang, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric( horizontal: 12, vertical: 10, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), ), items: const [ DropdownMenuItem( value: 'semua', child: Text('Semua Kandang'), ), DropdownMenuItem( value: 'kandang1', child: Text('Kandang 1'), ), DropdownMenuItem( value: 'kandang2', child: Text('Kandang 2'), ), ], onChanged: (value) { if (value == null) return; setState(() { _selectedKandang = value; }); }, ), const SizedBox(height: 8), LayoutBuilder( builder: (context, constraints) { final compact = constraints.maxWidth < 520; final updateText = _lastRefreshAt != null ? 'Update: ${DateFormat('dd/MM/yyyy HH:mm').format(_lastRefreshAt!)}' : 'Belum pernah refresh'; final actionButtons = Wrap( spacing: 8, runSpacing: 8, children: [ TextButton.icon( onPressed: _handleRefresh, icon: const Icon(Icons.refresh, size: 16), label: const Text('Refresh'), ), ElevatedButton.icon( onPressed: _downloadExcelReport, icon: const Icon(Icons.download, size: 16), label: const Text('Download Excel'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, ), ), ], ); if (compact) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( updateText, style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), const SizedBox(height: 8), actionButtons, ], ); } return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Text( updateText, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), ), const SizedBox(width: 8), actionButtons, ], ); }, ), ], ), ), // Content Expanded(child: _buildRiwayatList()), ], ), ), ), ); } Widget _buildRiwayatList() { final panenProvider = context.watch(); final panens = _getFilteredPanens(panenProvider); final groupedPanens = >{}; for (final panen in panens) { final key = DateTime( panen.tanggalPanen.year, panen.tanggalPanen.month, panen.tanggalPanen.day, ); groupedPanens.putIfAbsent(key, () => []).add(panen); } final sortedDates = groupedPanens.keys.toList() ..sort((a, b) => b.compareTo(a)); for (final key in sortedDates) { groupedPanens[key]! .sort((a, b) => b.tanggalPanen.compareTo(a.tanggalPanen)); } final totalTelur = panens.fold(0, (sum, p) => sum + p.jumlahTelur); final listChildren = [ Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.orange.shade300, Colors.orange.shade600], ), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.orange.shade300.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 5), ), ], ), padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Total Produksi', style: TextStyle( fontSize: 14, color: Colors.white.withOpacity(0.9), fontWeight: FontWeight.w500, ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '$totalTelur', style: const TextStyle( fontSize: 36, fontWeight: FontWeight.bold, color: Colors.white, ), ), const SizedBox(height: 4), Text( 'telur', style: TextStyle( fontSize: 12, color: Colors.white.withOpacity(0.8), ), ), ], ), const Text('🥚', style: TextStyle(fontSize: 48)), ], ), const SizedBox(height: 12), Text( '${panens.length} pencatatan panen', style: TextStyle( fontSize: 12, color: Colors.white.withOpacity(0.8), ), ), ], ), ), const SizedBox(height: 24), Text( 'Detail Panen Per Hari', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey.shade800, ), ), const SizedBox(height: 12), ]; if (panens.isEmpty) { listChildren.add( Padding( padding: const EdgeInsets.only(top: 48), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('📭', style: TextStyle(fontSize: 64)), const SizedBox(height: 16), Text( 'Tidak ada data panen', style: TextStyle( fontSize: 16, color: Colors.grey.shade600, fontWeight: FontWeight.bold, ), ), ], ), ), ), ); } else { for (final date in sortedDates) { final items = groupedPanens[date]!; final dailyTotal = items.fold(0, (sum, p) => sum + p.jumlahTelur); listChildren.add( Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( DateFormat('dd MMMM yyyy').format(date), style: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 10), ...items.map( (panen) => Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.grey.shade50, borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( panen.kandangNama, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 2), Text( '${panen.jam} • ${(panen.jenisPanen ?? 'manual').toUpperCase()}', style: TextStyle( fontSize: 11, color: Colors.grey.shade600, ), ), ], ), ), Text( '${panen.jumlahTelur} 🥚', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.orange.shade700, ), ), ], ), ), ), const SizedBox(height: 6), Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 8, ), decoration: BoxDecoration( color: Colors.orange.shade50, borderRadius: BorderRadius.circular(8), ), child: Text( 'Total telur hari ini: $dailyTotal', textAlign: TextAlign.right, style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.orange.shade800, ), ), ), ], ), ), ), ); } } return RefreshIndicator( onRefresh: _handleRefresh, child: ListView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16), children: listChildren, ), ); } List _getFilteredPanens(PanenProvider panenProvider) { final allPanens = List.from(panenProvider.panens); final startDate = _selectedStartDate; final endDate = _selectedEndDate; final startBoundary = startDate != null ? DateTime(startDate.year, startDate.month, startDate.day) : null; final endBoundary = endDate != null ? DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59, 999) : null; final filtered = allPanens.where((panen) { final inDateRange = (startBoundary == null || !panen.tanggalPanen.isBefore(startBoundary)) && (endBoundary == null || !panen.tanggalPanen.isAfter(endBoundary)); if (!inDateRange) return false; if (_selectedKandang == 'semua') return true; final kandangIdLower = panen.kandangId.toString().toLowerCase(); final kandangNamaLower = panen.kandangNama.toString().toLowerCase(); final normalized = '$kandangIdLower $kandangNamaLower'.replaceAll('_', ''); return normalized.contains(_selectedKandang); }).toList(); filtered.sort((a, b) => b.tanggalPanen.compareTo(a.tanggalPanen)); return filtered; } Future _handleRefresh() async { final panenProvider = context.read(); await panenProvider.loadTodaySnapshots(); await panenProvider.restorePanenHistoryFromFirebase(); if (!mounted) return; setState(() { _lastRefreshAt = DateTime.now(); }); } Future _selectStartDate() async { final DateTime? picked = await showDatePicker( context: context, initialDate: _selectedStartDate ?? DateTime.now(), firstDate: DateTime(2020), lastDate: DateTime.now(), ); if (picked != null) { setState(() { _selectedStartDate = picked; }); } } Future _selectEndDate() async { final DateTime? picked = await showDatePicker( context: context, initialDate: _selectedEndDate ?? DateTime.now(), firstDate: DateTime(2020), lastDate: DateTime.now(), ); if (picked != null) { setState(() { _selectedEndDate = picked; }); } } Future _downloadExcelReport() async { if (_selectedStartDate == null || _selectedEndDate == null) { _showErrorMessage('Pilih tanggal mulai dan akhir terlebih dahulu'); return; } if (_selectedStartDate!.isAfter(_selectedEndDate!)) { _showErrorMessage( 'Tanggal mulai tidak boleh lebih besar dari tanggal akhir'); return; } try { _showLoadingDialog('Membuat laporan Excel...'); final panenProvider = context.read(); final panens = _getFilteredPanens(panenProvider); final filePath = await ExcelExportService.exportWeeklyReport( panens: panens, startDate: _selectedStartDate!, endDate: _selectedEndDate!, kandangFilter: _selectedKandang, ); if (!mounted) return; Navigator.of(context).pop(); // Close loading dialog _showSuccessMessage('Laporan berhasil dibuat:\n$filePath'); } catch (e) { if (!mounted) return; Navigator.of(context).pop(); // Close loading dialog _showErrorMessage('Gagal membuat laporan: $e'); } } void _showLoadingDialog(String message) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( content: Column( mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), const SizedBox(height: 16), Text(message), ], ), ); }, ); } void _showSuccessMessage(String message) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('✓ Sukses'), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('OK'), ), ], ); }, ); } void _showErrorMessage(String message) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('✗ Error'), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('OK'), ), ], ); }, ); } }