import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../constants/item_categories.dart'; import '../services/ai_recommendation_service.dart'; import '../theme/app_theme.dart'; import 'add_item_screen.dart'; import 'ai_chat_screen.dart'; import 'item_screen.dart'; import 'perfil_screen.dart'; import 'week_screen.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { int _index = 0; static const _tabs = [ _HomeContent(), ItemScreen(), WeekScreen(), AiChatScreen(), PerfilScreen(), ]; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, body: IndexedStack(index: _index, children: _tabs), bottomNavigationBar: _buildBottomNav(), ); } Widget _buildBottomNav() { return Container( decoration: BoxDecoration( color: AppColors.surface, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.06), blurRadius: 20, offset: const Offset(0, -4), ), ], ), child: SafeArea( top: false, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _navItem(0, Icons.home_rounded, 'Início'), _navItem(1, Icons.inventory_2_rounded, 'Itens'), _navItem(2, Icons.calendar_month_rounded, 'Semana'), _navItem(3, Icons.auto_awesome_rounded, 'IA'), _navItem(4, Icons.person_rounded, 'Perfil'), ], ), ), ), ); } Widget _navItem(int idx, IconData icon, String label) { final selected = _index == idx; return Expanded( child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(AppRadius.lg), onTap: () => setState(() => _index = idx), child: AnimatedContainer( duration: const Duration(milliseconds: 220), curve: Curves.easeOut, padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: selected ? AppColors.primary.withValues(alpha: 0.08) : Colors.transparent, borderRadius: BorderRadius.circular(AppRadius.lg), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( icon, size: 24, color: selected ? AppColors.primary : AppColors.textSecondary, ), const SizedBox(height: 3), Text( label, style: TextStyle( fontSize: 11, fontWeight: selected ? FontWeight.w700 : FontWeight.w500, color: selected ? AppColors.primary : AppColors.textSecondary, ), ), ], ), ), ), ), ); } } // ============================================================ // Home tab content // ============================================================ class _HomeContent extends StatefulWidget { const _HomeContent(); @override State<_HomeContent> createState() => _HomeContentState(); } class _HomeContentState extends State<_HomeContent> { int _itemCount = 0; String _userName = ''; List> _todayItems = []; List> _recentItems = []; bool _isLoading = true; static const _weekdayLong = [ 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado', 'Domingo', ]; @override void initState() { super.initState(); _loadData(); } String _dateKey(DateTime d) => '${d.year.toString().padLeft(4, '0')}-' '${d.month.toString().padLeft(2, '0')}-' '${d.day.toString().padLeft(2, '0')}'; Future _loadData() async { setState(() => _isLoading = true); try { final user = Supabase.instance.client.auth.currentUser; if (user == null) return; final all = await Supabase.instance.client .from('items') .select('id') .eq('user_id', user.id); final recent = await Supabase.instance.client .from('items') .select('*, item_images(image_url)') .eq('user_id', user.id) .order('id', ascending: false) .limit(8); Map? userRow; try { userRow = await Supabase.instance.client .from('users') .select('nome') .eq('id', user.id) .maybeSingle(); } catch (_) {} final today = DateTime.now(); final plan = await Supabase.instance.client .from('plans') .select('plan_items(items(*, item_images(image_url)))') .eq('user_id', user.id) .eq('data', _dateKey(today)) .maybeSingle(); List> todayItems = []; if (plan != null) { final planItems = plan['plan_items'] as List? ?? []; todayItems = planItems .where((pi) => pi['items'] != null) .map>( (pi) => Map.from(pi['items']), ) .toList(); } if (!mounted) return; setState(() { _itemCount = all.length; _userName = userRow?['nome'] ?? user.email?.split('@').first ?? 'Utilizador'; _recentItems = List>.from(recent); _todayItems = todayItems; _isLoading = false; }); } catch (e) { debugPrint('Error loading home: $e'); if (mounted) setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, body: SafeArea( bottom: false, child: RefreshIndicator( onRefresh: _loadData, color: AppColors.primary, child: ListView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.fromLTRB(20, 20, 20, 100), children: [ _buildGreeting(), const SizedBox(height: 20), _buildHeroCard(), const SizedBox(height: 16), _buildAiSuggestionButton(), const SizedBox(height: 24), _buildSectionHeader('Hoje'), const SizedBox(height: 12), _buildTodaySection(), const SizedBox(height: 24), _buildSectionHeader('Itens recentes'), const SizedBox(height: 12), _buildRecentItems(), const SizedBox(height: 24), _buildAddCta(), ], ), ), ), ); } Widget _buildGreeting() { final hour = DateTime.now().hour; final saudacao = hour < 12 ? 'Bom dia' : hour < 19 ? 'Boa tarde' : 'Boa noite'; return Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(saudacao, style: AppText.bodySecondary), const SizedBox(height: 2), Text(_userName.isEmpty ? 'Olá!' : _userName, style: AppText.h2), ], ), ), Container( width: 44, height: 44, decoration: BoxDecoration( color: AppColors.surface, shape: BoxShape.circle, boxShadow: AppShadows.soft, ), child: const Icon( Icons.notifications_none_rounded, color: AppColors.textPrimary, ), ), ], ); } Widget _buildHeroCard() { final today = DateTime.now(); final dayName = _weekdayLong[today.weekday - 1]; return Container( decoration: BoxDecoration( gradient: AppColors.brandGradient, borderRadius: BorderRadius.circular(AppRadius.xl), boxShadow: AppShadows.brand, ), padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon( Icons.calendar_today_rounded, color: Colors.white, size: 18, ), const SizedBox(width: 8), Text( '$dayName, ${today.day}/${today.month}', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w600, ), ), ], ), const SizedBox(height: 14), Row( children: [ _heroStat( value: '${_todayItems.length}', label: 'Hoje', icon: Icons.today_rounded, ), const SizedBox(width: 12), _heroStat( value: '$_itemCount', label: 'No inventário', icon: Icons.inventory_2_rounded, ), ], ), ], ), ); } Widget _heroStat({ required String value, required String label, required IconData icon, }) { return Expanded( child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.18), borderRadius: BorderRadius.circular(AppRadius.md), border: Border.all(color: Colors.white.withValues(alpha: 0.25)), ), child: Row( children: [ Icon(icon, color: Colors.white, size: 22), const SizedBox(width: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( value, style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, height: 1, ), ), const SizedBox(height: 2), Text( label, style: TextStyle( color: Colors.white.withValues(alpha: 0.85), fontSize: 12, ), ), ], ), ], ), ), ); } Widget _buildSectionHeader(String title) { return Text(title, style: AppText.h3); } Widget _buildTodaySection() { if (_isLoading) { return _skeletonRow(); } if (_todayItems.isEmpty) { return Container( padding: const EdgeInsets.all(18), decoration: AppDecorations.card(), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: AppColors.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.md), ), child: const Icon( Icons.event_available_rounded, color: AppColors.primary, ), ), const SizedBox(width: 12), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Nada planeado para hoje', style: AppText.body), SizedBox(height: 2), Text( 'Vá à aba Semana para organizar', style: AppText.caption, ), ], ), ), ], ), ); } return SizedBox( height: 130, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: _todayItems.length, itemBuilder: (_, i) => _itemCard(_todayItems[i], compact: true), ), ); } Widget _buildRecentItems() { if (_isLoading) return _skeletonRow(); if (_recentItems.isEmpty) { return Container( padding: const EdgeInsets.all(18), decoration: AppDecorations.card(), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: AppColors.accent.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(AppRadius.md), ), child: const Icon(Icons.add_box_rounded, color: AppColors.accent), ), const SizedBox(width: 12), const Expanded( child: Text('Adicione o seu primeiro item', style: AppText.body), ), ], ), ); } return SizedBox( height: 160, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: _recentItems.length, itemBuilder: (_, i) => _itemCard(_recentItems[i]), ), ); } Widget _itemCard(Map item, {bool compact = false}) { final images = item['item_images'] as List?; final imageUrl = (images != null && images.isNotEmpty) ? images.first['image_url'] as String? : null; final cat = categoryById(item['categoria'] as String?); final size = compact ? 110.0 : 130.0; return Container( width: size, margin: const EdgeInsets.only(right: 12), decoration: AppDecorations.card(), clipBehavior: Clip.antiAlias, child: Material( color: Colors.transparent, child: InkWell( onTap: () {}, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack( children: [ Container( width: double.infinity, height: compact ? 72 : 90, color: cat.color.withValues(alpha: 0.15), child: imageUrl != null ? Image.network( imageUrl, fit: BoxFit.cover, errorBuilder: (_, _, _) => Center( child: Icon(cat.icon, color: cat.color, size: 32), ), ) : Center( child: Icon(cat.icon, color: cat.color, size: 32), ), ), ], ), Padding( padding: const EdgeInsets.fromLTRB(10, 8, 10, 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item['nome'] ?? '', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), const SizedBox(height: 2), Text( cat.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 11, color: cat.color, fontWeight: FontWeight.w600, ), ), ], ), ), ], ), ), ), ); } Widget _skeletonRow() { return SizedBox( height: 130, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: 3, itemBuilder: (_, _) => Container( width: 110, margin: const EdgeInsets.only(right: 12), decoration: BoxDecoration( color: AppColors.surfaceAlt, borderRadius: BorderRadius.circular(AppRadius.lg), ), ), ), ); } Widget _buildAiSuggestionButton() { return Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(AppRadius.lg), onTap: () async { final service = AiRecommendationService(); showDialog( context: context, barrierDismissible: false, builder: (_) => const Center(child: CircularProgressIndicator()), ); final response = await service.sendMessage( 'vou fazer uma viagem de 4 horas de onibus', silent: true, ); if (!mounted) return; Navigator.of(context).pop(); showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (_) => Container( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.6, ), margin: const EdgeInsets.all(12), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(AppRadius.xl), ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.auto_awesome, color: AppColors.primary), const SizedBox(width: 10), const Expanded( child: Text('Sugestao da IA', style: AppText.h3), ), IconButton( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close_rounded), ), ], ), const Divider(height: 20), Flexible( child: SingleChildScrollView( child: Text(response, style: AppText.body), ), ), ], ), ), ); }, child: Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( gradient: AppColors.warmGradient, borderRadius: BorderRadius.circular(AppRadius.lg), boxShadow: [ BoxShadow( color: AppColors.accent.withValues(alpha: 0.3), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.25), borderRadius: BorderRadius.circular(AppRadius.md), ), child: const Icon( Icons.auto_awesome, color: Colors.white, size: 24, ), ), const SizedBox(width: 14), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Pedir sugestao a IA', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: Colors.white, ), ), SizedBox(height: 2), Text( 'Monta um outfit para o teu dia', style: TextStyle(fontSize: 12, color: Colors.white70), ), ], ), ), const Icon( Icons.arrow_forward_ios_rounded, size: 16, color: Colors.white70, ), ], ), ), ), ); } Widget _buildAddCta() { return Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(AppRadius.lg), onTap: () { Navigator.of(context) .push(MaterialPageRoute(builder: (_) => const AddItemScreen())) .then((_) => _loadData()); }, child: Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(AppRadius.lg), border: Border.all( color: AppColors.primary.withValues(alpha: 0.3), style: BorderStyle.solid, width: 1.5, ), ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( gradient: AppColors.brandGradient, borderRadius: BorderRadius.circular(AppRadius.md), ), child: const Icon( Icons.add_rounded, color: Colors.white, size: 24, ), ), const SizedBox(width: 14), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Adicionar item', style: AppText.h3), SizedBox(height: 2), Text( 'Foto, categoria e tags em segundos', style: AppText.caption, ), ], ), ), const Icon( Icons.arrow_forward_ios_rounded, size: 16, color: AppColors.textTertiary, ), ], ), ), ), ); } }