import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:barber_app/core/database/database_service.dart'; import 'package:barber_app/core/theme/app_theme.dart'; import 'package:barber_app/features/auth/presentation/bloc/auth_bloc.dart'; import 'package:barber_app/features/finances/data/repositories/transaction_repository.dart'; import 'package:barber_app/features/haircuts/data/models/haircut_model.dart'; import 'package:barber_app/features/products/data/models/product_model.dart'; import 'package:barber_app/features/finances/data/models/transaction_model.dart'; import 'package:barber_app/features/haircuts/presentation/pages/haircuts_page.dart'; import 'package:barber_app/features/products/presentation/pages/products_page.dart'; import 'package:barber_app/features/finances/presentation/pages/finances_page.dart'; import 'package:barber_app/features/settings/presentation/pages/settings_page.dart'; import 'package:barber_app/shared/widgets/stat_card.dart'; import 'package:barber_app/features/home/presentation/widgets/revenue_chart_new.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { int _currentIndex = 0; final List _pages = [ const HomeContent(), const HaircutsPage(), const ProductsPage(), const FinancesPage(), const SettingsPage(), ]; @override Widget build(BuildContext context) { return Scaffold( extendBody: true, // Importante para a barra flutuante body: IndexedStack(index: _currentIndex, children: _pages), bottomNavigationBar: Container( margin: const EdgeInsets.fromLTRB(24, 0, 24, 30), // height: 70, // REMOVIDO: Altura fixa causava overflow. O child define a altura. constraints: const BoxConstraints(maxHeight: 100), decoration: BoxDecoration( color: AppColors.surface.withAlpha(230), borderRadius: BorderRadius.circular(35), border: Border.all(color: Colors.white.withAlpha(20), width: 1), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(100), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(35), child: Theme( data: Theme.of(context).copyWith( splashColor: Colors.transparent, highlightColor: Colors.transparent, ), child: MediaQuery.removePadding( context: context, removeBottom: true, child: BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) => setState(() => _currentIndex = index), backgroundColor: Colors.transparent, elevation: 0, selectedItemColor: AppColors.primaryColor, unselectedItemColor: AppColors.textSecondary, type: BottomNavigationBarType.fixed, showSelectedLabels: false, showUnselectedLabels: false, items: const [ BottomNavigationBarItem( icon: Icon(Icons.grid_view_rounded), activeIcon: Icon(Icons.grid_view_rounded), label: 'Início', ), BottomNavigationBarItem( icon: Icon(Icons.content_cut_rounded), label: 'Cortes', ), BottomNavigationBarItem( icon: Icon(Icons.inventory_2_rounded), label: 'Produtos', ), BottomNavigationBarItem( icon: Icon(Icons.account_balance_wallet_rounded), label: 'Finanças', ), BottomNavigationBarItem( icon: Icon(Icons.tune_rounded), label: 'Ajustes', ), ], ), ), ), ), ), ); } } class HomeContent extends StatefulWidget { const HomeContent({super.key}); @override State createState() => _HomeContentState(); } class _HomeContentState extends State { late TransactionRepository _transactionRepo; late NumberFormat _currencyFormat; @override void initState() { super.initState(); _transactionRepo = TransactionRepository(); _currencyFormat = NumberFormat.currency(locale: 'pt_BR', symbol: 'R\$'); } @override Widget build(BuildContext context) { final user = context.watch().state is AuthAuthenticated ? (context.watch().state as AuthAuthenticated).user : null; return ValueListenableBuilder( valueListenable: DatabaseService.transactionsBoxInstance.listenable(), builder: (context, Box box, _) { final transactions = _transactionRepo .getAllTransactions() .where( (t) => t.createdAt.day == DateTime.now().day && t.createdAt.month == DateTime.now().month && t.createdAt.year == DateTime.now().year, ) .toList(); final totalBalance = _transactionRepo.getBalance(); final todayRevenue = transactions .where((t) => t.type == TransactionType.revenue) .fold(0.0, (sum, t) => sum + t.amount); return ValueListenableBuilder( valueListenable: DatabaseService.haircutsBoxInstance.listenable(), builder: (context, Box haircutBox, _) { final todayHaircuts = haircutBox.values .where( (h) => h.userId == user?.id && h.dateTime.day == DateTime.now().day && h.dateTime.month == DateTime.now().month && h.dateTime.year == DateTime.now().year, ) .toList(); return ValueListenableBuilder( valueListenable: DatabaseService.productsBoxInstance.listenable(), builder: (context, Box productBox, _) { final lowStockProducts = productBox.values .where( (p) => p.userId == user?.id && p.quantity <= p.minStock, ) .toList(); final pendingReceivables = _transactionRepo .getPendingReceivables(); return Container( color: AppColors.background, child: CustomScrollView( physics: const BouncingScrollPhysics(), slivers: [ // Header Imersivo sem AppBar SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.fromLTRB(24, 60, 24, 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'BOM DIA, ${(user?.barberName ?? "BARBEIRO").toUpperCase()}', style: TextStyle( fontSize: 10, color: AppColors.primaryColor, fontWeight: FontWeight.w900, letterSpacing: 2, ), ), const SizedBox(height: 4), Text( DatabaseService.settingsBoxInstance .get(user?.id) ?.appName ?? user?.barberShopName ?? 'Barber App', style: Theme.of(context) .textTheme .displayLarge ?.copyWith(fontSize: 28), ), ], ), Container( decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.white.withAlpha(20), width: 1, ), ), child: IconButton( icon: const Icon( Icons.notifications_none_rounded, size: 28, ), onPressed: () => _showNotifications( context, lowStockProducts, pendingReceivables, ), ), ), ], ), ), ), // Card Principal Estilo FinTech Elite SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: GestureDetector( onTap: () { final homeState = context .findAncestorStateOfType<_HomePageState>(); if (homeState != null) { homeState.setState( () => homeState._currentIndex = 3, ); } }, child: Container( height: 200, width: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( colors: [ AppColors.surfaceLight, AppColors.surface, ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(36), border: Border.all( color: Colors.white.withAlpha(10), width: 1, ), boxShadow: AppColors.premiumShadow, ), child: Stack( children: [ Positioned( right: -30, top: -30, child: Icon( Icons.account_balance_wallet_rounded, size: 180, color: AppColors.primaryColor.withAlpha( 10, ), ), ), Padding( padding: const EdgeInsets.all(32), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'SALDO TOTAL', style: TextStyle( color: AppColors.textSecondary, fontWeight: FontWeight.w900, fontSize: 12, letterSpacing: 1.5, ), ), const SizedBox(height: 8), Text( _currencyFormat.format(totalBalance), style: TextStyle( fontSize: 42, fontWeight: FontWeight.w900, color: AppColors.textPrimary, fontFamily: 'Outfit', letterSpacing: -1, ), ), const SizedBox(height: 12), Container( padding: const EdgeInsets.symmetric( horizontal: 14, vertical: 8, ), decoration: BoxDecoration( color: AppColors.success.withAlpha( 20, ), borderRadius: BorderRadius.circular( 100, ), border: Border.all( color: AppColors.success .withAlpha(40), width: 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.trending_up_rounded, size: 14, color: AppColors.success, ), const SizedBox(width: 6), Text( '${_currencyFormat.format(todayRevenue)} HOJE', style: const TextStyle( color: AppColors.success, fontWeight: FontWeight.w900, fontSize: 10, ), ), ], ), ), ], ), ), ], ), ), ), ), ), // Atalhos de Ação Rápida SliverPadding( padding: const EdgeInsets.all(24), sliver: SliverToBoxAdapter( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildActionButton( context, 'NOVO CORTE', Icons.add_rounded, () { final homeState = context .findAncestorStateOfType< _HomePageState >(); if (homeState != null) { homeState.setState( () => homeState._currentIndex = 1, ); } }, ), const SizedBox(width: 16), _buildActionButton( context, 'PRODUTO', Icons.inventory_2_rounded, () { final homeState = context .findAncestorStateOfType< _HomePageState >(); if (homeState != null) { homeState.setState( () => homeState._currentIndex = 2, ); } }, ), ], ), ), ), // Grid de Métricas SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 24), sliver: SliverToBoxAdapter( child: Row( children: [ Expanded( child: StatCard( title: 'Cortes Hoje', value: todayHaircuts.length.toString(), icon: Icons.content_cut_rounded, isPrimary: true, // Agora todos são premium ), ), const SizedBox(width: 16), Expanded( child: StatCard( title: 'A Receber', value: _currencyFormat.format( pendingReceivables, ), icon: Icons.timer_outlined, isPrimary: false, iconColor: AppColors.warning, ), ), ], ), ), ), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'DESEMPENHO SEMANAL', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w900, color: AppColors.textSecondary, letterSpacing: 1.5, ), ), const SizedBox(height: 20), Container( height: 240, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(32), border: Border.all( color: Colors.white.withAlpha(10), width: 1, ), ), child: const RevenueChartNew(), ), const SizedBox( height: 120, ), // Espaço para a navbar flutuante ], ), ), ), ], ), ); }, ); }, ); }, ); } Widget _buildActionButton( BuildContext context, String label, IconData icon, VoidCallback onTap, ) { return Expanded( child: GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(vertical: 20), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(24), border: Border.all(color: Colors.white.withAlpha(10), width: 1), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, size: 20, color: AppColors.primaryColor), const SizedBox(width: 10), Flexible( child: Text( label, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontWeight: FontWeight.w900, fontSize: 11, fontFamily: 'Outfit', letterSpacing: 1, color: AppColors.textPrimary, ), ), ), ], ), ), ), ); } void _showNotifications( BuildContext context, List lowStock, double pending, ) { showModalBottomSheet( context: context, backgroundColor: AppColors.surface, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(32)), ), builder: (context) { return Container( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Notificações', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox(height: 24), if (lowStock.isEmpty && pending == 0) const Center(child: Text('Tudo sob controle!')), if (lowStock.isNotEmpty) ListTile( leading: const Icon( Icons.inventory_2, color: AppColors.error, ), title: Text('${lowStock.length} produtos em estoque baixo'), subtitle: const Text('Verifique seu inventário'), ), if (pending > 0) ListTile( leading: const Icon( Icons.attach_money, color: AppColors.warning, ), title: const Text('Valores pendentes'), subtitle: Text(_currencyFormat.format(pending)), ), const SizedBox(height: 32), ], ), ); }, ); } }