MIF_E31230549/lib/kader/dashboard_kader.dart

552 lines
18 KiB
Dart

import 'dart:convert';
import 'dart:async';
import 'dart:io'; // Tambahkan ini
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // Tambahkan ini untuk SystemNavigator.pop
import 'package:google_fonts/google_fonts.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import 'package:audioplayers/audioplayers.dart';
import '../layout/main_layout.dart';
import 'kader_drawer.dart';
import '../pages/login_page.dart';
import '../kader/data_kehamilan.dart';
import '../kader/data_balita.dart';
class DashboardKaderPage extends StatefulWidget {
const DashboardKaderPage({super.key});
@override
State<DashboardKaderPage> createState() => _DashboardKaderPageState();
}
class _DashboardKaderPageState extends State<DashboardKaderPage> {
DateTime selectedDate = DateTime.now();
List jadwalList = [];
Timer? _pollingTimer;
final AudioPlayer _audioPlayer = AudioPlayer();
int _notificationCount = 0;
String userName = "Kader";
String _lastJadwalDataString = "";
String? keteranganKegiatan;
String? tempatKegiatan;
String? jamMulai;
String? jamSelesai;
bool isLoadingJadwal = true;
int jumlahIbu = 0;
int jumlahBalita = 0;
bool isLoading = true;
int? kaderId;
static const String baseUrl = "http://ta.myhost.id/E31230549/mposyandu_api";
final String dashboardUrl = "$baseUrl/dashboard_kader.php";
final String jadwalUrl = "$baseUrl/jadwal_posyandu/get_jadwal_by_kader.php";
@override
void initState() {
super.initState();
_audioPlayer.setReleaseMode(ReleaseMode.stop);
_initPage();
_pollingTimer = Timer.periodic(const Duration(seconds: 10), (timer) {
if (kaderId != null && kaderId != 0) {
_loadJadwalKader();
}
});
}
@override
void dispose() {
_pollingTimer?.cancel();
_audioPlayer.dispose();
super.dispose();
}
Future<void> _playNotifSound() async {
try {
await _audioPlayer.stop();
await _audioPlayer.play(AssetSource('sounds/notif.mp3'));
} catch (e) {
debugPrint("DEBUG AUDIO ERROR: $e");
}
}
Future<void> _initPage() 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;
}
String? idString = prefs.getString('id_user');
int currentId = int.tryParse(idString ?? "0") ?? 0;
setState(() {
userName = prefs.getString('nama') ?? "Kader";
kaderId = currentId;
_notificationCount = prefs.getInt('unread_notif_count_$idString') ?? 0;
_lastJadwalDataString =
prefs.getString('last_jadwal_data_string_$idString') ?? "";
});
if (currentId != 0) {
await _loadDashboard();
await _loadJadwalKader();
} else {
if (mounted) setState(() => isLoading = false);
}
}
Future<void> _loadDashboard() async {
if (kaderId == null || kaderId == 0) {
final prefs = await SharedPreferences.getInstance();
kaderId = int.tryParse(prefs.getString('id_user') ?? "0");
}
if (kaderId == null || kaderId == 0) {
if (mounted) setState(() => isLoading = false);
return;
}
try {
final res = await http
.get(Uri.parse("$dashboardUrl?id_kader=$kaderId"))
.timeout(const Duration(seconds: 10));
final data = jsonDecode(res.body);
if (data['success'] == true) {
if (mounted) {
setState(() {
jumlahIbu = int.tryParse(data['jumlah_ibu'].toString()) ?? 0;
jumlahBalita = int.tryParse(data['jumlah_balita'].toString()) ?? 0;
isLoading = false;
});
}
} else {
debugPrint("API Error: ${data['message']}");
if (mounted) setState(() => isLoading = false);
}
} catch (e) {
debugPrint("Error Dashboard Connection: $e");
if (mounted) setState(() => isLoading = false);
}
}
Future<void> _loadJadwalKader() async {
if (kaderId == null || kaderId == 0) return;
try {
final url = "$jadwalUrl?kader_id=$kaderId";
final res =
await http.get(Uri.parse(url)).timeout(const Duration(seconds: 10));
final data = jsonDecode(res.body);
if (data['success'] == true) {
List fetchedJadwal = data['data'] ?? [];
String currentDataString = jsonEncode(fetchedJadwal);
final prefs = await SharedPreferences.getInstance();
String idStr = kaderId.toString();
if (_lastJadwalDataString.isNotEmpty &&
currentDataString != _lastJadwalDataString) {
List oldList = jsonDecode(_lastJadwalDataString);
if (fetchedJadwal.length >= oldList.length) {
_playNotifSound();
setState(() {
_notificationCount += 1;
});
await prefs.setInt('unread_notif_count_$idStr', _notificationCount);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text("Jadwal diperbarui atau ditambah!"),
backgroundColor: Colors.blue.shade700,
duration: const Duration(seconds: 2),
),
);
}
}
}
await prefs.setString(
'last_jadwal_data_string_$idStr', currentDataString);
if (mounted) {
setState(() {
jadwalList = fetchedJadwal;
_lastJadwalDataString = currentDataString;
isLoadingJadwal = false;
});
_updateJadwalByDate(selectedDate);
}
} else {
_resetJadwal();
}
} catch (e) {
debugPrint("Error polling: $e");
if (mounted) setState(() => isLoadingJadwal = false);
}
}
void _resetJadwal() {
if (mounted) {
setState(() {
keteranganKegiatan = null;
tempatKegiatan = null;
jamMulai = null;
jamSelesai = null;
isLoadingJadwal = false;
});
}
}
String formatTanggal(DateTime date) {
return "${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}";
}
void _updateJadwalByDate(DateTime date) {
String tanggalDipilih = formatTanggal(date);
Map<String, dynamic>? jadwalHariIni;
for (var j in jadwalList) {
if (j['tanggal'] != null) {
String tanggalApi = j['tanggal'].toString().substring(0, 10);
if (tanggalApi == tanggalDipilih) {
jadwalHariIni = j;
break;
}
}
}
if (jadwalHariIni != null) {
setState(() {
keteranganKegiatan = jadwalHariIni!['keterangan'];
tempatKegiatan = jadwalHariIni['lokasi'];
jamMulai = jadwalHariIni['jam_mulai'];
jamSelesai = jadwalHariIni['jam_selesai'];
});
} else {
_resetJadwal();
}
}
Future<void> _showNotifDialog() async {
final prefs = await SharedPreferences.getInstance();
String idStr = kaderId.toString();
setState(() {
_notificationCount = 0;
});
await prefs.setInt('unread_notif_count_$idStr', 0);
if (!mounted) return;
showDialog(
context: context,
builder: (_) {
return AlertDialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
title: Text("Pemberitahuan Jadwal",
style: GoogleFonts.poppins(fontWeight: FontWeight.w600)),
content: SizedBox(
width: 300,
child: jadwalList.isEmpty
? const Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child:
Text("Belum ada jadwal", textAlign: TextAlign.center),
)
: ListView.builder(
shrinkWrap: true,
itemCount: jadwalList.length > 5 ? 5 : jadwalList.length,
itemBuilder: (_, index) {
final item = jadwalList[(jadwalList.length - 1) - index];
return Card(
elevation: 2,
margin: const EdgeInsets.symmetric(vertical: 6),
child: ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.blue,
child: Icon(Icons.event,
color: Colors.white, size: 20),
),
title: Text(
item['keterangan'] ?? "Kegiatan Posyandu",
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 13),
),
subtitle: Text(
"Tgl: ${item['tanggal']?.toString().substring(0, 10) ?? '-'}\nLokasi: ${item['lokasi'] ?? '-'}",
style: GoogleFonts.poppins(fontSize: 11),
),
isThreeLine: true,
),
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Tutup"))
],
);
},
);
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false, // Menahan aksi "back" standar
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
// Keluar dari aplikasi secara total
if (Platform.isAndroid) {
SystemNavigator.pop();
} else if (Platform.isIOS) {
exit(0);
}
},
child: Theme(
data: Theme.of(context).copyWith(
textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme),
dividerColor: Colors.transparent,
),
child: Stack(
children: [
MainLayout(
title: " ",
drawer: const KaderDrawer(),
body: _buildDashboard(),
),
Positioned(
top: MediaQuery.of(context).padding.top +
(kToolbarHeight - 48) / 2,
right: 8,
child: Stack(
alignment: Alignment.center,
children: [
IconButton(
icon: const Icon(Icons.notifications,
color: Colors.white, size: 26),
onPressed: _showNotifDialog,
),
if (_notificationCount > 0)
Positioned(
right: 8,
top: 8,
child: Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 1.5),
),
constraints:
const BoxConstraints(minWidth: 18, minHeight: 18),
child: Center(
child: Text(
_notificationCount > 9
? "9+"
: _notificationCount.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 9,
fontWeight: FontWeight.bold),
),
),
),
)
],
),
),
],
),
),
);
}
Widget _buildDashboard() {
return RefreshIndicator(
onRefresh: () async {
await _loadDashboard();
await _loadJadwalKader();
},
child: Padding(
padding: const EdgeInsets.all(16),
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: 'Selamat Datang Kader $userName\n',
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w700,
color: const Color.fromARGB(255, 19, 133, 226)),
),
TextSpan(
text: 'Tetap semangat melayani masyarakat ',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.black87),
),
const WidgetSpan(
child: Icon(Icons.favorite,
color: Colors.blue, size: 18)),
],
),
),
),
const SizedBox(height: 15),
Center(
child: Image.asset('assets/images/logoo.webp',
width: 300, height: 200, fit: BoxFit.contain)),
const SizedBox(height: 25),
isLoading
? const Center(child: CircularProgressIndicator())
: Row(
children: [
_infoBoxClickable(jumlahIbu.toString(),
'Jumlah Ibu Hamil', Colors.pink, () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const DataIbuHamilPage()));
_loadDashboard();
}),
const SizedBox(width: 10),
_infoBoxClickable(
jumlahBalita.toString(),
'Jumlah Balita',
const Color.fromARGB(255, 240, 220, 39), () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const DataBalitaPage()));
_loadDashboard();
}),
],
),
const SizedBox(height: 30),
const Text('Jadwal Posyandu',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 15),
isLoadingJadwal
? const CircularProgressIndicator()
: keteranganKegiatan == null
? const Text("Tidak ada kegiatan posyandu hari ini",
style: TextStyle(color: Colors.red))
: Center(
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildDetailRow(Icons.access_time, "Jam",
"${jamMulai ?? '-'} s/d ${jamSelesai ?? '-'}"),
const SizedBox(height: 8),
_buildDetailRow(Icons.location_on, "Lokasi",
tempatKegiatan ?? "-"),
const SizedBox(height: 8),
_buildDetailRow(Icons.event_available,
"Kegiatan", keteranganKegiatan ?? "-"),
],
),
),
),
const SizedBox(height: 25),
Container(
width: 260,
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2), blurRadius: 5)
],
),
child: Transform.scale(
scale: 0.8,
child: CalendarDatePicker(
initialDate: selectedDate,
firstDate: DateTime(2020),
lastDate: DateTime(2030),
onDateChanged: (date) {
setState(() => selectedDate = date);
_updateJadwalByDate(date);
},
),
),
),
],
),
),
),
);
}
Widget _buildDetailRow(IconData icon, String label, String value) {
return Row(
children: [
Icon(icon, size: 18, color: Colors.blue),
const SizedBox(width: 10),
SizedBox(
width: 90,
child: Text(label,
style: const TextStyle(
fontWeight: FontWeight.w600, fontSize: 12))),
const SizedBox(
width: 10,
child: Text(":", style: TextStyle(fontWeight: FontWeight.bold))),
Expanded(
child: Text(value,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 12))),
],
);
}
static Widget _infoBoxClickable(
String value, String label, Color color, VoidCallback onTap) {
return Expanded(
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: color, borderRadius: BorderRadius.circular(12)),
child: Column(
children: [
Text(value,
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold)),
const SizedBox(height: 5),
Text(label,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white)),
],
),
),
),
);
}
}