import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import 'package:intl/date_symbol_data_local.dart'; import '../layout/main_layout.dart'; import 'bidan_drawer.dart'; import '../bidan/crud_jadwal/riwayat_jadwal_posyandu.dart'; import '../bidan/crud_jadwal/edit_jadwal.dart'; // Import Dashboard Bidan agar navigasi PopScope tidak error import '../bidan/dashboard_bidan.dart'; const String baseUrl = "http://ta.myhost.id/E31230549/mposyandu_api"; class Dusun { final int id; final String nama; Dusun({required this.id, required this.nama}); } class Kader { final int id; final String nama; final int dunsunId; Kader({required this.id, required this.nama, required this.dunsunId}); } class DataJadwalPosyanduPage extends StatefulWidget { const DataJadwalPosyanduPage({super.key}); @override State createState() => _DataJadwalPosyanduPageState(); } class _DataJadwalPosyanduPageState extends State { final _tanggalController = TextEditingController(); final _jamMulaiController = TextEditingController(); final _jamSelesaiController = TextEditingController(); final _lokasiController = TextEditingController(); final _keteranganController = TextEditingController(); List daftarDusun = []; List daftarKader = []; List dunsunDipilih = []; List> _dataJadwalLokal = []; bool _isLoading = false; @override void initState() { super.initState(); initializeDateFormatting('id', null); loadDusun(); loadLatestJadwal(); } String _formatTanggal(String? dateStr) { if (dateStr == null || dateStr == "-" || dateStr.isEmpty) return "-"; try { DateTime dt = DateTime.parse(dateStr); return DateFormat('dd MMMM yyyy', 'id').format(dt); } catch (e) { return dateStr; } } Future loadLatestJadwal() async { try { final res = await http .get(Uri.parse("$baseUrl/jadwal_posyandu/get_latest_jadwal.php")); if (res.statusCode == 200) { final responseData = jsonDecode(res.body); if (responseData['success'] == true && responseData['data'] != null) { setState(() { if (responseData['data'] is List) { _dataJadwalLokal = List>.from(responseData['data']); } else { _dataJadwalLokal = [responseData['data']]; } }); } } } catch (e) { debugPrint("Error load latest jadwal: $e"); } } Future loadDusun() async { try { final res = await http.get(Uri.parse("$baseUrl/dusun/get_dusun.php")); if (res.statusCode == 200) { final responseData = jsonDecode(res.body); final List listData = responseData['data']; setState(() { daftarDusun = listData .map((d) => Dusun( id: int.parse(d['id'].toString()), nama: d['nama_dusun'].toString(), )) .toList(); }); } } catch (e) { debugPrint("Error load dunsun: $e"); } } Future loadKaderByDusun() async { if (dunsunDipilih.isEmpty) { setState(() => daftarKader.clear()); return; } final String ids = dunsunDipilih.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(), dunsunId: int.parse(k['dusun_id'].toString()), )) .toList(); }); } } catch (e) { debugPrint("Error load kader: $e"); } } Future _simpanJadwal() async { if (_tanggalController.text.isEmpty || dunsunDipilih.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Lengkapi Tanggal dan Dusun!", style: GoogleFonts.poppins(fontSize: 12))), ); return; } setState(() => _isLoading = true); try { final response = await http.post( Uri.parse("$baseUrl/jadwal_posyandu/tambah_jadwal.php"), body: { "bidan_id": "1", "tanggal": _tanggalController.text, "jam_mulai": _jamMulaiController.text, "jam_selesai": _jamSelesaiController.text, "lokasi": _lokasiController.text, "keterangan": _keteranganController.text, "dusun_ids": dunsunDipilih.join(","), }, ); final result = jsonDecode(response.body); if (result['success']) { loadLatestJadwal(); setState(() { _tanggalController.clear(); _jamMulaiController.clear(); _jamSelesaiController.clear(); _lokasiController.clear(); _keteranganController.clear(); dunsunDipilih.clear(); daftarKader.clear(); }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Jadwal Berhasil Disimpan", style: GoogleFonts.poppins(fontSize: 12))), ); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Terjadi kesalahan: $e", style: GoogleFonts.poppins(fontSize: 12))), ); } finally { setState(() => _isLoading = false); } } InputDecoration _input(String label) => InputDecoration( labelText: label, labelStyle: GoogleFonts.poppins(fontSize: 12), border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), ); Widget rowData(String label, String value) { return Padding( padding: const EdgeInsets.only(bottom: 6), child: Table( columnWidths: const { 0: FixedColumnWidth(140), 1: FixedColumnWidth(15), 2: FlexColumnWidth() }, children: [ TableRow(children: [ Text(label, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500)), Text(" : ", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500)), Text(value.isEmpty ? "-" : value, style: GoogleFonts.poppins(fontSize: 12)), ]) ], ), ); } @override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) async { if (didPop) return; // ✅ FIX FINAL: selalu kembali ke Dashboard Bidan Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (_) => const DashboardBidanPage(), ), (route) => false, ); }, child: MainLayout( title: "", drawer: const BidanDrawer(), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: [ // FORM TAMBAH 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("Tambah Jadwal", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold))), const SizedBox(height: 15), TextField( controller: _tanggalController, style: GoogleFonts.poppins(fontSize: 12), readOnly: true, onTap: () 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')}"); }, 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: () async { TimeOfDay? t = await showTimePicker( context: context, initialTime: TimeOfDay.now()); if (t != null) setState(() => _jamMulaiController.text = "${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}"); }, decoration: _input("Jam Mulai"))), const SizedBox(width: 8), Expanded( child: TextField( controller: _jamSelesaiController, style: GoogleFonts.poppins(fontSize: 12), readOnly: true, onTap: () async { TimeOfDay? t = await showTimePicker( context: context, initialTime: TimeOfDay.now()); if (t != null) setState(() => _jamSelesaiController .text = "${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}"); }, decoration: _input("Jam Selesai"))), ], ), 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)), const Divider(), ...daftarDusun.map((d) => CheckboxListTile( visualDensity: VisualDensity.compact, contentPadding: EdgeInsets.zero, title: Text(d.nama, style: GoogleFonts.poppins(fontSize: 12)), value: dunsunDipilih.contains(d.id), onChanged: (val) { setState(() { if (val == true) { dunsunDipilih.add(d.id); } else { dunsunDipilih.remove(d.id); } }); loadKaderByDusun(); }, )), const SizedBox(height: 10), Text("Kader Otomatis Bertugas:", 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, runSpacing: 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: 20), // BUTTON SIMPAN JADWAL SizedBox( width: double.infinity, child: OutlinedButton( onPressed: _isLoading ? null : _simpanJadwal, style: OutlinedButton.styleFrom( side: const BorderSide(color: Colors.blue), padding: const EdgeInsets.symmetric(vertical: 12), shape: const StadiumBorder()), child: _isLoading ? const SizedBox( height: 18, width: 18, child: CircularProgressIndicator( color: Colors.blue, strokeWidth: 2)) : Text( "Simpan Jadwal", style: GoogleFonts.poppins( color: Colors.blue, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), ), ], ), ), ), const SizedBox(height: 15), // BUTTON RIWAYAT Container( constraints: const BoxConstraints(maxWidth: 500), child: ElevatedButton.icon( onPressed: () => Navigator.push( context, MaterialPageRoute( builder: (context) => const RiwayatJadwalPosyanduPage())), icon: const Icon(Icons.history, size: 18, color: Colors.black87), label: Text("Riwayat Jadwal Posyandu", style: GoogleFonts.poppins( color: Colors.black87, fontSize: 12)), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFF1F1F1), minimumSize: const Size(double.infinity, 45), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10))), ), ), const SizedBox(height: 20), // CARD HASIL if (_dataJadwalLokal.isNotEmpty) ...[ Container( constraints: const BoxConstraints(maxWidth: 500), child: Column( children: [ Align( alignment: Alignment.centerLeft, child: Text("Jadwal Posyandu Terbaru", style: GoogleFonts.poppins( fontWeight: FontWeight.bold, fontSize: 12, color: Colors.green))), const SizedBox(height: 8), ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: _dataJadwalLokal.length, itemBuilder: (context, index) { final item = _dataJadwalLokal[index]; return Card( color: Colors.white, elevation: 3, margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Row( children: [ const Icon(Icons.calendar_month, size: 18, color: Colors.blue), const SizedBox(width: 8), Text("Data Jadwal Posyandu", style: GoogleFonts.poppins( fontWeight: FontWeight.bold, fontSize: 12)), const Spacer(), // BUTTON EDIT OutlinedButton.icon( onPressed: () async { bool? refresh = await Navigator.push( context, MaterialPageRoute( builder: (context) => EditJadwalPage( data: item))); if (refresh == true) loadLatestJadwal(); }, icon: const Icon(Icons.edit, size: 16, color: Colors.orange), label: Text("Edit", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.orange)), style: OutlinedButton.styleFrom( side: const BorderSide( color: Colors.orange), padding: const EdgeInsets.symmetric( horizontal: 12), shape: const StadiumBorder()), ), ], ), const Divider(), rowData("Tanggal Posyandu", _formatTanggal(item['tanggal'])), rowData("Jam", "${item['jam_mulai']} - ${item['jam_selesai']}"), rowData("Lokasi", item['lokasi'] ?? "-"), rowData("Dusun", item['dusun'] ?? "-"), rowData( "Kader Bertugas", item['kader'] ?? "-"), rowData( "Keterangan", item['keterangan'] ?? "-"), ], ), ), ); }, ), ], ), ), ] ], ), ), ), ); } }