MIF_E31230549/lib/bidan/imunisasi.dart

434 lines
18 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import '../layout/main_layout.dart';
import 'bidan_drawer.dart';
// Import file crud
import '../bidan/crud_imunisasi/tambah_imunisasi.dart';
import '../bidan/crud_imunisasi/edit_imunisasi.dart';
import '../bidan/crud_imunisasi/data_imunisasi_balita.dart';
// Import Dashboard Bidan agar navigasi berfungsi
import '../bidan/dashboard_bidan.dart';
class DataImunisasiPage extends StatefulWidget {
const DataImunisasiPage({super.key});
@override
State<DataImunisasiPage> createState() => _DataImunisasiPageState();
}
class _DataImunisasiPageState extends State<DataImunisasiPage> {
List masterImunisasi = [];
String _searchQuery = "";
bool isLoading = true;
// Variabel Pagination
int _currentPage = 0;
final int _rowsPerPage = 10;
// Endpoint API
final String baseMaster =
"http://ta.myhost.id/E31230549/mposyandu_api/imunisasi/get_master_imunisasi.php";
final String urlDelete =
"http://ta.myhost.id/E31230549/mposyandu_api/imunisasi/hapus_imunisasi.php";
@override
void initState() {
super.initState();
fetchMasterData();
}
Future fetchMasterData() async {
try {
final resMaster = await http.get(Uri.parse(baseMaster));
if (mounted) {
setState(() {
masterImunisasi = jsonDecode(resMaster.body)["data"] ?? [];
isLoading = false;
});
}
} catch (e) {
if (mounted) setState(() => isLoading = false);
}
}
Future _deleteData(String id) async {
try {
final response = await http.post(
Uri.parse(urlDelete),
body: {"id": id},
);
final data = jsonDecode(response.body);
if (data['status'] == 'success') {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(data['message'] ?? "Data berhasil dihapus"),
backgroundColor: Colors.blue,
),
);
fetchMasterData();
} else {
throw data['message'] ?? "Gagal menghapus data";
}
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Kesalahan: $e"), backgroundColor: Colors.red),
);
}
}
void _showDeleteDialog(Map item) {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
title: Text("Konfirmasi Hapus",
style:
GoogleFonts.poppins(fontWeight: FontWeight.bold, fontSize: 16)),
content: Text(
"Apakah Anda yakin ingin menghapus '${item['nama_imunisasi']}'?",
style: GoogleFonts.poppins(fontSize: 13)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child:
Text("Batal", style: GoogleFonts.poppins(color: Colors.grey)),
),
OutlinedButton(
onPressed: () {
Navigator.pop(context);
_deleteData(item['id'].toString());
},
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.red),
shape: const StadiumBorder(),
),
child: Text("Hapus",
style: GoogleFonts.poppins(
color: Colors.red, fontWeight: FontWeight.bold)),
),
],
),
);
}
List get _filteredMaster {
return masterImunisasi.where((item) {
final nama = item["nama_imunisasi"].toString().toLowerCase();
final ket = item["keterangan"].toString().toLowerCase();
final query = _searchQuery.toLowerCase();
return nama.contains(query) || ket.contains(query);
}).toList();
}
List get _paginatedData {
final start = _currentPage * _rowsPerPage;
final end = start + _rowsPerPage;
if (start >= _filteredMaster.length) return [];
return _filteredMaster.sublist(
start,
end > _filteredMaster.length ? _filteredMaster.length : end,
);
}
@override
Widget build(BuildContext context) {
final totalPages = (_filteredMaster.length / _rowsPerPage).ceil() == 0
? 1
: (_filteredMaster.length / _rowsPerPage).ceil();
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) async {
if (didPop) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const DashboardBidanPage()),
(route) => false,
);
},
child: MainLayout(
title: "",
drawer: const BidanDrawer(),
body: isLoading
? const Center(child: CircularProgressIndicator())
: Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
"Data Jenis Imunisasi",
style: GoogleFonts.poppins(
fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 15),
// --- INPUT CARI ---
Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 850),
child: TextField(
onChanged: (value) => setState(() {
_searchQuery = value;
_currentPage = 0;
}),
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
hintText: "Cari nama atau keterangan...",
hintStyle: GoogleFonts.poppins(fontSize: 12),
prefixIcon:
const Icon(Icons.search, size: 20),
filled: true,
fillColor: Colors.grey[50],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
BorderSide(color: Colors.grey[300]!),
),
contentPadding:
const EdgeInsets.symmetric(vertical: 8),
),
),
),
),
const SizedBox(height: 20),
// --- TABEL DENGAN DATATABLE ---
Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 850),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
headingRowColor:
MaterialStateProperty.all(Colors.blue),
columnSpacing: 25,
columns: [
_buildDataColumn("Nama Imunisasi"),
_buildDataColumn("Min"),
_buildDataColumn("Max"),
_buildDataColumn("Keterangan"),
_buildDataColumn("Aksi"),
],
rows: _paginatedData.isEmpty
? [
DataRow(cells: [
DataCell(Text(
"Data tidak ditemukan",
style: GoogleFonts.poppins(
fontSize: 12))),
const DataCell(SizedBox()),
const DataCell(SizedBox()),
const DataCell(SizedBox()),
const DataCell(SizedBox()),
])
]
: _paginatedData.map((item) {
return DataRow(cells: [
DataCell(Text(
item["nama_imunisasi"] ?? "-",
style: GoogleFonts.poppins(
fontSize: 12))),
DataCell(Text(
"${item["usia_min"]} Bln",
style: GoogleFonts.poppins(
fontSize: 12))),
DataCell(Text(
"${item["usia_max"]} Bln",
style: GoogleFonts.poppins(
fontSize: 12))),
DataCell(
Container(
constraints:
const BoxConstraints(
maxWidth: 180),
child: Text(
item["keterangan"] ?? "-",
style: GoogleFonts.poppins(
fontSize: 12),
overflow:
TextOverflow.ellipsis,
maxLines: 2,
),
),
),
DataCell(
Row(
children: [
IconButton(
icon: const Icon(
Icons.edit,
color: Colors.orange,
size: 20),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
EditImunisasiPage(
data: item),
),
).then((value) {
if (value == true)
fetchMasterData();
});
},
),
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.red,
size: 20),
onPressed: () =>
_showDeleteDialog(
item),
),
],
),
),
]);
}).toList(),
),
),
),
),
),
const SizedBox(height: 25),
// --- BUTTON ACTION ---
Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 850),
child: Row(
children: [
Expanded(
child: _actionBtnLonjong(
Icons.add, "Tambah Jenis", Colors.blue,
() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const TambahImunisasiPage(),
),
).then((_) => fetchMasterData());
}),
),
const SizedBox(width: 12),
Expanded(
child: _actionBtnLonjong(
Icons.visibility,
"Imunisasi Balita",
Colors.deepPurple, () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const DataImunisasiBalitaPage(),
),
);
}),
),
],
),
),
),
],
),
),
),
// --- PAGINATION ---
Container(
width: double.infinity,
color: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey[200]!)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Halaman ${_currentPage + 1} dari $totalPages",
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w500)),
Row(
children: [
IconButton(
icon: const Icon(Icons.chevron_left),
onPressed: _currentPage == 0
? null
: () => setState(() => _currentPage--),
),
IconButton(
icon: const Icon(Icons.chevron_right),
onPressed: _currentPage >= totalPages - 1
? null
: () => setState(() => _currentPage++),
),
],
),
],
),
),
],
),
),
);
}
DataColumn _buildDataColumn(String label) {
return DataColumn(
label: Text(
label,
style: GoogleFonts.poppins(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13,
),
),
);
}
Widget _actionBtnLonjong(
IconData icon, String label, Color color, VoidCallback onTap) {
return OutlinedButton.icon(
onPressed: onTap,
icon: Icon(icon, size: 18, color: color),
style: OutlinedButton.styleFrom(
side: BorderSide(color: color, width: 1.5),
shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 14),
),
label: Text(
label,
style: GoogleFonts.poppins(
fontSize: 12, color: color, fontWeight: FontWeight.bold),
),
);
}
}