import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:posyandu_care/kader/crud_ibu/edit_ibu.dart'; import 'package:posyandu_care/kader/crud_ibu/tambah_ibu.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart' as http; import '../layout/main_layout.dart'; import 'kader_drawer.dart'; import '../pages/login_page.dart'; import '../kader/dashboard_kader.dart'; class DataIbuPage extends StatefulWidget { const DataIbuPage({super.key}); @override State createState() => _DataIbuPageState(); } class _DataIbuPageState extends State { List _data = []; List _filteredData = []; bool _isLoading = true; int _currentPage = 0; final int _rowsPerPage = 5; final TextEditingController _searchC = TextEditingController(); Timer? _debounce; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { _checkLogin(); _fetchData(); }); _searchC.addListener(_onSearchChanged); } @override void dispose() { _searchC.dispose(); _debounce?.cancel(); super.dispose(); } // FORMAT TANGGAL INDONESIA String formatTanggal(String? tanggal) { if (tanggal == null || tanggal.isEmpty) return "-"; try { DateTime date = DateTime.parse(tanggal); List bulan = [ "", "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember" ]; return "${date.day} ${bulan[date.month]} ${date.year}"; } catch (e) { return tanggal; } } void _onSearchChanged() { if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 400), () { final keyword = _searchC.text.toLowerCase(); setState(() { _currentPage = 0; // Reset halaman ke 0 setiap kali user mengetik pencarian baru _filteredData = _data.where((item) { final nama = (item["nama"] ?? "").toString().toLowerCase(); final nik = (item["nik"] ?? "").toString().toLowerCase(); final namaSuami = (item["nama_suami"] ?? "").toString().toLowerCase(); return nama.contains(keyword) || nik.contains(keyword) || namaSuami.contains(keyword); }).toList(); }); }); } Future _checkLogin() async { final prefs = await SharedPreferences.getInstance(); final isLogin = prefs.getBool('isLogin') ?? false; if (!isLogin && mounted) { Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (_) => const LoginPage(fromGuard: true), ), (route) => false, ); } } Future _fetchData() async { setState(() => _isLoading = true); try { final prefs = await SharedPreferences.getInstance(); final rawDusunId = prefs.get('dusun_id'); String dusunId = rawDusunId?.toString() ?? ""; final url = Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/ibu/get_ibu.php?dusun_id=$dusunId"); final response = await http.get(url); final jsonData = json.decode(response.body); if (jsonData["success"] == true) { setState(() { _data = jsonData["data"]; _filteredData = _data; _isLoading = false; }); } else { setState(() { _data = []; _filteredData = []; _isLoading = false; }); } } catch (e) { debugPrint("ERROR GET IBU: $e"); setState(() => _isLoading = false); } } Future _confirmDelete(String id) async { final yes = await showDialog( context: context, builder: (_) => AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), title: Text("Konfirmasi Hapus", style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.bold)), content: Text( "Data Ibu dan Akun Login terkait akan dihapus permanen. Lanjutkan?", style: GoogleFonts.poppins(fontSize: 12)), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text("Batal", style: GoogleFonts.poppins(fontSize: 12))), OutlinedButton( style: OutlinedButton.styleFrom( side: const BorderSide(color: Colors.red), shape: const StadiumBorder(), ), onPressed: () => Navigator.pop(context, true), child: Text("Hapus", style: GoogleFonts.poppins( color: Colors.red, fontSize: 12, fontWeight: FontWeight.bold)), ), ], ), ); if (yes == true) _deleteData(id); } Future _deleteData(String id) async { try { final url = Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/ibu/hapus_ibu.php"); final response = await http.post(url, body: {"id": id}); final data = json.decode(response.body); if (data["success"] == true && mounted) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text("Data berhasil dihapus", style: GoogleFonts.poppins(fontSize: 12)))); _fetchData(); } else { throw Exception(data["message"]); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text("Gagal hapus: $e", style: GoogleFonts.poppins(fontSize: 12)))); } } List get _paginatedData { final start = _currentPage * _rowsPerPage; final end = start + _rowsPerPage; if (start >= _filteredData.length) return []; return _filteredData.sublist( start, end > _filteredData.length ? _filteredData.length : end); } @override Widget build(BuildContext context) { final totalPages = _filteredData.isEmpty ? 1 : (_filteredData.length / _rowsPerPage).ceil(); return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) async { if (didPop) return; Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (_) => const DashboardKaderPage(), ), (route) => false, ); }, child: MainLayout( title: "", drawer: const KaderDrawer(), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Center( child: Text( "Data Ibu", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold), ), ), const SizedBox(height: 20), Row( children: [ Expanded( child: TextField( controller: _searchC, style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( hintText: "Cari Nama/NIK/Nama Suami...", hintStyle: GoogleFonts.poppins(fontSize: 12), prefixIcon: const Icon(Icons.search, size: 20), contentPadding: const EdgeInsets.symmetric(vertical: 10), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10)), ), ), ), const SizedBox(width: 10), OutlinedButton.icon( onPressed: () async { await Navigator.push( context, MaterialPageRoute( builder: (_) => const TambahIbuPage())); _fetchData(); }, icon: const Icon(Icons.add, color: Colors.blueAccent, size: 18), label: Text("Tambah", style: GoogleFonts.poppins( color: Colors.blueAccent, fontSize: 12, fontWeight: FontWeight.bold)), style: OutlinedButton.styleFrom( side: const BorderSide( color: Colors.blueAccent, width: 1.5), shape: const StadiumBorder(), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10), ), ), ], ), const SizedBox(height: 15), Expanded( child: _isLoading ? const Center(child: CircularProgressIndicator()) : _filteredData.isEmpty ? Center( child: Text( "Tidak ada data ibu", style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ), ) : ListView.builder( itemCount: _paginatedData.length, itemBuilder: (context, index) { final item = _paginatedData[index]; return Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 8, offset: Offset(0, 4)) ], ), child: Padding( padding: const EdgeInsets.all(14), child: Column( children: [ _gridRow("No KK", item["no_kk"]), _gridRow("NIK", item["nik"]), _gridRow("Nama Ibu", item["nama"]), _gridRow("Tempat, Tgl Lahir", "${item["tempat_lahir"] ?? "-"}, ${formatTanggal(item["tanggal_lahir"])}"), _gridRow( "Gol. Darah", item["golongan_darah"]), _gridRow("Agama", item["agama"]), _gridRow("Desa", item["nama_desa"]), _gridRow("Dusun", item["nama_dusun"]), _gridRow("Alamat Detail", item["alamat_detail"]), _gridRow( "Pendidikan", item["pendidikan"]), _gridRow("Pekerjaan", item["pekerjaan"]), _gridRow("Email", item["email"]), _gridRow("No HP", item["no_hp"]), _gridRow( "Nama Suami", item["nama_suami"]), const Divider(height: 25), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ _smallButton( text: "Edit", icon: Icons.edit, color: Colors.orange, onPressed: () async { final result = await Navigator.push( context, MaterialPageRoute( builder: (_) => EditIbuPage( data: item)), ); if (result == true) _fetchData(); }, ), const SizedBox(width: 8), _smallButton( text: "Hapus", icon: Icons.delete, color: Colors.red, onPressed: () => _confirmDelete( item["id"].toString()), ), ], ), ], ), ), ); }, ), ), Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( 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 _gridRow(String label, dynamic value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 120, child: Text( label, style: GoogleFonts.poppins( fontWeight: FontWeight.w600, fontSize: 12, color: Colors.black87), ), ), Text(" : ", style: GoogleFonts.poppins(fontSize: 12)), Expanded( child: Text( "${value ?? '-'}", style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), ), ), ], ), ); } Widget _smallButton({ required String text, required IconData icon, required Color color, required VoidCallback onPressed, }) { return OutlinedButton.icon( onPressed: onPressed, icon: Icon(icon, size: 14, color: color), label: Text(text, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.bold, color: color)), style: OutlinedButton.styleFrom( side: BorderSide(color: color), shape: const StadiumBorder(), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 0), minimumSize: const Size(0, 32), ), ); } }