import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; class EditGiziBalitaPage extends StatefulWidget { final Map dataGizi; const EditGiziBalitaPage({super.key, required this.dataGizi}); @override State createState() => _EditGiziBalitaPageState(); } class _EditGiziBalitaPageState extends State { final _formKey = GlobalKey(); late TextEditingController zbbController; late TextEditingController ztbController; late TextEditingController zbbtbController; late TextEditingController tindakController; late TextEditingController saranController; // Variabel penampung teks rentang standar deviasi (SD) String keteranganBBU = "-"; String keteranganTBU = "-"; String keteranganBBTB = "-"; // Tiga status gizi berbeda sesuai dengan database baru String? _selectedStatusBBU; String? _selectedStatusTBU; String? _selectedStatusBBTB; bool isLoading = false; // List kategori indikator masing-masing gizi final List _listBBU = [ 'Gizi Buruk', 'Gizi Kurang', 'Gizi Baik', 'Risiko Gizi Lebih' ]; final List _listTBU = ['Sangat Pendek', 'Pendek', 'Normal', 'Tinggi']; final List _listBBTB = [ 'Sangat Kurus', 'Kurus', 'Normal', 'Gemuk', 'Obesitas' ]; @override void initState() { super.initState(); zbbController = TextEditingController( text: widget.dataGizi["zscore_bb_u"]?.toString() ?? ""); ztbController = TextEditingController( text: widget.dataGizi["zscore_tb_u"]?.toString() ?? ""); zbbtbController = TextEditingController( text: widget.dataGizi["zscore_bb_tb"]?.toString() ?? ""); tindakController = TextEditingController(text: widget.dataGizi["tindak_lanjut"] ?? ""); saranController = TextEditingController(text: widget.dataGizi["saran"] ?? ""); // Ambil rentang standar deviasi otomatis berdasarkan nilai Z-Score yang ada double zBBU = double.tryParse(zbbController.text) ?? 0; double zTBU = double.tryParse(ztbController.text) ?? 0; double zBBTB = double.tryParse(zbbtbController.text) ?? 0; keteranganBBU = _getSDStatus(zBBU); keteranganTBU = _getSDStatus(zTBU); keteranganBBTB = _getSDStatus(zBBTB); // Set nilai awal dropdown berdasarkan data yang ada di database if (_listBBU.contains(widget.dataGizi["status_bbu"])) { _selectedStatusBBU = widget.dataGizi["status_bbu"]; } if (_listTBU.contains(widget.dataGizi["status_tbu"])) { _selectedStatusTBU = widget.dataGizi["status_tbu"]; } if (_listBBTB.contains(widget.dataGizi["status_bbtb"])) { _selectedStatusBBTB = widget.dataGizi["status_bbtb"]; } } // Fungsi penentu keterangan standar deviasi (Sama dengan kode tambah) String _getSDStatus(double z) { if (z < -3) return "Di bawah -3 SD"; if (z < -2) return "-3 SD s/d -2 SD"; if (z <= 1) return "-2 SD s/d +1 SD"; if (z <= 2) return "Di atas +1 SD s/d +2 SD"; return "Di atas +2 SD"; } 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; } } String hitungUsia(String? tglLahir) { if (tglLahir == null || tglLahir.isEmpty) return "-"; try { DateTime lahir = DateTime.parse(tglLahir); DateTime sekarang = DateTime.now(); int bulan = (sekarang.year - lahir.year) * 12 + sekarang.month - lahir.month; return "$bulan Bulan"; } catch (e) { return "-"; } } Future updateData() async { if (!_formKey.currentState!.validate()) return; setState(() => isLoading = true); try { final response = await http.post( Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/gizi_balita/update_gizi_balita.php"), body: { "id_gizi": widget.dataGizi["id_gizi"].toString(), "zscore_bb_u": zbbController.text, "zscore_tb_u": ztbController.text, "zscore_bb_tb": zbbtbController.text, "status_bbu": _selectedStatusBBU ?? "", "status_tbu": _selectedStatusTBU ?? "", "status_bbtb": _selectedStatusBBTB ?? "", "tindak_lanjut": tindakController.text, "saran": saranController.text, }, ); final data = json.decode(response.body); if (data["success"] == true) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Data berhasil diupdate"))); Navigator.pop(context, true); } else { if (!mounted) return; ScaffoldMessenger.of(context) .showSnackBar(const SnackBar(content: Text("Gagal update"))); } } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text("Error: $e"))); } if (mounted) setState(() => isLoading = false); } @override Widget build(BuildContext context) { final usia = hitungUsia(widget.dataGizi["tanggal_lahir"]?.toString()); final tglPeriksa = formatTanggal(widget.dataGizi["tanggal_pemeriksaan"]?.toString()); final catatan = widget.dataGizi["catatan"] ?? "-"; return Scaffold( backgroundColor: Colors.grey[100], appBar: AppBar( title: const Text(""), backgroundColor: Colors.blue, iconTheme: const IconThemeData(color: Colors.white), ), body: isLoading ? const Center(child: CircularProgressIndicator()) : Center( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 500), child: Form( key: _formKey, child: Column( children: [ Text( "Edit Data Gizi Balita", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black, ), textAlign: TextAlign.center, ), const SizedBox(height: 15), // CARD INFO BALITA (BIRU) Container( width: double.infinity, padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(10)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.dataGizi["nama"] ?? "-", style: GoogleFonts.poppins( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 15)), const Divider(color: Colors.white), _rowInfo("Tgl Pemeriksaan", tglPeriksa), _rowInfo("Usia Balita", usia), _rowInfo("BB / TB / LK", "${widget.dataGizi['bb']} kg / ${widget.dataGizi['tb']} cm / ${widget.dataGizi['lk']} cm"), _rowInfo("Catatan Kader", catatan), ], ), ), const SizedBox(height: 15), // CARD INPUT (PUTIH) Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10) ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Bagian Input Z-Score & Tampilan Standar Deviasi (Sama dengan Kode Tambah) _buildSection( "Z-Score BB/U", zbbController, keteranganBBU, _listBBU, _selectedStatusBBU, (v) { setState(() => _selectedStatusBBU = v); }), _buildSection( "Z-Score TB/U", ztbController, keteranganTBU, _listTBU, _selectedStatusTBU, (v) { setState(() => _selectedStatusTBU = v); }), _buildSection( "Z-Score BB/TB", zbbtbController, keteranganBBTB, _listBBTB, _selectedStatusBBTB, (v) { setState(() => _selectedStatusBBTB = v); }), const Padding( padding: EdgeInsets.symmetric(vertical: 10), child: Divider(), ), _buildLabel("Tindak Lanjut"), _buildTextField(tindakController, hint: "Masukkan rencana tindakan..."), _buildLabel("Saran"), _buildTextField(saranController, hint: "Masukkan saran..."), const SizedBox(height: 25), // --- TOMBOL SIMPAN PERUBAHAN --- SizedBox( width: double.infinity, child: OutlinedButton( onPressed: isLoading ? null : updateData, style: OutlinedButton.styleFrom( side: const BorderSide( color: Colors.blue, width: 2), shape: const StadiumBorder(), padding: const EdgeInsets.symmetric( vertical: 15), ), child: Text( "Simpan Perubahan", style: GoogleFonts.poppins( color: Colors.blue, fontSize: 13, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ], ), ), ), ), ), ); } // Komponen pembangun struktur input + kotak Standar Deviasi + Dropdown Widget _buildSection(String title, TextEditingController controller, String sd, List items, String? val, Function(String?) onChanged) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabel(title), Row( children: [ Expanded( flex: 2, child: TextFormField( controller: controller, readOnly: true, decoration: _inputDeco(isFilled: true), style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.blue[900]), ), ), const SizedBox(width: 10), Expanded( flex: 3, child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.amber[50], borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.amber[200]!)), child: Text( sd, style: GoogleFonts.poppins( fontSize: 11, fontWeight: FontWeight.bold, color: Colors.amber[900]), ), ), ), ], ), const SizedBox(height: 10), DropdownButtonFormField( value: val, hint: Text("Pilih Status $title", style: GoogleFonts.poppins(fontSize: 12)), isExpanded: true, items: items .map((e) => DropdownMenuItem( value: e, child: Text(e, style: GoogleFonts.poppins(fontSize: 12)))) .toList(), onChanged: onChanged, decoration: _inputDeco(), validator: (v) => v == null ? "Wajib dipilih" : null, ), const SizedBox(height: 10), ], ); } Widget _rowInfo(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 110, child: Text(label, style: GoogleFonts.poppins( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600))), const Text(": ", style: TextStyle(color: Colors.white)), Expanded( child: Text(value, style: GoogleFonts.poppins( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500))), ], ), ); } Widget _buildLabel(String t) => Padding( padding: const EdgeInsets.only(top: 10, bottom: 4), child: Text(t, style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))); Widget _buildTextField(TextEditingController c, {String? hint}) { return TextFormField( controller: c, maxLines: 2, style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), decoration: _inputDeco(hint: hint), ); } InputDecoration _inputDeco({String? hint, bool isFilled = false}) { return InputDecoration( hintText: hint, hintStyle: GoogleFonts.poppins(fontSize: 11), filled: true, fillColor: isFilled ? Colors.blue[50] : Colors.grey[50], contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.grey[300]!)), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.grey[300]!)), ); } }