MIF_E31230549/lib/pages/data_dusun.dart

314 lines
11 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_dusun.dart';
import '../pages/edit_dusun.dart';
class DataDusunPage extends StatefulWidget {
const DataDusunPage({super.key});
@override
State<DataDusunPage> createState() => _DataDusunPageState();
}
class _DataDusunPageState extends State<DataDusunPage> {
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/dusun/get_dusun.php";
final String deleteUrl =
"http://ta.myhost.id/E31230549/mposyandu_api/dusun/delete_dusun.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 fetchDusun();
}
Future<void> fetchDusun() 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 DUSUN: $e");
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
Future<void> _deleteDusun(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: Text("Yakin ingin menghapus dusun ini?",
style: GoogleFonts.poppins(fontSize: 13)),
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"] == true) {
fetchDusun();
}
} 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: [
// Judul Halaman
Text("Data Dusun",
style: GoogleFonts.poppins(
fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
// Search + Tambah
Row(
children: [
Expanded(
child: TextField(
controller: _searchController,
style: GoogleFonts.poppins(fontSize: 13),
decoration: InputDecoration(
hintText: "Cari nama dusun...",
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;
fetchDusun();
});
},
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () async {
final res = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const TambahDusunPage()));
if (res == true) fetchDusun();
},
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),
// Card List Area
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(16),
child: Row(
children: [
// Konten Dusun
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text("Nama Dusun",
style: GoogleFonts.poppins(
fontSize: 10,
color: Colors.grey)),
Text(item["nama_dusun"] ?? "",
style: GoogleFonts.poppins(
fontWeight: FontWeight.bold,
fontSize: 15)),
const SizedBox(height: 4),
],
),
),
// Tombol Aksi (Edit & Hapus)
_actionButton(Icons.edit, Colors.orange,
() async {
final res = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
EditDusunPage(data: item)));
if (res == true) fetchDusun();
}),
const SizedBox(width: 8),
_actionButton(
Icons.delete,
Colors.red,
() => _deleteDusun(
item["id"].toString())),
],
),
),
);
},
),
),
// Pagination Area
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--);
fetchDusun();
},
),
IconButton(
icon: const Icon(Icons.chevron_right),
onPressed: _currentPage >= totalPages - 1
? null
: () {
setState(() => _currentPage++);
fetchDusun();
},
),
],
)
],
)
],
),
),
);
}
// Widget Tombol Aksi dengan Border sesuai gaya Petugas/Desa
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),
),
),
);
}
}