418 lines
15 KiB
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),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|