import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:gen_rentals/Screens/HelpScreens/EnquiryScreen.dart'; import 'package:gen_rentals/Screens/HelpScreens/HelpScreen.dart'; import 'package:gen_rentals/Screens/ProductsDetailScreen.dart'; import 'package:gen_rentals/Screens/TransactionsScreen.dart'; import 'package:gen_rentals/Utility/AppColors.dart'; import 'package:provider/provider.dart'; import '../Models/DashboardResponse.dart'; import '../Notifier/DashboardProvider.dart'; import 'authScreen/LoginScreen.dart'; class DashboardScreen extends StatefulWidget { final String accId; final String sessionId; const DashboardScreen({super.key, required this.accId, required this.sessionId}); @override State createState() => _DashboardScreenState(); } class _DashboardScreenState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { // Only fetch dashboard if accId is valid if (widget.accId.isNotEmpty && widget.accId != "null") { Provider.of(context, listen: false).fetchDashboard(accId: widget.accId, sessionId: widget.sessionId); } else { // If accId is invalid, navigate back to login WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => LoginScreen()), ); }); } }); } @override Widget build(BuildContext context) { final dashboardProvider = Provider.of(context); final dashboardData = dashboardProvider.dashboardData; double screenWidth = MediaQuery.of(context).size.width; double screenHeight = MediaQuery.of(context).size.height; double bottomPadding = MediaQuery.of(context).padding.bottom; // Add null check at the beginning if (dashboardProvider.isLoading && dashboardData == null) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 20), Text("Loading dashboard..."), ], ), ), ); } return SafeArea( top: false, child: Scaffold( body: Container( color: const Color(0xFFF3F3F3), height: screenHeight, child: SingleChildScrollView( child: Column( children: [ // Top background image section Stack( children: [ // Background image Container( width: double.infinity, decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.white54, Color(0xFFF3F3F3), ], ), ), child: Image.asset( 'assets/images/sky_blue_bg.jpg', width: double.infinity, height: 400, fit: BoxFit.cover, ), ), // Content overlay Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header with profile Container( width: double.infinity, height: 440, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0x22FFFFFF), Color(0x33FFFFFF), Color(0x88FFFFFF), Color(0xFFF3F3F3), Color(0xFFF3F3F3), ], ), ), child: Column( children: [ const SizedBox(height: 50), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ // Profile icon Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.white.withOpacity(0.19), shape: BoxShape.circle, border: Border.all( color: Colors.white.withOpacity(0.5), width: 2, ), ), child: const Icon( Icons.person, color: Colors.white, size: 30, ), ), const SizedBox(width: 20) ], ), Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ Image.asset( 'assets/images/gene_png.png', height: 250, width: 250, ), const Text( "Welcome!", style: TextStyle( fontFamily: "Poppins", color: Colors.black, fontSize: 30, fontWeight: FontWeight.w500, ), ), // Use provider data for name Text( dashboardData?.raname ?? "Loading...", style: const TextStyle( fontFamily: "Poppins", color: Colors.grey, fontSize: 16, fontWeight: FontWeight.w400, ), ), ], ), ], ), ), const SizedBox(height: 10), // Main content section Container( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( children: [ // Balance Amount Card Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ InkResponse( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => TransactionsScreen( sessionId: widget.sessionId, accId: widget.accId, )), ); }, child: 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: [ // Use provider data for balance amount - FIXED NULL CHECK Text( dashboardData?.balanceAmount.toString() ?? "0", style: const TextStyle( fontFamily: "Poppins", color: Colors.black, fontSize: 32, fontWeight: FontWeight.w500, ), ), InkResponse( onTap: () => showPaymentBottomSheet(context), 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, ), ), ], ), ), const SizedBox(height: 20), const SizedBox(height: 20), // Subscribed Orders Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Section Title Text( "Subscribed Orders", style: TextStyle( fontFamily: "Poppins", color: Colors.grey.shade800, fontSize: 18, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), // Show loading or products list if (dashboardProvider.isLoading && dashboardData == null) const Center( child: CircularProgressIndicator(), ) else if (dashboardData?.orders == null || dashboardData!.orders!.isEmpty) const Text( "No products subscribed", style: TextStyle( fontFamily: "Poppins", color: Colors.grey, fontSize: 14, ), ) else // List of subscribed products from API Column( children: dashboardData!.orders!.map((product) { return Column( children: [ InkResponse( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => ProductsDetailScreen( sessionId: widget.sessionId, accId: widget.accId, orderId: product.orderid.toString(), )), ); }, child: _buildProductItemFromApi(product), ), const SizedBox(height: 16), ], ); }).toList(), ), ], ), ), // Feature cards grid Container( width: double.infinity, padding: const EdgeInsets.all(2), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Big card - Any Requirements Expanded( flex: 2, child: InkResponse( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => EnquiryScreen(sessionId: widget.sessionId, accId: widget.accId,) ) ); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.grey.shade200, width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 16), Text( "Any \nRequirements ?", style: TextStyle( fontFamily: "Poppins", color: Color(0xFF008CDE), fontSize: 16, fontWeight: FontWeight.w400, ), ), const SizedBox(height: 6), Text( "Submit your enquiry for requirement", style: TextStyle( fontFamily: "Poppins", color: Colors.grey.shade600, fontSize: 14, ), ), ], ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ SvgPicture.asset( "assets/svg/requirements.svg", height: 58, width: 58, ), ], ), ], ), ), ), ), const SizedBox(width: 10), // Side cards column Expanded( flex: 2, child: Column( children: [ // Have Complaints card Container( width: double.infinity, padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.grey.shade200, width: 1, ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 5, child: InkResponse( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => HelpScreen( sessionId: widget.sessionId, accId: widget.accId, )), ); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( flex: 5, child: Text( "Need Help?", style: TextStyle( fontFamily: "Poppins", color: Color(0xFF008CDE), fontSize: 14, fontWeight: FontWeight.w400, ), ), ), Expanded( flex: 2, child: SvgPicture.asset( "assets/svg/have_compaints.svg", height: 35, width: 35, ), ), ], ), const SizedBox(height: 4), Text( "Raise a ticket to resolve your issues.", style: TextStyle( fontFamily: "Poppins", color: Colors.grey.shade600, fontWeight: FontWeight.w400, fontSize: 12, ), ), ], ), ), ), ], ), ), const SizedBox(height: 8), // Know Your Payments card InkResponse( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => TransactionsScreen( sessionId: widget.sessionId, accId: widget.accId, )), ); }, child: Container( width: double.infinity, padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.grey.shade200, width: 1, ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 5, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( flex: 5, child: Text( "Transactions!", style: TextStyle( fontFamily: "Poppins", color: Color(0xFF008CDE), fontSize: 14, fontWeight: FontWeight.w400, ), ), ), Expanded( flex: 2, child: SvgPicture.asset( "assets/svg/know_pay.svg", height: 35, width: 35, ), ), ], ), const SizedBox(height: 4), Text( "View your all transactions with us.", style: TextStyle( fontFamily: "Poppins", color: Colors.grey.shade600, fontWeight: FontWeight.w400, fontSize: 12, ), ), ], ), ), ], ), ), ), ], ), ), ], ), ), SizedBox(height: 10,), ], ), ), ], ), ], ), ], ), ), ), ), ); } // Helper widget for product item from API data Widget _buildProductItemFromApi(Orders product) { final bool hasPending = product.hasPendingPayment == true; final productList = product.products ?? []; return Container( margin: const EdgeInsets.symmetric(vertical: 6), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.15), blurRadius: 6, offset: const Offset(0, 2), ), ], ), child: Stack( children: [ // ===== Red Strip (Behind Card) ===== if (hasPending) Positioned.fill( top: null, child: Align( alignment: Alignment.bottomCenter, child: Container( height: 45, decoration: const BoxDecoration( color: Color(0xFFFFE2E0), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(15), bottomRight: Radius.circular(15), ), ), child: Row( children: [ const SizedBox(width: 12), const Icon(Icons.info_outline, color: Colors.red, size: 18), const SizedBox(width: 6), Expanded( child: Text( product.pendingPaymentText ?? "Payment Pending. Please Pay before incurring fines.", style: const TextStyle( fontFamily: "Poppins", color: Colors.red, fontSize: 12, fontWeight: FontWeight.w400, ), ), ), const SizedBox(width: 12), ], ), ), ), ), // ===== Main White Card ===== Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15), ), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ /// Header Row (image, order id, date, badge) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xffF2F2F2), borderRadius: BorderRadius.circular(16), ), child: Image.network( product.productImage ?? "", height: 40, width: 40, fit: BoxFit.contain, errorBuilder: (context, error, stack) => Image.asset('assets/images/gene_png.png', height: 40, width: 40), ), ), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "#${product.orderid ?? "0"}", style: const TextStyle( fontFamily: "Poppins", color: Color(0xFF008CDE), fontSize: 16, fontWeight: FontWeight.w500, height: 1.2, ), ), Text( product.rentedDate ?? "Rented date not available", style: TextStyle( fontFamily: "Poppins", color: Colors.grey.shade600, fontSize: 12, ), ), ], ), ], ), // ✅ Gradient expiry badge if (product.expiringText != null && product.expiringText!.isNotEmpty) Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6), decoration: BoxDecoration( gradient: _getGradientByColor(product.expiringInColor), borderRadius: BorderRadius.circular(8), ), child: Text( product.expiringText!, style: const TextStyle( color: Colors.black87, fontSize: 12, fontWeight: FontWeight.w500, ), ), ), ], ), const SizedBox(height: 6), const Divider(), /// ===== Product List (with +3 More on same line) ===== Builder( builder: (context) { final visibleItems = productList.take(2).toList(); final remaining = productList.length - visibleItems.length; return Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Left side → Product list (bulleted) Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (int i = 0; i < visibleItems.length; i++) Padding( padding: const EdgeInsets.only(bottom: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(width: 8), const Text( "• ", style: TextStyle(color: Colors.black, fontSize: 16), ), Expanded( child: Text( visibleItems[i], style: const TextStyle( color: Colors.black, fontSize: 16, fontWeight: FontWeight.w400, ), ), ), ], ), ), ], ), ), // Right side → +x More (vertically centered) if (remaining > 0) Padding( padding: const EdgeInsets.only(left: 8, right: 4), child: Align( alignment: Alignment.center, child: Text( "+$remaining More", style: const TextStyle( fontFamily: "Poppins", color: Color(0xFF008CDE), fontSize: 14, fontWeight: FontWeight.w500, ), ), ), ), ], ); }, ), ], ), ), ], ), ); } // Gradient helper LinearGradient _getGradientByColor(String? color) { switch (color) { case "Red": return const LinearGradient( colors: [Color(0xFFFFE0E0), Color(0xFFFFC0C0)], begin: Alignment.topLeft, end: Alignment.bottomRight, ); case "Green": default: return const LinearGradient( colors: [Color(0xFFE9FFDD), Color(0xFFB5FFD1)], begin: Alignment.topLeft, end: Alignment.bottomRight, ); } } // Helper method to get gradient based on color from API // LinearGradient _getGradientByColor(String? color) { // switch (color) { // case "red": // return const LinearGradient( // begin: Alignment.topLeft, // end: Alignment.bottomRight, // colors: [ // Color(0xFFFFDDDD), // Color(0xFFFFB5B5), // ], // ); // case "green": // default: // return const LinearGradient( // begin: Alignment.topLeft, // end: Alignment.bottomRight, // colors: [ // Color(0xFFE9FFDD), // Color(0xFFB5FFD1), // ], // ); // } // } void showPaymentBottomSheet( BuildContext context, { String? payTotal = "4218", String? payBill = "", }) { showModalBottomSheet( context: context, isScrollControlled: true, // This is important backgroundColor: Colors.transparent, isDismissible: true, enableDrag: true, builder: (BuildContext context) { return PaymentBottomSheetContent( payTotal: payTotal, payBill: payBill, flag: false, ); }, ); } } class PaymentBottomSheetContent extends StatefulWidget { final String? payTotal; final String? payBill; final bool flag; const PaymentBottomSheetContent({ super.key, this.payTotal, this.payBill, required this.flag, }); @override State createState() => _PaymentBottomSheetContentState(); } class _PaymentBottomSheetContentState extends State { int selectedOption = -1; // -1 = none, 0 = total, 1 = bill, 2 = part final TextEditingController partAmountController = TextEditingController(); final FocusNode partAmountFocusNode = FocusNode(); @override void initState() { super.initState(); // Auto-focus when part payment is selected partAmountFocusNode.addListener(() { if (selectedOption == 2 && partAmountFocusNode.hasFocus) { // Ensure the bottom sheet scrolls to show the text field Future.delayed(const Duration(milliseconds: 300), () { Scrollable.ensureVisible( partAmountFocusNode.context!, duration: const Duration(milliseconds: 300), ); }); } }); } @override void dispose() { partAmountController.dispose(); partAmountFocusNode.dispose(); super.dispose(); } void _handleRadioChange(int? value) { setState(() { selectedOption = value!; }); // Auto-focus on part amount field when part payment is selected if (value == 2) { Future.delayed(const Duration(milliseconds: 100), () { partAmountFocusNode.requestFocus(); }); } else { // Clear focus when other options are selected partAmountFocusNode.unfocus(); } } void _handleContinuePayment() { // ✅ Validation if (selectedOption == -1) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Please select a payment option."), backgroundColor: Colors.redAccent, ), ); return; } if (selectedOption == 2) { if (partAmountController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Please enter amount for part payment."), backgroundColor: Colors.redAccent, ), ); return; } final amount = double.tryParse(partAmountController.text.trim()); if (amount == null || amount <= 0) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Please enter a valid amount."), backgroundColor: Colors.redAccent, ), ); return; } } Navigator.pop(context); String selectedText = selectedOption == 0 ? "Pay Total ₹${widget.payTotal}" : selectedOption == 1 ? "Pay Bill ₹${widget.payBill}" : "Part Payment ₹${partAmountController.text}"; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Selected: $selectedText"), backgroundColor: Colors.green, ), ); } @override Widget build(BuildContext context) { final mediaQuery = MediaQuery.of(context); final bottomPadding = mediaQuery.viewInsets.bottom; return AnimatedPadding( padding: EdgeInsets.only(bottom: bottomPadding), duration: const Duration(milliseconds: 300), child: SafeArea( child: Container( decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(24), topRight: Radius.circular(24), ), ), child: SingleChildScrollView( // Add this to make it scrollable when keyboard is open child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header handle Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(height: 20), Text( "Balance Amount Bill", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.normalText, ), ), const SizedBox(height: 16), const Divider(height: 1, color: Color(0xFFEEEEEE)), const SizedBox(height: 16), // ====== PAY OPTIONS ====== Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Pay Total Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Radio( value: 0, groupValue: selectedOption, onChanged: _handleRadioChange, activeColor: const Color(0xFF008CDE), ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Pay Total", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black87, ), ), Text( "Avoid late payment fees.", style: TextStyle( fontSize: 13, fontWeight: FontWeight.w400, color: Colors.green.shade600, ), ), ], ), ], ), Text( "₹${widget.payTotal}", style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black, ), ), ], ), // Pay Bill if (widget.flag == true) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Radio( value: 1, groupValue: selectedOption, onChanged: _handleRadioChange, activeColor: const Color(0xFF008CDE), ), const Text( "Pay Bill", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black87, ), ), ], ), Text( "₹${widget.payBill}", style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black, ), ), ], ), // Part Payment Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Row( children: [ Radio( value: 2, groupValue: selectedOption, onChanged: _handleRadioChange, activeColor: const Color(0xFF008CDE), ), const Text( "Part Payment", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black87, ), ), const SizedBox(width: 10), if (selectedOption == 2) Expanded( child: Container( height: 50, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(12), ), child: TextField( controller: partAmountController, focusNode: partAmountFocusNode, keyboardType: TextInputType.number, textAlign: TextAlign.right, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black, ), decoration: const InputDecoration( hintText: "Enter amount", hintStyle: TextStyle( color: Colors.grey, fontSize: 14, ), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), prefixText: "₹", prefixStyle: TextStyle( color: Colors.black, fontSize: 14, fontWeight: FontWeight.w500, ), ), onChanged: (value) { // Optional: Add real-time validation if needed setState(() {}); }, ), ), ), ], ), ), ], ), ], ), const SizedBox(height: 18), const Divider(height: 1, color: Color(0xFFEEEEEE)), const SizedBox(height: 18), // Continue Payment Button SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _handleContinuePayment, 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( color: Colors.white, fontSize: 16, ), ), SvgPicture.asset( "assets/svg/continue_ic.svg", color: Colors.white, height: 25, width: 25, ), ], ), ), ), ), // Add extra space at bottom for better visibility SizedBox(height: mediaQuery.viewInsets.bottom > 0 ? 20 : 0), ], ), ), ), ), ), ); } }