MIF_E31230549/lib/bidan/crud_data_gizi/edit_gizi_balita.dart

442 lines
16 KiB
Dart

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<String, dynamic> dataGizi;
const EditGiziBalitaPage({super.key, required this.dataGizi});
@override
State<EditGiziBalitaPage> createState() => _EditGiziBalitaPageState();
}
class _EditGiziBalitaPageState extends State<EditGiziBalitaPage> {
final _formKey = GlobalKey<FormState>();
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<String> _listBBU = [
'Gizi Buruk',
'Gizi Kurang',
'Gizi Baik',
'Risiko Gizi Lebih'
];
final List<String> _listTBU = ['Sangat Pendek', 'Pendek', 'Normal', 'Tinggi'];
final List<String> _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<String> 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<void> 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<String> 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<String>(
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]!)),
);
}
}