import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:gen_rentals/Screens/TransactionScreens/BillStatusToast.dart'; import 'package:provider/provider.dart'; import 'package:razorpay_flutter/razorpay_flutter.dart'; import '../../Notifier/BillProvider.dart'; import '../../Notifier/PayAmountProvider.dart'; import '../../Notifier/TransactionsProvider.dart'; import '../../Utility/AppColors.dart'; import '../../Utility/CustomSnackbar.dart'; import '../BillScreens/BillDetailScreen.dart'; import '../DashboardScreen.dart'; import 'BillPendingToast.dart'; import 'PaymentSuccessfailScreen.dart'; class TransactionsScreen extends StatefulWidget { final String sessionId; final String accId; const TransactionsScreen({ super.key, required this.sessionId, required this.accId, }); @override State createState() => _TransactionsScreenState(); } class _TransactionsScreenState extends State { bool isSuccess = false; late Razorpay _razorpay; var paymentMethod = ""; var User_contact = "0"; @override void initState() { _razorpay = Razorpay(); super.initState(); Future.microtask(() { Provider.of(context, listen: false) .fetchRentalTransactions(widget.sessionId, widget.accId); }); } void _handlePaymentSuccess(PaymentSuccessResponse response) { setState(() async { final provider = Provider.of(context, listen: false); await provider.getPaymentStatus( sessionId: widget.sessionId, empId: widget.accId, razorpayOrderId: response.orderId.toString() ); final data = provider.statusResponse; Navigator.push( context, MaterialPageRoute(builder: (context) => PaymentSuccessFaillScreen( total: "${data?.amount}", date: "${data?.date}", payMode: "UPI", status: "Success", )), ); _razorpay.clear(); CustomSnackBar.showSuccess( context: context, message: data?.message ?? "Payment Success!", ); // buttonLoading = false; }); } void _handlePaymentError(PaymentFailureResponse response) { setState(() async { final provider = Provider.of(context, listen: false); await provider.getPaymentStatus( sessionId: widget.sessionId, empId: widget.accId, razorpayOrderId: "" ); final data = provider.statusResponse; Navigator.push( context, MaterialPageRoute(builder: (context) => PaymentSuccessFaillScreen( total: "${data?.amount}", date: "${data?.date}", payMode: "UPI", status: "Fail", )), ); CustomSnackBar.showError( context: context, message: "Payment failed, please try again.", ); }); _razorpay.clear(); CustomSnackBar.showError( context: context, message: "Payment failed, please try again.", ); } void _handleExternalWallet(ExternalWalletResponse response) { _razorpay.clear(); } Future payAmountFunction(String amount) async { try { final provider = Provider.of(context, listen: false); await provider.payAmount( sessionId: widget.sessionId, empId: widget.accId, amount: amount, refType: "Payment", refId: "1", ); final data = provider.payResponse; if (data != null) { if (data.error == "0") { openCheckout(data.orderId, data.razorKey!); } else { CustomSnackBar.showError( context: context, message: "${data.message}", ); debugPrint("❌ Could not Complete Payment: ${data.message}"); } } else { debugPrint("❌ No response received from PayAmount API"); } } catch (e) { _razorpay.clear(); debugPrint("❌ 'Error occurred: $e'"); } } //razorpay payments__________________________________________________________ void openCheckout(razorPayOrderId, String razorpayKey) async { _razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess); _razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError); _razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet); // Map options = { 'key': razorpayKey, 'amount': int.parse("${((0) * 100).round()}"), 'name': 'Gen Rentals', 'order_id': razorPayOrderId, 'description': "Payment", 'currency': 'INR', 'method': 'upi', 'prefill': {'contact': User_contact, 'email': ''} }; // print(options); try { _razorpay.open(options); } catch (e, s) { debugPrint(e.toString()); } } void verifyPayment(String orderId) { isSuccess = true; setState(() { // toast(context, "Order Placed Successfully"); // print("Verify Payment"); }); _razorpay.clear(); } @override Widget build(BuildContext context) { final provider = Provider.of(context); final data = provider.transactionsResponse; return SafeArea( top: false, child: Scaffold( backgroundColor: AppColors.backgroundRegular, appBar: AppBar( automaticallyImplyLeading: false, backgroundColor: Colors.white, title: Row( children: [ InkResponse( onTap: () => Navigator.pop(context, true), child: SvgPicture.asset( "assets/svg/continue_left_ic.svg", height: 25, ), ), const SizedBox(width: 10), const Text( "Transactions", style: TextStyle( fontSize: 16, fontFamily: "Poppins", fontWeight: FontWeight.w400, color: Colors.black87, ), ), ], ), ), /// ✅ Main Content body: provider.isLoading ? const Center(child: CircularProgressIndicator()) : data == null ? const Center(child: Text("No transactions available")) : RefreshIndicator.adaptive( color: AppColors.subtitleText, onRefresh: () async { // Remove the delay and directly call the refresh await provider.fetchRentalTransactions(widget.sessionId, widget.accId); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), // Important for RefreshIndicator padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildBalanceCard(data), const SizedBox(height: 16), /// 🗓️ Monthly Grouped Transactions ...data.transactions!.entries.map((entry) { final month = entry.key; final items = entry.value; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), Text( month, style: const TextStyle( fontSize: 14, fontFamily: "Poppins", fontWeight: FontWeight.w500, color: Colors.black, ), ), SizedBox(height: 8), ...items.map((txn) => InkResponse( onTap: () async { final amount = txn.amount ?? 0; final provider = Provider.of(context, listen: false); await provider.fetchPaymentReceiptDetails( widget.sessionId, widget.accId, txn.paymentId.toString(), ); if (txn.type == "Credit") { showDialog( context: context, builder: (context) => BillStatusToast( sessionId: widget.sessionId, accId: widget.accId, ledgerId: txn.paymentId.toString(), ), ); } else if (txn.type == "Debit") { showDialog( context: context, builder: (context) => BillPendingToast( sessionId: widget.sessionId, accId: widget.accId, ledgerId: txn.paymentId.toString(), onPayNow: () { Navigator.pop(context); // handle payment navigation payAmountFunction(amount.toString()); }, ), ); } }, child: _buildTransactionItem( type: txn.type ?? "", title: txn.purpose ?? "-", date: txn.date ?? "", amount: "₹${txn.amount ?? "0.00"}", ), )), ], ); }), ], ), ), ), ), ); } /// 💰 Balance Card (Top Section) Widget _buildBalanceCard(data) { final balance = data.balanceAmount ?? "0"; final credit = data.creditAmount ?? "0"; final debit = data.debitAmount ?? "0"; return Stack( children: [ Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0xFAF3F3F3), Color(0xFAB5FFD1), ], ), borderRadius: BorderRadius.circular(16), boxShadow: [ // BoxShadow( // color: Colors.black.withOpacity(0.03), // blurRadius: 8, // offset: const Offset(0, 2), // ) ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 140,), /// Credit / Debit Row Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // ✅ Bill Paid Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( height: 46, width: 46, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(22), ), child: Center( child: SvgPicture.asset( "assets/svg/cross_up_arrow.svg", height: 18, width: 18, fit: BoxFit.contain, // Ensures the SVG scales within bounds ), ), ), const SizedBox(width: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "₹$credit", style: TextStyle( fontFamily: "Poppins", fontWeight: FontWeight.w400, fontSize: 14, fontStyle: FontStyle.normal, color: Colors.black, ), ), Text( "Bill Paid", style: TextStyle( fontFamily: "Poppins", fontSize: 12, fontStyle: FontStyle.normal, fontWeight: FontWeight.w400, color: Colors.black54, ), ), ], ), ], ), const SizedBox(width: 40), // ❌ Bill Pending Row( children: [ Container( height: 46, width: 46, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(22), ), child: Center( child: SvgPicture.asset( "assets/svg/cross_down_arrow.svg", height: 18, width: 18, fit: BoxFit.contain, // Ensures the SVG scales within bounds ), ), ), const SizedBox(width: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "₹$debit", style: const TextStyle( fontFamily: "Poppins", fontWeight: FontWeight.w400, fontSize: 14, color: Colors.black, ), ), Text( "Bill Pending", style: TextStyle( fontFamily: "Poppins", fontSize: 12, fontStyle: FontStyle.normal, fontWeight: FontWeight.w400, color: Colors.black54, ), ) ], ), ], ), ], ), ], ), ), /// White overlay card (unchanged) Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Text( "Balance Amount", style: TextStyle( fontFamily: "Poppins", color: Color(0xFFF00000), fontSize: 14, fontWeight: FontWeight.w400, ), ), const SizedBox(width: 4), SvgPicture.asset( "assets/svg/continue_ic.svg", color: const Color(0xFFF00000), height: 18, width: 18, ), ], ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "₹$balance", style: const TextStyle( fontFamily: "Poppins", color: Colors.black, fontSize: 34, fontWeight: FontWeight.w500, ), ), InkResponse( onTap: () => _openPaymentSheet(context , balance.toString()), child: Text( "Pay Now", style: TextStyle( fontFamily: "Poppins", color: Color(0xFF008CDE), fontSize: 14, fontWeight: FontWeight.w500, ), ), ), ], ), const SizedBox(height: 12), Text( "*Make sure to pay before you incur any fines.", style: TextStyle( fontFamily: "Poppins", color: Colors.grey.shade500, fontSize: 12, fontWeight: FontWeight.w400, ), ), ], ), ), ], ); } /// 📋 Transaction Item Widget _buildTransactionItem({ required String type, required String title, required String date, required String amount, }) { final isCredit = type.toLowerCase() == "credit"; return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(14), ), child: Row( children: [ CircleAvatar( radius: 20, backgroundColor: isCredit ? Colors.green.shade50 : Colors.red.shade50, child: SvgPicture.asset( isCredit ? "assets/svg/cross_up_arrow.svg" : "assets/svg/cross_down_arrow.svg", height: 18, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, overflow: TextOverflow.ellipsis, style: TextStyle( fontFamily: "Poppins", fontSize: 14, fontWeight: FontWeight.normal, color: Colors.black, ), ), const SizedBox(height: 4), Text( date, style: TextStyle( fontFamily: "Poppins", fontSize: 12, fontWeight: FontWeight.w400, color: Colors.grey, ), ), ], ), ), const SizedBox(width: 12), Text( amount, style: TextStyle( fontFamily: "Poppins", fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black87, ), ), ], ), ); } void _openPaymentSheet(BuildContext context, String totalAmountStr) { TextEditingController amountController = TextEditingController(); bool isPartPayment = false; final double totalAmount = double.tryParse(totalAmountStr) ?? 0; showModalBottomSheet( isScrollControlled: true, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(24)), ), context: context, builder: (context) { return StatefulBuilder( builder: (context, setState) { return Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, top: 16, left: 16, right: 16, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Handle Bar Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(4), ), ), ), const SizedBox(height: 16), // Title const Text( "Balance Amount Bill", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, fontFamily: "Poppins", ), ), const SizedBox(height: 6), Divider(), const SizedBox(height: 10), // Pay Total Option GestureDetector( onTap: () { setState(() => isPartPayment = false); }, child: Row( children: [ Radio( value: false, groupValue: isPartPayment, onChanged: (v) => setState(() => isPartPayment = v!), activeColor: const Color(0xFF008CDE), ), // Radio( // value: false, // groupValue: isPartPayment, // onChanged: (v) => setState(() => isPartPayment = v!), // ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Pay Total", style: TextStyle( fontSize: 14, fontFamily: "Poppins", fontWeight: FontWeight.w400, ), ), Text( "Avoid late payment fees.", style: TextStyle( color: Color(0xff5FBB54), fontSize: 12, fontFamily: "Poppins", ), ), ], ), const Spacer(), Text( "₹${totalAmount.toStringAsFixed(0)}", style: const TextStyle( fontSize: 14, fontFamily: "Poppins", fontWeight: FontWeight.w400, ), ), ], ), ), const SizedBox(height: 10), // Part Payment Option GestureDetector( onTap: () { setState(() => isPartPayment = true); }, child: Row( children: [ Radio( value: true, groupValue: isPartPayment, onChanged: (v) => setState(() => isPartPayment = v!), activeColor: const Color(0xFF008CDE), ), const Text( "Part Payment", style: TextStyle( fontSize: 14, fontFamily: "Poppins", fontWeight: FontWeight.w400, ), ), const SizedBox(width: 24), Expanded( child: Container( height: 50, alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 10), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(12), ), child: TextFormField( controller: amountController, enabled: isPartPayment, style: const TextStyle( fontSize: 14, fontFamily: "Poppins", color: Colors.black87, ), keyboardType: TextInputType.number, decoration: const InputDecoration( hintText: "Enter amount", hintStyle: TextStyle( fontSize: 14, fontFamily: "Poppins", color: Colors.grey, ), border: InputBorder.none, ), ), ), ), ], ), ), const SizedBox(height: 6), Divider(), const SizedBox(height: 6), // Continue Button SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () { double enteredAmount = isPartPayment ? double.tryParse(amountController.text) ?? 0 : totalAmount; if (enteredAmount <= 0) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Please enter a valid amount"), ), ); return; } if (isPartPayment && enteredAmount > totalAmount) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( "Entered amount cannot exceed total amount", ), ), ); return; } Navigator.pop(context); // Pass selected amount to your payAmountFunction payAmountFunction(enteredAmount.toStringAsFixed(2)); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF008CDE), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), padding: const EdgeInsets.symmetric(vertical: 16), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 22), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "Continue Payment", style: TextStyle( fontFamily: "Poppins", color: Colors.white, fontSize: 16, ), ), SvgPicture.asset( "assets/svg/continue_ic.svg", color: Colors.white, height: 25, width: 25, ), ], ), ), ), ), const SizedBox(height: 16), ], ), ); }, ); }, ); } }