import 'package:flutter/material.dart'; import 'package:jago/services/db_helper.dart'; import 'package:excel/excel.dart' as excel; import 'dart:io'; import 'package:intl/intl.dart'; class AngkaKematianPage extends StatefulWidget { @override _AngkaKematianPageState createState() => _AngkaKematianPageState(); } class _AngkaKematianPageState extends State { final List> dataKematian = []; final DatabaseHelper _dbHelper = DatabaseHelper(); String selectedDate = ""; // Tambahkan variabel untuk row pages int _rowsPerPage = 10; List _rowsPerPageOptions = [5, 10, 20, 50, 100]; int _currentPage = 0; @override void initState() { super.initState(); _loadData(); } Future _loadData() async { await _autoDeleteOldData(); List> dbData = await _dbHelper.getAngkaKematian(); setState(() { dataKematian.clear(); dataKematian.addAll(dbData.map((e) => { 'tanggal': e['tanggal'], 'angka': e['angka'].toString(), 'kloter': e['kloter'].toString(), })); }); } Future _saveDataToDB(String tanggal, String angka, String kloter) async { await _dbHelper.insertAngkaKematian({'tanggal': tanggal, 'angka': int.parse(angka), 'kloter': int.parse(kloter)}); _loadData(); } Future _autoDeleteOldData() async { final currentDate = DateTime.now(); final thresholdDate = currentDate.subtract(Duration(days: 30)); // Example: delete entries older than 30 days List> allData = await _dbHelper.getAngkaKematian(); for (var item in allData) { DateTime itemDate = DateTime.parse(item['tanggal']); if (itemDate.isBefore(thresholdDate)) { await _dbHelper.deleteAngkaKematian(item['tanggal'], item['kloter']); } } } void _showAddDataDialog() { TextEditingController dateController = TextEditingController(); TextEditingController angkaController = TextEditingController(); TextEditingController kloterController = TextEditingController(); // Set tanggal hari ini sebagai default dateController.text = DateTime.now().toString().split(' ')[0]; showDialog( context: context, builder: (context) { return AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), title: Column( children: [ Icon(Icons.analytics, color: Color(0xFFA82429), size: 40), SizedBox(height: 10), Text( 'Tambah Data Kematian', style: TextStyle( color: Color(0xFFA82429), fontWeight: FontWeight.bold ) ), ], ), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: dateController, decoration: InputDecoration( labelText: 'Tanggal:', labelStyle: TextStyle(color: Color(0xFFA82429)), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade400), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Color(0xFFA82429)), ), suffixIcon: IconButton( icon: Icon(Icons.calendar_today, color: Color(0xFFA82429)), onPressed: () async { DateTime? pickedDate = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2101), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: ColorScheme.light( primary: Color(0xFFA82429), onPrimary: Colors.white, ), ), child: child!, ); }, ); if (pickedDate != null) { dateController.text = "${pickedDate.toLocal()}".split(' ')[0]; } }, ), ), ), SizedBox(height: 16), TextField( controller: angkaController, decoration: InputDecoration( labelText: 'Angka kematian:', labelStyle: TextStyle(color: Color(0xFFA82429)), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade400), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Color(0xFFA82429)), ), ), keyboardType: TextInputType.number, ), SizedBox(height: 16), TextField( controller: kloterController, decoration: InputDecoration( labelText: 'Kloter (angka):', labelStyle: TextStyle(color: Color(0xFFA82429)), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade400), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Color(0xFFA82429)), ), ), keyboardType: TextInputType.number, ), SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () { Navigator.pop(context); }, child: Text('Batal', style: TextStyle(color: Colors.grey[600])), style: TextButton.styleFrom( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), ), SizedBox(width: 16), ElevatedButton( onPressed: () { // Validasi input if (dateController.text.isEmpty || angkaController.text.isEmpty || kloterController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Semua kolom harus diisi')) ); return; } _saveDataToDB(dateController.text, angkaController.text, kloterController.text); Navigator.pop(context); }, child: Text('Tambah', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFA82429), padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ), ], ), ], ), ); }, ); } void _selectDate() async { DateTime? pickedDate = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2101), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: ColorScheme.light( primary: Color(0xFFA82429), onPrimary: Colors.white, ), ), child: child!, ); }, ); if (pickedDate != null) { setState(() { selectedDate = "${pickedDate.toLocal()}".split(' ')[0]; }); } } Future _downloadDataAsExcel() async { var excelFile = excel.Excel.createExcel(); var sheet = excelFile['Sheet1']; sheet.appendRow(['Tanggal', 'Angka Kematian', 'Kloter']); for (var item in dataKematian) { sheet.appendRow([item['tanggal'], item['angka'], item['kloter']]); } final directory = Directory('/storage/emulated/0/Documents/Data Angka Kematian'); if (!(await directory.exists())) { await directory.create(recursive: true); } String filePath = '${directory.path}/AngkaKematian.xlsx'; File(filePath).writeAsBytesSync(excelFile.encode()!); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Data angka kematian berhasil diunduh sebagai Excel'), backgroundColor: Color(0xFFA82429), behavior: SnackBarBehavior.floating, action: SnackBarAction( label: 'OK', textColor: Colors.white, onPressed: () {}, ), ) ); } // Tambahkan method untuk paginasi List> _getPaginatedData() { // Filter data berdasarkan tanggal jika dipilih List> filteredData = selectedDate.isEmpty ? dataKematian : dataKematian.where((item) => item['tanggal'] == selectedDate).toList(); if (filteredData.isEmpty) return []; int startIndex = _currentPage * _rowsPerPage; int endIndex = startIndex + _rowsPerPage; if (startIndex >= filteredData.length) { _currentPage = 0; startIndex = 0; endIndex = _rowsPerPage; } if (endIndex > filteredData.length) { endIndex = filteredData.length; } return filteredData.sublist(startIndex, endIndex); } int getTotalKematian() { int total = 0; for (var item in dataKematian) { total += int.parse(item['angka'] ?? '0'); } return total; } @override Widget build(BuildContext context) { // Dapatkan data yang sudah dipaginasi final paginatedData = _getPaginatedData(); // Hitung total halaman berdasarkan data yang sudah difilter final filteredData = selectedDate.isEmpty ? dataKematian : dataKematian.where((item) => item['tanggal'] == selectedDate).toList(); final int totalPages = (filteredData.length / _rowsPerPage).ceil(); return Scaffold( appBar: AppBar( backgroundColor: Color(0xFFA82429), elevation: 0, leading: IconButton( icon: Icon(Icons.arrow_back, color: Colors.white), onPressed: () => Navigator.pop(context), ), title: Text( 'Data Angka Kematian', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ) ), actions: [ IconButton( icon: Icon(Icons.file_download, color: Colors.white), onPressed: _downloadDataAsExcel, ), ], ), body: Column( children: [ // Header dengan background curved Container( padding: EdgeInsets.fromLTRB(16, 0, 16, 20), decoration: BoxDecoration( color: Color(0xFFA82429), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(30), bottomRight: Radius.circular(30), ), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), spreadRadius: 2, blurRadius: 5, offset: Offset(0, 3), ), ], ), child: Column( children: [ // Filter tanggal Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 4, offset: Offset(0, 2), ), ], ), child: InkWell( onTap: _selectDate, child: Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ Icon( Icons.calendar_today, color: Color(0xFFA82429), size: 20, ), SizedBox(width: 12), Expanded( child: Text( selectedDate.isEmpty ? 'Semua Tanggal' : 'Tanggal: ${DateFormat('dd/MM/yyyy').format(DateTime.parse(selectedDate))}', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, ), ), ), if (selectedDate.isNotEmpty) IconButton( icon: Icon(Icons.close, color: Colors.grey, size: 20), onPressed: () { setState(() { selectedDate = ""; }); }, ), ], ), ), ), ), SizedBox(height: 16), // Card informasi Card( color: Colors.white, elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), child: Padding( padding: EdgeInsets.all(16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Total Kematian', style: TextStyle( color: Colors.grey[600], fontSize: 14, ), ), SizedBox(height: 4), Text( '${getTotalKematian()}', style: TextStyle( color: Color(0xFFA82429), fontWeight: FontWeight.bold, fontSize: 22, ), ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Jumlah Data', style: TextStyle( color: Colors.grey[600], fontSize: 14, ), ), SizedBox(height: 4), Text( '${dataKematian.length}', style: TextStyle( color: Colors.blue, fontWeight: FontWeight.bold, fontSize: 22, ), ), ], ), ], ), ), ), ], ), ), // Paginasi controls Padding( padding: EdgeInsets.fromLTRB(16, 16, 16, 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Text( 'Baris per halaman: ', style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 0), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(4), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: _rowsPerPage, isDense: true, items: _rowsPerPageOptions.map((int value) { return DropdownMenuItem( value: value, child: Text(value.toString()), ); }).toList(), onChanged: (newValue) { if (newValue != null) { setState(() { _rowsPerPage = newValue; _currentPage = 0; // Reset ke halaman pertama }); } }, ), ), ), ], ), if (totalPages > 0) Row( children: [ IconButton( icon: Icon(Icons.arrow_back_ios, size: 16), color: _currentPage > 0 ? Color(0xFFA82429) : Colors.grey, onPressed: _currentPage > 0 ? () { setState(() { _currentPage--; }); } : null, ), Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Color(0xFFA82429), borderRadius: BorderRadius.circular(12), ), child: Text( '${_currentPage + 1} / $totalPages', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), IconButton( icon: Icon(Icons.arrow_forward_ios, size: 16), color: _currentPage < totalPages - 1 ? Color(0xFFA82429) : Colors.grey, onPressed: _currentPage < totalPages - 1 ? () { setState(() { _currentPage++; }); } : null, ), ], ), ], ), ), // List of kematian items Expanded( child: RefreshIndicator( onRefresh: _loadData, color: Color(0xFFA82429), child: paginatedData.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.cloud_off, size: 64, color: Colors.grey, ), SizedBox(height: 16), Text( selectedDate.isEmpty ? 'Tidak ada data kematian' : 'Tidak ada data kematian untuk tanggal ini', style: TextStyle( color: Colors.grey[600], fontSize: 16, ), textAlign: TextAlign.center, ), ], ), ) : ListView.builder( padding: EdgeInsets.symmetric(horizontal: 16), itemCount: paginatedData.length, itemBuilder: (context, index) { final item = paginatedData[index]; return Dismissible( key: Key(item['tanggal']! + item['kloter']!), background: Container( padding: EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(15), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(Icons.delete, color: Colors.white), Icon(Icons.delete, color: Colors.white), ], ), ), confirmDismiss: (direction) async { bool? result = await showDialog( context: context, builder: (context) { return AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), title: Text( 'Hapus Data', style: TextStyle( color: Color(0xFFA82429), fontWeight: FontWeight.bold ), ), content: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.warning_amber_rounded, color: Colors.orange, size: 48, ), SizedBox(height: 16), Text('Apakah Anda yakin ingin menghapus data untuk kloter ${item['kloter']} pada tanggal ${DateFormat('dd/MM/yyyy').format(DateTime.parse(item['tanggal']!))}?'), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text('Batal'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), child: Text('Hapus', style: TextStyle(color: Colors.white)), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ], ); }, ); if (result == true) { await _dbHelper.deleteAngkaKematian(item['tanggal']!, item['kloter']!); _loadData(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Data untuk kloter ${item['kloter']} berhasil dihapus'), backgroundColor: Color(0xFFA82429), behavior: SnackBarBehavior.floating, ) ); } return result; }, child: Card( elevation: 3, margin: EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), child: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(Icons.calendar_month, color: Color(0xFFA82429), size: 20), SizedBox(width: 8), Text( DateFormat('dd/MM/yyyy').format(DateTime.parse(item['tanggal']!)), style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), ], ), Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( color: Color(0xFFA82429).withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( 'Kloter ${item['kloter']}', style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Color(0xFFA82429), ), ), ), ], ), SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Jumlah Kematian:', style: TextStyle( color: Colors.grey[600], fontSize: 14, ), ), Row( children: [ Icon(Icons.remove_circle_outline, color: int.parse(item['angka']!) > 5 ? Colors.red : Colors.orange, size: 18), SizedBox(width: 4), Text( '${item['angka']} ekor', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: int.parse(item['angka']!) > 5 ? Colors.red : Colors.orange, ), ), ], ), ], ), ], ), ), ), ); }, ), ), ), ], ), floatingActionButton: FloatingActionButton( backgroundColor: Color(0xFFA82429), child: Icon(Icons.add, color: Colors.white), onPressed: _showAddDataDialog, ), ); } }