import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import '../providers/dashboard_providers.dart'; import '../../../widgets/error_view.dart'; class ReportsScreen extends ConsumerStatefulWidget { const ReportsScreen({super.key}); @override ConsumerState createState() => _ReportsScreenState(); } class _ReportsScreenState extends ConsumerState { DateTime _start = DateTime.now().subtract(const Duration(days: 30)); DateTime _end = DateTime.now(); String? _statusFilter; Map get _params => { 'startDate': '${_start.toIso8601String().substring(0, 10)}T00:00:00.000Z', 'endDate': '${_end.toIso8601String().substring(0, 10)}T23:59:59.000Z', 'status': ?_statusFilter, }; Future _pickRange() async { final range = await showDateRangePicker( context: context, firstDate: DateTime(2020), lastDate: DateTime.now(), initialDateRange: DateTimeRange(start: _start, end: _end), ); if (range != null) { setState(() { _start = range.start; _end = range.end; }); } } @override Widget build(BuildContext context) { final fmt = DateFormat('MMM d, yyyy'); final fmtMoney = NumberFormat.currency(symbol: '\$', decimalDigits: 2); final reportAsync = ref.watch(reportProvider(_params)); return Scaffold( appBar: AppBar( title: const Text('Reports'), actions: [ PopupMenuButton( icon: Icon( Icons.filter_list, color: _statusFilter != null ? const Color(0xFF1A56DB) : null, ), onSelected: (v) => setState(() => _statusFilter = v), itemBuilder: (_) => const [ PopupMenuItem(value: null, child: Text('All statuses')), PopupMenuItem(value: 'CONFIRMED', child: Text('Confirmed')), PopupMenuItem(value: 'ACTIVE', child: Text('Active')), PopupMenuItem(value: 'CLOSED', child: Text('Closed')), PopupMenuItem(value: 'CANCELLED', child: Text('Cancelled')), ], ), ], ), body: Column( children: [ _DateRangeBar( start: _start, end: _end, fmt: fmt, onTap: _pickRange, ), Expanded( child: reportAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => ErrorView( message: 'Failed to load report', onRetry: () => ref.invalidate(reportProvider(_params)), ), data: (report) => CustomScrollView( slivers: [ SliverPadding( padding: const EdgeInsets.all(16), sliver: SliverToBoxAdapter( child: Column( children: [ Row( children: [ Expanded( child: _MetricCard( label: 'Reservations', value: report.totalReservations.toString(), icon: Icons.event_note, color: const Color(0xFF1A56DB), ), ), const SizedBox(width: 12), Expanded( child: _MetricCard( label: 'Revenue', value: fmtMoney .format(report.rentalRevenue / 100), icon: Icons.attach_money, color: const Color(0xFF0E9F6E), ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _MetricCard( label: 'Collected', value: fmtMoney .format(report.totalPaid / 100), icon: Icons.check_circle_outline, color: const Color(0xFF0E9F6E), ), ), const SizedBox(width: 12), Expanded( child: _MetricCard( label: 'Outstanding', value: fmtMoney .format(report.totalOutstanding / 100), icon: Icons.pending_outlined, color: const Color(0xFFE02424), ), ), ], ), ], ), ), ), if (report.items.isNotEmpty) ...[ const SliverPadding( padding: EdgeInsets.fromLTRB(16, 0, 16, 8), sliver: SliverToBoxAdapter( child: Text( 'Reservations', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15, ), ), ), ), SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 16), sliver: SliverList.separated( itemCount: report.items.length, separatorBuilder: (_, _) => const Divider(height: 1), itemBuilder: (_, i) { final item = report.items[i]; return ListTile( contentPadding: EdgeInsets.zero, title: Text( item.customerName ?? 'Walk-in', style: const TextStyle( fontWeight: FontWeight.w500), ), subtitle: Text( '${item.vehicleName ?? 'Vehicle'} • ${fmt.format(item.startDate)} – ${fmt.format(item.endDate)}', style: const TextStyle(fontSize: 12), ), trailing: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( fmtMoney.format(item.totalAmount / 100), style: const TextStyle( fontWeight: FontWeight.w600, ), ), Text( item.paymentStatus, style: TextStyle( fontSize: 11, color: item.paymentStatus == 'PAID' ? const Color(0xFF0E9F6E) : const Color(0xFF9CA3AF), ), ), ], ), ); }, ), ), ], ], ), ), ), ], ), ); } } class _DateRangeBar extends StatelessWidget { final DateTime start; final DateTime end; final DateFormat fmt; final VoidCallback onTap; const _DateRangeBar({ required this.start, required this.end, required this.fmt, required this.onTap, }); @override Widget build(BuildContext context) { return InkWell( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: const BoxDecoration( color: Color(0xFFF9FAFB), border: Border(bottom: BorderSide(color: Color(0xFFE5E7EB))), ), child: Row( children: [ const Icon(Icons.date_range_outlined, size: 18, color: Color(0xFF6B7280)), const SizedBox(width: 8), Text( '${fmt.format(start)} → ${fmt.format(end)}', style: const TextStyle(fontWeight: FontWeight.w500), ), const Spacer(), const Text('Change', style: TextStyle( color: Color(0xFF1A56DB), fontSize: 13)), ], ), ), ); } } class _MetricCard extends StatelessWidget { final String label; final String value; final IconData icon; final Color color; const _MetricCard({ required this.label, required this.value, required this.icon, required this.color, }); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 20, color: color), const SizedBox(height: 8), Text( value, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: color, ), ), const SizedBox(height: 4), Text( label, style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280), ), ), ], ), ), ); } }