import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'dart:ui'; // Diperlukan untuk efek Glassmorphism BackdropFilter import 'login_page.dart'; import 'tambah_siswa.dart'; import 'data_siswa.dart'; import 'absensi_page.dart'; import 'laporan_page.dart'; class DashboardGuru extends StatelessWidget { const DashboardGuru({super.key}); void logout(BuildContext context) async { await FirebaseAuth.instance.signOut(); Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (_) => const LoginPage()), (route) => false, ); } /// Komponen Kartu Menu Utama - Ukuran Super Ringkas & Anti Overflow Widget menuCard({ required IconData icon, required Color baseColor, required String title, required String subtitle, required List cardGradients, required VoidCallback onTap, bool hasExtraNode = false, }) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), // Diperkecil dari 24 ke 20 boxShadow: [ BoxShadow( color: baseColor.withOpacity(0.06), blurRadius: 16, spreadRadius: 0, offset: const Offset(0, 8), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Stack( children: [ // Lengkungan pojok kiri atas (diperkecil ukurannya) Positioned( top: 0, left: 0, child: CustomPaint( size: const Size(65, 65), // Diperkecil dari 85 ke 65 painter: CornerCurvePainter(color: baseColor), ), ), // Titik-titik hiasan di pojok kanan atas Positioned( top: 14, right: 14, child: Column( children: List.generate(3, (_) => Row( children: List.generate(3, (_) => Container( width: 3, height: 3, margin: const EdgeInsets.all(1.5), decoration: BoxDecoration( color: baseColor.withOpacity(0.2), shape: BoxShape.circle, ), )), )), ), ), if (hasExtraNode) Positioned( top: 18, right: 28, child: Opacity( opacity: 0.25, child: Icon(Icons.mediation_rounded, size: 16, color: baseColor), ), ), Material( color: Colors.transparent, child: InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.all(14.0), // Padding diperkecil agar ruang makin lega child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Lingkaran Icon Lebih Kecil Container( height: 40, // Diperkecil dari 48 ke 40 width: 40, decoration: BoxDecoration( shape: BoxShape.circle, color: baseColor.withOpacity(0.1), ), child: Icon(icon, size: 20, color: baseColor), ), const SizedBox(height: 10), // Teks dengan ukuran lebih ketat Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, // Memaksa layout mengambil ruang sekecil mungkin children: [ Text( title, style: const TextStyle( fontSize: 14, // Diperkecil ke 14 fontWeight: FontWeight.bold, color: Color(0xff12175E), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), Text( subtitle, style: TextStyle( fontSize: 10.5, // Diperkecil ke 10.5 color: const Color(0xff12175E).withOpacity(0.55), height: 1.2, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), // Tombol Panah Aksi Lebih Kecil Align( alignment: Alignment.bottomRight, child: Container( height: 26, // Diperkecil dari 32 ke 26 width: 26, decoration: BoxDecoration( shape: BoxShape.circle, color: baseColor, ), child: const Icon(Icons.arrow_forward_rounded, color: Colors.white, size: 14), ), ), ], ), ), ), ), ], ), ), ); } @override Widget build(BuildContext context) { final user = FirebaseAuth.instance.currentUser; if (user == null) { return const Scaffold(body: Center(child: Text("User belum login"))); } final screenHeight = MediaQuery.of(context).size.height; final screenWidth = MediaQuery.of(context).size.width; return Scaffold( backgroundColor: const Color(0xffF6F5FB), body: StreamBuilder( stream: FirebaseFirestore.instance.collection('users').doc(user.uid).snapshots(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } final data = snapshot.hasData && snapshot.data!.exists ? snapshot.data!.data() as Map : {}; final nama = data['nama'] ?? 'Guru'; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ /// ==================== 1. HEADER AREA ==================== Container( width: double.infinity, height: screenHeight * 0.38, decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xffE6DFFF), Color(0xffCCBFFF)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(40), bottomRight: Radius.circular(40), ), boxShadow: [ BoxShadow( color: Color(0xff7F56D9), blurRadius: 24, offset: Offset(0, 8), ), ], ), child: ClipRRect( borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(40), bottomRight: Radius.circular(40), ), child: Stack( children: [ Positioned( top: -40, left: -20, child: Container( width: 180, height: 180, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white.withOpacity(0.15), ), ), ), Positioned.fill( child: CustomPaint(painter: HeaderWavePainter()), ), Positioned(top: 50, left: 24, child: _buildGridDots()), Positioned(top: 70, left: screenWidth * 0.46, child: _buildGridDots()), // Tombol Logout Positioned( top: 50, right: 24, child: Container( height: 44, width: 44, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(14), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.06), blurRadius: 12, offset: const Offset(0, 4), ) ], ), child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(14), onTap: () => logout(context), child: const Icon(Icons.logout_rounded, color: Color(0xff7F56D9), size: 20), ), ), ), ), // Gambar Guru Positioned( right: -22, bottom: -10, child: Image.asset( 'lib/assets/icon_guru.png', height: screenHeight * 0.30, fit: BoxFit.contain, errorBuilder: (_, __, ___) => Container( padding: const EdgeInsets.only(right: 30, bottom: 20), child: const Icon(Icons.account_box_rounded, size: 130, color: Color(0xff9181F4)), ), ), ), // Kartu Glassmorphic Welcome Positioned( left: 20, bottom: 24, child: ClipRRect( borderRadius: BorderRadius.circular(24), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12), child: Container( width: screenWidth * 0.40, padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: Colors.white.withOpacity(0.45), borderRadius: BorderRadius.circular(24), border: Border.all(color: Colors.white.withOpacity(0.5), width: 1.5), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Image.asset( 'lib/assets/sekolah.png', height: 50, fit: BoxFit.contain, errorBuilder: (_, __, ___) => const Icon(Icons.school, size: 40, color: Colors.green), ), const SizedBox(height: 12), const Text( "Selamat Datang di", style: TextStyle(color: Color(0xff12175E), fontSize: 13, fontWeight: FontWeight.w600), ), const SizedBox(height: 2), Text( "Dashboard\nGuru $nama", style: const TextStyle( color: Color(0xff12175E), fontSize: 22, fontWeight: FontWeight.bold, height: 1.2, letterSpacing: -0.5, ), ), const SizedBox(height: 10), Row( children: [ Container(width: 35, height: 4, decoration: BoxDecoration(color: const Color(0xff7F56D9), borderRadius: BorderRadius.circular(2))), const SizedBox(width: 4), Container(width: 4, height: 4, decoration: const BoxDecoration(color: Color(0xff7F56D9), shape: BoxShape.circle)), ], ), ], ), ), ), ), ), ], ), ), ), const SizedBox(height: 24), /// ==================== 2. TITLE MENU UTAMA ==================== Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.grid_view_rounded, color: const Color(0xff7F56D9), size: 22), const SizedBox(width: 8), const Text( "Menu Utama", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xff12175E)), ), ], ), const SizedBox(height: 6), Row( children: [ Container(width: 24, height: 3, decoration: BoxDecoration(color: const Color(0xff7F56D9), borderRadius: BorderRadius.circular(1.5))), const SizedBox(width: 3), Container(width: 3, height: 3, decoration: const BoxDecoration(color: Color(0xff7F56D9), shape: BoxShape.circle)), ], ), ], ), ), const SizedBox(height: 16), /// ==================== 3. GRID MENU UTAMA ==================== Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: GridView( physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 16, mainAxisSpacing: 16, childAspectRatio: 0.92, // ✅ Diubah ke 0.92 agar rasio kotak lebih pendek & aman overflow ), children: [ menuCard( icon: Icons.person_add_alt_1_rounded, baseColor: const Color(0xff7F56D9), title: "Tambah Siswa", subtitle: "Tambah data siswa baru ke sistem", cardGradients: [const Color(0xffF9F5FF), const Color(0xffF3E8FF)], hasExtraNode: true, onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const TambahSiswa())), ), menuCard( icon: Icons.groups_rounded, baseColor: const Color(0xff0284C7), title: "Data Siswa", subtitle: "Lihat & kelola informasi siswa", cardGradients: [const Color(0xffF0F9FF), const Color(0xffE0F2FE)], onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const DataSiswa())), ), menuCard( icon: Icons.assignment_turned_in_rounded, baseColor: const Color(0xff16A34A), title: "Absensi", subtitle: "Catat & periksa kehadiran siswa", cardGradients: [const Color(0xffF0FDF4), const Color(0xffDCFCE7)], onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const AbsensiPage())), ), menuCard( icon: Icons.bar_chart_rounded, baseColor: const Color(0xffEA580C), title: "Laporan Absensi", subtitle: "Lihat ringkasan laporan kehadiran", cardGradients: [const Color(0xffFFF7ED), const Color(0xffFFEDD5)], onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const LaporanPage())), ), ], ), ), ), const SizedBox(height: 12), ], ); }, ), ); } Widget _buildGridDots() { return Opacity( opacity: 0.25, child: Column( children: List.generate(4, (_) => Row( children: List.generate(4, (_) => Container( width: 2.5, height: 2.5, margin: const EdgeInsets.all(2.5), decoration: const BoxDecoration(color: Color(0xff12175E), shape: BoxShape.circle), )), )), ), ); } } class CornerCurvePainter extends CustomPainter { final Color color; CornerCurvePainter({required this.color}); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = color.withOpacity(0.15) ..style = PaintingStyle.fill; final path = Path(); path.moveTo(0, 0); path.lineTo(size.width, 0); path.quadraticBezierTo(size.width * 0.6, size.height * 0.45, size.width * 0.5, size.height); path.lineTo(0, size.height); path.close(); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class HeaderWavePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.white.withOpacity(0.18) ..style = PaintingStyle.stroke ..strokeWidth = 2.0; final path1 = Path(); path1.moveTo(0, size.height * 0.3); path1.cubicTo(size.width * 0.25, size.height * 0.1, size.width * 0.6, size.height * 0.5, size.width, size.height * 0.25); canvas.drawPath(path1, paint); final path2 = Path(); path2.moveTo(0, size.height * 0.5); path2.cubicTo(size.width * 0.35, size.height * 0.2, size.width * 0.7, size.height * 0.6, size.width, size.height * 0.4); canvas.drawPath(path2, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; }