import 'dart:convert'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' as rootBundle; import 'package:flutter/services.dart'; import 'package:forward_chaining_man_app/app/controllers/developer_controller.dart'; import 'package:forward_chaining_man_app/app/views/about/page_about.dart'; import 'package:forward_chaining_man_app/app/views/developer/page/page_developer_viewer.dart'; import 'package:forward_chaining_man_app/app/views/page_intro.dart'; import 'package:forward_chaining_man_app/app/views/page_login.dart'; import 'package:forward_chaining_man_app/app/views/page_profile.dart'; import 'package:forward_chaining_man_app/app/views/student/feature/quiz/view/page_select_major.dart'; import 'package:forward_chaining_man_app/app/views/student/feature/recomendation_screen/view/page_recmendation_detail_screen.dart'; import 'package:forward_chaining_man_app/app/views/student/feature/recomendation_screen/view/page_recomendation_screen_history.dart'; import 'package:forward_chaining_man_app/app/views/student/model/data_student.dart'; import 'package:get/get.dart'; import 'dart:math' as math; import 'package:intl/intl.dart' as intl; import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; import 'feature/quiz/view/page_select_economy.dart'; class DevDataViewerController extends GetxController { final RxList programStudiKerja = [].obs; final RxList programStudiKuliah = [].obs; final RxString currentView = 'overview'.obs; // overview, kerja, kuliah, rules, ugm final RxBool isLoading = true.obs; final RxString loadingError = ''.obs; final RxList> rulesData = >[].obs; // Data for UGM tuition fees final RxList> biayaKuliahD4UGM = >[].obs; final RxList> biayaKuliahS1UGM = >[].obs; @override void onInit() { super.onInit(); loadAllData(); } /// Load all data for analysis void loadAllData() async { isLoading.value = true; loadingError.value = ''; try { // Load Kerja data await loadProgramData(true, programStudiKerja); // Load Kuliah data await loadProgramData(false, programStudiKuliah); // Load UGM tuition fee data await loadUGMTuitionData(); // Generate sample rules for analysis generateSampleRules(); isLoading.value = false; } catch (e) { loadingError.value = e.toString(); isLoading.value = false; } } void setCurrentView(String view) { currentView.value = view; } /// Memuat data ProgramStudi dari file JSON (Sains + Teknik) tergantung Kerja/Kuliah Future loadProgramData( bool isKerja, RxList target) async { // Tentukan file sains final sainsFile = isKerja ? 'assets/ipa_sains_kerja.json' : 'assets/ipa_sains_kuliah.json'; // File teknik final teknikFile = isKerja ? 'assets/ipa_teknik_kerja.json' : 'assets/ipa_teknik_kuliah.json'; // Baca JSON sains final sainsString = await rootBundle.rootBundle.loadString(sainsFile); final sainsMap = json.decode(sainsString) as Map; // Baca JSON teknik final teknikString = await rootBundle.rootBundle.loadString(teknikFile); final teknikMap = json.decode(teknikString) as Map; // Ubah ke list ProgramStudi final programs = []; // Parsing sains for (var entry in sainsMap.entries) { programs.add(ProgramStudi.fromJson(entry.value)); } // Parsing teknik for (var entry in teknikMap.entries) { programs.add(ProgramStudi.fromJson(entry.value)); } target.value = programs; } /// Load UGM tuition fee data Future loadUGMTuitionData() async { try { // Load D4 data final d4String = await rootBundle.rootBundle .loadString('assets/biaya_kuliah_d4_ugm.json'); final d4List = json.decode(d4String) as List; biayaKuliahD4UGM.value = d4List.cast>(); // Load S1 data final s1String = await rootBundle.rootBundle .loadString('assets/biaya_kuliah_s1_ugm.json'); final s1List = json.decode(s1String) as List; biayaKuliahS1UGM.value = s1List.cast>(); } catch (e) { print('Error loading UGM data: $e'); // Continue even if UGM data fails to load } } /// Generate sample rules untuk analisis void generateSampleRules() { final rules = >[]; // Flatten pertanyaan dari programStudiKerja untuk contoh rules int counter = 1; for (var prog in programStudiKerja) { for (var minatEntry in prog.minat.entries) { final minatKey = minatEntry.key; final minatVal = minatEntry.value; for (var p in minatVal.pertanyaan) { final bobot = extractBobot(p); final cleaned = cleanPertanyaan(p); final qId = 'Q$counter'; counter++; rules.add({ 'id': 'R$counter', 'type': 'Forward Chaining Rule', 'condition': 'IF $qId = Yes', 'action': 'THEN Score("${prog.name}|$minatKey") += $bobot', 'question': cleaned, 'weight': bobot, 'programName': prog.name, 'minatKey': minatKey, }); } } } rulesData.value = rules; } /// Get total question count int getTotalQuestions() { int count = 0; // Count questions from kerja for (var prog in programStudiKerja) { for (var minat in prog.minat.values) { count += minat.pertanyaan.length; } } // Count questions from kuliah for (var prog in programStudiKuliah) { for (var minat in prog.minat.values) { count += minat.pertanyaan.length; } } return count; } /// Count total minat int getTotalMinat() { int kerjaMinat = programStudiKerja.fold(0, (sum, prog) => sum + prog.minat.length); int kuliahMinat = programStudiKuliah.fold(0, (sum, prog) => sum + prog.minat.length); return kerjaMinat + kuliahMinat; } /// Count total jurusan int getTotalJurusan() { Set allJurusan = {}; // Collect unique jurusan from Kerja for (var prog in programStudiKerja) { for (var minat in prog.minat.values) { allJurusan.addAll(minat.jurusanTerkait); } } // Collect unique jurusan from Kuliah for (var prog in programStudiKuliah) { for (var minat in prog.minat.values) { allJurusan.addAll(minat.jurusanTerkait); } } return allJurusan.length; } /// Count total karir int getTotalKarir() { Set allKarir = {}; // Collect unique karir from Kerja for (var prog in programStudiKerja) { for (var minat in prog.minat.values) { allKarir.addAll(minat.karir); } } // Collect unique karir from Kuliah for (var prog in programStudiKuliah) { for (var minat in prog.minat.values) { allKarir.addAll(minat.karir); } } return allKarir.length; } } class PageStudentDashboard extends StatelessWidget { const PageStudentDashboard({Key? key}) : super(key: key); Future _findStudentInAllSchools(String? userId) async { if (userId == null) { // Return an empty document that won't exist return FirebaseFirestore.instance.collection('dummy').doc('dummy').get(); } final schoolsSnapshot = await FirebaseFirestore.instance.collection('schools').get(); for (var schoolDoc in schoolsSnapshot.docs) { final studentDoc = await schoolDoc.reference.collection('students').doc(userId).get(); if (studentDoc.exists) { // Save the school ID for future use final prefs = await SharedPreferences.getInstance(); await prefs.setString('school_id', schoolDoc.id); return studentDoc; } } // If student not found in any school, return an empty document return FirebaseFirestore.instance.collection('dummy').doc('dummy').get(); } @override Widget build(BuildContext context) { final User? currentUser = FirebaseAuth.instance.currentUser; return Scaffold( body: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Colors.blue.shade800, Colors.indigo.shade900, ], ), ), // Use a ListView as the main container instead of Column + SingleChildScrollView child: SafeArea( bottom: false, child: ListView( padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 8.0), children: [ // Top bar with buttons Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Profile button GestureDetector( onTap: () { Get.to(() => const ProfilePage()); }, child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2), ), ], border: Border.all( color: Colors.indigo.shade100, width: 1.5, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ currentUser?.photoURL != null ? CircleAvatar( radius: 14, backgroundImage: NetworkImage(currentUser!.photoURL!), ) : CircleAvatar( radius: 14, backgroundColor: Colors.indigo.shade100, child: Icon( Icons.person, color: Colors.indigo.shade700, size: 16, ), ), const SizedBox(width: 6), Text( "Profil", style: TextStyle( color: Colors.indigo.shade700, fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ), ), ), // About button GestureDetector( onTap: () { Get.to(() => AboutPage()); }, child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2), ), ], border: Border.all( color: Colors.indigo.shade100, width: 1.5, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.info_outline_rounded, color: Colors.indigo.shade700, size: 18, ), const SizedBox(width: 6), Text( "Tentang", style: TextStyle( color: Colors.indigo.shade700, fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ), ), ), ], ), const SizedBox(height: 20), // App Logo with Hero animation - centered Center( child: Hero( tag: 'app_logo', child: Container( width: 100, height: 100, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(25), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 15, offset: const Offset(0, 8), ), ], ), child: const Center( child: Icon( Icons.psychology, size: 60, color: Colors.indigo, ), ), ), ), ), const SizedBox(height: 20), // App Title - centered Center( child: TweenAnimationBuilder( tween: Tween(begin: 0.8, end: 1), duration: const Duration(milliseconds: 800), curve: Curves.easeOutQuad, builder: (context, double value, child) { return Transform.scale( scale: value, child: child, ); }, child: Column( children: [ const Text( 'EduGuide', style: TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: Colors.white, letterSpacing: 1.2, ), ), const SizedBox(height: 4), const Text( 'Sistem Rekomendasi Karir & Kuliah', style: TextStyle( fontSize: 15, color: Colors.white70, letterSpacing: 0.5, ), ), ], ), ), ), const SizedBox(height: 24), // User welcome section FutureBuilder( // First, get the school ID from SharedPreferences future: () async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('school_id') ?? ''; }(), builder: (context, schoolSnapshot) { if (schoolSnapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } // Once we have the school ID (or not), proceed String schoolId = schoolSnapshot.data ?? ''; return FutureBuilder( future: schoolId.isNotEmpty // If we have a school ID, try to get the student directly ? FirebaseFirestore.instance .collection('schools') .doc(schoolId) .collection('students') .doc(currentUser?.uid) .get() // If no school ID, find the student in all schools : _findStudentInAllSchools(currentUser?.uid), builder: (context, snapshot) { String userName = "Siswa"; String userClass = ""; if (snapshot.hasData && snapshot.data!.exists) { final userData = snapshot.data!.data() as Map; userName = userData['name'] ?? "Siswa"; userClass = userData['class'] ?? ""; } return Container( padding: const EdgeInsets.symmetric( vertical: 10, horizontal: 20), decoration: BoxDecoration( color: Colors.white.withOpacity(0.15), borderRadius: BorderRadius.circular(15), ), child: Row( children: [ Icon( Icons.waving_hand_rounded, color: Colors.amber.shade300, size: 24, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Hai, $userName!', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white, ), ), if (userClass.isNotEmpty) Text( 'Kelas $userClass', style: const TextStyle( fontSize: 14, color: Colors.white70, ), ), ], ), ), ], ), ); }, ); }, ), const SizedBox(height: 16), // User Recommendation History if (currentUser != null) ...[ // Recommendation History Section Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(15), ), padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.history, color: Colors.amber.shade200, size: 20, ), const SizedBox(width: 8), const Text( 'Riwayat Rekomendasi', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.white, ), ), const Spacer(), TextButton( onPressed: () { Get.to(() => const RecommendationHistoryPage()); }, style: TextButton.styleFrom( foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6), backgroundColor: Colors.blue.withOpacity(0.2), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), child: const Text( 'Lihat Semua', style: TextStyle(fontSize: 12), ), ) ], ), const SizedBox(height: 8), SizedBox( height: 170, // Fixed height for the history list child: FutureBuilder( // First get the school ID future: () async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('school_id') ?? ''; }(), builder: (context, schoolSnapshot) { if (schoolSnapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Colors.white70), strokeWidth: 2, ), ); } if (schoolSnapshot.hasError || !schoolSnapshot.hasData || schoolSnapshot.data!.isEmpty) { // If we can't get school ID, search in all schools return _buildAllSchoolsStreamBuilder( currentUser.uid); } final schoolId = schoolSnapshot.data!; // Now we have the school ID, use it to query the subcollection return StreamBuilder( stream: FirebaseFirestore.instance .collection('schools') .doc(schoolId) .collection('recommendation_history') .where('userId', isEqualTo: currentUser.uid) .orderBy('timestamp', descending: true) .limit(5) .snapshots(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Colors.white70), strokeWidth: 2, ), ); } if (snapshot.hasError) { return Center( child: Text( 'Error: ${snapshot.error}', style: const TextStyle( color: Colors.white70), ), ); } if (!snapshot.hasData || snapshot.data!.docs.isEmpty) { return Container( height: 170, decoration: BoxDecoration( color: Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(12), ), child: const Center( child: Text( 'Belum ada riwayat rekomendasi.\nMulai aplikasi untuk mendapatkan rekomendasi.', textAlign: TextAlign.center, style: TextStyle( color: Colors.white70, fontSize: 13, ), ), ), ); } return ListView.builder( padding: EdgeInsets.zero, itemCount: snapshot.data!.docs.length, itemBuilder: (context, index) { final doc = snapshot.data!.docs[index]; final data = doc.data() as Map; // Extract recommendation data String questionMode = data['questionMode'] ?? 'Tidak diketahui'; final timestamp = data['timestamp'] as Timestamp?; final formattedDate = timestamp != null ? intl.DateFormat('dd/MM/yyyy HH:mm') .format(timestamp.toDate()) : 'Tidak ada tanggal'; // Get top recommendation if available String topRecommendation = 'Tidak ada rekomendasi'; if (data['recommendations'] != null && (data['recommendations'] as List) .isNotEmpty) { final recommendations = data['recommendations'] as List; if (recommendations.isNotEmpty) { topRecommendation = recommendations[0] ['title'] ?? 'Tidak ada judul'; } } return GestureDetector( onTap: () { // Navigate to recommendation detail with school ID Get.to(() => RecommendationDetailPage( data: data, documentId: doc.id, )); }, child: Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.white.withOpacity(0.1), width: 1, ), ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: questionMode .contains('Karir') ? Colors.orange .withOpacity(0.2) : Colors.green .withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: Center( child: Icon( questionMode.contains('Karir') ? Icons.work : Icons.school, color: questionMode .contains('Karir') ? Colors.orange.shade300 : Colors.green.shade300, size: 20, ), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( topRecommendation, style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( '$questionMode • $formattedDate', style: TextStyle( color: Colors.white .withOpacity(0.7), fontSize: 12, ), ), ], ), ), Icon( Icons.chevron_right, color: Colors.white.withOpacity(0.5), size: 20, ), ], ), ), ); }, ); }, ); }, ), ), ], ), ), const SizedBox(height: 16), ], // Main Content Area - Now directly in ListView, no nested scrolling Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(30), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 20, offset: const Offset(0, 10), ), ], ), padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 12), // Welcome message with visually distinct styling Center( child: Text( 'Selamat Datang!', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: Colors.indigo.shade800, ), ), ), const SizedBox(height: 12), // Subtitle with improved styling Container( padding: const EdgeInsets.symmetric(horizontal: 16), child: const Text( 'Aplikasi ini akan membantumu menemukan program studi dan karir yang paling sesuai dengan minatmu.', textAlign: TextAlign.center, style: TextStyle( fontSize: 15, color: Colors.black54, height: 1.4, ), ), ), const SizedBox(height: 24), // Visual indicator to show content continues - arrow indicator Center( child: Icon( Icons.keyboard_double_arrow_down, color: Colors.indigo.shade200, size: 28, ), ), const SizedBox(height: 16), // User tips card with action indicator Stack( children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.blue.shade100, width: 1, ), ), child: Column( children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.blue.shade100, borderRadius: BorderRadius.circular(10), ), child: Icon( Icons.lightbulb_outline, size: 20, color: Colors.blue.shade800, ), ), const SizedBox(width: 12), Text( 'Tips Penggunaan', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.blue.shade800, ), ), ], ), const SizedBox(height: 12), const Text( 'Jawab pertanyaan dengan jujur untuk mendapatkan rekomendasi karir dan program studi yang paling sesuai dengan minat dan bakatmu.', style: TextStyle( fontSize: 13, color: Colors.black54, height: 1.4, ), ), ], ), ), // Subtle indicator to show this is important Positioned( top: 8, right: 8, child: Container( width: 8, height: 8, decoration: BoxDecoration( color: Colors.blue.shade400, shape: BoxShape.circle, ), ), ), ], ), const SizedBox(height: 24), // Action Buttons - with enhanced styling and visual cues // Primary Button Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.indigo.withOpacity(0.3), blurRadius: 8, offset: const Offset(0, 4), ), ], ), child: ElevatedButton( onPressed: () => Get.to(() => const MajorPreferencePage()), style: ElevatedButton.styleFrom( backgroundColor: Colors.indigo.shade800, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 16), minimumSize: const Size(double.infinity, 56), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), elevation: 0, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ Icon(Icons.play_arrow_rounded, size: 24), SizedBox(width: 12), Text( 'Mulai Aplikasi', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, letterSpacing: 0.5, ), ), ], ), ), ), const SizedBox(height: 16), const SizedBox(height: 16), // Footer attribution Center( child: Text( 'v1.0.0', style: TextStyle( fontSize: 12, color: Colors.grey.shade400, ), ), ), const SizedBox(height: 8), ], ), ), // Bottom spacing const SizedBox(height: 24), ], ), ), ), ); } Widget _buildAllSchoolsStreamBuilder(String userId) { return StreamBuilder( stream: FirebaseFirestore.instance.collection('schools').snapshots(), builder: (context, schoolsSnapshot) { if (schoolsSnapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white70), strokeWidth: 2, ), ); } if (schoolsSnapshot.hasError || !schoolsSnapshot.hasData || schoolsSnapshot.data!.docs.isEmpty) { return Container( height: 170, decoration: BoxDecoration( color: Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(12), ), child: const Center( child: Text( 'Tidak dapat menemukan data sekolah', textAlign: TextAlign.center, style: TextStyle( color: Colors.white70, fontSize: 13, ), ), ), ); } // Use FutureBuilder to find the right school and its recommendation history return FutureBuilder>( future: _findUserRecommendations(userId, schoolsSnapshot.data!.docs), builder: (context, recommendationsSnapshot) { if (recommendationsSnapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white70), strokeWidth: 2, ), ); } if (recommendationsSnapshot.hasError || !recommendationsSnapshot.hasData || recommendationsSnapshot.data!.isEmpty) { return Container( height: 170, decoration: BoxDecoration( color: Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(12), ), child: const Center( child: Text( 'Belum ada riwayat rekomendasi.\nMulai aplikasi untuk mendapatkan rekomendasi.', textAlign: TextAlign.center, style: TextStyle( color: Colors.white70, fontSize: 13, ), ), ), ); } List recommendations = recommendationsSnapshot.data!; // Sort recommendations by timestamp recommendations.sort((a, b) { final aTimestamp = (a.data() as Map)['timestamp'] as Timestamp?; final bTimestamp = (b.data() as Map)['timestamp'] as Timestamp?; if (aTimestamp == null) return 1; if (bTimestamp == null) return -1; return bTimestamp.compareTo(aTimestamp); }); // Limit to 5 recommendations if (recommendations.length > 5) { recommendations = recommendations.sublist(0, 5); } return ListView.builder( padding: EdgeInsets.zero, itemCount: recommendations.length, itemBuilder: (context, index) { final doc = recommendations[index]; final data = doc.data() as Map; final schoolId = doc.reference.parent.parent!.id; // Extract recommendation data String questionMode = data['questionMode'] ?? 'Tidak diketahui'; final timestamp = data['timestamp'] as Timestamp?; final formattedDate = timestamp != null ? intl.DateFormat('dd/MM/yyyy HH:mm') .format(timestamp.toDate()) : 'Tidak ada tanggal'; // Get top recommendation if available String topRecommendation = 'Tidak ada rekomendasi'; if (data['recommendations'] != null && (data['recommendations'] as List).isNotEmpty) { final recommendations = data['recommendations'] as List; if (recommendations.isNotEmpty) { topRecommendation = recommendations[0]['title'] ?? 'Tidak ada judul'; } } return GestureDetector( onTap: () { // Save the found school ID for future use SharedPreferences.getInstance().then((prefs) { prefs.setString('school_id', schoolId); }); // Navigate to recommendation detail Get.to(() => RecommendationDetailPage( data: data, documentId: doc.id, )); }, child: Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.white.withOpacity(0.1), width: 1, ), ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: questionMode.contains('Karir') ? Colors.orange.withOpacity(0.2) : Colors.green.withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: Center( child: Icon( questionMode.contains('Karir') ? Icons.work : Icons.school, color: questionMode.contains('Karir') ? Colors.orange.shade300 : Colors.green.shade300, size: 20, ), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( topRecommendation, style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( '$questionMode • $formattedDate', style: TextStyle( color: Colors.white.withOpacity(0.7), fontSize: 12, ), ), ], ), ), Icon( Icons.chevron_right, color: Colors.white.withOpacity(0.5), size: 20, ), ], ), ), ); }, ); }, ); }, ); } // Helper function to find user recommendations across all schools Future> _findUserRecommendations( String userId, List schools) async { List results = []; for (var schoolDoc in schools) { try { QuerySnapshot recommendationsSnapshot = await schoolDoc.reference .collection('recommendation_history') .where('userId', isEqualTo: userId) .orderBy('timestamp', descending: true) .limit(5) .get(); if (recommendationsSnapshot.docs.isNotEmpty) { results.addAll(recommendationsSnapshot.docs); // Save the school ID for future use if we found recommendations if (recommendationsSnapshot.docs.isNotEmpty) { final prefs = await SharedPreferences.getInstance(); await prefs.setString('school_id', schoolDoc.id); } } } catch (e) { print('Error fetching recommendations from school ${schoolDoc.id}: $e'); } } return results; } }