MIF_E31230549/lib/kader/crud_kehadiran/riwayat_kehadiran.dart

369 lines
14 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 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
class RiwayatKehadiranPage extends StatefulWidget {
final String balitaId;
final String namaBalita;
const RiwayatKehadiranPage({
super.key,
required this.balitaId,
required this.namaBalita,
});
@override
State<RiwayatKehadiranPage> createState() => _RiwayatKehadiranPageState();
}
class _RiwayatKehadiranPageState extends State<RiwayatKehadiranPage> {
List _riwayatOriginal = [];
List _riwayatFiltered = [];
bool _isLoading = true;
final TextEditingController _searchController = TextEditingController();
/// ================= PAGINATION =================
int _currentPage = 0;
final int _rowsPerPage = 5;
List get _paginatedData {
if (_riwayatFiltered.isEmpty) return [];
final start = _currentPage * _rowsPerPage;
final end = start + _rowsPerPage;
if (start >= _riwayatFiltered.length) return [];
return _riwayatFiltered.sublist(
start,
end > _riwayatFiltered.length ? _riwayatFiltered.length : end,
);
}
@override
void initState() {
super.initState();
initializeDateFormatting('id_ID', null);
_fetchRiwayat();
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
String _formatTanggal(String? tanggal) {
if (tanggal == null || tanggal == "" || tanggal == "0000-00-00") {
return "-";
}
try {
DateTime dt = DateTime.parse(tanggal);
return DateFormat('d MMMM yyyy', 'id_ID').format(dt);
} catch (e) {
return tanggal;
}
}
void _filterData(String query) {
setState(() {
if (query.isEmpty) {
_riwayatFiltered = _riwayatOriginal;
} else {
_riwayatFiltered = _riwayatOriginal.where((item) {
String tglFormatted =
_formatTanggal(item["tanggal_posyandu"]).toLowerCase();
String tglDatabase =
(item["tanggal_posyandu"] ?? "").toString().toLowerCase();
return tglFormatted.contains(query.toLowerCase()) ||
tglDatabase.contains(query.toLowerCase());
}).toList();
}
_currentPage = 0;
});
}
Future<void> _fetchRiwayat() async {
try {
final response = await http.get(
Uri.parse(
"http://ta.myhost.id/E31230549/mposyandu_api/kehadiran/get_riwayat_balita.php?balita_id=${widget.balitaId}"),
);
if (response.statusCode == 200) {
final res = json.decode(response.body);
if (res["success"] == true) {
setState(() {
_riwayatOriginal = res["data"];
_riwayatFiltered = res["data"];
});
}
}
} catch (e) {
debugPrint("Error Fetch Riwayat: ${e.toString()}");
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
Future<void> _hapusData(String id) async {
try {
final response = await http.post(
Uri.parse(
"http://ta.myhost.id/E31230549/mposyandu_api/kehadiran/hapus_kehadiran.php"),
body: {"id": id},
);
final res = json.decode(response.body);
if (res["success"] == true) {
_fetchRiwayat();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Data berhasil dihapus")),
);
}
} catch (e) {
debugPrint("Error Hapus: ${e.toString()}");
}
}
@override
Widget build(BuildContext context) {
final totalPages = _riwayatFiltered.isEmpty
? 1
: (_riwayatFiltered.length / _rowsPerPage).ceil();
return Scaffold(
backgroundColor: const Color(0xfff4f6fb),
appBar: AppBar(
backgroundColor: Colors.blueAccent,
foregroundColor: Colors.white,
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context, true),
),
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: Column(
children: [
const SizedBox(height: 14),
Text(
"Riwayat Kehadiran",
style: GoogleFonts.poppins(
fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
/// SEARCH
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: TextField(
controller: _searchController,
onChanged: _filterData,
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
hintText: "Cari tanggal...",
hintStyle: GoogleFonts.poppins(fontSize: 12),
prefixIcon: const Icon(Icons.search, size: 18),
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(vertical: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
),
),
),
const SizedBox(height: 15),
Expanded(
child: _riwayatFiltered.isEmpty
? Center(
child: Text("Data tidak ditemukan",
style: GoogleFonts.poppins(
fontSize: 12, color: Colors.grey)))
: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: _paginatedData.length,
itemBuilder: (context, index) {
final item = _paginatedData[index];
return Card(
elevation: 2,
color: Colors.white, // Warna card putih
margin: const EdgeInsets.only(bottom: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: Column(
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: const BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.vertical(
top: Radius.circular(12)),
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(widget.namaBalita,
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold)),
Text(
_formatTanggal(
item["tanggal_posyandu"]),
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 12)),
],
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
children: [
_infoRowAligned("Status Kehadiran",
item["status_hadir"] ?? "-"),
const SizedBox(height: 4),
_infoRowAligned("Catatan",
item["keterangan"] ?? "-"),
const Divider(),
const SizedBox(height: 4),
Align(
alignment: Alignment.centerRight,
child: SizedBox(
width: 110,
child: OutlinedButton.icon(
onPressed: () =>
_showDeleteDialog(
item["id"].toString()),
icon: const Icon(
Icons.delete_outline,
size: 14,
color: Colors.red),
style: OutlinedButton.styleFrom(
side: const BorderSide(
color: Colors.red,
width: 1.5),
shape: const StadiumBorder(),
padding:
const EdgeInsets.symmetric(
vertical: 8),
),
label: Text(
"Hapus",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.red,
fontWeight:
FontWeight.bold),
),
),
),
)
],
),
)
],
),
);
},
),
),
/// PAGINATION
Container(
width: double.infinity,
padding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
decoration: BoxDecoration(
color: Colors.white,
border:
Border(top: BorderSide(color: Colors.grey.shade200))),
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++),
),
],
),
],
),
),
],
),
);
}
Widget _infoRowAligned(String label, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 120,
child: Text(label,
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w500))),
const Text(" : ", style: TextStyle(fontSize: 12)),
Expanded(
child: Text(value,
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w600))),
],
);
}
void _showDeleteDialog(String id) {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
title: Text("Konfirmasi",
style:
GoogleFonts.poppins(fontWeight: FontWeight.bold, fontSize: 12)),
content: Text("Data riwayat ini akan dihapus permanen.",
style: GoogleFonts.poppins(fontSize: 12)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Batal",
style:
GoogleFonts.poppins(color: Colors.grey, fontSize: 12))),
OutlinedButton(
onPressed: () {
Navigator.pop(context);
_hapusData(id);
},
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.red),
shape: const StadiumBorder()),
child: Text("Hapus",
style: GoogleFonts.poppins(
color: Colors.red,
fontWeight: FontWeight.bold,
fontSize: 12)),
),
],
),
);
}
}