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 data; const EditJadwalPage({ super.key, required this.data, }); @override State createState() => _EditJadwalPageState(); } class _EditJadwalPageState extends State { late TextEditingController _tanggalController; late TextEditingController _jamMulaiController; late TextEditingController _jamSelesaiController; late TextEditingController _lokasiController; late TextEditingController _keteranganController; List daftarDusun = []; List daftarKader = []; List 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 _initialLoad() async { await loadDusun(); if (dusunDipilih.isNotEmpty) { await loadKaderByDusun(); } } 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); setState(() { daftarDusun = responseData['data'] ?? []; }); } } catch (e) { debugPrint("Error load dusun: $e"); } } Future 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 _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 _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 _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), ), ), ), ], ), ), ), ), ); } }