MIF_E31230549/lib/pages/petugas.dart

372 lines
14 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:google_fonts/google_fonts.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../layout/main_layout.dart';
import '../pages/petugas_drawer.dart';
import '../pages/login_page.dart';
import '../pages/tambah_petugas.dart';
import '../pages/edit_petugas.dart';
class DataPetugasPage extends StatefulWidget {
const DataPetugasPage({super.key});
@override
State<DataPetugasPage> createState() => _DataPetugasPageState();
}
class _DataPetugasPageState extends State<DataPetugasPage> {
final TextEditingController _searchController = TextEditingController();
Timer? _debounce;
int _rowsPerPage = 10;
int _currentPage = 0;
int _totalData = 0;
List<Map<String, dynamic>> _allData = [];
bool _isLoading = false;
bool _isAuthorized = false;
final String baseUrl = "http://ta.myhost.id/E31230549/mposyandu_api/petugas/get_users.php";
final String deleteUrl =
"http://ta.myhost.id/E31230549/mposyandu_api/petugas/delete_petugas.php";
@override
void initState() {
super.initState();
checkLogin();
}
@override
void dispose() {
_debounce?.cancel();
_searchController.dispose();
super.dispose();
}
Future<void> checkLogin() async {
final prefs = await SharedPreferences.getInstance();
final isLogin = prefs.getBool("isLogin") ?? false;
if (!isLogin) {
if (!mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const LoginPage(fromGuard: true)),
(route) => false,
);
return;
}
setState(() => _isAuthorized = true);
await fetchUsers();
}
Future<void> fetchUsers() async {
if (!_isAuthorized || !mounted) return;
setState(() => _isLoading = true);
try {
final url = Uri.parse(baseUrl).replace(queryParameters: {
"page": (_currentPage + 1).toString(),
"limit": _rowsPerPage.toString(),
"search": _searchController.text.trim(),
});
final response = await http.get(url);
if (response.statusCode == 200) {
final decoded = json.decode(response.body);
if (decoded["success"] == true) {
setState(() {
_allData = List<Map<String, dynamic>>.from(decoded["data"] ?? []);
_totalData = decoded["total"] ?? 0;
});
}
}
} catch (e) {
debugPrint("ERROR FETCH: $e");
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
// --- LOGIKA SEMBUNYIKAN DESA/DUSUN BERDASARKAN ROLE ---
void _showDetailDialog(Map<String, dynamic> data) {
// Ambil role dan ubah ke lowercase agar pengecekan lebih akurat
String role = (data["role"] ?? "").toString().toLowerCase();
bool isStaff = role == "admin" || role == "bidan";
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Detail Petugas",
style: GoogleFonts.poppins(
fontWeight: FontWeight.bold, fontSize: 16)),
IconButton(
icon: const Icon(Icons.close, color: Colors.red),
onPressed: () => Navigator.pop(context)),
],
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_detailItem("Email", data["email"]),
// Tampilkan Desa & Dusun HANYA jika role BUKAN admin/bidan
if (!isStaff) ...[
_detailItem("Desa", data["nama_desa"]),
_detailItem("Dusun", data["nama_dusun"]),
],
_detailItem("No HP", data["no_hp"]),
],
),
),
),
);
}
Widget _detailItem(String label, dynamic value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style:
GoogleFonts.poppins(fontSize: 10, color: Colors.grey[600])),
const SizedBox(height: 2),
Text(value?.toString() ?? "-",
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.black87)),
const Divider(thickness: 0.5),
],
),
);
}
Future<void> _deletePetugas(String id) async {
final confirm = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text("Konfirmasi",
style:
GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.bold)),
content: const Text("Yakin ingin menghapus petugas ini?"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text("Batal")),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Text("Hapus", style: TextStyle(color: Colors.white)),
),
],
),
);
if (confirm != true) return;
try {
final res = await http.post(Uri.parse(deleteUrl), body: {"id": id});
if (json.decode(res.body)["success"]) fetchUsers();
} catch (e) {
debugPrint("Delete error: $e");
}
}
@override
Widget build(BuildContext context) {
int totalPages = (_totalData / _rowsPerPage).ceil();
if (totalPages == 0) totalPages = 1;
return MainLayout(
title: "",
drawer: const DrawerPetugas(),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text("Data Petugas",
style: GoogleFonts.poppins(
fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: TextField(
controller: _searchController,
style: GoogleFonts.poppins(fontSize: 13),
decoration: InputDecoration(
hintText: "Cari nama...",
prefixIcon: const Icon(Icons.search, size: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
contentPadding:
const EdgeInsets.symmetric(horizontal: 10),
),
onChanged: (v) {
_debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
_currentPage = 0;
fetchUsers();
});
},
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () async {
final res = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const TambahPetugasPage()));
if (res == true) fetchUsers();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: const EdgeInsets.all(12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
child: const Icon(Icons.add, color: Colors.white),
)
],
),
const SizedBox(height: 16),
Expanded(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: _allData.isEmpty
? const Center(child: Text("Data tidak ditemukan"))
: ListView.builder(
itemCount: _allData.length,
itemBuilder: (context, index) {
final item = _allData[index];
return Card(
elevation: 0,
margin: const EdgeInsets.only(bottom: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: Colors.grey.shade300, width: 1),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(item["nama"] ?? "",
style: GoogleFonts.poppins(
fontWeight: FontWeight.bold,
fontSize: 14)),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color:
Colors.blue.withOpacity(0.1),
borderRadius:
BorderRadius.circular(6),
),
child: Text(item["role"] ?? "",
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.blue,
fontWeight:
FontWeight.w600)),
),
],
),
),
_actionButton(Icons.visibility, Colors.blue,
() => _showDetailDialog(item)),
const SizedBox(width: 8),
_actionButton(Icons.edit, Colors.orange,
() async {
final res = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
EditPetugasPage(data: item)));
if (res == true) fetchUsers();
}),
const SizedBox(width: 8),
_actionButton(
Icons.delete,
Colors.red,
() => _deletePetugas(
item["id"].toString())),
],
),
),
);
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Halaman ${_currentPage + 1} dari $totalPages",
style: GoogleFonts.poppins(fontSize: 12)),
Row(
children: [
IconButton(
icon: const Icon(Icons.chevron_left),
onPressed: _currentPage == 0
? null
: () {
setState(() => _currentPage--);
fetchUsers();
},
),
IconButton(
icon: const Icon(Icons.chevron_right),
onPressed: _currentPage >= totalPages - 1
? null
: () {
setState(() => _currentPage++);
fetchUsers();
},
),
],
)
],
)
],
),
),
);
}
Widget _actionButton(IconData icon, Color color, VoidCallback onTap) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color, width: 1.5),
),
child: Icon(icon, color: color, size: 18),
),
),
);
}
}