import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import 'package:intl/date_symbol_data_local.dart'; class RiwayatKehadiranPage extends StatefulWidget { final String balitaId; final String namaBalita; const RiwayatKehadiranPage({ super.key, required this.balitaId, required this.namaBalita, }); @override State createState() => _RiwayatKehadiranPageState(); } class _RiwayatKehadiranPageState extends State { List _riwayatOriginal = []; List _riwayatFiltered = []; bool _isLoading = true; final TextEditingController _searchController = TextEditingController(); /// ================= PAGINATION ================= int _currentPage = 0; final int _rowsPerPage = 5; List get _paginatedData { if (_riwayatFiltered.isEmpty) return []; final start = _currentPage * _rowsPerPage; final end = start + _rowsPerPage; if (start >= _riwayatFiltered.length) return []; return _riwayatFiltered.sublist( start, end > _riwayatFiltered.length ? _riwayatFiltered.length : end, ); } @override void initState() { super.initState(); initializeDateFormatting('id_ID', null); _fetchRiwayat(); } @override void dispose() { _searchController.dispose(); super.dispose(); } String _formatTanggal(String? tanggal) { if (tanggal == null || tanggal == "" || tanggal == "0000-00-00") { return "-"; } try { DateTime dt = DateTime.parse(tanggal); return DateFormat('d MMMM yyyy', 'id_ID').format(dt); } catch (e) { return tanggal; } } void _filterData(String query) { setState(() { if (query.isEmpty) { _riwayatFiltered = _riwayatOriginal; } else { _riwayatFiltered = _riwayatOriginal.where((item) { String tglFormatted = _formatTanggal(item["tanggal_posyandu"]).toLowerCase(); String tglDatabase = (item["tanggal_posyandu"] ?? "").toString().toLowerCase(); return tglFormatted.contains(query.toLowerCase()) || tglDatabase.contains(query.toLowerCase()); }).toList(); } _currentPage = 0; }); } Future _fetchRiwayat() async { try { final response = await http.get( Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/kehadiran/get_riwayat_balita.php?balita_id=${widget.balitaId}"), ); if (response.statusCode == 200) { final res = json.decode(response.body); if (res["success"] == true) { setState(() { _riwayatOriginal = res["data"]; _riwayatFiltered = res["data"]; }); } } } catch (e) { debugPrint("Error Fetch Riwayat: ${e.toString()}"); } finally { if (mounted) setState(() => _isLoading = false); } } Future _hapusData(String id) async { try { final response = await http.post( Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/kehadiran/hapus_kehadiran.php"), body: {"id": id}, ); final res = json.decode(response.body); if (res["success"] == true) { _fetchRiwayat(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Data berhasil dihapus")), ); } } catch (e) { debugPrint("Error Hapus: ${e.toString()}"); } } @override Widget build(BuildContext context) { final totalPages = _riwayatFiltered.isEmpty ? 1 : (_riwayatFiltered.length / _rowsPerPage).ceil(); return Scaffold( backgroundColor: const Color(0xfff4f6fb), appBar: AppBar( backgroundColor: Colors.blueAccent, foregroundColor: Colors.white, elevation: 0, centerTitle: true, leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.pop(context, true), ), ), body: _isLoading ? const Center(child: CircularProgressIndicator()) : Column( children: [ const SizedBox(height: 14), Text( "Riwayat Kehadiran", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), /// SEARCH Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: TextField( controller: _searchController, onChanged: _filterData, style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( hintText: "Cari tanggal...", hintStyle: GoogleFonts.poppins(fontSize: 12), prefixIcon: const Icon(Icons.search, size: 18), filled: true, fillColor: Colors.white, contentPadding: const EdgeInsets.symmetric(vertical: 10), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10)), ), ), ), const SizedBox(height: 15), Expanded( child: _riwayatFiltered.isEmpty ? Center( child: Text("Data tidak ditemukan", style: GoogleFonts.poppins( fontSize: 12, color: Colors.grey))) : ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: _paginatedData.length, itemBuilder: (context, index) { final item = _paginatedData[index]; return Card( elevation: 2, color: Colors.white, // Warna card putih margin: const EdgeInsets.only(bottom: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: const BoxDecoration( color: Colors.blueAccent, borderRadius: BorderRadius.vertical( top: Radius.circular(12)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.namaBalita, style: GoogleFonts.poppins( color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold)), Text( _formatTanggal( item["tanggal_posyandu"]), style: GoogleFonts.poppins( color: Colors.white, fontSize: 12)), ], ), ), Padding( padding: const EdgeInsets.all(12), child: Column( children: [ _infoRowAligned("Status Kehadiran", item["status_hadir"] ?? "-"), const SizedBox(height: 4), _infoRowAligned("Catatan", item["keterangan"] ?? "-"), const Divider(), const SizedBox(height: 4), Align( alignment: Alignment.centerRight, child: SizedBox( width: 110, child: OutlinedButton.icon( onPressed: () => _showDeleteDialog( item["id"].toString()), icon: const Icon( Icons.delete_outline, size: 14, color: Colors.red), style: OutlinedButton.styleFrom( side: const BorderSide( color: Colors.red, width: 1.5), shape: const StadiumBorder(), padding: const EdgeInsets.symmetric( vertical: 8), ), label: Text( "Hapus", style: GoogleFonts.poppins( fontSize: 12, color: Colors.red, fontWeight: FontWeight.bold), ), ), ), ) ], ), ) ], ), ); }, ), ), /// PAGINATION Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), decoration: BoxDecoration( color: Colors.white, border: Border(top: BorderSide(color: Colors.grey.shade200))), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Halaman ${_currentPage + 1} dari $totalPages", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500)), Row( children: [ IconButton( icon: const Icon(Icons.chevron_left), onPressed: _currentPage == 0 ? null : () => setState(() => _currentPage--), ), IconButton( icon: const Icon(Icons.chevron_right), onPressed: _currentPage >= totalPages - 1 ? null : () => setState(() => _currentPage++), ), ], ), ], ), ), ], ), ); } Widget _infoRowAligned(String label, String value) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 120, child: Text(label, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500))), const Text(" : ", style: TextStyle(fontSize: 12)), Expanded( child: Text(value, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600))), ], ); } void _showDeleteDialog(String id) { showDialog( context: context, builder: (context) => AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), title: Text("Konfirmasi", style: GoogleFonts.poppins(fontWeight: FontWeight.bold, fontSize: 12)), content: Text("Data riwayat ini akan dihapus permanen.", style: GoogleFonts.poppins(fontSize: 12)), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text("Batal", style: GoogleFonts.poppins(color: Colors.grey, fontSize: 12))), OutlinedButton( onPressed: () { Navigator.pop(context); _hapusData(id); }, style: OutlinedButton.styleFrom( side: const BorderSide(color: Colors.red), shape: const StadiumBorder()), child: Text("Hapus", style: GoogleFonts.poppins( color: Colors.red, fontWeight: FontWeight.bold, fontSize: 12)), ), ], ), ); } }