import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:qpay/models/responsive_policy.dart'; import 'package:qpay/screens/transaction_controller.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:qpay/screens/history/history_controller.dart'; import 'package:timeago/timeago.dart' as timeago; class HistoryScreen extends StatefulWidget { const HistoryScreen({super.key}); @override State createState() => _HistoryScreenState(); } class _HistoryScreenState extends State with TickerProviderStateMixin { late HistoryController controller; late SharedPreferences prefs; late AnimationController _controller; late TransactionController transactionController; final TextEditingController _searchController = TextEditingController(); final Map _queryParams = {}; final int _pageSize = 8; @override void initState() { super.initState(); controller = HistoryController(context); setupData(); transactionController = Provider.of( context, listen: false, ); _controller = AnimationController( duration: const Duration(milliseconds: 3000), vsync: this, )..forward(); } void setupData() async { prefs = await SharedPreferences.getInstance(); await controller.getTransactions({ 'userId': prefs.getString("userId")!, 'page': '0', 'size': _pageSize.toString(), 'sort': 'createdAt,desc', }); } @override void dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( slivers: [ SliverAppBar( title: Text( 'History', style: TextStyle(fontSize: 20, color: Colors.black), ), centerTitle: true, ), SliverToBoxAdapter( child: ListenableBuilder( listenable: controller, builder: (context, child) { return Container( padding: const EdgeInsets.all(16), child: Center( child: LayoutBuilder( builder: (context, constraints) { return SizedBox( width: double.parse( constraints.maxWidth > ResponsivePolicy.md ? ResponsivePolicy.md.toString() : constraints.maxWidth.toString(), ), child: Column( children: [ _buildSearchField(controller), SizedBox(height: 10), if (controller.model.transactions.isEmpty) Column( children: [ SizedBox(height: 30), Text( 'No transactions yet. Make a payment to see your recent activity.', style: TextStyle(fontSize: 16), textAlign: TextAlign.center, ), ], ), if (controller.model.transactions.isNotEmpty) Column( children: [ ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ ...controller.model.transactions.map( (e) => _buildTransactionItem(e, context), ), ], ), SizedBox(height: 10), if (controller.model.pageableModel != null && controller.model.pageableModel!.number < controller .model .pageableModel! .totalPages) OutlinedButton( child: Text('Load more'), onPressed: () { controller.getTransactions({ 'userId': prefs.getString( "userId", )!, 'page': (controller .model .pageableModel! .number + 1) .toString(), 'size': _pageSize.toString(), 'sort': 'createdAt,desc', }); }, ), ], ), ], ), ); }, ), ), ); }, ), ), ], ), ); } Widget _buildSearchField(HistoryController controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: TextFormField( textInputAction: TextInputAction.search, onFieldSubmitted: (value) { _queryParams.clear(); setState(() { _queryParams['creditAccount'] = _searchController.text; _queryParams['creditEmail'] = _searchController.text; _queryParams['creditPhone'] = _searchController.text; _queryParams['creditName'] = _searchController.text; _queryParams['billName'] = _searchController.text; _queryParams['amount'] = _searchController.text; _queryParams['userId'] = prefs.getString("userId")!; controller.getTransactions(_queryParams); }); }, controller: _searchController, keyboardType: TextInputType.text, decoration: InputDecoration( hintText: 'Search by name, account, email, phone', focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Theme.of(context).colorScheme.secondary, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Theme.of( context, ).colorScheme.primary.withOpacity(0.2), ), ), suffixIcon: _searchController.text.isNotEmpty ? IconButton( onPressed: () { _searchController.clear(); _queryParams.clear(); controller.getTransactions({ 'userId': prefs.getString("userId")!, }); }, icon: Icon(Icons.clear), color: Theme.of(context).colorScheme.primary, ) : null, ), onChanged: (value) { // controller.updateCreditAccount(value); }, validator: (value) { if (value == null || value.isEmpty) { return 'Please enter an amount'; } if (double.tryParse(value) == null) { return 'Please enter a valid amount'; } return null; }, ), ), ], ), ], ); } Widget _buildTransactionItem(Map e, BuildContext context) { int index = controller.model.transactions.indexOf(e); Animation animation = Tween(begin: Offset(0, -0.25), end: Offset.zero).animate( CurvedAnimation( parent: _controller, curve: Interval(0.175 * index, 1.0, curve: Curves.easeIn), ), ); return SlideTransition( position: animation, child: Skeletonizer( enabled: controller.model.isLoading, child: InkWell( customBorder: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), onTap: () { transactionController.updateReceiptData(e); context.push("/receipt"); }, child: ListTile( leading: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Theme.of( context, ).colorScheme.primary.withValues(alpha: 0.1), shape: BoxShape.circle, ), child: Image( image: AssetImage("assets/${e['paymentProcessorImage']}"), ), ), title: Row( children: [ Text( e['billName'], style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(width: 10), Icon( e['integrationStatus'] == 'SUCCESS' ? Icons.check_circle : e['integrationStatus'] == 'PENDING' ? Icons.pending : Icons.cancel, size: 16, color: e['integrationStatus'] == 'SUCCESS' ? Colors.green : e['integrationStatus'] == 'PENDING' ? Colors.orange : Colors.red, ), ], ), subtitle: Text( e['createdAt'] != null ? timeago.format(DateTime.parse(e['createdAt'])) : '', style: TextStyle(fontSize: 12), ), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( e['amount'].toStringAsFixed(2), style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), ), Text( e['totalAmount'].toStringAsFixed(2), style: TextStyle(fontSize: 12), ), ], ), ), ), ), ); } }