MIF_E31230549/lib/kader/crud_ibu/tambah_ibu.dart

412 lines
14 KiB
Dart

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<TambahIbuPage> createState() => _TambahIbuPageState();
}
class _TambahIbuPageState extends State<TambahIbuPage> {
final _formKey = GlobalKey<FormState>();
// ================= 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<String> golDarahList = const ['A', 'B', 'AB', 'O', '-'];
final List<String> 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<void> _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<void> _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<void> _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<String>(
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<String>(
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),
);
}
}