import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; class EditIbuHamilPage extends StatefulWidget { final Map data; const EditIbuHamilPage({super.key, required this.data}); @override State createState() => _EditIbuHamilPageState(); } class _EditIbuHamilPageState extends State { final _formKey = GlobalKey(); String? selectedIbuId; List listIbu = []; final hphtC = TextEditingController(); final usiaC = TextEditingController(); final hplC = TextEditingController(); final tglPersalinanSblmC = TextEditingController(); final gravidaC = TextEditingController(); final paraC = TextEditingController(); final abortusC = TextEditingController(); final hidupC = TextEditingController(); // Dropdown Data String? selectedStatus; final List listStatus = ['aktif', 'keguguran', 'melahirkan']; String? selectedPembiayaan; final List listPembiayaan = [ 'JKN', 'JAMPERSAL', 'ASURANSI KESEHATAN LAIN', 'MANDIRI' ]; bool _isSaving = false; bool _isLoadingIbu = true; @override void initState() { super.initState(); // Inisialisasi Data Awal dari widget.data hphtC.text = widget.data["hpht"] ?? ""; hplC.text = widget.data["hpl"] ?? ""; tglPersalinanSblmC.text = widget.data["tanggal_persalinan_sebelumnya"] ?? ""; gravidaC.text = widget.data["gravida"]?.toString() ?? ""; paraC.text = widget.data["para"]?.toString() ?? ""; abortusC.text = widget.data["abortus"]?.toString() ?? ""; hidupC.text = widget.data["hidup"]?.toString() ?? ""; selectedIbuId = widget.data["ibu_id"]?.toString(); selectedStatus = widget.data["status"]?.toString().toLowerCase(); selectedPembiayaan = widget.data["pembiayaan"]; // Hitung usia kehamilan awal jika HPHT tersedia if (hphtC.text.isNotEmpty) { try { DateTime hphtDate = DateTime.parse(hphtC.text); _hitungDariHPHT(hphtDate); } catch (e) { debugPrint("Error parse HPHT: $e"); } } _fetchIbu(); } @override void dispose() { hphtC.dispose(); usiaC.dispose(); hplC.dispose(); tglPersalinanSblmC.dispose(); gravidaC.dispose(); paraC.dispose(); abortusC.dispose(); hidupC.dispose(); super.dispose(); } Future _fetchIbu() async { try { final prefs = await SharedPreferences.getInstance(); final dynamic rawDusunId = prefs.get("dusun_id"); final String userDusunId = rawDusunId?.toString() ?? ""; final url = Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/ibu/get_ibu.php?dusun_id=$userDusunId"); final response = await http.get(url).timeout(const Duration(seconds: 10)); final data = json.decode(response.body); if (data["success"] == true) { if (mounted) { setState(() { listIbu = data["data"] ?? []; _isLoadingIbu = false; bool exists = listIbu.any((item) => item["id"].toString() == selectedIbuId); if (!exists) { listIbu.add({ "id": widget.data["ibu_id"], "nama": widget.data["nama_ibu"] ?? widget.data["nama"] ?? "Data Saat Ini", "nama_suami": widget.data["nama_suami"] ?? "-" }); } }); } } else { if (mounted) setState(() => _isLoadingIbu = false); } } catch (e) { if (mounted) setState(() => _isLoadingIbu = false); } } Future _pickDate(TextEditingController controller, {bool updateHpl = false}) async { final picked = await showDatePicker( context: context, firstDate: DateTime(2000), lastDate: DateTime.now().add(const Duration(days: 365)), initialDate: DateTime.tryParse(controller.text) ?? DateTime.now(), ); if (picked != null) { if (mounted) { setState(() { controller.text = picked.toIso8601String().split("T").first; if (updateHpl) _hitungDariHPHT(picked); }); } } } void _hitungDariHPHT(DateTime hphtDate) { final now = DateTime.now(); final diffDays = now.difference(hphtDate).inDays; final minggu = diffDays < 0 ? 0 : (diffDays / 7).floor(); usiaC.text = minggu.toString(); final hplDate = hphtDate.add(const Duration(days: 280)); hplC.text = hplDate.toIso8601String().split("T").first; } Future _update() async { if (!_formKey.currentState!.validate()) return; if (selectedIbuId == null) { _showMsg("Ibu wajib dipilih"); return; } if (selectedStatus == null) { _showMsg("Status wajib dipilih"); return; } setState(() => _isSaving = true); try { final url = Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/ibu_hamil/update_ibu_hamil.php"); final response = await http.post(url, body: { "id": widget.data["id"].toString(), "ibu_id": selectedIbuId!, "hpht": hphtC.text.trim(), "hpl": hplC.text.trim(), "tanggal_persalinan_sebelumnya": tglPersalinanSblmC.text.trim(), "gravida": gravidaC.text.trim(), "para": paraC.text.trim(), "abortus": abortusC.text.trim(), "hidup": hidupC.text.trim(), "status": selectedStatus!, "pembiayaan": selectedPembiayaan ?? "", // Boleh kosong }); final data = json.decode(response.body); if (data["success"] == true) { if (!mounted) return; _showMsg("Data berhasil diupdate"); Navigator.pop(context, true); } else { _showMsg(data["message"] ?? "Gagal update"); } } catch (e) { _showMsg("Terjadi kesalahan koneksi: $e"); } if (mounted) setState(() => _isSaving = false); } void _showMsg(String msg) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(msg, style: GoogleFonts.poppins(fontSize: 12))), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0, backgroundColor: Colors.blue, foregroundColor: Colors.white, title: Text("", style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600)), ), backgroundColor: const Color(0xfff4f6fb), body: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 420), child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: const [ BoxShadow( blurRadius: 16, color: Colors.black12, offset: Offset(0, 8)) ], ), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Center( child: Text("Edit Data Ibu Hamil", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold)), ), const SizedBox(height: 24), _isLoadingIbu ? const Center(child: CircularProgressIndicator()) : DropdownButtonFormField( isExpanded: true, value: selectedIbuId, style: GoogleFonts.poppins( fontSize: 12, color: Colors.black), hint: Text("Pilih Ibu", style: GoogleFonts.poppins(fontSize: 12)), decoration: _decoration("Pilih Ibu"), items: listIbu.map>((item) { return DropdownMenuItem( value: item["id"].toString(), child: Text( "${item["nama"]} (${item["nama_suami"] ?? '-'})", overflow: TextOverflow.ellipsis, style: GoogleFonts.poppins(fontSize: 12)), ); }).toList(), onChanged: (value) => setState(() => selectedIbuId = value), validator: (v) => v == null ? "Ibu wajib dipilih" : null, ), const SizedBox(height: 16), TextFormField( controller: hphtC, readOnly: true, onTap: () => _pickDate(hphtC, updateHpl: true), style: GoogleFonts.poppins(fontSize: 12), decoration: _decoration("HPHT (Hari Pertama Haid Terakhir)") .copyWith( suffixIcon: const Icon(Icons.calendar_today, size: 18, color: Colors.black), ), validator: (v) => v == null || v.isEmpty ? "HPHT wajib diisi" : null, ), const SizedBox(height: 16), TextFormField( controller: usiaC, readOnly: true, style: GoogleFonts.poppins(fontSize: 12), decoration: _decoration("Usia Kehamilan (Minggu)"), ), const SizedBox(height: 16), TextFormField( controller: hplC, readOnly: true, style: GoogleFonts.poppins(fontSize: 12), decoration: _decoration("HPL (Hari Perkiraan Lahir)"), ), const SizedBox(height: 16), TextFormField( controller: tglPersalinanSblmC, readOnly: true, onTap: () => _pickDate(tglPersalinanSblmC), style: GoogleFonts.poppins(fontSize: 12), decoration: _decoration( "Tanggal Persalinan Sebelumnya (Opsional)") .copyWith( suffixIcon: const Icon(Icons.history, size: 18, color: Colors.black), ), ), const SizedBox(height: 16), _input("Gravida (G)", gravidaC, keyboard: TextInputType.number), const SizedBox(height: 16), _input("Para (P)", paraC, keyboard: TextInputType.number), const SizedBox(height: 16), _input("Abortus (A)", abortusC, keyboard: TextInputType.number), const SizedBox(height: 16), _input("Hidup", hidupC, keyboard: TextInputType.number), const SizedBox(height: 16), // DROPDOWN PEMBIAYAAN (Opsional) DropdownButtonFormField( isExpanded: true, value: (selectedPembiayaan == null || selectedPembiayaan!.isEmpty) ? null : selectedPembiayaan, style: GoogleFonts.poppins( fontSize: 12, color: Colors.black), decoration: _decoration("Pembiayaan"), hint: Text("Pilih Pembiayaan (Opsional)", style: GoogleFonts.poppins(fontSize: 12)), items: listPembiayaan .map((e) => DropdownMenuItem( value: e, child: Text(e, style: GoogleFonts.poppins(fontSize: 12)))) .toList(), onChanged: (v) => setState(() => selectedPembiayaan = v), ), const SizedBox(height: 16), // DROPDOWN STATUS DropdownButtonFormField( value: selectedStatus, style: GoogleFonts.poppins( fontSize: 12, color: Colors.black), decoration: _decoration("Status"), items: listStatus .map((e) => DropdownMenuItem( value: e, child: Text(e.toUpperCase(), style: GoogleFonts.poppins(fontSize: 12)))) .toList(), onChanged: (v) => setState(() => selectedStatus = v), validator: (v) => v == null ? "Wajib dipilih" : null, ), const SizedBox(height: 28), SizedBox( height: 50, child: OutlinedButton( onPressed: _isSaving ? null : _update, style: OutlinedButton.styleFrom( side: const BorderSide( color: Colors.blueAccent, width: 2), shape: const StadiumBorder(), ), child: _isSaving ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2)) : Text("Simpan Perubahan", style: GoogleFonts.poppins( fontWeight: FontWeight.bold, fontSize: 14, color: Colors.blueAccent)), ), ), ], ), ), ), ), ), ), ); } Widget _input(String label, TextEditingController c, {TextInputType keyboard = TextInputType.text}) { return TextFormField( controller: c, keyboardType: keyboard, style: GoogleFonts.poppins(fontSize: 12), inputFormatters: keyboard == TextInputType.number ? [FilteringTextInputFormatter.digitsOnly] : null, decoration: _decoration(label), validator: (v) => v == null || v.isEmpty ? "$label wajib diisi" : null, ); } InputDecoration _decoration(String label) { return InputDecoration( filled: true, fillColor: Colors.white, labelText: label, labelStyle: GoogleFonts.poppins(fontSize: 12), border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), ); } }