import 'dart:io'; import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:excel/excel.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'rfid/nilai_praktik.dart'; import 'riwayat_page.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { // 🎨 THEME WARNA BARU (SESUAI PERMINTAAN) static const Color primaryColor = Color(0xff4E7C34); static const Color secondaryColor = Color(0xff6A994E); static const Color backgroundColor = Color(0xffFFF8E8); // 🔥 EXPORT EXCEL Future exportToExcel() async { if (!mounted) return; try { final storagePermission = await Permission.storage.request(); if (!storagePermission.isGranted) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text("Izin penyimpanan belum diberikan"))); return; } final snapshot = await FirebaseDatabase.instance.ref("cards").get(); if (!snapshot.exists || snapshot.value == null) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text("Data kosong"))); return; } final data = Map.from(snapshot.value as Map); final excel = Excel.createExcel(); final Sheet sheet = excel['Nilai']; sheet.appendRow([ TextCellValue('UID'), TextCellValue('Nama'), TextCellValue('Nilai'), TextCellValue('Tanggal & Jam'), ]); for (final entry in data.entries) { final uid = entry.key; final card = Map.from(entry.value as Map); final nilai = card['nilai'] ?? 0; final historySnapshot = await FirebaseDatabase.instance .ref("history/$uid") .get(); String tanggalJam = "-"; if (historySnapshot.exists && historySnapshot.value != null) { final historyData = Map.from( historySnapshot.value as Map, ); final historyList = historyData.entries .map((item) => Map.from(item.value as Map)) .toList(); historyList.sort((a, b) { final aTime = ((a['timestamp'] ?? 0) as num).toInt(); final bTime = ((b['timestamp'] ?? 0) as num).toInt(); return bTime.compareTo(aTime); }); if (historyList.isNotEmpty) { final lastHistory = historyList.first; final timestamp = (lastHistory['timestamp'] as num?)?.toInt() ?? 0; if (timestamp > 0) { final date = DateTime.fromMillisecondsSinceEpoch(timestamp); tanggalJam = "${DateFormat('dd-MM-yyyy').format(date)} ${DateFormat('HH:mm:ss').format(date)}"; } } } sheet.appendRow([ TextCellValue(uid), TextCellValue(card['name'] ?? '-'), IntCellValue(nilai), TextCellValue(tanggalJam), ]); } Directory directory; if (Platform.isAndroid) { final downloadsDir = Directory('/storage/emulated/0/Download'); if (await downloadsDir.exists()) { directory = downloadsDir; } else { final appDir = await getApplicationDocumentsDirectory(); directory = appDir; } } else { directory = await getApplicationDocumentsDirectory(); } final path = "${directory.path}/nilai_${DateTime.now().millisecondsSinceEpoch}.xlsx"; final file = File(path); if (await file.exists()) { await file.delete(); } final bytes = excel.encode(); if (bytes != null) { await file.create(recursive: true); await file.writeAsBytes(bytes); } if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text("Berhasil download:\n$path"))); } catch (e) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text("Error: $e"))); } } @override Widget build(BuildContext context) { final userUid = FirebaseAuth.instance.currentUser!.uid; return FutureBuilder( future: FirebaseFirestore.instance.collection("users").doc(userUid).get(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } final data = snapshot.data?.data() as Map?; final userName = data?['name'] ?? 'USER'; return Scaffold( backgroundColor: backgroundColor, body: SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ // 🔥 HEADER Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( gradient: LinearGradient( colors: [ backgroundColor, secondaryColor.withOpacity(0.15), ], ), borderRadius: BorderRadius.circular(24), ), child: Row( children: [ Container( width: 70, height: 70, decoration: BoxDecoration( borderRadius: BorderRadius.circular(18), color: Colors.white, ), child: const Padding( padding: EdgeInsets.all(8), child: Image(image: AssetImage("assets/logo.png")), ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "HALO, ${userName.toUpperCase()}", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 22, color: primaryColor, ), ), const SizedBox(height: 6), const Text( "Monitoring Nilai Papan Pembelajaran", style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: secondaryColor, ), ), const SizedBox(height: 6), const Text( "Silahkan Daftarkan Nama Siswa di Halaman Kartu", style: TextStyle( fontSize: 13, color: Colors.black87, ), ), ], ), ), ], ), ), const SizedBox(height: 20), // 🔥 BUTTON DOWNLOAD Container( width: double.infinity, height: 65, decoration: BoxDecoration( gradient: const LinearGradient( colors: [primaryColor, secondaryColor], ), borderRadius: BorderRadius.circular(22), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: ElevatedButton.icon( onPressed: exportToExcel, style: ElevatedButton.styleFrom( backgroundColor: Colors.transparent, shadowColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(22), ), ), icon: const Icon( Icons.download, color: Colors.white, size: 28, ), label: const Text( "Unduh Data Nilai Siswa", style: TextStyle( color: Colors.white, fontSize: 17, fontWeight: FontWeight.bold, ), ), ), ), const SizedBox(height: 22), // 🔥 LIST SISWA Expanded( child: StreamBuilder( stream: FirebaseDatabase.instance.ref("cards").onValue, builder: (context, snapshot) { if (!snapshot.hasData || snapshot.data!.snapshot.value == null) { return const Center(child: Text("Belum ada data")); } final cards = Map.from( snapshot.data!.snapshot.value as Map, ); final entries = cards.entries.toList(); return ListView.builder( itemCount: entries.length, itemBuilder: (context, index) { final uid = entries[index].key; final card = Map.from( entries[index].value, ); return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 8, offset: const Offset(0, 4), ), ], ), child: Row( children: [ Container( width: 55, height: 55, decoration: BoxDecoration( gradient: const LinearGradient( colors: [primaryColor, secondaryColor], ), borderRadius: BorderRadius.circular(18), ), child: const Icon( Icons.person, color: Colors.white, size: 28, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( card["name"] ?? "-", style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 6), Text( "Nilai: ${card['nilai'] ?? 0}", style: TextStyle( fontSize: 14, color: Colors.grey[700], ), ), ], ), ), Row( children: [ IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => RiwayatPage(uid: uid), ), ); }, icon: const Icon( Icons.history, color: primaryColor, ), ), IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => CardDetailPage( uid: uid, name: card["name"] ?? "-", ), ), ); }, icon: const Icon( Icons.arrow_forward_ios, size: 18, color: secondaryColor, ), ), ], ), ], ), ); }, ); }, ), ), ], ), ), ), ); }, ); } }