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 TambahIbuPage extends StatefulWidget { const TambahIbuPage({super.key}); @override State createState() => _TambahIbuPageState(); } class _TambahIbuPageState extends State { final _formKey = GlobalKey(); // ================= CONTROLLER ================= final nikC = TextEditingController(); final noKkC = TextEditingController(); final namaC = TextEditingController(); final namaSuamiC = TextEditingController(); final tempatLahirC = TextEditingController(); final tglC = TextEditingController(); final pendidikanC = TextEditingController(); final pekerjaanC = TextEditingController(); final alamatC = TextEditingController(); final hpC = TextEditingController(); final emailC = TextEditingController(); final passwordC = TextEditingController(); final desaC = TextEditingController(); final dusunC = TextEditingController(); bool _isSaving = false; String? _emailError; // Menyimpan pesan error email dari server String? selectedGolDarah; String? selectedAgama; String? selectedDesa; String? selectedDusun; String? currentKaderId; bool isKader = false; final List golDarahList = const ['A', 'B', 'AB', 'O', '-']; final List agamaList = const [ 'Islam', 'Kristen', 'Katolik', 'Hindu', 'Buddha', 'Konghucu' ]; @override void initState() { super.initState(); _checkUserRole(); nikC.addListener(() { String nikValue = nikC.text.trim(); if (nikValue.length >= 6) { passwordC.text = nikValue.substring(nikValue.length - 6); } else { passwordC.text = ""; } }); } @override void dispose() { nikC.dispose(); noKkC.dispose(); namaC.dispose(); namaSuamiC.dispose(); tempatLahirC.dispose(); tglC.dispose(); pendidikanC.dispose(); pekerjaanC.dispose(); alamatC.dispose(); hpC.dispose(); emailC.dispose(); passwordC.dispose(); desaC.dispose(); dusunC.dispose(); super.dispose(); } Future _checkUserRole() async { try { final prefs = await SharedPreferences.getInstance(); final role = prefs.getString('role'); final sessionDesaId = prefs.get('desa_id')?.toString(); final sessionDusunId = prefs.get('dusun_id')?.toString(); final sessionIdUser = prefs.getString('id_user'); final sessionNamaDesa = prefs.getString('nama_desa') ?? ""; final sessionNamaDusun = prefs.getString('nama_dusun') ?? ""; if (role == 'kader' && sessionDusunId != null) { if (mounted) { setState(() { isKader = true; selectedDesa = sessionDesaId; selectedDusun = sessionDusunId; currentKaderId = sessionIdUser; desaC.text = sessionNamaDesa; dusunC.text = sessionNamaDusun; }); } } } catch (e) { debugPrint("Error Check Role: $e"); } } dynamic _safeJson(String body) { try { if (body.trim().startsWith("<")) return null; return json.decode(body); } catch (e) { return null; } } Future _simpan() async { // Reset status error email sebelum mencoba simpan setState(() => _emailError = null); if (!_formKey.currentState!.validate()) return; if (selectedDesa == null || selectedDusun == null) { _showMsg("Wilayah tidak terdeteksi"); return; } if (currentKaderId == null) { _showMsg("Sesi login Kader tidak valid, silakan login ulang"); return; } setState(() => _isSaving = true); try { final res = await http.post( Uri.parse("http://ta.myhost.id/E31230549/mposyandu_api/ibu/tambah_ibu.php"), body: { "nik": nikC.text.trim(), "no_kk": noKkC.text.trim(), "nama": namaC.text.trim(), "nama_suami": namaSuamiC.text.trim(), "tempat_lahir": tempatLahirC.text.trim(), "tanggal_lahir": tglC.text.trim(), "golongan_darah": selectedGolDarah ?? "-", "pendidikan": pendidikanC.text.trim(), "pekerjaan": pekerjaanC.text.trim(), "agama": selectedAgama ?? "", "no_hp": hpC.text.trim(), "email": emailC.text.trim(), "password": passwordC.text.trim(), "desa_id": selectedDesa, "dusun_id": selectedDusun, "alamat_detail": alamatC.text.trim(), "kader_id": currentKaderId, }, ); final data = _safeJson(res.body); if (data != null && data["success"] == true) { _showMsg("Data berhasil disimpan"); if (mounted) Navigator.pop(context, true); } else { String serverMsg = data?["message"] ?? "Gagal menyimpan"; // Logika pengecekan email terdaftar if (serverMsg.toLowerCase().contains("email")) { setState(() => _emailError = "Email sudah terdaftar"); } else { _showMsg(serverMsg); } } } catch (e) { _showMsg("Gagal terhubung ke server"); } 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))), ); } Future _pickDate() async { final picked = await showDatePicker( context: context, firstDate: DateTime(1950), lastDate: DateTime.now(), initialDate: DateTime(2000), ); if (picked != null) { tglC.text = picked.toIso8601String().split("T").first; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("", style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600)), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), backgroundColor: const Color(0xfff4f6fb), body: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 900), 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( children: [ Text("Tambah Data Ibu", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 24), Wrap( spacing: 16, runSpacing: 16, children: [ _field("NIK", nikC, keyboard: TextInputType.number, isNik: true), _field("No KK", noKkC, keyboard: TextInputType.number, isKk: true), _field("Nama Lengkap", namaC), _field("Nama Suami", namaSuamiC), _field("Tempat Lahir", tempatLahirC), SizedBox( width: 260, child: TextFormField( controller: tglC, readOnly: true, onTap: _pickDate, style: GoogleFonts.poppins(fontSize: 12), decoration: _decoration("Tanggal Lahir").copyWith( suffixIcon: const Icon(Icons.calendar_today, size: 18, color: Colors.black), ), validator: (v) => v!.isEmpty ? "Wajib diisi" : null, ), ), SizedBox( width: 260, child: DropdownButtonFormField( value: selectedGolDarah, style: GoogleFonts.poppins( fontSize: 12, color: Colors.black), decoration: _decoration("Golongan Darah"), items: golDarahList .map((e) => DropdownMenuItem(value: e, child: Text(e))) .toList(), onChanged: (v) => setState(() => selectedGolDarah = v), validator: (v) => v == null ? "Wajib dipilih" : null, ), ), _field("Pendidikan", pendidikanC), _field("Pekerjaan", pekerjaanC), SizedBox( width: 260, child: DropdownButtonFormField( value: selectedAgama, style: GoogleFonts.poppins( fontSize: 12, color: Colors.black), decoration: _decoration("Agama"), items: agamaList .map((e) => DropdownMenuItem(value: e, child: Text(e))) .toList(), onChanged: (v) => setState(() => selectedAgama = v), validator: (v) => v == null ? "Agama wajib dipilih" : null, ), ), _field("Desa", desaC, readOnly: true), _field("Dusun", dusunC, readOnly: true), _field("No HP", hpC, keyboard: TextInputType.phone, isPhone: true), _field("Email", emailC, keyboard: TextInputType.emailAddress, isEmail: true), SizedBox( width: 260, child: TextFormField( controller: passwordC, obscureText: true, readOnly: true, style: GoogleFonts.poppins(fontSize: 12), decoration: _decoration("Password akan terisi otomatis"), validator: (v) => (v == null || v.isEmpty) ? "Password belum terisi (isi NIK dulu)" : null, ), ), _field("Alamat Detail", alamatC), ], ), const SizedBox(height: 32), SizedBox( width: double.infinity, height: 50, child: OutlinedButton( onPressed: _isSaving ? null : _simpan, style: OutlinedButton.styleFrom( side: const BorderSide(color: Colors.blue, width: 2), shape: const StadiumBorder(), ), child: _isSaving ? const Center( child: SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.blue)), ) : Text("Simpan Data Ibu", style: GoogleFonts.poppins( fontWeight: FontWeight.bold, color: Colors.blue)), ), ) ], ), ), ), ), ), ), ); } Widget _field(String label, TextEditingController c, {TextInputType keyboard = TextInputType.text, bool isNik = false, bool isKk = false, bool isPhone = false, bool isEmail = false, bool readOnly = false}) { return SizedBox( width: 260, child: TextFormField( controller: c, keyboardType: keyboard, readOnly: readOnly, onChanged: (v) { // Menghilangkan pesan error saat user mulai mengetik ulang di kolom email if (isEmail && _emailError != null) { setState(() => _emailError = null); } }, inputFormatters: (isNik || isKk || isPhone) ? [FilteringTextInputFormatter.digitsOnly] : null, style: GoogleFonts.poppins(fontSize: 12), decoration: _decoration(label).copyWith( fillColor: readOnly ? Colors.grey[100] : Colors.white, // Tampilan teks merah di bawah kolom jika error email ditemukan errorText: isEmail ? _emailError : null, errorStyle: GoogleFonts.poppins(color: Colors.red, fontSize: 10), ), validator: (v) { if (v == null || v.isEmpty) return "$label wajib diisi"; if ((isNik || isKk) && v.length != 16) return "Harus 16 digit"; if (isPhone && (v.length < 10 || v.length > 13)) return "Digit 10-13"; return 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), ); } }