import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:animated_bottom_navigation_bar/animated_bottom_navigation_bar.dart'; import '../../core/constants/app_constants.dart'; import '../../models/recommendation_model.dart'; import '../../models/user_model.dart'; import '../../services/firebase_service.dart'; import '../../services/daily_checkin_service.dart'; import '../camera/photo_picker_screen.dart'; import '../history/history_screen.dart'; import '../profile/profile_screen.dart'; import '../onboarding/daily_checkin_screen.dart'; import '../recommendation/recommendation_screen.dart'; import '../chatbot/amor_chatbot_screen.dart'; class HomeScreen extends ConsumerStatefulWidget { const HomeScreen({super.key}); @override ConsumerState createState() => _HomeScreenState(); } class _HomeScreenState extends ConsumerState with SingleTickerProviderStateMixin { int _currentIndex = 0; final PageController _pageController = PageController(); final GlobalKey<_HomeTabState> _homeTabKey = GlobalKey<_HomeTabState>(); late AnimationController _fabAnimController; @override void initState() { super.initState(); _fabAnimController = AnimationController( vsync: this, duration: const Duration(milliseconds: 400), )..forward(); // Show daily check-in when app first opens (non-mandatory, user can skip) WidgetsBinding.instance.addPostFrameCallback((_) { _checkDailyCheckin(isMandatory: false); }); } @override void dispose() { _pageController.dispose(); _fabAnimController.dispose(); super.dispose(); } void _onTabTapped(int index) { setState(() => _currentIndex = index); _pageController.animateToPage( index, duration: AppConstants.shortAnimation, curve: Curves.easeInOut, ); } @override Widget build(BuildContext context) { return Scaffold( extendBody: true, body: PageView( controller: _pageController, onPageChanged: (index) => setState(() => _currentIndex = index), children: [ _HomeTab(key: _homeTabKey), const _HistoryTab(), const _FavoritesTab(), const _ProfileTab(), ], ), floatingActionButton: ScaleTransition( scale: CurvedAnimation( parent: _fabAnimController, curve: Curves.elasticOut, ), child: Container( width: 62, height: 62, decoration: BoxDecoration( shape: BoxShape.circle, gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFFD4A574), Color(0xFF8B5E3C), Color(0xFF6B3A2A)], ), boxShadow: [ BoxShadow( color: const Color(0xFF8B5E3C).withOpacity(0.45), blurRadius: 16, spreadRadius: 2, offset: const Offset(0, 6), ), BoxShadow( color: const Color(0xFFD4A574).withOpacity(0.3), blurRadius: 24, offset: const Offset(0, 2), ), ], ), child: FloatingActionButton( onPressed: () => _handleScanMenu(context), backgroundColor: Colors.transparent, elevation: 0, shape: const CircleBorder(), child: const Icon( Icons.document_scanner_rounded, color: Colors.white, size: 26, ), ), ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, bottomNavigationBar: AnimatedBottomNavigationBar( icons: const [ Icons.home_rounded, Icons.history_rounded, Icons.favorite_rounded, Icons.person_rounded, ], activeIndex: _currentIndex, gapLocation: GapLocation.center, notchSmoothness: NotchSmoothness.softEdge, notchMargin: 8, leftCornerRadius: 24, rightCornerRadius: 24, activeColor: const Color(0xFF6B3A2A), inactiveColor: const Color(0xFFB0A090), backgroundColor: Colors.white, splashColor: const Color(0xFF6B3A2A).withOpacity(0.08), splashSpeedInMilliseconds: 300, elevation: 16, shadow: Shadow( color: Colors.black.withOpacity(0.08), offset: const Offset(0, -4), blurRadius: 20, ), onTap: _onTabTapped, ), ); } Future _handleScanMenu(BuildContext context) async { final result = await _checkDailyCheckin(isMandatory: true); if (result && mounted) _showImageSourceOptions(context); } Future _checkDailyCheckin({bool isMandatory = false}) async { try { final user = FirebaseService().currentUser; if (user == null) return false; final shouldShow = await DailyCheckinService().shouldShowCheckin( user.uid, ); // Already done today — skip the screen regardless of context if (!shouldShow) return true; if (mounted) { final result = await Navigator.of(context).push( MaterialPageRoute( builder: (context) => DailyCheckinScreen(isMandatory: isMandatory), ), ); if (result == true) { // Refresh recommendations list on the home tab _homeTabKey.currentState?._loadLatestRecommendations(); return true; } return false; } return false; } catch (e) { debugPrint('Error checking daily check-in: $e'); return false; } } void _showImageSourceOptions(BuildContext context) { _openPhotoPicker(context); } void _openPhotoPicker(BuildContext context) async { final nav = Navigator.of(context); await nav.push( MaterialPageRoute(builder: (_) => const PhotoPickerScreen()), ); _homeTabKey.currentState?._loadLatestRecommendations(); } } // ───────────────────────────────────────────── // HOME TAB // ───────────────────────────────────────────── class _HomeTab extends ConsumerStatefulWidget { const _HomeTab({super.key}); @override ConsumerState<_HomeTab> createState() => _HomeTabState(); } class _HomeTabState extends ConsumerState<_HomeTab> { List _latestRecommendations = []; bool _isLoadingRecommendations = true; UserModel? _userData; @override void initState() { super.initState(); _loadLatestRecommendations(); } Future _loadLatestRecommendations() async { try { final user = FirebaseService().currentUser; if (user == null) { if (mounted) setState(() => _isLoadingRecommendations = false); return; } // Fetch a larger batch so after the 3-day filter we still have up to 10 final all = await FirebaseService().getUserRecommendations( user.uid, limit: 50, ); final cutoff = DateTime.now().subtract(const Duration(days: 3)); final recent = all .where((r) => r.createdAt.isAfter(cutoff) && r.selectedMenuName != null) .take(10) .toList(); // Fetch user data for nickname final userData = await FirebaseService().getUserData(user.uid); if (mounted) { setState(() { _latestRecommendations = recent; _isLoadingRecommendations = false; _userData = userData; }); } } catch (e) { debugPrint('Error loading recommendations: $e'); if (mounted) setState(() => _isLoadingRecommendations = false); } } Future _clearLatestRecommendations(BuildContext context) async { final confirm = await showDialog( context: context, builder: (ctx) => AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), title: const Text( 'Hapus Riwayat Scan?', style: TextStyle(fontFamily: 'Poppins', fontWeight: FontWeight.bold), ), content: const Text( 'Semua riwayat scan terbaru akan dihapus secara permanen. Lanjutkan?', style: TextStyle(fontFamily: 'Poppins', fontSize: 13), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text( 'Batal', style: TextStyle(fontFamily: 'Poppins', color: Colors.grey), ), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade600, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), onPressed: () => Navigator.pop(ctx, true), child: const Text( 'Hapus', style: TextStyle(fontFamily: 'Poppins', color: Colors.white), ), ), ], ), ); if (confirm != true) return; try { for (final rec in _latestRecommendations) { await FirebaseService().deleteRecommendation(rec.id); } if (mounted) { setState(() => _latestRecommendations.clear()); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text( 'Riwayat scan berhasil dihapus', style: TextStyle(fontFamily: 'Poppins'), ), backgroundColor: Colors.green.shade600, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Gagal menghapus: $e', style: const TextStyle(fontFamily: 'Poppins'), ), backgroundColor: Colors.red.shade600, behavior: SnackBarBehavior.floating, ), ); } } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF8F4EF), body: RefreshIndicator( onRefresh: _loadLatestRecommendations, color: const Color(0xFF6B3A2A), backgroundColor: Colors.white, child: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), slivers: [ // Gradient Header SliverToBoxAdapter(child: _buildGradientHeader(context)), // Stats Row SliverToBoxAdapter(child: _buildQuickStats()), // Section Title SliverToBoxAdapter(child: _buildSectionTitle(context)), // Content _isLoadingRecommendations ? SliverToBoxAdapter(child: _buildShimmerLoading()) : _latestRecommendations.isEmpty ? SliverToBoxAdapter(child: _buildEmptyState()) : _buildRecommendationsSliverList(), // Bottom padding for FAB const SliverToBoxAdapter(child: SizedBox(height: 100)), ], ), ), ); } Widget _buildGradientHeader(BuildContext context) { final user = FirebaseService().currentUser; // Gunakan nickname dari Firestore jika ada, jika tidak fallback ke displayName Auth atau default final displayName = _userData?.nickname ?? user?.displayName ?? 'Pengguna'; final firstName = displayName.split(' ').first; return Container( padding: EdgeInsets.only( top: MediaQuery.of(context).padding.top + 20, left: 24, right: 24, bottom: 28, ), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFF3C2415), // deep espresso Color(0xFF6B3A2A), // rich brown Color(0xFF8B5E3C), // warm brown ], stops: [0.0, 0.5, 1.0], ), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(32), bottomRight: Radius.circular(32), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Selamat Datang 👋', style: TextStyle( color: const Color(0xFFD4C4B0), fontSize: 13, fontWeight: FontWeight.w500, letterSpacing: 0.5, ), ), const SizedBox(height: 4), Text( firstName, style: const TextStyle( color: Colors.white, fontSize: 26, fontWeight: FontWeight.w800, letterSpacing: -0.5, ), ), ], ), ), _buildAvatar(user?.photoURL, firstName), ], ), const SizedBox(height: 20), // Action buttons row Row( children: [ Expanded( child: _buildHeaderAction( icon: Icons.document_scanner_outlined, label: 'Scan Menu', sublabel: 'Foto menu kafe', onTap: () => _navigateToScan(), ), ), const SizedBox(width: 12), Expanded( child: _buildHeaderAction( icon: Icons.smart_toy_outlined, label: 'Tanya Seputar', sublabel: 'Menu Amor Coffee', onTap: () => _navigateToChat(), ), ), ], ), ], ), ); } void _navigateToScan() async { await Navigator.of( context, ).push(MaterialPageRoute(builder: (_) => const PhotoPickerScreen())); _loadLatestRecommendations(); } void _navigateToChat() { Navigator.of(context).push( MaterialPageRoute( builder: (_) => const AmorChatbotScreen(), ), ); } Widget _buildHeaderAction({ required IconData icon, required String label, required String sublabel, required VoidCallback onTap, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.14), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.white.withOpacity(0.18)), ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0xFFD4A574).withOpacity(0.25), borderRadius: BorderRadius.circular(10), ), child: Icon(icon, color: const Color(0xFFD4A574), size: 18), ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w700, fontSize: 13, ), ), Text( sublabel, style: TextStyle( color: Colors.white.withOpacity(0.6), fontSize: 11, ), ), ], ), ), Icon( Icons.arrow_forward_ios_rounded, color: Colors.white.withOpacity(0.4), size: 12, ), ], ), ), ); } Widget _buildAvatar(String? photoURL, String firstName) { return Container( padding: const EdgeInsets.all(3), decoration: BoxDecoration( shape: BoxShape.circle, gradient: const LinearGradient( colors: [Color(0xFFD4A574), Color(0xFFE8C9A8)], ), boxShadow: [ BoxShadow( color: const Color(0xFFD4A574).withOpacity(0.4), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: Container( width: 48, height: 48, decoration: const BoxDecoration( shape: BoxShape.circle, color: Color(0xFF3C2415), ), child: photoURL != null ? ClipOval( child: Image.network( photoURL, fit: BoxFit.cover, width: 48, height: 48, errorBuilder: (_, __, ___) => _buildAvatarText(firstName), ), ) : _buildAvatarText(firstName), ), ); } Widget _buildAvatarText(String name) { return Center( child: Text( name.isNotEmpty ? name[0].toUpperCase() : 'U', style: const TextStyle( color: Color(0xFFD4A574), fontWeight: FontWeight.w800, fontSize: 20, ), ), ); } Widget _buildQuickStats() { final totalScans = _latestRecommendations.length; final selectedCount = _latestRecommendations .where((r) => r.selectedMenuName != null) .length; return Padding( padding: const EdgeInsets.fromLTRB(20, 20, 20, 0), child: Row( children: [ Expanded( child: _buildStatCard( icon: Icons.document_scanner_rounded, value: '$totalScans', label: 'Total Scan', color: const Color(0xFF6B3A2A), bgColor: const Color(0xFFFAF6F1), ), ), const SizedBox(width: 12), Expanded( child: _buildStatCard( icon: Icons.check_circle_rounded, value: '$selectedCount', label: 'Menu Dipilih', color: const Color(0xFF4CAF50), bgColor: const Color(0xFFF1F8F2), ), ), const SizedBox(width: 12), Expanded( child: _buildStatCard( icon: Icons.star_rounded, value: _getAverageScore(), label: 'Rata-rata', color: const Color(0xFFE5A100), bgColor: const Color(0xFFFFF8E8), ), ), ], ), ); } String _getAverageScore() { if (_latestRecommendations.isEmpty) return '-'; double total = 0; int count = 0; for (var rec in _latestRecommendations) { if (rec.recommendations.isNotEmpty) { total += rec.topRecommendations.first.score * 100; count++; } } if (count == 0) return '-'; return '${(total / count).toStringAsFixed(0)}%'; } Widget _buildStatCard({ required IconData icon, required String value, required String label, required Color color, required Color bgColor, }) { return Container( padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(18), border: Border.all(color: color.withOpacity(0.1)), boxShadow: [ BoxShadow( color: color.withOpacity(0.06), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( children: [ Icon(icon, color: color, size: 22), const SizedBox(height: 8), Text( value, style: TextStyle( fontSize: 20, fontWeight: FontWeight.w800, color: color, ), ), const SizedBox(height: 2), Text( label, style: const TextStyle( fontSize: 10, fontWeight: FontWeight.w500, color: Color(0xFF9E8E7E), ), textAlign: TextAlign.center, ), ], ), ); } Widget _buildSectionTitle(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(24, 24, 24, 12), child: Row( children: [ Container( width: 4, height: 22, decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFF8B5E3C), Color(0xFFD4A574)], ), borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 10), const Expanded( child: Text( 'Riwayat Scan Terbaru', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w800, color: Color(0xFF3C2415), letterSpacing: -0.3, ), ), ), if (_latestRecommendations.isNotEmpty) ...[ // Tombol hapus GestureDetector( onTap: () => _clearLatestRecommendations(context), child: Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.delete_outline_rounded, size: 14, color: Colors.red.shade600, ), const SizedBox(width: 4), Text( 'Hapus', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.red.shade600, ), ), ], ), ), ), const SizedBox(width: 8), // Tombol lihat semua GestureDetector( onTap: () { final homeState = context .findAncestorStateOfType<_HomeScreenState>(); homeState?._onTabTapped(1); }, child: Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: const Color(0xFF6B3A2A).withOpacity(0.08), borderRadius: BorderRadius.circular(20), ), child: const Row( mainAxisSize: MainAxisSize.min, children: [ Text( 'Semua', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF6B3A2A), ), ), SizedBox(width: 2), Icon( Icons.arrow_forward_ios_rounded, size: 10, color: Color(0xFF6B3A2A), ), ], ), ), ), ], ], ), ); } Widget _buildShimmerLoading() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Column( children: List.generate(3, (i) { return Container( margin: const EdgeInsets.only(bottom: 12), height: 88, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(18), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( width: 56, height: 56, margin: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFFF0EBE4), borderRadius: BorderRadius.circular(14), ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( height: 14, width: 140, decoration: BoxDecoration( color: const Color(0xFFF0EBE4), borderRadius: BorderRadius.circular(4), ), ), const SizedBox(height: 8), Container( height: 10, width: 100, decoration: BoxDecoration( color: const Color(0xFFF0EBE4), borderRadius: BorderRadius.circular(4), ), ), ], ), ), ], ), ); }), ), ); } Widget _buildEmptyState() { return Container( margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(vertical: 48, horizontal: 24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: const Color(0xFF8B5E3C).withOpacity(0.05), blurRadius: 20, offset: const Offset(0, 4), ), ], ), child: Column( children: [ // Decorative icon Container( width: 80, height: 80, decoration: BoxDecoration( gradient: LinearGradient( colors: [ const Color(0xFF6B3A2A).withOpacity(0.08), const Color(0xFFD4A574).withOpacity(0.12), ], ), shape: BoxShape.circle, ), child: const Center( child: Text('📋', style: TextStyle(fontSize: 36)), ), ), const SizedBox(height: 20), const Text( 'Belum Ada Riwayat Scan', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w700, color: Color(0xFF3C2415), ), ), const SizedBox(height: 8), Text( 'Tekan tombol scan di bawah untuk\nmemulai menemukan menu terbaik!', textAlign: TextAlign.center, style: TextStyle( fontSize: 13, color: const Color(0xFF9E8E7E), height: 1.6, ), ), const SizedBox(height: 24), // Decorative dots Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( 3, (i) => Container( width: 6, height: 6, margin: const EdgeInsets.symmetric(horizontal: 3), decoration: BoxDecoration( shape: BoxShape.circle, color: const Color(0xFFD4A574).withOpacity(0.3 + i * 0.2), ), ), ), ), ], ), ); } SliverList _buildRecommendationsSliverList() { return SliverList( delegate: SliverChildBuilderDelegate((context, index) { final rec = _latestRecommendations[index]; MenuRecommendation? selectedMenu; if (rec.selectedMenuName != null) { selectedMenu = rec.recommendations .cast() .firstWhere( (r) => r!.menuName == rec.selectedMenuName, orElse: () => null, ); } selectedMenu ??= rec.recommendations.isNotEmpty ? rec.topRecommendations.first : null; if (selectedMenu == null) return const SizedBox.shrink(); // Wrap with Dismissible untuk swipe-to-delete return Dismissible( key: ValueKey(rec.id), direction: DismissDirection.endToStart, background: Container( margin: EdgeInsets.only( left: 20, right: 20, bottom: 10, top: index == 0 ? 0 : 2, ), decoration: BoxDecoration( color: Colors.red.shade500, borderRadius: BorderRadius.circular(20), ), alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 20), child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.delete_rounded, color: Colors.white, size: 26), SizedBox(height: 4), Text( 'Hapus', style: TextStyle( color: Colors.white, fontSize: 11, fontWeight: FontWeight.w600, fontFamily: 'Poppins', ), ), ], ), ), confirmDismiss: (_) async { return await showDialog( context: context, builder: (ctx) => AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), title: const Text( 'Hapus Riwayat Ini?', style: TextStyle( fontFamily: 'Poppins', fontWeight: FontWeight.bold, ), ), content: const Text( 'Riwayat scan ini akan dihapus secara permanen.', style: TextStyle(fontFamily: 'Poppins', fontSize: 13), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text( 'Batal', style: TextStyle( fontFamily: 'Poppins', color: Colors.grey, ), ), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade600, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), onPressed: () => Navigator.pop(ctx, true), child: const Text( 'Hapus', style: TextStyle( fontFamily: 'Poppins', color: Colors.white, ), ), ), ], ), ); }, onDismissed: (_) => _deleteOneRecommendation(rec), child: _buildPremiumCard(rec, selectedMenu, index), ); }, childCount: _latestRecommendations.length), ); } Future _deleteOneRecommendation(RecommendationModel rec) async { try { await FirebaseService().deleteRecommendation(rec.id); if (mounted) { setState( () => _latestRecommendations.removeWhere((r) => r.id == rec.id), ); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text( 'Riwayat dihapus', style: TextStyle(fontFamily: 'Poppins'), ), backgroundColor: Colors.green.shade600, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), duration: const Duration(seconds: 2), ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Gagal menghapus: $e', style: const TextStyle(fontFamily: 'Poppins'), ), backgroundColor: Colors.red.shade600, behavior: SnackBarBehavior.floating, ), ); // Reload agar state tidak kacau _loadLatestRecommendations(); } } } Widget _buildPremiumCard( RecommendationModel recommendation, MenuRecommendation menu, int index, ) { final isSelected = recommendation.selectedMenuName != null; final score = (menu.score * 100).toInt(); final categoryIcon = _getCategoryIcon(menu.category); return GestureDetector( onTap: () { Navigator.of(context) .push( MaterialPageRoute( builder: (context) => RecommendationScreen(recommendation: recommendation), ), ) .then((_) => _loadLatestRecommendations()); }, child: Container( margin: EdgeInsets.only( left: 20, right: 20, bottom: 10, top: index == 0 ? 0 : 2, ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all( color: isSelected ? const Color(0xFFD4A574).withOpacity(0.3) : const Color(0xFFF0EBE4), ), boxShadow: [ BoxShadow( color: isSelected ? const Color(0xFF8B5E3C).withOpacity(0.08) : Colors.black.withOpacity(0.03), blurRadius: isSelected ? 16 : 10, offset: const Offset(0, 3), ), ], ), child: Row( children: [ // Icon with gradient Container( width: 52, height: 52, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isSelected ? [const Color(0xFF8B5E3C), const Color(0xFF6B3A2A)] : [const Color(0xFFE8DFD4), const Color(0xFFD4CFC8)], ), borderRadius: BorderRadius.circular(15), boxShadow: isSelected ? [ BoxShadow( color: const Color(0xFF8B5E3C).withOpacity(0.25), blurRadius: 8, offset: const Offset(0, 3), ), ] : [], ), child: Icon( categoryIcon, color: isSelected ? Colors.white : const Color(0xFF8B7B6B), size: 24, ), ), const SizedBox(width: 14), // Content Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Menu name + badge Row( children: [ Expanded( child: Text( menu.menuName, style: const TextStyle( fontWeight: FontWeight.w700, fontSize: 15, color: Color(0xFF3C2415), letterSpacing: -0.2, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), if (isSelected) ...[ const SizedBox(width: 6), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3, ), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF43A047), Color(0xFF66BB6A)], ), borderRadius: BorderRadius.circular(8), ), child: const Text( '✓ Dipilih', style: TextStyle( fontSize: 9, fontWeight: FontWeight.w700, color: Colors.white, letterSpacing: 0.3, ), ), ), ], ], ), const SizedBox(height: 6), // Baris bawah: harga+skor kiri, tanggal kanan Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Kiri: harga · bintang skor Row( mainAxisSize: MainAxisSize.min, children: [ Text( menu.formattedPrice, style: const TextStyle( color: Color(0xFF8B5E3C), fontWeight: FontWeight.w700, fontSize: 13, ), ), Container( width: 3, height: 3, margin: const EdgeInsets.symmetric(horizontal: 8), decoration: const BoxDecoration( color: Color(0xFFD4CFC8), shape: BoxShape.circle, ), ), Icon( Icons.star_rounded, size: 14, color: score >= 80 ? const Color(0xFFE5A100) : const Color(0xFFBDB5AB), ), const SizedBox(width: 2), Text( '$score%', style: TextStyle( fontSize: 12, color: score >= 80 ? const Color(0xFFE5A100) : const Color(0xFF9E8E7E), fontWeight: FontWeight.w600, ), ), ], ), // Kanan: tanggal const SizedBox(width: 8), Flexible( child: Text( recommendation.formattedDate, style: const TextStyle( fontSize: 11, color: Color(0xFFBDB5AB), ), maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.right, ), ), ], ), ], ), ), const SizedBox(width: 6), // Tombol hapus per item GestureDetector( onTap: () async { final confirm = await showDialog( context: context, builder: (ctx) => AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), title: const Text( 'Hapus Riwayat Ini?', style: TextStyle( fontFamily: 'Poppins', fontWeight: FontWeight.bold, ), ), content: const Text( 'Riwayat scan ini akan dihapus secara permanen.', style: TextStyle(fontFamily: 'Poppins', fontSize: 13), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text( 'Batal', style: TextStyle( fontFamily: 'Poppins', color: Colors.grey, ), ), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade600, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), onPressed: () => Navigator.pop(ctx, true), child: const Text( 'Hapus', style: TextStyle( fontFamily: 'Poppins', color: Colors.white, ), ), ), ], ), ); if (confirm == true) _deleteOneRecommendation(recommendation); }, child: Container( width: 30, height: 30, decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.delete_outline_rounded, color: Colors.red.shade400, size: 16, ), ), ), const SizedBox(width: 6), Container( width: 28, height: 28, decoration: BoxDecoration( color: const Color(0xFFF8F4EF), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.chevron_right_rounded, color: Color(0xFFBDB5AB), size: 18, ), ), ], ), ), ); } IconData _getCategoryIcon(String category) { final lower = category.toLowerCase(); if (lower.contains('coffee') || lower.contains('kopi')) return Icons.coffee; if (lower.contains('tea') || lower.contains('teh')) { return Icons.emoji_food_beverage; } if (lower.contains('food') || lower.contains('makanan')) { return Icons.restaurant; } if (lower.contains('drink') || lower.contains('minuman')) { return Icons.local_drink; } if (lower.contains('dessert') || lower.contains('snack')) return Icons.cake; return Icons.local_cafe; } } // ───────────────────────────────────────────── // OTHER TABS // ───────────────────────────────────────────── class _HistoryTab extends ConsumerWidget { const _HistoryTab(); @override Widget build(BuildContext context, WidgetRef ref) { return const HistoryScreen(initialIndex: 0); } } class _FavoritesTab extends ConsumerWidget { const _FavoritesTab(); @override Widget build(BuildContext context, WidgetRef ref) { return const HistoryScreen(initialIndex: 1); } } class _ProfileTab extends ConsumerWidget { const _ProfileTab(); @override Widget build(BuildContext context, WidgetRef ref) { return const ProfileScreen(); } }