import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:gen_rentals/Models/BillsModels/BillDetailsResponse.dart'; import 'package:gen_rentals/Utility/AppColors.dart'; import 'package:gen_rentals/Utility/Reusablewidgets.dart'; import 'package:provider/provider.dart'; import 'package:razorpay_flutter/razorpay_flutter.dart'; import '../../Notifier/BillProvider.dart'; import '../../Notifier/PayAmountProvider.dart'; import '../../Utility/CustomSnackbar.dart'; import '../../Utility/SharedpreferencesService.dart'; import '../DashboardScreen.dart'; import '../TransactionScreens/PaymentSuccessfailScreen.dart'; import 'BillDetailScreen.dart'; class BillDetailListScreen extends StatefulWidget { final String sessionId; final String accId; final String orderId; BillDetailListScreen({ super.key, required this.sessionId, required this.accId, required this.orderId, }); @override State createState() => _BillDetailListScreenState(); } class _BillDetailListScreenState extends State { late Razorpay _razorpay; bool? isSuccess; var paymentMethod = ""; var User_contact = "0"; final prefs = SharedPreferencesService.instance; @override void initState() { super.initState(); _razorpay =Razorpay(); Future.microtask(() { final provider = Provider.of(context, listen: false); provider.fetchBillList(widget.sessionId, widget.orderId, 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(() { CustomSnackBar.showError( context: context, message: "Payment failed!", ); Navigator.push( context, MaterialPageRoute(builder: (context) => PaymentSuccessFaillScreen( total: "10218", date: "8th Oct, 2025", payMode: "UPI", status: "Fail", )), ); }); _razorpay.clear(); CustomSnackBar.showError( context: context, message: "Payment failed, please try again.", ); } void _handleExternalWallet(ExternalWalletResponse response) { _razorpay.clear(); } Future payAmountFunction(String amount, String id) async { try { final provider = Provider.of(context, listen: false); await provider.payAmount( sessionId: widget.sessionId, empId: widget.accId, amount: amount, refType: "Payment", refId: id, ); final data = provider.payResponse; if (data != null) { if (data.error == "0") { openCheckout(data.orderId, data.razorKey!); } else { CustomSnackBar.showError( context: context, message: "Could not Complete Payment: ${data.message}", ); debugPrint("❌ Could not Complete Payment: ${data.message}"); } } else { debugPrint("❌ No response received from PayAmount API"); } } catch (e) { _razorpay.clear(); CustomSnackBar.showError( context: context, message: 'Error occurred: $e', ); } } //razorpay payments__________________________________________________________ void openCheckout(razorPayOrderId, String razorpayKey) async { final String? mobNumber = await prefs.getString("mob_number"); _razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess); _razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError); _razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet); // _buildCheckWidget(); Map options = { 'key': razorpayKey, 'amount': int.parse("${((0) * 100).round()}"), 'name': 'Gen Rentals', 'order_id': razorPayOrderId, 'description': "Bill", 'currency': 'INR', 'method': 'upi', 'prefill': {'contact': mobNumber, 'email': ''} }; // print(options); try { _razorpay.open(options); } catch (e, s) { // FirebaseCrashlytics.instance.log('Error occurred: $e'); // FirebaseCrashlytics.instance.recordError(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) { return Consumer( builder: (context, provider, _) { if (provider.isLoading) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } final latest = provider.latestBill; final bills = provider.bills; return SafeArea( top: false, child: Scaffold( 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( "Bill List", style: TextStyle( fontSize: 16, fontFamily: "Poppins", fontWeight: FontWeight.w400, color: Colors.black87, ), ), ], ), ), backgroundColor: AppColors.backgroundRegular, body: RefreshIndicator.adaptive( color: AppColors.subtitleText, onRefresh: () async { final provider = Provider.of(context, listen: false); await provider.fetchBillList(widget.sessionId, widget.orderId, widget.accId); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SectionHeading( title: 'Latest Bill', textStyle: TextStyle( fontFamily: "Poppins", color: AppColors.normalText, fontSize: 14, fontWeight: FontWeight.w500, ), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), ), const SizedBox(height: 12), // Latest Bill Card if (latest != null) InkResponse( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => BillDetailScreen( sessionId: widget.sessionId, accId: widget.accId, billId: latest.orderId.toString(), )) ); }, child: Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFFE9FFDD), Color(0xFFB5FFD1), ], ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ InkResponse( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => PaymentSuccessFaillScreen( total: "10218", date: "8th Oct, 2025", payMode: "UPI", status: "Fail", )), ); }, child: Text( "₹${latest.totalPrice ?? '0'}", style: TextStyle( fontFamily: "Poppins", color: AppColors.cardAmountText, fontSize: 34, fontWeight: FontWeight.w500, ), ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), ), child: Stack( children: [ if(latest.billPaid == "Yes") Row( children: [ SvgPicture.asset( "assets/svg/success_ic.svg", height: 18, width: 18, ), const SizedBox(width: 6), const Text( "Bill Paid", style: TextStyle( fontFamily: "Poppins", color: Color(0xFF212121), fontSize: 14, fontStyle: FontStyle.normal, fontWeight: FontWeight.w400, ), ), ], ), // Pending if(latest.billPaid == "No") InkResponse( onTap: () => _openPaymentSheet(context, provider.billListResponse!.payAmount.toString(), latest.totalPrice ?? '0', latest.orderId.toString()), child: Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), decoration: BoxDecoration( color: const Color(0xE0008CDE), borderRadius: BorderRadius.circular(14), ), child: const Text( "Pay Now", style: TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, ), ), ), ), ], ), ), ], ), const SizedBox(height: 16), Container( height: 1, color: const Color(0xFF777777).withOpacity(0.3), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Invoice raised against", style: TextStyle( fontFamily: "Poppins", fontStyle: FontStyle.normal, color: AppColors.normalText, fontWeight: FontWeight.w400, fontSize: 14, ), ), Text( "#${latest.id ?? '-'}", style: TextStyle( fontFamily: "Poppins", fontStyle: FontStyle.normal, color: AppColors.amountText, fontWeight: FontWeight.w400, fontSize: 14, ), ), ], ), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Order ID: ${latest.orderId ?? '-'}", style: TextStyle( fontFamily: "Poppins", fontStyle: FontStyle.normal, color: AppColors.normalText, fontWeight: FontWeight.w400, fontSize: 14, ), ), Text( latest.billDate ?? '', style: TextStyle( fontStyle: FontStyle.normal, fontFamily: "Poppins", color: AppColors.normalText, fontWeight: FontWeight.w400, fontSize: 14, ), ), ], ), ], ), ), ) else const Center(child: Text("No Latest Bill Found")), const SizedBox(height: 24), SectionHeading( title: 'All Previous Bills', textStyle: TextStyle( fontFamily: "Poppins", color: AppColors.normalText, fontSize: 14, fontWeight: FontWeight.w500, ), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), ), const SizedBox(height: 8), // ✅ Bill List bills.isEmpty ? const Padding( padding: EdgeInsets.symmetric(vertical: 20), child: Center(child: Text("No Previous Bills")), ) : Column( children: [ ...bills.map((bill) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: InkResponse( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => BillDetailScreen( sessionId: widget.sessionId, accId: widget.accId, billId: bill.orderId.toString(), )), ); }, child: _buildBillItem( orderId: "#${bill.id ?? ''}", fromDate: bill.billDate ?? '', toDate: bill.billDate ?? '', amount: bill.totalPrice ?? '0', ), ), ); }).toList(), const SizedBox(height: 20), // Add some bottom padding ], ), ], ), ), ), ), ), ); }, ); } Widget _buildBillItem({ String orderId = "#1253", String title = "Invoice raised against", required String fromDate, required String toDate, required String amount, }) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: Colors.grey.shade50, borderRadius: BorderRadius.circular(18), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( orderId, style: TextStyle( fontFamily: "Poppins", color: AppColors.amountText, fontWeight: FontWeight.w400, fontStyle: FontStyle.normal, fontSize: 14, ), ), const SizedBox(height: 2), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( title, style: TextStyle( fontFamily: "Poppins", color: AppColors.normalText, fontWeight: FontWeight.w400, fontStyle: FontStyle.normal, fontSize: 14, ), ), Text( fromDate, style: TextStyle( fontFamily: "Poppins", color: AppColors.subtitleText, fontSize: 12, fontStyle: FontStyle.normal, fontWeight: FontWeight.w400, ), ), ], ), const SizedBox(height: 2), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Order ID : ${orderId.replaceAll('#', '')}", style: TextStyle( fontFamily: "Poppins", color: AppColors.normalText, fontWeight: FontWeight.w400, fontStyle: FontStyle.normal, fontSize: 14, ), ), Text( "₹$amount", style: TextStyle( fontFamily: "Poppins", color: AppColors.cardAmountText, fontSize: 14, fontStyle: FontStyle.normal, fontWeight: FontWeight.w400, ), ), ], ), ], ), ); } /// Pay balance sheet void _openPaymentSheet(BuildContext context, String totalAmountStr, String billAmount, String id) { 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( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Radio( value: true, groupValue: isPartPayment, onChanged: (v) => setState(() => isPartPayment = v!), activeColor: const Color(0xFF008CDE), ), Text( "Pay Bill", style: TextStyle( fontSize: 14, fontFamily: "Poppins", fontWeight: FontWeight.w400, ), ), ], ), const SizedBox(width: 24), Text( "₹${billAmount}", style: const TextStyle( fontSize: 14, fontFamily: "Poppins", fontWeight: FontWeight.w400, ), ), ], ), ), 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), id); }, 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), ], ), ); }, ); }, ); } void showPaymentBottomSheet( BuildContext context, { String? payTotal = "4218", String? payBill = "2018", }) { showModalBottomSheet( context: context, isScrollControlled: true, // This is important backgroundColor: Colors.transparent, isDismissible: true, enableDrag: true, builder: (BuildContext context) { return PaymentBottomSheetContent( payTotal: payTotal, payBill: payBill, billFlag: true, partFlag: false, ); }, ); } }