import 'package:flutter/material.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:intl/intl.dart'; import 'package:jago/services/db_helper.dart'; import 'package:jago/services/notification_service.dart'; // Import layanan notifikasi import 'package:excel/excel.dart' as excel; import 'dart:io'; import 'dart:async'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; class NotificationPage extends StatefulWidget { @override _NotificationPageState createState() => _NotificationPageState(); } class _NotificationPageState extends State { List> notifications = []; List> filteredNotifications = []; late DatabaseHelper dbHelper; DateTime? selectedDate; int ageInWeeks = 2; late DatabaseReference ageRef; int selectedAge = 0; // 0 untuk umur saat ini, 1-4 untuk umur spesifik // Tambahkan variabel untuk row pages int _rowsPerPage = 10; List _rowsPerPageOptions = [5, 10, 20, 50, 100]; int _currentPage = 0; // Inisialisasi notifikasi lokal final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // Daftar subscription untuk dibersihkan saat dispose final List _subscriptions = []; @override void initState() { super.initState(); dbHelper = DatabaseHelper(); ageRef = FirebaseDatabase.instance.ref("chicken_age"); // Periksa tabel notifications dbHelper.checkTables().then((_) { _fetchAgeFromFirebase(); _loadNotificationsFromDB(); }); // Pastikan NotificationService sudah diinisialisasi NotificationService().initialize(); } @override void dispose() { // Membersihkan semua subscription untuk mencegah memory leak for (var subscription in _subscriptions) { subscription.cancel(); } super.dispose(); } void _fetchAgeFromFirebase() { final subscription = ageRef.onValue.listen((event) { final data = event.snapshot.value; if (data != null) { int newAge = int.parse(data.toString()); if (newAge != ageInWeeks) { print("🐔 Umur ayam berubah dari $ageInWeeks ke $newAge"); setState(() { ageInWeeks = newAge; }); // Muat ulang notifikasi saat umur berubah _loadNotificationsFromDB(); } } }); _subscriptions.add(subscription); } Future _loadNotificationsFromDB() async { List> dbData = await dbHelper.getNotifications(); print("🔍 Total notifikasi dari database: ${dbData.length}"); setState(() { notifications = _filterUniqueNotifications(dbData); _applyFilter(); }); print("✅ Berhasil memuat ${notifications.length} notifikasi dari database"); } Future _refreshNotifications() async { await _loadNotificationsFromDB(); } List> _filterUniqueNotifications(List> data) { List> uniqueData = []; Map? lastEntry; for (var entry in data) { if (lastEntry == null || lastEntry['event'] != entry['event']) { uniqueData.add(entry); lastEntry = entry; } } return uniqueData; } void _applyFilter() { setState(() { // Filter berdasarkan tanggal jika dipilih List> dateFiltered; if (selectedDate == null) { dateFiltered = List.from(notifications); } else { String selectedDateString = DateFormat('yyyy-MM-dd').format(selectedDate!); dateFiltered = notifications.where((notif) { DateTime dateTime = DateTime.parse(notif['timestamp']); return DateFormat('yyyy-MM-dd').format(dateTime) == selectedDateString; }).toList(); } // Filter berdasarkan umur ayam yang dipilih filteredNotifications = dateFiltered.where((notif) { // Ekstrak umur dari pesan notifikasi jika ada String eventText = notif['event'] ?? ''; // Cek apakah notifikasi berisi informasi umur if (eventText.contains("[Umur:")) { // Ekstrak umur dari notifikasi RegExp regExp = RegExp(r"\[Umur: (\d+) minggu\]"); Match? match = regExp.firstMatch(eventText); if (match != null && match.groupCount >= 1) { int notifAge = int.parse(match.group(1)!); // Jika selectedAge adalah 0, gunakan umur ayam saat ini // Jika tidak, gunakan umur yang dipilih if (selectedAge == 0) { return notifAge == ageInWeeks; } else { return notifAge == selectedAge; } } return false; } else { // Untuk notifikasi lama yang tidak memiliki tag umur, tampilkan di umur 2 minggu if (selectedAge == 0) { // Jika memilih "umur saat ini", tampilkan data lama jika umur saat ini adalah 2 return ageInWeeks == 2; } else { // Jika memilih umur spesifik, tampilkan data lama hanya jika memilih umur 2 return selectedAge == 2; } } }).toList(); // Reset halaman saat filter berubah _currentPage = 0; }); } Future _pickDate(BuildContext context) async { DateTime? pickedDate = await showDatePicker( context: context, initialDate: selectedDate ?? DateTime.now(), firstDate: DateTime(2024), lastDate: DateTime(2030), 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; _applyFilter(); }); } } void _downloadNotificationsAsExcel() async { // Filter notifikasi sesuai umur yang dipilih List> ageFilteredData = notifications.where((notif) { String eventText = notif['event'] ?? ''; if (eventText.contains("[Umur:")) { RegExp regExp = RegExp(r"\[Umur: (\d+) minggu\]"); Match? match = regExp.firstMatch(eventText); if (match != null && match.groupCount >= 1) { int notifAge = int.parse(match.group(1)!); // Jika selectedAge adalah 0, gunakan umur ayam saat ini // Jika tidak, gunakan umur yang dipilih if (selectedAge == 0) { return notifAge == ageInWeeks; } else { return notifAge == selectedAge; } } return false; } else { // Untuk notifikasi lama yang tidak memiliki tag umur, tampilkan di umur 2 minggu if (selectedAge == 0) { // Jika memilih "umur saat ini", tampilkan data lama jika umur saat ini adalah 2 return ageInWeeks == 2; } else { // Jika memilih umur spesifik, tampilkan data lama hanya jika memilih umur 2 return selectedAge == 2; } } }).toList(); var excelFile = excel.Excel.createExcel(); var sheet = excelFile['Sheet1']; sheet.appendRow(['Timestamp', 'Event', 'Umur Ayam']); for (var notif in ageFilteredData) { String eventText = notif['event'] ?? ''; // Ekstrak umur dari teks event jika ada int eventAge = 2; // Default umur untuk data lama tanpa tag if (eventText.contains("[Umur:")) { RegExp regExp = RegExp(r"\[Umur: (\d+) minggu\]"); Match? match = regExp.firstMatch(eventText); if (match != null && match.groupCount >= 1) { eventAge = int.parse(match.group(1)!); } // Hapus tag umur dari tampilan eventText = eventText.replaceAll(regExp, "").trim(); } sheet.appendRow([notif['timestamp'], eventText, eventAge]); } // Tentukan nama file berdasarkan mode filter umur int exportAge = selectedAge == 0 ? ageInWeeks : selectedAge; final directory = Directory('/storage/emulated/0/Documents/Data Notifikasi'); if (!(await directory.exists())) { await directory.create(recursive: true); } String filePath = '${directory.path}/Notifikasi_${DateFormat('yyyyMMdd').format(DateTime.now())}_Umur${exportAge}minggu.xlsx'; File(filePath)..writeAsBytesSync(excelFile.encode()!); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Data notifikasi berhasil diunduh sebagai Excel'), backgroundColor: Color(0xFFA82429), behavior: SnackBarBehavior.floating, action: SnackBarAction( label: 'OK', textColor: Colors.white, onPressed: () {}, ), ) ); } // Tambahkan method untuk paginasi List> _getPaginatedData() { if (filteredNotifications.isEmpty) return []; int startIndex = _currentPage * _rowsPerPage; int endIndex = startIndex + _rowsPerPage; if (startIndex >= filteredNotifications.length) { _currentPage = 0; startIndex = 0; endIndex = _rowsPerPage; } if (endIndex > filteredNotifications.length) { endIndex = filteredNotifications.length; } return filteredNotifications.sublist(startIndex, endIndex); } @override Widget build(BuildContext context) { // Dapatkan data yang sudah dipaginasi final paginatedData = _getPaginatedData(); final int totalPages = (filteredNotifications.length / _rowsPerPage).ceil(); // Tentukan judul AppBar berdasarkan mode filter umur String appBarTitle = selectedAge == 0 ? 'Notifikasi (Umur: $ageInWeeks minggu)' : 'Notifikasi (Umur: $selectedAge minggu)'; 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( appBarTitle, style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ) ), ), body: Column( children: [ // Header section with curved background 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: [ Row( children: [ Expanded( child: Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 4, offset: Offset(0, 2), ), ], ), child: Row( children: [ Icon( Icons.calendar_today, color: Color(0xFFA82429), size: 20, ), SizedBox(width: 8), Expanded( child: Text( selectedDate == null ? 'Semua Tanggal' : DateFormat('dd/MM/yyyy').format(selectedDate!), style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87, ), ), ), TextButton( onPressed: () => _pickDate(context), child: Text( 'Pilih', style: TextStyle( color: Color(0xFFA82429), fontWeight: FontWeight.bold, ), ), style: TextButton.styleFrom( minimumSize: Size(0, 0), padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), ), ), ], ), ), ), ], ), SizedBox(height: 12), Row( children: [ Expanded( child: Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 4, offset: Offset(0, 2), ), ], ), child: DropdownButtonHideUnderline( child: DropdownButton( value: selectedAge, isExpanded: true, icon: Icon(Icons.filter_list, color: Color(0xFFA82429)), items: [ DropdownMenuItem( value: 0, child: Text('Umur Saat Ini (${ageInWeeks} minggu)'), ), DropdownMenuItem( value: 1, child: Text('Umur 1 minggu'), ), DropdownMenuItem( value: 2, child: Text('Umur 2 minggu'), ), DropdownMenuItem( value: 3, child: Text('Umur 3 minggu'), ), DropdownMenuItem( value: 4, child: Text('Umur 4 minggu'), ), ], onChanged: (int? newValue) { if (newValue != null) { setState(() { selectedAge = newValue; _applyFilter(); }); } }, ), ), ), ), SizedBox(width: 10), 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: IconButton( icon: Icon(Icons.file_download, color: Color(0xFFA82429)), onPressed: _downloadNotificationsAsExcel, tooltip: 'Unduh Data Excel', ), ), ], ), ], ), ), // 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 notifications Expanded( child: RefreshIndicator( onRefresh: _refreshNotifications, color: Color(0xFFA82429), child: paginatedData.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.notifications_off, size: 64, color: Colors.grey, ), SizedBox(height: 16), Text( selectedAge == 0 ? 'Tidak ada notifikasi untuk umur ayam $ageInWeeks minggu' : 'Tidak ada notifikasi untuk umur ayam $selectedAge minggu', 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 notif = paginatedData[index]; final DateTime dateTime = DateTime.parse(notif['timestamp']); String eventText = notif['event'] ?? 'Data tidak tersedia'; // Ekstrak umur dari teks event jika ada int eventAge = 2; // Default untuk data lama tanpa tag if (eventText.contains("[Umur:")) { RegExp regExp = RegExp(r"\[Umur: (\d+) minggu\]"); Match? match = regExp.firstMatch(eventText); if (match != null && match.groupCount >= 1) { eventAge = int.parse(match.group(1)!); } // Hapus tag umur dari tampilan eventText = eventText.replaceAll(regExp, "").trim(); } // Tentukan warna berdasarkan jenis notifikasi Color cardColor; Color timeColor; IconData iconData; if (eventText.contains('Kritis') || eventText.contains('Sangat Tinggi') || eventText.contains('Sangat Rendah') || eventText.contains('darurat')) { cardColor = Colors.red; timeColor = Colors.red[200]!; // Use temperature or humidity specific icons when relevant if (eventText.contains('Suhu')) { iconData = Icons.thermostat; } else if (eventText.contains('Kelembapan')) { iconData = Icons.water_drop; } else { iconData = Icons.warning; } } else if (eventText.contains('tinggi') || eventText.contains('rendah')) { cardColor = Colors.orange; timeColor = Colors.orange[200]!; // Use temperature or humidity specific icons when relevant if (eventText.contains('Suhu')) { iconData = Icons.thermostat; } else if (eventText.contains('Kelembapan')) { iconData = Icons.water_drop; } else { iconData = Icons.warning_amber; } } else if (eventText.contains('Fuzzy')) { cardColor = Colors.blue; timeColor = Colors.blue[200]!; // Use temperature or humidity specific icons for Fuzzy notifications if (eventText.contains('Suhu')) { iconData = Icons.thermostat; } else if (eventText.contains('Kelembapan')) { iconData = Icons.water_drop; } else { iconData = Icons.auto_graph; } } else if (eventText.contains('Status')) { cardColor = Colors.purple; timeColor = Colors.purple[200]!; // Use temperature or humidity specific icons for Status notifications if (eventText.contains('Suhu')) { iconData = Icons.thermostat; } else if (eventText.contains('Kelembapan')) { iconData = Icons.water_drop; } else { iconData = Icons.info; } } else if (eventText.contains('normal') || eventText.contains('Normal') || eventText.contains('Optimal')) { cardColor = Colors.green; timeColor = Colors.green[200]!; // Use temperature or humidity specific icons for normal notifications if (eventText.contains('Suhu')) { iconData = Icons.thermostat; } else if (eventText.contains('Kelembapan')) { iconData = Icons.water_drop; } else { iconData = Icons.check_circle; } } else { cardColor = Colors.blueGrey; timeColor = Colors.blueGrey[200]!; iconData = Icons.notifications; } return Card( elevation: 3, margin: EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: Column( children: [ // Colored header Container( color: cardColor, padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ Icon( iconData, color: Colors.white, size: 20, ), SizedBox(width: 8), Expanded( child: Text( DateFormat('dd/MM/yyyy HH:mm').format(dateTime), style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: timeColor, borderRadius: BorderRadius.circular(12), ), child: Text( 'Umur: $eventAge minggu', style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: cardColor, ), ), ), ], ), ), // Message body Container( width: double.infinity, padding: EdgeInsets.all(16), child: Text( eventText, style: TextStyle( fontSize: 16, color: Colors.black87, ), ), ), ], ), ), ); }, ), ), ), ], ), ); } }