import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import '../layout/main_layout.dart'; import '../bidan/bidan_drawer.dart'; // Import Dashboard Bidan agar navigasi PopScope berfungsi import '../bidan/dashboard_bidan.dart'; class JadwalAncPage extends StatefulWidget { const JadwalAncPage({super.key}); @override State createState() => _JadwalAncPageState(); } class _JadwalAncPageState extends State { final TextEditingController _dateController = TextEditingController(); final TextEditingController _timeController = TextEditingController(); final TextEditingController _locationController = TextEditingController(); final TextEditingController _noteController = TextEditingController(); final TextEditingController _searchController = TextEditingController(); List _allJadwal = []; List _filteredJadwal = []; bool _isLoading = false; bool _isFetching = true; int _currentPage = 1; final int _itemsPerPage = 10; @override void initState() { super.initState(); _loadData(); } Future _loadData() async { setState(() => _isFetching = true); try { final response = await http.get(Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/jadwal_anc/get_jadwal_anc.php")); if (response.statusCode == 200) { final data = jsonDecode(response.body); setState(() { _allJadwal = data['status'] == 'success' ? data['data'] : []; _filteredJadwal = _allJadwal; _isFetching = false; }); } } catch (e) { setState(() => _isFetching = false); } } void _filterJadwal(String query) { setState(() { _filteredJadwal = _allJadwal.where((item) { final trimester = item['trimester_target'].toString().toLowerCase(); final tanggal = item['tanggal_periksa'].toString().toLowerCase(); return trimester.contains(query.toLowerCase()) || tanggal.contains(query.toLowerCase()); }).toList(); _currentPage = 1; }); } Future _hapusJadwal(String id) async { final response = await http.post( Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/jadwal_anc/hapus_jadwal_anc.php"), body: {"id_jadwal": id}, ); final data = jsonDecode(response.body); if (data['status'] == 'success') { _loadData(); _showSnackBar("Jadwal berhasil dihapus", Colors.red); } } Future _simpanAtauUpdateJadwal(String trimester, {String? idJadwal}) async { if (_dateController.text.isEmpty || _locationController.text.isEmpty || _timeController.text.isEmpty) { _showErrorSnackBar("Harap isi semua field!"); return; } setState(() => _isLoading = true); String url = idJadwal == null ? "http://ta.myhost.id/E31230549/mposyandu_api/jadwal_anc/tambah_jadwal_anc.php" : "http://ta.myhost.id/E31230549/mposyandu_api/jadwal_anc/update_jadwal_anc.php"; Map body = { "trimester_target": trimester, "tanggal_periksa": _dateController.text, "jam_periksa": _timeController.text, "lokasi": _locationController.text, "catatan": _noteController.text, }; if (idJadwal != null) body["id_jadwal"] = idJadwal; try { final response = await http.post(Uri.parse(url), body: body); final data = jsonDecode(response.body); if (data['status'] == 'success') { _clearForm(); if (!mounted) return; Navigator.pop(context); _loadData(); _showSnackBar(data['message'], Colors.green); } } catch (e) { _showErrorSnackBar("Error: $e"); } finally { setState(() => _isLoading = false); } } void _tampilkanFormJadwal(String trimester, {Map? editItem}) { if (editItem != null) { _dateController.text = editItem['tanggal_periksa']; _timeController.text = editItem['jam_periksa']; _locationController.text = editItem['lokasi']; _noteController.text = editItem['catatan_bidan'] ?? ""; } else { _dateController.text = DateFormat('yyyy-MM-dd').format(DateTime.now()); _timeController.clear(); _locationController.clear(); _noteController.clear(); } showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => Center( child: Container( constraints: const BoxConstraints(maxWidth: 450), margin: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20)), padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom + 20, left: 20, right: 20, top: 20), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( editItem == null ? "Tambah Jadwal $trimester" : "Edit Jadwal $trimester", style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.bold, color: MainLayout.mainColor)), const SizedBox(height: 15), _buildTextField( "Tanggal", Icons.calendar_today, _dateController, isDate: true), const SizedBox(height: 10), _buildTextField("Jam", Icons.access_time, _timeController, isTime: true), const SizedBox(height: 10), _buildTextField( "Lokasi", Icons.location_on, _locationController), const SizedBox(height: 10), _buildTextField("Catatan", Icons.event_note, _noteController, maxLines: 2), const SizedBox(height: 20), Row( children: [ Expanded( child: OutlinedButton( onPressed: () { _clearForm(); Navigator.pop(context); }, child: const Text("Batal"))), const SizedBox(width: 10), Expanded( flex: 2, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: MainLayout.mainColor), onPressed: _isLoading ? null : () => _simpanAtauUpdateJadwal(trimester, idJadwal: editItem?['id_jadwal']), child: _isLoading ? const CircularProgressIndicator( color: Colors.white) : const Text("Simpan", style: TextStyle(color: Colors.white)))), ], ), ], ), ), ), ), ); } void _clearForm() { _dateController.clear(); _timeController.clear(); _locationController.clear(); _noteController.clear(); } @override Widget build(BuildContext context) { int totalPages = (_filteredJadwal.length / _itemsPerPage).ceil(); if (totalPages == 0) totalPages = 1; final startIndex = (_currentPage - 1) * _itemsPerPage; final pagedJadwal = _filteredJadwal.skip(startIndex).take(_itemsPerPage).toList(); 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: Scaffold( appBar: AppBar( backgroundColor: MainLayout.mainColor, elevation: 0, iconTheme: const IconThemeData(color: Colors.white)), drawer: const BidanDrawer(), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( children: [ _headerSection(), const SizedBox(height: 20), Center( child: Wrap( spacing: 15, runSpacing: 15, children: [ _buildCompactControlCard( "Trimester 1", Colors.blue.shade600, Icons.looks_one), _buildCompactControlCard( "Trimester 2", Colors.teal.shade600, Icons.looks_two), _buildCompactControlCard( "Trimester 3", Colors.orange.shade700, Icons.looks_3), ], ), ), const SizedBox(height: 40), Text("Data Jadwal", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 20), SizedBox( width: double.infinity, child: TextField( controller: _searchController, onChanged: _filterJadwal, style: GoogleFonts.poppins(color: Colors.black, fontSize: 14), decoration: InputDecoration( hintText: "Cari Trimester atau Tanggal...", hintStyle: GoogleFonts.poppins(fontSize: 13, color: Colors.grey), prefixIcon: Icon(Icons.search, color: Colors.grey.shade700), filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300)), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300)), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide( color: MainLayout.mainColor, width: 2)), ), ), ), const SizedBox(height: 20), _isFetching ? const Center(child: CircularProgressIndicator()) : ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: pagedJadwal.length, itemBuilder: (context, index) => _buildResultCard(pagedJadwal[index]), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Halaman $_currentPage dari $totalPages", style: GoogleFonts.poppins( fontSize: 12, color: Colors.black)), Row( children: [ IconButton( icon: const Icon(Icons.arrow_back_ios, size: 16, color: Colors.black), onPressed: _currentPage > 1 ? () => setState(() => _currentPage--) : null, ), IconButton( icon: const Icon(Icons.arrow_forward_ios, size: 16, color: Colors.black), onPressed: _currentPage < totalPages ? () => setState(() => _currentPage++) : null, ), ], ), ], ), ], ), ), ), ); } Widget _buildResultCard(Map item) { return Card( margin: const EdgeInsets.only(bottom: 12), color: Colors.white, elevation: 0.5, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), side: BorderSide(color: Colors.grey.shade200, width: 1), ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(item['trimester_target'], style: GoogleFonts.poppins( fontWeight: FontWeight.bold, color: MainLayout.mainColor, fontSize: 15)), Row( children: [ IconButton( icon: const Icon(Icons.edit, color: Colors.orange, size: 20), onPressed: () => _tampilkanFormJadwal( item['trimester_target'], editItem: item)), IconButton( icon: const Icon(Icons.delete, color: Colors.red, size: 20), onPressed: () => _showDeleteDialog(item['id_jadwal'].toString()), ), ], ), ], ), const Divider(), _infoRow("Tanggal", item['tanggal_periksa']), _infoRow("Jam", item['jam_periksa']), _infoRow("Lokasi", item['lokasi']), if (item['catatan_bidan'] != null && item['catatan_bidan'] != "") _infoRow("Catatan", item['catatan_bidan']), ], ), ), ); } Widget _infoRow(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 70, child: Text(label, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black)), ), Text(": ", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black)), Expanded( child: Text(value, style: GoogleFonts.poppins(fontSize: 12, color: Colors.black)), ), ], ), ); } void _showDeleteDialog(String id) { showDialog( context: context, builder: (context) => AlertDialog( title: Text("Hapus Jadwal?", style: GoogleFonts.poppins(fontWeight: FontWeight.bold)), content: Text("Data ini akan dihapus permanen.", style: GoogleFonts.poppins()), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text("Batal")), TextButton( onPressed: () { Navigator.pop(context); _hapusJadwal(id); }, child: const Text("Hapus", style: TextStyle(color: Colors.red))), ], ), ); } Widget _buildCompactControlCard( String trimester, Color color, IconData icon) { return Container( width: 140, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: color.withOpacity(0.3))), child: Column( children: [ Icon(icon, color: color, size: 28), const SizedBox(height: 5), Text(trimester, style: GoogleFonts.poppins( fontSize: 11, fontWeight: FontWeight.bold, color: Colors.black)), const SizedBox(height: 10), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: color, padding: EdgeInsets.zero, minimumSize: const Size(80, 30)), onPressed: () => _tampilkanFormJadwal(trimester), child: const Text("Buat", style: TextStyle( fontSize: 11, color: Colors.white, fontWeight: FontWeight.bold)), ) ], ), ); } Widget _headerSection() { return Column(children: [ Center( child: Text("Manajemen Jadwal ANC", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black))), Center( child: Text("Kelola pemeriksaan rutin berdasarkan trimester.", style: GoogleFonts.poppins(fontSize: 12, color: Colors.black54))), ]); } Widget _buildTextField( String label, IconData icon, TextEditingController controller, {bool isDate = false, bool isTime = false, int maxLines = 1}) { return TextField( controller: controller, readOnly: isDate || isTime, maxLines: maxLines, style: GoogleFonts.poppins(color: Colors.black, fontSize: 14), decoration: InputDecoration( labelText: label, labelStyle: GoogleFonts.poppins(fontSize: 13, color: Colors.black87), prefixIcon: Icon(icon, size: 18, color: MainLayout.mainColor), filled: true, fillColor: Colors.grey.shade50, border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade300)), ), onTap: () { if (isDate) _selectDate(context); if (isTime) _selectTime(context); }, ); } Future _selectDate(BuildContext context) async { DateTime? picked = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime(2100)); if (picked != null) setState( () => _dateController.text = DateFormat('yyyy-MM-dd').format(picked)); } Future _selectTime(BuildContext context) async { TimeOfDay? picked = await showTimePicker(context: context, initialTime: TimeOfDay.now()); if (picked != null) setState(() => _timeController.text = picked.format(context)); } void _showSnackBar(String msg, Color color) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(msg), backgroundColor: color, behavior: SnackBarBehavior.floating)); } void _showErrorSnackBar(String msg) => _showSnackBar(msg, Colors.red); }