import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:auto_route/auto_route.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import '../../../application/report/inventory_report/inventory_report_bloc.dart'; import '../../../application/report/transaction_report/transaction_report_bloc.dart'; import '../../../common/extension/extension.dart'; import '../../../common/theme/theme.dart'; import '../../../common/utils/pdf_service.dart'; import '../../../common/utils/permission.dart'; import '../../../injection.dart'; import '../../components/appbar/appbar.dart'; import '../../components/field/date_range_picker_field.dart'; import '../../components/report/inventory_report.dart'; import '../../components/report/transaction_report.dart'; import '../../components/toast/flushbar.dart'; @RoutePage() class DownloadReportPage extends StatefulWidget implements AutoRouteWrapper { const DownloadReportPage({super.key}); @override State createState() => _DownloadReportPageState(); @override Widget wrappedRoute(BuildContext context) => MultiBlocProvider( providers: [ BlocProvider( create: (context) => getIt() ..add(InventoryReportEvent.fetchedOutlet()), ), BlocProvider( create: (context) => getIt() ..add(TransactionReportEvent.fetchedOutlet()), ), ], child: this, ); } class _DownloadReportPageState extends State with TickerProviderStateMixin { late AnimationController _fadeController; late AnimationController _slideController; late AnimationController _scaleController; late Animation _fadeAnimation; // Date range variables for each report type DateTime? _transactionStartDate; DateTime? _transactionEndDate; DateTime? _inventoryStartDate; DateTime? _inventoryEndDate; // DateTime? _salesStartDate; // DateTime? _salesEndDate; // DateTime? _customerStartDate; // DateTime? _customerEndDate; @override void initState() { super.initState(); // Initialize animation controllers _fadeController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _slideController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); _scaleController = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, ); // Initialize animations _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: _fadeController, curve: Curves.easeInOut), ); // Start animations _fadeController.forward(); _slideController.forward(); _scaleController.forward(); } @override void dispose() { _fadeController.dispose(); _slideController.dispose(); _scaleController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColor.background, body: CustomScrollView( slivers: [ // SliverAppBar with gradient SliverAppBar( expandedHeight: 120, floating: false, pinned: true, elevation: 0, backgroundColor: AppColor.primary, flexibleSpace: CustomAppBar(title: context.lang.download_report), ), // Content SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(20), child: Column( children: [ // Report Options FadeTransition( opacity: _fadeAnimation, child: Column( children: [ // Transaction Report Card BlocBuilder< TransactionReportBloc, TransactionReportState >( builder: (context, state) { return _ReportOptionCard( title: context.lang.transaction_report, subtitle: context.lang.transaction_report_desc, icon: Icons.receipt_long_outlined, gradient: const [ AppColor.primary, AppColor.primaryLight, ], startDate: _transactionStartDate, endDate: _transactionEndDate, isLoading: state.isFetching, onDateRangeChanged: (start, end) { setState(() { _transactionStartDate = start; _transactionEndDate = end; }); if (start != null || end != null) { context.read().add( TransactionReportEvent.fetchedTransaction( start!, end!, ), ); } }, onDownload: () async { try { final status = await PermessionHelper() .checkPermission(); if (status) { final pdfFile = await TransactionReport.previewPdf( searchDateFormatted: "${_transactionStartDate?.toServerDate} - ${_transactionEndDate?.toServerDate}", outlet: state.outlet, categoryAnalyticData: state.categoryAnalytic, profitLossData: state.profitLossAnalytic, paymentMethodAnalyticData: state.paymentMethodAnalytic, productAnalyticData: state.productAnalytic, ); log("pdfFile: $pdfFile"); await HelperPdfService.openFile(pdfFile); } else { AppFlushbar.showError( context, 'Storage permission is required to save PDF', ); } } catch (e) { log("Error generating PDF: $e"); AppFlushbar.showError( context, 'Failed to generate PDF: $e', ); } }, delay: 200, ); }, ), const SizedBox(height: 20), // Inventory Report Card BlocBuilder( builder: (context, state) { return _ReportOptionCard( title: context.lang.invetory_report, subtitle: context.lang.invetory_report_desc, icon: Icons.inventory_2_outlined, gradient: const [ AppColor.secondary, AppColor.secondaryLight, ], startDate: _inventoryStartDate, endDate: _inventoryEndDate, isLoading: state.isFetching, onDateRangeChanged: (start, end) { setState(() { _inventoryStartDate = start; _inventoryEndDate = end; }); if (start != null || end != null) { context.read().add( InventoryReportEvent.fetchedInventory( start!, end!, ), ); } }, onDownload: () async { try { final status = await PermessionHelper() .checkPermission(); if (status) { final pdfFile = await InventoryReport.previewPdf( searchDateFormatted: "${_inventoryStartDate?.toServerDate} - ${_inventoryEndDate?.toServerDate}", inventory: state.inventoryAnalytic, outlet: state.outlet, ); log("pdfFile: $pdfFile"); await HelperPdfService.openFile(pdfFile); } else { AppFlushbar.showError( context, 'Storage permission is required to save PDF', ); } } catch (e) { log("Error generating PDF: $e"); AppFlushbar.showError( context, 'Failed to generate PDF: $e', ); } }, delay: 400, ); }, ), const SizedBox(height: 20), // Sales Report Card // _ReportOptionCard( // title: 'Sales Report', // subtitle: 'Export sales performance and revenue data', // icon: Icons.trending_up_outlined, // gradient: const [AppColor.info, Color(0xFF64B5F6)], // startDate: _salesStartDate, // endDate: _salesEndDate, // onDateRangeChanged: (start, end) { // setState(() { // _salesStartDate = start; // _salesEndDate = end; // }); // }, // onDownload: () => _downloadReport( // 'Sales Report', // _salesStartDate, // _salesEndDate, // ), // delay: 600, // ), // const SizedBox(height: 20), // // Customer Report Card // _ReportOptionCard( // title: 'Customer Report', // subtitle: // 'Export customer data and behavior analytics', // icon: Icons.people_outline, // gradient: const [AppColor.warning, Color(0xFFFFB74D)], // startDate: _customerStartDate, // endDate: _customerEndDate, // onDateRangeChanged: (start, end) { // setState(() { // _customerStartDate = start; // _customerEndDate = end; // }); // }, // onDownload: () => _downloadReport( // 'Customer Report', // _customerStartDate, // _customerEndDate, // ), // delay: 800, // ), ], ), ), const SizedBox(height: 40), ], ), ), ), ], ), ); } } class _ReportOptionCard extends StatefulWidget { final String title; final String subtitle; final IconData icon; final List gradient; final DateTime? startDate; final DateTime? endDate; final Function(DateTime? startDate, DateTime? endDate) onDateRangeChanged; final VoidCallback onDownload; final bool isLoading; final int delay; const _ReportOptionCard({ required this.title, required this.subtitle, required this.icon, required this.gradient, required this.startDate, required this.endDate, required this.onDateRangeChanged, required this.onDownload, required this.delay, required this.isLoading, }); @override State<_ReportOptionCard> createState() => _ReportOptionCardState(); } class _ReportOptionCardState extends State<_ReportOptionCard> with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _slideAnimation; bool _isExpanded = false; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _slideAnimation = CurvedAnimation( parent: _animationController, curve: Curves.easeInOutCubic, ); } @override void dispose() { _animationController.dispose(); super.dispose(); } void _toggleExpanded() { setState(() { _isExpanded = !_isExpanded; if (_isExpanded) { _animationController.forward(); } else { _animationController.reverse(); } }); } @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: widget.gradient, ), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: widget.gradient.first.withOpacity(0.3), blurRadius: 15, offset: const Offset(0, 8), ), ], ), child: Column( children: [ // Header Section GestureDetector( onTap: _toggleExpanded, child: Container( padding: const EdgeInsets.all(20), child: Row( children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColor.white.withOpacity(0.2), borderRadius: BorderRadius.circular(16), ), child: Icon(widget.icon, color: AppColor.white, size: 32), ), const SizedBox(width: 20), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.title, style: AppStyle.lg.copyWith( color: AppColor.white, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( widget.subtitle, style: AppStyle.sm.copyWith( color: AppColor.white.withOpacity(0.8), ), ), ], ), ), AnimatedRotation( turns: _isExpanded ? 0.25 : 0, duration: const Duration(milliseconds: 300), child: Icon( Icons.arrow_forward_ios, color: AppColor.white.withOpacity(0.8), size: 20, ), ), ], ), ), ), // Expandable Content SizeTransition( sizeFactor: _slideAnimation, child: Container( padding: const EdgeInsets.fromLTRB(20, 0, 20, 20), child: Column( children: [ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppColor.white.withOpacity(0.1), borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.lang.select_date_range, style: AppStyle.md.copyWith( color: AppColor.white, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), // Date Range Picker Field Style DateRangePickerField( placeholder: context.lang.select_date_range, startDate: widget.startDate, endDate: widget.endDate, onChanged: widget.onDateRangeChanged, ), const SizedBox(height: 20), // Download Button SizedBox( width: double.infinity, child: ElevatedButton( onPressed: widget.startDate != null && widget.endDate != null ? widget.onDownload : null, style: ElevatedButton.styleFrom( backgroundColor: AppColor.white, foregroundColor: widget.gradient.first, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 0, ), child: widget.isLoading ? Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SpinKitCircle( color: widget.gradient.first, size: 24, ), const SizedBox(width: 8), Text( 'Loading', style: AppStyle.md.copyWith( color: widget.gradient.first, fontWeight: FontWeight.bold, ), ), ], ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.download_rounded, color: widget.gradient.first, ), const SizedBox(width: 8), Text( context.lang.download_report, style: AppStyle.md.copyWith( color: widget.gradient.first, fontWeight: FontWeight.bold, ), ), ], ), ), ), ], ), ), ], ), ), ), ], ), ); } }