import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; import '../../../services/auth_service.dart'; import '../../../services/notification_admin_service.dart'; import 'notification_admin_screen.dart'; import 'data_petugas_screen.dart'; import 'laporan_admin_screen.dart'; class AdminHome extends StatefulWidget { const AdminHome({super.key}); @override State createState() => _AdminHomeState(); } class _AdminHomeState extends State { Map userData = {}; late String _timeString; late Timer _timer; late Timer _notifTimer; int totalPetugas = 0; int laporanMasuk = 0; int laporanDisetujui = 0; int laporanPending = 0; int notifCount = 0; @override void initState() { super.initState(); _timeString = DateFormat('HH:mm:ss').format(DateTime.now()); _timer = Timer.periodic( const Duration(seconds: 1), (Timer t) => _getCurrentTime(), ); // 🔥 AUTO REFRESH NOTIF _notifTimer = Timer.periodic( const Duration(seconds: 5), (Timer t) => loadNotif(), ); fetchDashboard(); loadProfile(); loadNotif(); // pertama kali load } @override void dispose() { _timer.cancel(); _notifTimer.cancel(); super.dispose(); } void _getCurrentTime() { final String formattedDateTime = DateFormat( 'HH:mm:ss', ).format(DateTime.now()); setState(() { _timeString = formattedDateTime; }); } Future loadNotif() async { final count = await NotificationAdminService.getUnreadCount(); // ✅ FIX if (!mounted) return; setState(() { notifCount = count; }); } String getTanggalHari() { final now = DateTime.now(); return DateFormat('EEEE, dd MMMM yyyy', 'id_ID').format(now); } Future fetchDashboard() async { try { String? token = await AuthService.getToken(); final response = await http.get( Uri.parse("${AuthService.baseUrl}/dashboard-statistik"), headers: { "Accept": "application/json", "Authorization": "Bearer $token", }, ); if (response.statusCode == 200) { final data = jsonDecode(response.body); setState(() { totalPetugas = data['total_petugas'] ?? 0; laporanMasuk = data['laporan_masuk'] ?? 0; laporanPending = data['laporan_pending'] ?? 0; laporanDisetujui = data['laporan_disetujui'] ?? 0; }); } } catch (e) { debugPrint("Error dashboard: $e"); } } Future loadProfile() async { final result = await AuthService.getProfile(); if (!mounted) return; if (result['success']) { setState(() { userData = result['data']; }); } } // ================= STAT CARD (PERCANTIK) ================= Widget _buildStatCard( String title, String count, Color color, IconData icon, ) { return Expanded( child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: color.withOpacity(0.08), blurRadius: 12, offset: const Offset(0, 4), ), ], border: Border.all(color: color.withOpacity(0.1), width: 1.5), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, color: color, size: 20), ), const SizedBox(height: 12), Text( count, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87, ), ), Text( title, style: TextStyle( fontSize: 12, color: Colors.grey.shade600, fontWeight: FontWeight.w500, ), ), ], ), ), ); } // ================= MENU CARD (PERCANTIK) ================= Widget _buildMenuCard( BuildContext context, String title, IconData icon, Color color, VoidCallback onTap, ) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(24), child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.04), blurRadius: 15, offset: const Offset(0, 6), ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: color.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon(icon, color: color, size: 30), ), const SizedBox(height: 12), Text( title, textAlign: TextAlign.center, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 14, color: Colors.black87, ), ), ], ), ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F7FB), body: SingleChildScrollView( child: Column( children: [ // ================= HEADER ================= Stack( clipBehavior: Clip.none, children: [ Container( height: 240, width: double.infinity, decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF1A39B1), Color(0xFF4263EB)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(40), bottomRight: Radius.circular(40), ), ), padding: const EdgeInsets.fromLTRB(25, 60, 25, 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Patroli Kampus', style: TextStyle( color: Colors.white, fontSize: 26, fontWeight: FontWeight.bold, letterSpacing: 0.8, ), ), Text( 'Politeknik Negeri Jember', style: TextStyle( color: Colors.white70, fontSize: 13, ), ), ], ), Row( children: [ Stack( children: [ IconButton( icon: const Icon( Icons.notifications_rounded, color: Colors.white, size: 28, ), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const NotificationAdminScreen(), ), ).then((_) => loadNotif()); }, ), if (notifCount > 0) Positioned( right: 6, top: 6, child: Container( padding: const EdgeInsets.all(5), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), constraints: const BoxConstraints( minWidth: 18, minHeight: 18, ), child: Text( notifCount > 9 ? '9+' : notifCount.toString(), style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), ), ), ], ), const SizedBox(width: 10), Hero( tag: 'logo_polije', child: Image.asset( 'assets/images/logopolije.png', height: 60, ), ), ], ), ], ), const SizedBox(height: 20), Container( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 12, ), decoration: BoxDecoration( color: Colors.white.withOpacity(0.15), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.calendar_today_rounded, size: 14, color: Colors.white, ), const SizedBox(width: 8), Text( getTanggalHari(), style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500, ), ), const SizedBox(width: 15), const VerticalDivider( color: Colors.white54, thickness: 1, ), const Icon( Icons.watch_later_rounded, size: 14, color: Colors.white, ), const SizedBox(width: 8), Text( _timeString, style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), ), ], ), ), // ================= CARD PROFIL ================= Padding( padding: const EdgeInsets.only(top: 185, left: 20, right: 20), child: Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.06), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(3), decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: const Color(0xFF4263EB), width: 2, ), ), child: CircleAvatar( radius: 26, backgroundColor: const Color(0xFFF0F3FF), backgroundImage: (userData['foto'] != null && userData['foto'].toString().isNotEmpty) ? NetworkImage(userData['foto']) : null, child: (userData['foto'] == null || userData['foto'].toString().isEmpty) ? const Icon( Icons.admin_panel_settings_rounded, color: Color(0xFF1A39B1), size: 30, ) : null, ), ), const SizedBox(width: 15), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Selamat Datang kembali,', style: TextStyle( fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w500, ), ), Text( userData['role'] == 'super_admin' ? 'Super Admin' : 'Admin', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF1E293B), ), ), ], ), ], ), ), ), ], ), const SizedBox(height: 45), // ================= CONTENT ================= Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Ringkasan Laporan', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF1E293B), ), ), const SizedBox(height: 16), Row( children: [ _buildStatCard( "Petugas", totalPetugas.toString(), Colors.blue, Icons.people_rounded, ), const SizedBox(width: 14), _buildStatCard( "Masuk", laporanMasuk.toString(), Colors.purple, Icons.assignment_returned_rounded, ), ], ), const SizedBox(height: 14), Row( children: [ _buildStatCard( "Disetujui", laporanDisetujui.toString(), Colors.green, Icons.check_circle_rounded, ), const SizedBox(width: 14), _buildStatCard( "Pending", laporanPending.toString(), Colors.orange, Icons.hourglass_empty_rounded, ), ], ), const SizedBox(height: 32), const Text( 'Menu Utama', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF1E293B), ), ), const SizedBox(height: 16), GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 2, crossAxisSpacing: 18, mainAxisSpacing: 18, childAspectRatio: 1.25, children: [ _buildMenuCard( context, "Data Petugas", Icons.badge_rounded, Colors.teal, () { Navigator.push( context, MaterialPageRoute( builder: (_) => const DataPetugasScreen(), ), ); }, ), _buildMenuCard( context, "Riwayat Aktivitas", Icons.history_edu_rounded, Colors.deepOrangeAccent, () { Navigator.push( context, MaterialPageRoute( builder: (_) => const LaporanAdminScreen(), ), ); }, ), ], ), const SizedBox(height: 40), ], ), ), ], ), ), ); } }