import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import '../layout/main_layout.dart'; import 'bidan_drawer.dart'; import '../bidan/crud_data_gizi/tambah_gizi_balita.dart'; import '../bidan/crud_data_gizi/riwayat_gizi_balita.dart'; import '../bidan/dashboard_bidan.dart'; class DataGiziBalitaPage extends StatefulWidget { const DataGiziBalitaPage({super.key}); @override State createState() => _DataGiziBalitaPageState(); } class _DataGiziBalitaPageState extends State { List> _dataBalita = []; bool _isLoading = true; int _currentPage = 0; final int _rowsPerPage = 5; String _searchQuery = ""; @override void initState() { super.initState(); fetchData(); } String formatAngka(dynamic value, String satuan) { if (value == null || value.toString().trim().isEmpty || value.toString().toLowerCase() == "null") { return "-"; } return "${value.toString()}$satuan"; } String formatTanggal(String? tgl) { if (tgl == null || tgl == "-" || tgl.isEmpty) return "-"; try { DateTime dt = DateTime.parse(tgl); List bulanIndo = [ "", "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember" ]; return "${dt.day} ${bulanIndo[dt.month]} ${dt.year}"; } catch (e) { return tgl; } } // PERBAIKAN: Menghitung usia berdasarkan Tanggal Pemeriksaan, bukan Tanggal Sekarang String hitungUsia(String? tglLahir, String? tglPeriksa) { if (tglLahir == null || tglLahir == "-" || tglLahir.isEmpty) return "-"; try { DateTime lahir = DateTime.parse(tglLahir); // Menggunakan tanggal pemeriksaan sebagai acuan utama, jika kosong baru pakai waktu sekarang DateTime acuanPeriksa = (tglPeriksa != null && tglPeriksa != "-" && tglPeriksa.isNotEmpty) ? DateTime.parse(tglPeriksa) : DateTime.now(); // Hitung selisih bulan dasar int bulan = (acuanPeriksa.year - lahir.year) * 12 + acuanPeriksa.month - lahir.month; // Logika pembulatan hari: Jika sisa hari pada bulan berjalan > 15 hari, bulatkan ke atas (+1 bulan) DateTime tanggalTargetUlangTahun = DateTime(lahir.year, lahir.month + bulan, lahir.day); Duration selisihHari = acuanPeriksa.difference(tanggalTargetUlangTahun); if (selisihHari.inDays > 15) { bulan += 1; } // Pengondisian jika hasil hitung bernilai negatif karena variasi tanggal if (bulan < 0) bulan = 0; return "$bulan Bulan"; } catch (e) { return "-"; } } Future fetchData() async { try { final response = await http.get( Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/gizi_balita/get_gizi_balita.php"), ); if (response.statusCode == 200) { final data = json.decode(response.body); setState(() { _dataBalita = List>.from(data); _isLoading = false; }); } else { setState(() => _isLoading = false); } } catch (e) { debugPrint("Error Fetch: $e"); setState(() => _isLoading = false); } } List> get _filteredData { return _dataBalita .where((item) => item["nama"] .toString() .toLowerCase() .contains(_searchQuery.toLowerCase())) .toList(); } 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.length / _rowsPerPage).ceil() == 0 ? 1 : (_filteredData.length / _rowsPerPage).ceil(); return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) async { if (didPop) return; Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (_) => const DashboardBidanPage()), (route) => false); }, child: MainLayout( title: "", drawer: const BidanDrawer(), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Center( child: Text("Data Gizi Balita", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.black87)), ), const SizedBox(height: 12), TextField( onChanged: (value) => setState(() { _searchQuery = value; _currentPage = 0; }), style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( hintText: "Cari nama balita...", hintStyle: GoogleFonts.poppins(fontSize: 12), prefixIcon: const Icon(Icons.search, size: 20), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10)), contentPadding: const EdgeInsets.symmetric(vertical: 8), ), ), const SizedBox(height: 15), Expanded( child: _isLoading ? const Center(child: CircularProgressIndicator()) : _filteredData.isEmpty ? Center( child: Text("Data tidak tersedia", style: GoogleFonts.poppins(fontSize: 12))) : ListView.builder( itemCount: _paginatedData.length, itemBuilder: (context, index) { final balita = _paginatedData[index]; final bb = formatAngka(balita["bb"], "kg"); final tb = formatAngka(balita["tb"], "cm"); final lk = formatAngka(balita["lk"], "cm"); return Container( margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 8, offset: Offset(0, 4)) ], ), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.all(15), decoration: const BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.vertical( top: Radius.circular(12)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( balita["nama"] ?? "-", style: GoogleFonts.poppins( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), ), _badgeHadir( balita["status_hadir"]), ], ), const Divider(color: Colors.white54), _rowWhite("Orang Tua", balita["nama_orang_tua"] ?? "-"), _rowWhite("Alamat", balita["alamat"] ?? "-"), // PERBAIKAN: Mengirimkan dua parameter (tanggal lahir & tanggal pemeriksaan) _rowWhite( "Usia", hitungUsia( balita["tanggal_lahir"], balita[ "tanggal_pemeriksaan"])), _rowWhite( "Tgl Pemeriksaan", formatTanggal(balita[ "tanggal_pemeriksaan"])), _rowWhite("BB / TB / LK", "$bb / $tb / $lk"), _rowWhite("Catatan Kader", balita["catatan"] ?? "-"), ], ), ), Container( width: double.infinity, padding: const EdgeInsets.all(15), decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical( bottom: Radius.circular(12)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Pemeriksaan Gizi Terakhir", style: GoogleFonts.poppins( fontWeight: FontWeight.w600, fontSize: 12)), const SizedBox(height: 8), _rowGizi("Z-Score BB/U", balita["zscore_bb_u"] ?? "-"), _rowGizi("Z-Score TB/U", balita["zscore_tb_u"] ?? "-"), _rowGizi("Z-Score BB/TB", balita["zscore_bb_tb"] ?? "-"), const Divider(), _rowStatus("Status Gizi", balita), _rowGizi("Tindak Lanjut", balita["tindak_lanjut"] ?? "-"), _rowGizi( "Saran", balita["saran"] ?? "-"), const SizedBox(height: 12), Row( children: [ _smallButton(Icons.add, "Input", Colors.blue, () async { await Navigator.push( context, MaterialPageRoute( builder: (context) => TambahGiziBalitaPage( balita: balita))); fetchData(); }), _smallButton( Icons.history, "Riwayat", Colors.deepPurple, () async { await Navigator.push( context, MaterialPageRoute( builder: (context) => RiwayatGiziBalitaPage( balita: balita))); fetchData(); }), ], ) ], ), ), ], ), ); }, ), ), _buildPagination(totalPages), ], ), ), ), ); } Widget _badgeHadir(String? status) { String text = status ?? "Belum Absen"; Color badgeColor; if (text.toLowerCase() == "hadir") { badgeColor = Colors.greenAccent[700]!; } else if (text.toLowerCase() == "tidak hadir") { badgeColor = Colors.redAccent; } else { badgeColor = Colors.orangeAccent; } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: badgeColor.withOpacity(0.3), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.white, width: 1), ), child: Text( text.toUpperCase(), style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.white), ), ); } Widget _rowWhite(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 100, child: Text(label, style: GoogleFonts.poppins( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600))), Text(" : ", style: GoogleFonts.poppins(color: Colors.white, fontSize: 12)), Expanded( child: Text(value, style: GoogleFonts.poppins(color: Colors.white, fontSize: 12))), ], ), ); } Widget _rowGizi(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 120, child: Text(label, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.grey[800]))), const Text(" : ", style: TextStyle(fontSize: 12)), Expanded( child: Text(value, style: GoogleFonts.poppins(fontSize: 12, color: Colors.black))), ], ), ); } Widget _rowStatus(String label, Map balita) { return Container( margin: const EdgeInsets.symmetric(vertical: 5), padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.orange[50], borderRadius: BorderRadius.circular(8)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("$label :", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.orange[900])), const SizedBox(height: 4), Padding( padding: const EdgeInsets.only(left: 12), child: Column( children: [ _rowStatusDetail("BB / U", balita["status_bbu"] ?? "-"), _rowStatusDetail("TB / U", balita["status_tbu"] ?? "-"), _rowStatusDetail("BB / TB", balita["status_bbtb"] ?? "-"), ], ), ), ], ), ); } Widget _rowStatusDetail(String indikator, String nilai) { return Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Row( children: [ SizedBox( width: 65, child: Text( indikator, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.orange[900]), ), ), Text( " : ", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.orange[900]), ), Expanded( child: Text( nilai, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.orange[900]), ), ), ], ), ); } Widget _smallButton( IconData icon, String text, Color color, VoidCallback onPressed) { return Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 2), child: OutlinedButton.icon( onPressed: onPressed, icon: Icon(icon, size: 14, color: color), label: Text(text, style: GoogleFonts.poppins( fontSize: 12, color: color, fontWeight: FontWeight.w500)), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 8), side: BorderSide(color: color.withOpacity(0.5)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), ), ), ); } Widget _buildPagination(int totalPages) { return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Halaman ${_currentPage + 1} dari $totalPages", style: GoogleFonts.poppins(fontSize: 12)), 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++)), ], ), ], ), ); } }