import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import '../../../core/providers/auth_provider.dart'; import '../providers/dashboard_providers.dart'; import 'dashboard_shell.dart'; import '../../../widgets/stat_card.dart'; import '../../../widgets/error_view.dart'; import '../../../widgets/reservation_card.dart'; class HomeScreen extends ConsumerWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final employee = ref.watch(authProvider).employee; final metricsAsync = ref.watch(dashboardMetricsProvider); final recentAsync = ref.watch( reservationsProvider({'page': 1, 'pageSize': 5}), ); final onlineAsync = ref.watch( reservationsProvider({ 'status': 'DRAFT', 'source': 'MARKETPLACE', 'page': 1, 'pageSize': 100, }), ); final pendingOnline = onlineAsync.valueOrNull?.data.length ?? 0; final unreadCount = ref.watch(unreadCountProvider).valueOrNull ?? 0; return DashboardShell( appBar: AppBar( title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Hello, ${employee?.firstName ?? 'there'}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), Text( DateFormat('EEEE, MMMM d').format(DateTime.now()), style: const TextStyle(fontSize: 12, color: Color(0xFF6B7280)), ), ], ), actions: [ IconButton( icon: unreadCount > 0 ? Badge( label: Text('$unreadCount'), child: const Icon(Icons.notifications_outlined), ) : const Icon(Icons.notifications_outlined), onPressed: () => context.push('/dashboard/notifications'), ), _AvatarMenu(employee: employee), ], ), body: RefreshIndicator( onRefresh: () async { ref.invalidate(dashboardMetricsProvider); ref.invalidate(reservationsProvider); }, child: ListView( padding: const EdgeInsets.all(16), children: [ metricsAsync.when( loading: () => const SizedBox( height: 200, child: Center(child: CircularProgressIndicator()), ), error: (e, _) => ErrorView( message: 'Could not load metrics', onRetry: () => ref.invalidate(dashboardMetricsProvider), ), data: (metrics) => Column( children: [ Row( children: [ Expanded( child: StatCard( title: 'Total Vehicles', value: '${metrics.totalVehicles}', icon: Icons.directions_car, color: const Color(0xFF1A56DB), subtitle: '${metrics.availableVehicles} available', ), ), const SizedBox(width: 12), Expanded( child: StatCard( title: 'Active Rentals', value: '${metrics.activeReservations}', icon: Icons.event_available, color: const Color(0xFF0E9F6E), ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: StatCard( title: 'Revenue', value: '\$${NumberFormat.compact().format(metrics.totalRevenue / 100)}', icon: Icons.attach_money, color: const Color(0xFF5521B5), ), ), const SizedBox(width: 12), Expanded( child: StatCard( title: 'Customers', value: '${metrics.totalCustomers}', icon: Icons.people, color: const Color(0xFFB45309), ), ), ], ), const SizedBox(height: 12), Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Fleet Occupancy', style: TextStyle( fontWeight: FontWeight.w600, color: Color(0xFF374151), ), ), const SizedBox(height: 12), LinearProgressIndicator( value: metrics.occupancyRate / 100, backgroundColor: const Color(0xFFE5E7EB), valueColor: const AlwaysStoppedAnimation( Color(0xFF1A56DB), ), minHeight: 8, borderRadius: BorderRadius.circular(4), ), const SizedBox(height: 8), Text( '${metrics.occupancyRate.toStringAsFixed(1)}% occupied', style: const TextStyle( fontSize: 13, color: Color(0xFF6B7280), ), ), ], ), ), ), ], ), ), if (pendingOnline > 0) ...[ const SizedBox(height: 16), InkWell( onTap: () => context.push('/dashboard/online-reservations'), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), decoration: BoxDecoration( color: const Color(0xFFFFF3CD), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFFF6B00)), ), child: Row( children: [ const Icon( Icons.public, color: Color(0xFFFF6B00), size: 22, ), const SizedBox(width: 12), Expanded( child: Text( '$pendingOnline online request${pendingOnline == 1 ? '' : 's'} pending approval', style: const TextStyle( fontWeight: FontWeight.w600, color: Color(0xFF92400E), ), ), ), const Icon(Icons.chevron_right, color: Color(0xFFFF6B00)), ], ), ), ), ], const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Recent Reservations', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF111928), ), ), TextButton( onPressed: () => context.go('/dashboard/reservations'), child: const Text('View all'), ), ], ), const SizedBox(height: 8), recentAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => const ErrorView( message: 'Could not load recent reservations', ), data: (res) => res.data.isEmpty ? const Center( child: Padding( padding: EdgeInsets.all(24), child: Text( 'No reservations yet', style: TextStyle(color: Color(0xFF9CA3AF)), ), ), ) : Column( children: res.data .map( (r) => Padding( padding: const EdgeInsets.only(bottom: 10), child: ReservationCard( reservation: r, onTap: () => context.push( '/dashboard/reservations/${r.id}', ), ), ), ) .toList(), ), ), ], ), ), ); } } class _AvatarMenu extends ConsumerWidget { final dynamic employee; const _AvatarMenu({required this.employee}); @override Widget build(BuildContext context, WidgetRef ref) { final initial = employee?.firstName.substring(0, 1).toUpperCase() ?? 'U'; final name = employee?.fullName ?? 'Employee'; final role = employee?.role ?? ''; return Padding( padding: const EdgeInsets.only(right: 8), child: PopupMenuButton( onSelected: (value) async { if (value == 'logout') { final confirm = await showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Sign out'), content: const Text('Are you sure you want to sign out?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Cancel'), ), TextButton( onPressed: () => Navigator.pop(context, true), style: TextButton.styleFrom( foregroundColor: const Color(0xFFE02424), ), child: const Text('Sign out'), ), ], ), ); if (confirm == true) { await ref.read(authProvider.notifier).logout(); if (context.mounted) context.go('/landing'); } } }, offset: const Offset(0, 44), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), itemBuilder: (_) => [ PopupMenuItem( enabled: false, padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( name, style: const TextStyle( fontWeight: FontWeight.w600, color: Color(0xFF111928), fontSize: 14, ), ), if (role.isNotEmpty) Text( role, style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280), ), ), const SizedBox(height: 4), const Divider(height: 1), ], ), ), const PopupMenuItem( value: 'logout', child: Row( children: [ Icon(Icons.logout, size: 18, color: Color(0xFFE02424)), SizedBox(width: 10), Text('Sign out', style: TextStyle(color: Color(0xFFE02424))), ], ), ), ], child: CircleAvatar( backgroundColor: const Color(0xFF1A56DB), radius: 18, child: Text( initial, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14, ), ), ), ), ); } }