431 lines
16 KiB
Dart
431 lines
16 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 EditIbuHamilPage extends StatefulWidget {
|
|
final Map<String, dynamic> data;
|
|
|
|
const EditIbuHamilPage({super.key, required this.data});
|
|
|
|
@override
|
|
State<EditIbuHamilPage> createState() => _EditIbuHamilPageState();
|
|
}
|
|
|
|
class _EditIbuHamilPageState extends State<EditIbuHamilPage> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
String? selectedIbuId;
|
|
List<dynamic> 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<String> listStatus = ['aktif', 'keguguran', 'melahirkan'];
|
|
|
|
String? selectedPembiayaan;
|
|
final List<String> 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<void> _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<void> _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<void> _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<String>(
|
|
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<DropdownMenuItem<String>>((item) {
|
|
return DropdownMenuItem<String>(
|
|
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<String>(
|
|
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<String>(
|
|
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),
|
|
);
|
|
}
|
|
}
|