MIF_E31230549/lib/bidan/crud_jadwal/edit_jadwal.dart

418 lines
15 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
const String baseUrl = "http://ta.myhost.id/E31230549/mposyandu_api";
class Kader {
final int id;
final String nama;
final int dusunId;
Kader({
required this.id,
required this.nama,
required this.dusunId,
});
}
class EditJadwalPage extends StatefulWidget {
final Map<String, dynamic> data;
const EditJadwalPage({
super.key,
required this.data,
});
@override
State<EditJadwalPage> createState() => _EditJadwalPageState();
}
class _EditJadwalPageState extends State<EditJadwalPage> {
late TextEditingController _tanggalController;
late TextEditingController _jamMulaiController;
late TextEditingController _jamSelesaiController;
late TextEditingController _lokasiController;
late TextEditingController _keteranganController;
List<dynamic> daftarDusun = [];
List<Kader> daftarKader = [];
List<int> dusunDipilih = [];
String dusunTeksAwal = "";
bool _isLoading = false;
@override
void initState() {
super.initState();
_tanggalController =
TextEditingController(text: widget.data['tanggal']?.toString() ?? "");
_jamMulaiController =
TextEditingController(text: widget.data['jam_mulai']?.toString() ?? "");
_jamSelesaiController = TextEditingController(
text: widget.data['jam_selesai']?.toString() ?? "");
_lokasiController =
TextEditingController(text: widget.data['lokasi']?.toString() ?? "");
_keteranganController = TextEditingController(
text: widget.data['keterangan']?.toString() ?? "");
dusunTeksAwal = widget.data['dusun']?.toString() ?? "-";
_parseInitialDusun();
WidgetsBinding.instance.addPostFrameCallback((_) {
_initialLoad();
});
}
void _parseInitialDusun() {
try {
var rawDusunIds = widget.data['dusun_ids'];
if (rawDusunIds != null && rawDusunIds.toString().isNotEmpty) {
dusunDipilih = rawDusunIds
.toString()
.split(',')
.map((e) => int.tryParse(e.trim()) ?? 0)
.where((e) => e > 0)
.toList();
}
} catch (e) {
debugPrint("Error parsing dusun: $e");
}
}
Future<void> _initialLoad() async {
await loadDusun();
if (dusunDipilih.isNotEmpty) {
await loadKaderByDusun();
}
}
Future<void> loadDusun() async {
try {
final res = await http.get(Uri.parse("$baseUrl/dusun/get_dusun.php"));
if (res.statusCode == 200) {
final responseData = jsonDecode(res.body);
setState(() {
daftarDusun = responseData['data'] ?? [];
});
}
} catch (e) {
debugPrint("Error load dusun: $e");
}
}
Future<void> loadKaderByDusun() async {
if (dusunDipilih.isEmpty) {
setState(() => daftarKader.clear());
return;
}
String ids = dusunDipilih.join(",");
try {
final res = await http.get(
Uri.parse("$baseUrl/petugas/get_kader_by_dusun.php?ids=$ids"),
);
if (res.statusCode == 200) {
final responseData = jsonDecode(res.body);
final List listData = responseData['data'] ?? [];
setState(() {
daftarKader = listData
.map((k) => Kader(
id: int.parse(k['id'].toString()),
nama: k['nama'].toString(),
dusunId: int.parse(k['dusun_id'].toString()),
))
.toList();
});
}
} catch (e) {
debugPrint("Error load kader: $e");
}
}
Future<void> _pickTanggal() async {
DateTime? picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2024),
lastDate: DateTime(2030),
);
if (picked != null) {
setState(() {
_tanggalController.text =
"${picked.year}-${picked.month.toString().padLeft(2, '0')}-${picked.day.toString().padLeft(2, '0')}";
});
}
}
Future<void> _pickJam(TextEditingController controller) async {
TimeOfDay? picked = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (picked != null) {
setState(() {
controller.text =
"${picked.hour.toString().padLeft(2, '0')}:${picked.minute.toString().padLeft(2, '0')}";
});
}
}
Future<void> _updateJadwal() async {
if (_tanggalController.text.isEmpty || dusunDipilih.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Data tidak boleh kosong!",
style: GoogleFonts.poppins(fontSize: 12))),
);
return;
}
setState(() => _isLoading = true);
try {
final response = await http.post(
Uri.parse("$baseUrl/jadwal_posyandu/edit_jadwal.php"),
body: {
"id": widget.data['id'].toString(),
"bidan_id": widget.data['bidan_id']?.toString() ?? "",
"tanggal": _tanggalController.text,
"jam_mulai": _jamMulaiController.text,
"jam_selesai": _jamSelesaiController.text,
"lokasi": _lokasiController.text,
"keterangan": _keteranganController.text,
"dusun_ids": dusunDipilih.join(","),
},
);
final result = jsonDecode(response.body);
if (result['success']) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Jadwal Berhasil Diperbarui",
style: GoogleFonts.poppins(fontSize: 12))),
);
Navigator.pop(context, true);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content:
Text("Error: $e", style: GoogleFonts.poppins(fontSize: 12))));
}
setState(() => _isLoading = false);
}
InputDecoration _input(String label) {
return InputDecoration(
labelText: label,
labelStyle: GoogleFonts.poppins(fontSize: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("",
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600)),
backgroundColor: Colors.blue,
iconTheme: const IconThemeData(color: Colors.white),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 500),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: const [
BoxShadow(color: Colors.black12, blurRadius: 8)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Text("Edit Data Jadwal",
style: GoogleFonts.poppins(
fontSize: 16, fontWeight: FontWeight.bold))),
const SizedBox(height: 15),
TextField(
controller: _tanggalController,
style: GoogleFonts.poppins(fontSize: 12),
readOnly: true,
onTap: _pickTanggal,
decoration: _input("Tanggal").copyWith(
suffixIcon: const Icon(Icons.calendar_today, size: 18),
),
),
const SizedBox(height: 10),
Row(
children: [
Expanded(
child: TextField(
controller: _jamMulaiController,
style: GoogleFonts.poppins(fontSize: 12),
readOnly: true,
onTap: () => _pickJam(_jamMulaiController),
decoration: _input("Jam Mulai").copyWith(
suffixIcon: const Icon(Icons.access_time, size: 18),
),
),
),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: _jamSelesaiController,
style: GoogleFonts.poppins(fontSize: 12),
readOnly: true,
onTap: () => _pickJam(_jamSelesaiController),
decoration: _input("Jam Selesai").copyWith(
suffixIcon: const Icon(Icons.access_time, size: 18),
),
),
),
],
),
const SizedBox(height: 10),
TextField(
controller: _lokasiController,
style: GoogleFonts.poppins(fontSize: 12),
decoration: _input("Lokasi Posyandu")),
const SizedBox(height: 15),
Text("Dusun Yang Dilayani",
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 12)),
Container(
width: double.infinity,
margin: const EdgeInsets.only(top: 5, bottom: 10),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue.shade100),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Dusun sebelumnya:",
style: GoogleFonts.poppins(
fontSize: 11,
fontWeight: FontWeight.bold,
color: Colors.blue)),
Text(dusunTeksAwal,
style: GoogleFonts.poppins(
fontSize: 12, color: Colors.black87)),
const SizedBox(height: 4),
Text(
"*Silakan centang ulang di bawah jika ingin merubah pilihan dusun.",
style: GoogleFonts.poppins(
fontSize: 10,
fontStyle: FontStyle.italic,
color: Colors.grey)),
],
),
),
const Divider(),
daftarDusun.isEmpty
? Center(
child: Text("Memuat daftar dusun...",
style: GoogleFonts.poppins(
fontSize: 12, color: Colors.grey)))
: Column(
children: daftarDusun.map((d) {
int id = int.parse(d['id'].toString());
return CheckboxListTile(
visualDensity: VisualDensity.compact,
contentPadding: EdgeInsets.zero,
title: Text(d['nama_dusun'] ?? "",
style: GoogleFonts.poppins(fontSize: 12)),
value: dusunDipilih.contains(id),
onChanged: (val) {
setState(() {
if (val == true) {
if (!dusunDipilih.contains(id))
dusunDipilih.add(id);
} else {
dusunDipilih.remove(id);
}
});
loadKaderByDusun();
},
);
}).toList(),
),
const SizedBox(height: 10),
Text("Kader Otomatis:",
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 12)),
const SizedBox(height: 5),
daftarKader.isEmpty
? Text("-",
style: GoogleFonts.poppins(
color: Colors.grey, fontSize: 12))
: Wrap(
spacing: 5,
children: daftarKader
.map((k) => Chip(
label: Text(k.nama,
style: GoogleFonts.poppins(fontSize: 12)),
padding: EdgeInsets.zero,
backgroundColor: Colors.blue.shade50,
))
.toList(),
),
const SizedBox(height: 15),
TextField(
controller: _keteranganController,
style: GoogleFonts.poppins(fontSize: 12),
decoration: _input("Keterangan")),
const SizedBox(height: 25),
// --- TOMBOL SIMPAN PERUBAHAN (OUTLINED STADIUM) ---
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: _isLoading ? null : _updateJadwal,
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.blue),
shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 14)),
child: _isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
color: Colors.blue, strokeWidth: 2))
: Text(
"Simpan Perubahan",
style: GoogleFonts.poppins(
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: 12),
),
),
),
],
),
),
),
),
);
}
}