import 'dart:ui'; import 'package:bookmywages/consts_widgets/app_assets.dart'; import 'package:bookmywages/consts_widgets/app_colors.dart'; import 'package:bookmywages/consts_widgets/vendor_flow_drawer.dart'; import 'package:bookmywages/model/cancel_booking.dart'; import 'package:bookmywages/model/vendor_model/vendor_booking_status.dart'; import 'package:bookmywages/routers/consts_router.dart'; import 'package:bookmywages/routers/router.dart'; import 'package:bookmywages/view/user_main_screens/main_contoller.dart'; import 'package:bookmywages/view/user_main_screens/notification_page.dart'; import 'package:bookmywages/view/vendor_main_screens/vendor_maincontoller.dart'; import 'package:bookmywages/viewmodel/api_controller.dart'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:shared_preferences/shared_preferences.dart'; // For date formatting class VendorHomepage extends ConsumerStatefulWidget { const VendorHomepage({super.key}); @override ConsumerState createState() => _VendorHomepageState(); } class _VendorHomepageState extends ConsumerState with AutomaticKeepAliveClientMixin, WidgetsBindingObserver { final GlobalKey _scaffoldKey = GlobalKey(); final CarouselSliderController _controller = CarouselSliderController(); int? expandedIndex; @override bool get wantKeepAlive => true; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); Future.microtask(() async { final prefs = await SharedPreferences.getInstance(); final vendorId = prefs.getString('vendor_id') ?? ''; await updateNotificationCount(ref, type: 2, userId: vendorId); }); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed && mounted) { _refreshData(); } } void _refreshData() { if (!mounted) return; try { ref.invalidate(bannerListProvider); ref.invalidate(vendorbookingdetailsProvider); ref.invalidate(vendorserviceProvider); ref.invalidate(enquriylistProvider); ref.invalidate(vendorexpiredPlanProvider); } catch (e) { // Handle refresh error silently } } // Helper function to get status color and text Map getStatusInfo(int status) { switch (status) { case 0: return { 'text': 'Pending', 'color': Colors.orange, 'bgColor': Colors.orange.shade100, }; case 1: return { 'text': 'Scheduled', 'color': Colors.green, 'bgColor': Colors.green.shade100, }; case 2: return { 'text': 'Completed', 'color': Colors.blue, 'bgColor': Colors.blue.shade100, }; case 3: return { 'text': 'Canceled', 'color': Colors.red, 'bgColor': Colors.red.shade100, }; default: return { 'text': 'pending', 'color': Colors.blue, 'bgColor': Colors.blue.shade100, }; } } // Format date from API response String formatDate(String dateStr) { try { final date = DateTime.parse(dateStr); return DateFormat('MMMM dd, yyyy').format(date); } catch (e) { return dateStr; } } String _formatDate(String dateString) { try { return DateFormat('dd/MM/yyyy').format(DateTime.parse(dateString)); } catch (e) { return dateString; } } Widget _infoText(String label, String value) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 13, height: 1.239, ), ), Expanded( child: Text( value, style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 13, height: 1.239, color: Color(0xFF373636), ), overflow: TextOverflow.ellipsis, maxLines: 2, ), ), ], ); } Widget _buildBannerSection(AsyncValue bannerAsyncValue) { return bannerAsyncValue.when( data: (banners) { if (banners == null || banners.isEmpty) { return Container( height: 180, margin: const EdgeInsets.only(left: 16, top: 40, right: 16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.grey[200], ), child: const Center( child: Icon(Icons.image, color: Colors.grey, size: 50), ), ); } final imageUrls = banners.map((b) => b.documentUrl).toList(); return Padding( padding: const EdgeInsets.only(left: 16, top: 40, right: 16), child: CarouselSlider( options: CarouselOptions( height: 180, autoPlay: true, enlargeCenterPage: true, viewportFraction: 1.0, autoPlayCurve: Curves.fastOutSlowIn, enableInfiniteScroll: true, ), carouselController: _controller, items: imageUrls.map((url) { return Builder( builder: (BuildContext context) { return Container( height: 180, width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), image: DecorationImage( image: NetworkImage(url), fit: BoxFit.cover, onError: (exception, stackTrace) { // Handle image loading error silently }, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Left arrow Transform.translate( offset: const Offset(-15, 0), child: Padding( padding: const EdgeInsets.only(left: 8.0), child: GestureDetector( onTap: () { try { _controller.previousPage(); } catch (e) { // Handle carousel error silently } }, child: Container( width: 30, height: 30, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 5, offset: const Offset(0, 3), ), ], ), child: Center( child: Image.asset( AppAssets.arrowbutton, width: 50, height: 50, color: AppColors.thridprimary, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.arrow_back, size: 20, ); }, ), ), ), ), ), ), // Right arrow Transform.translate( offset: const Offset(15, 0), child: Padding( padding: const EdgeInsets.only(right: 8.0), child: GestureDetector( onTap: () { try { _controller.nextPage(); } catch (e) { // Handle carousel error silently } }, child: Container( width: 30, height: 30, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 5, offset: const Offset(0, 3), ), ], ), child: Center( child: Transform.rotate( angle: 3.14, child: Image.asset( AppAssets.arrowbutton, width: 50, height: 50, color: AppColors.thridprimary, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.arrow_forward, size: 20, ); }, ), ), ), ), ), ), ), ], ), ); }, ); }).toList(), ), ); }, loading: () => Container( height: 180, margin: const EdgeInsets.only(left: 16, top: 40, right: 16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.grey[200], ), child: const Center( child: Icon(Icons.image, color: Colors.grey, size: 50), ), ), error: (err, stack) => Container( height: 180, margin: const EdgeInsets.only(left: 16, top: 40, right: 16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.grey[200], ), child: const Center( child: Icon(Icons.image, color: Colors.grey, size: 50), ), ), ); } Widget _buildExpiredPlanSection(AsyncValue expiredPlan) { return expiredPlan.when( data: (plan) { if (plan == null || plan.endDate == null || plan.planName == null) { return const SizedBox(); } String formattedEndDate = ''; try { final date = DateTime.parse(plan.endDate.toString()); formattedEndDate = DateFormat('MMMM dd').format(date); } catch (_) { formattedEndDate = plan.endDate?.toString() ?? 'Not available'; } final indexController = InheritedVendorIndexController.of(context); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Container( decoration: BoxDecoration( color: AppColors.lightBlue, borderRadius: BorderRadius.circular(10), ), child: Padding( padding: const EdgeInsets.all(12), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Subscription plan', textAlign: TextAlign.center, style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 24, height: 27.59 / 24, letterSpacing: 1.0, color: Color(0xFF9C34C2), ), ), GestureDetector( onTap: () async { try { // Get the index controller reference Get.offAllNamed( RouterConts.vendorhistory, arguments: { 'historyTab': 2, // Enquiry list tab }, ); } catch (e) { // Handle navigation error silently debugPrint('Navigation error: $e'); } }, child: Text( 'View more', style: TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 15, height: 11.17 / 15, letterSpacing: 0.0, color: Color(0xFF534E4E), ), ), ), ], ), const SizedBox(height: 10), Row( children: [ Image.asset( AppAssets.subscription, width: 60, height: 60, errorBuilder: (context, error, stackTrace) { return Container( width: 60, height: 60, color: Colors.grey[200], child: const Icon(Icons.subscriptions), ); }, ), const SizedBox(width: 16), Flexible( child: Text.rich( TextSpan( children: [ TextSpan( text: 'Your ', style: TextStyle( fontFamily: 'Gilroy-Medium', color: Color(0xFF585454), fontWeight: FontWeight.w400, fontSize: 18, height: 1.86, letterSpacing: 0.1957, ), ), TextSpan( text: plan.planName ?? 'Subscription', style: TextStyle( fontFamily: 'Gilroy-Medium', color: Color(0xFFFF0000), fontWeight: FontWeight.w700, fontSize: 18, height: 1.86, letterSpacing: 0.1957, ), ), TextSpan( text: ' subscription plan was expired on ', style: TextStyle( fontFamily: 'Gilroy-Medium', color: Color(0xFF585454), fontWeight: FontWeight.w400, fontSize: 18, height: 1.86, letterSpacing: 0.1957, ), ), TextSpan( text: formattedEndDate, style: TextStyle( fontFamily: 'Gilroy-Medium', color: Color(0xFFFF0000), fontWeight: FontWeight.w700, fontSize: 18, letterSpacing: 0.1957, ), ), ], ), ), ), ], ), const SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: Text( 'Start Date: ${plan.createdDate?.split(' ').first ?? 'Not available'}', style: TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 14, height: 13 / 14, letterSpacing: 0.14, color: Color(0xFF4F4F4F), ), ), ), Flexible( child: Text( 'End Date: ${plan.endDate ?? 'Not available'}', style: TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 14, height: 13 / 14, letterSpacing: 0.14, color: Color(0xFF4F4F4F), ), ), ), ], ), const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ ElevatedButton( onPressed: () { try { Get.offAllNamed( RouterConts.vendorpackage, arguments: 1, ); } catch (e) { // Handle navigation error silently } }, style: ElevatedButton.styleFrom( backgroundColor: AppColors.primary, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), ), child: const Text( 'Renewal', style: TextStyle(color: Colors.white), ), ), ], ), ], ), ), ), ); }, loading: () => const SizedBox(), error: (e, _) => const SizedBox(), ); } Widget _buildBookingsSection(AsyncValue bookingsAsyncValue) { return bookingsAsyncValue.when( data: (bookings) { if (bookings == null || bookings.isEmpty) { return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( children: [ // Bookings header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Your Bookings', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), GestureDetector( onTap: () async { try { Get.offAllNamed( RouterConts.vendorhistory, arguments: { 'historyTab': 0, // Enquiry list tab }, ); } catch (e) { // Handle navigation error silently debugPrint('Navigation error: $e'); } }, child: const Text( 'View more', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: AppColors.thridprimary, ), ), ), ], ), const SizedBox(height: 16), // Bookings list ListView.builder( itemCount: bookings.length, shrinkWrap: true, physics: const ClampingScrollPhysics(), itemBuilder: (context, index) { final booking = bookings[index]; return Container( margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all(color: AppColors.lightGrey), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ID and View order Padding( padding: const EdgeInsets.only( right: 12, left: 12, top: 12, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'ID : ${booking.id ?? 'N/A'}', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), ], ), ), const Divider(), // Image, Company, and Status Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack( children: [ ClipRRect( borderRadius: BorderRadius.circular(12), child: (booking.images1 != null && booking.images1!.isNotEmpty) ? Image.network( booking.images1!, width: 100, height: 110, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Image.asset( AppAssets.cleaning, width: 100, height: 110, fit: BoxFit.cover, ); }, ) : Image.asset( AppAssets.cleaning, width: 100, height: 110, fit: BoxFit.cover, ), ), Positioned( top: 6, left: 6, child: Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 6, height: 6, decoration: const BoxDecoration( color: Colors.green, shape: BoxShape.circle, ), ), const SizedBox(width: 4), const Text( 'Live', style: TextStyle( fontSize: 12, color: Colors.green, fontWeight: FontWeight.bold, ), ), ], ), ), ), ], ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( booking.name ?? 'No data', style: const TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 16.11, height: 14.5 / 16.11, letterSpacing: 0.01 * 16.11, ), overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Row( children: [ Expanded( flex: 3, child: Text( booking.serviceName ?? '', style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w500, fontSize: 13.91, height: 1.3, letterSpacing: 0.01 * 13.91, color: Color(0xFF5A5A5A), ), overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 8), Expanded( flex: 2, child: Container( height: 25, decoration: BoxDecoration( color: booking.status == 3 ? const Color(0xFFFFEEEE) : booking.status == 1 ? const Color(0xFFDAE9FF) : const Color(0xFFE6F7E6), borderRadius: BorderRadius.circular(6.05), ), child: Center( child: Text( booking.status == 3 ? 'Cancel' : booking.status == 1 ? 'completed' : booking.status == 4 ? 'Scheduled' : 'pending', style: TextStyle( fontFamily: 'Gilroy-Bold', fontSize: 10.92, fontWeight: FontWeight.w400, color: booking.status == 3 ? const Color(0xFFFF0000) : booking.status == 1 ? const Color(0xFF0066FF) : const Color(0xFF2E8B57), letterSpacing: 1.0, height: 0.98, ), overflow: TextOverflow.ellipsis, ), ), ), ), ], ), const SizedBox(height: 6), Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Date :', style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), ), const SizedBox(height: 10), Text( booking.serviceDate != null ? _formatDate( booking.serviceDate!, ) : 'No date', style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), overflow: TextOverflow.ellipsis, ), ], ), ), const SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Time :', style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), ), const SizedBox(height: 10), Text( booking.serviceDate != null ? DateFormat( 'h:mm a', ).format( DateTime.parse( booking.serviceDate!, ), ) : 'No time', style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ], ), ), ], ), ), const SizedBox(height: 16), /// Contact information Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Text( "Mobile number : ", style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), ), const SizedBox(width: 7), Expanded( child: Text( booking.mobileNumber?.toString() ?? 'N/A', style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 13), Row( children: [ const Text( "E-mail ID :", style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), ), const SizedBox(width: 7), Expanded( child: Text( booking.email?.toString() ?? 'N/A', style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 13), Row( children: [ const Text( "Address : ", style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), ), const SizedBox(width: 7), Expanded( child: Text( booking.address?.toString() ?? 'N/A', style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), overflow: TextOverflow.ellipsis, maxLines: 2, ), ), ], ), const SizedBox(height: 13), const Text( "Message : ", style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), ), const SizedBox(height: 13), Text( booking.message?.toString() ?? 'N/A', style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0, ), maxLines: 3, overflow: TextOverflow.ellipsis, ), ], ), ), const SizedBox(height: 20), // Action buttons based on booking status if (booking.status == 0) Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ Expanded( child: ElevatedButton( onPressed: () async { try { final data = VendorBookingStatus( id: booking.id.toString(), status: "4", ); await ref.read( changebookingProvider(data).future, ); if (mounted) { Fluttertoast.showToast( msg: 'Booking confirmed successfully', backgroundColor: Colors.green, textColor: Colors.white, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); _refreshData(); } } catch (e) { if (mounted) { Fluttertoast.showToast( msg: 'Failed to confirm booking', backgroundColor: Colors.red, textColor: Colors.white, toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, ); } } }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0066FF), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), child: const Text( "Confirmed", style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w800, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0.289, color: Colors.white, ), ), ), ), const SizedBox(width: 12), Expanded( child: GestureDetector( onTap: () async { try { final data = CancelBookingRequest( id: booking.id.toString(), serviceId: booking.serviceId?.toString() ?? "0", type: booking.type?.toString() ?? "0", ); await ref.read( cancelbookingProvider(data).future, ); if (mounted) { Fluttertoast.showToast( msg: 'Booking cancelled successfully', backgroundColor: Colors.green, textColor: Colors.white, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); _refreshData(); } } catch (e) { if (mounted) { Fluttertoast.showToast( msg: 'Failed to cancel booking', backgroundColor: Colors.red, textColor: Colors.white, toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, ); } } }, child: Container( height: 42, decoration: BoxDecoration( border: Border.all( color: const Color(0xff534E4E), ), borderRadius: BorderRadius.circular(20), ), child: const Center( child: Text( 'Cancel Booking', style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w400, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0.02, color: Color(0xff534E4E), ), ), ), ), ), ), ], ), ) else if (booking.status == 4) Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ Expanded( child: ElevatedButton( onPressed: null, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0066FF), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), child: const Text( "pending", style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w800, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0.289, color: Colors.white, ), ), ), ), const SizedBox(width: 12), Expanded( child: GestureDetector( onTap: () async { try { final data = VendorBookingStatus( id: booking.id.toString(), status: "1", ); await ref.read( changebookingProvider(data).future, ); if (mounted) { Fluttertoast.showToast( msg: 'Booking completed successfully', backgroundColor: Colors.green, textColor: Colors.white, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); _refreshData(); } } catch (e) { if (mounted) { Fluttertoast.showToast( msg: 'Failed to complete booking', backgroundColor: Colors.red, textColor: Colors.white, toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, ); } } }, child: Container( height: 42, decoration: BoxDecoration( border: Border.all( color: const Color(0xff534E4E), ), borderRadius: BorderRadius.circular(20), ), child: const Center( child: Text( 'complete', style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w400, fontSize: 14.45, height: 13 / 14.45, letterSpacing: 0.02, color: Color(0xff534E4E), ), ), ), ), ), ), ], ), ), const SizedBox(height: 10), ], ), ); }, ), ], ), ); }, loading: () => const SizedBox.shrink(), error: (err, stack) => const SizedBox.shrink(), ); } Widget _buildEnquirySection(AsyncValue enquiryListAsync) { return enquiryListAsync.when( data: (enquiries) { if (enquiries == null || enquiries.isEmpty) { return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( children: [ // User Enquiry Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'User Enquiry', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), GestureDetector( onTap: () async { try { Get.offAllNamed( RouterConts.vendorhistory, arguments: { 'historyTab': 4, // Enquiry list tab }, ); } catch (e) { // Handle navigation error silently debugPrint('Navigation error: $e'); } }, child: const Text( 'View more', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: AppColors.thridprimary, ), ), ), ], ), const SizedBox(height: 16), // Enquiry List ListView.builder( itemCount: enquiries.length, shrinkWrap: true, physics: const ClampingScrollPhysics(), itemBuilder: (context, index) { final enquiry = enquiries[index]; final isExpanded = expandedIndex == index; return SizedBox( width: double.infinity, child: Column( children: [ Container( margin: const EdgeInsets.symmetric( vertical: 10, horizontal: 16, ), decoration: BoxDecoration( color: AppColors.secondprimary, borderRadius: BorderRadius.circular(11), border: Border.all( color: const Color(0xFFE8E8E8), width: 1, ), boxShadow: const [ BoxShadow( color: Color(0x66AEAEAE), offset: Offset(0, 2), blurRadius: 4, ), ], ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( borderRadius: BorderRadius.circular(10), child: (enquiry.images1?.isEmpty ?? true) ? Container( width: 89, height: 64, color: Colors.grey[300], child: Icon( Icons.image, color: Colors.grey[600], ), ) : Image.network( enquiry.images1!.first, width: 89, height: 64, fit: BoxFit.cover, errorBuilder: ( context, error, stackTrace, ) => Container( width: 89, height: 64, color: Colors.grey[300], child: Icon( Icons.broken_image, color: Colors.grey[600], ), ), ), ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( enquiry.vendorName ?? 'vendorName', style: const TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 16, height: 18.14 / 16, letterSpacing: 0.16, color: Colors.black, ), overflow: TextOverflow.ellipsis, ), const SizedBox(height: 12), GestureDetector( onTap: () { if (mounted) { setState(() { expandedIndex = isExpanded ? null : index; }); } }, child: Row( children: [ Expanded( child: Text( enquiry.serviceName ?? '', textAlign: TextAlign.left, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 16.11, height: 17.3 / 16.11, letterSpacing: 0.1611, color: Colors.black, ), ), ), const SizedBox(width: 4), Row( mainAxisSize: MainAxisSize.min, children: [ Text( isExpanded ? "View Less" : "View More", style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 13.15, height: 5.71 / 13.15, color: Color( 0xFFFF3D00, ), ), ), const SizedBox(width: 5), Icon( isExpanded ? Icons .keyboard_arrow_up : Icons .keyboard_arrow_down, color: const Color( 0xffFF3D00, ), ), ], ), ], ), ), ], ), ), ], ), ], ), ), ), // Animated expansion AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, height: isExpanded ? null : 0, child: isExpanded ? Container( width: double.infinity, margin: const EdgeInsets.symmetric( horizontal: 16, ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16.43), border: Border.all( color: const Color(0xFFE8E8E8), width: 0.82, ), boxShadow: const [ BoxShadow( color: Color(0xA9A9A940), blurRadius: 3.29, offset: Offset(0, 0.82), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text.rich( TextSpan( children: [ const TextSpan( text: "Name : ", style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 13, height: 1.239, ), ), TextSpan( text: enquiry.name ?? 'No Name', style: const TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 13, height: 1.239, color: Color(0xFF373636), ), ), ], ), ), ), IconButton( icon: const Icon( Icons.delete, color: AppColors.red, size: 20, ), onPressed: () async { final shouldDelete = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text( 'Delete Enquiry', ), content: const Text( 'Are you sure you want to delete this enquiry?', ), actions: [ TextButton( onPressed: () => Navigator.of( context, ).pop(false), child: const Text( 'Cancel', ), ), TextButton( onPressed: () => Navigator.of( context, ).pop(true), child: const Text( 'Delete', ), ), ], ), ); if (shouldDelete == true && mounted) { try { final success = await ref .read( enquriydeleteProvider( enquiry.id.toString(), ).future, ); if (success && mounted) { setState(() { expandedIndex = null; }); await Future.delayed( const Duration( milliseconds: 300, ), ); _refreshData(); Fluttertoast.showToast( msg: 'Enquiry deleted successfully', toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); } else if (mounted) { Fluttertoast.showToast( msg: 'Failed to delete enquiry', toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); } } catch (e) { if (mounted) { Fluttertoast.showToast( msg: 'Error deleting enquiry', toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); } } } }, ), ], ), const SizedBox(height: 8), _infoText( "Mobile number : ", enquiry.mobile ?? '', ), const SizedBox(height: 13), _infoText( "E-mail Id : ", enquiry.email ?? '', ), const SizedBox(height: 13), _infoText( "Message : ", enquiry.message ?? '', ), ], ), ) : const SizedBox.shrink(), ), ], ), ); }, ), ], ), ); }, loading: () => const SizedBox.shrink(), error: (error, stack) => const SizedBox.shrink(), ); } Widget _buildServicesSection(AsyncValue vendorServiceAsyncValue) { return vendorServiceAsyncValue.when( data: (services) { if (services == null || services.isEmpty) { return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Your Service', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), GestureDetector( onTap: () async { try { Get.offAllNamed( RouterConts.vendorhistory, arguments: { 'historyTab': 1, // Enquiry list tab }, ); } catch (e) { // Handle navigation error silently debugPrint('Navigation error: $e'); } }, child: const Text( 'View more', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: AppColors.thridprimary, ), ), ), ], ), const SizedBox(height: 12), SizedBox( height: 220, child: ListView.builder( itemCount: services.length, scrollDirection: Axis.horizontal, itemBuilder: (context, index) { final service = services[index]; return Container( width: 162, margin: const EdgeInsets.only(right: 12), child: GestureDetector( onTap: () { try { Get.toNamed( RouterConts.vendorserviceupload, arguments: service, ); } catch (e) { // Handle navigation error silently print( 'Navigation error: $e', ); // Optional: for debugging } }, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SizedBox( height: 203, child: Stack( children: [ ClipRRect( borderRadius: BorderRadius.circular(20), child: (service.images1?.isNotEmpty ?? false) ? Image.network( service.images1!.first, width: double.infinity, height: double.infinity, fit: BoxFit.cover, errorBuilder: ( context, error, stackTrace, ) => Container( color: Colors.grey[200], child: const Center( child: Icon( Icons.image_not_supported, size: 50, color: Colors.grey, ), ), ), ) : Container( color: Colors.grey[200], child: const Center( child: Icon( Icons.image_not_supported, size: 50, color: Colors.grey, ), ), ), ), Positioned( bottom: 0, left: 0, right: 0, child: ClipRRect( borderRadius: const BorderRadius.vertical( bottom: Radius.circular(20), ), child: BackdropFilter( filter: ImageFilter.blur( sigmaX: 5, sigmaY: 5, ), child: Container( padding: const EdgeInsets.all(12), color: Colors.black.withOpacity(0.3), child: Center( child: Text( service.serviceName ?? 'Service', style: const TextStyle( color: Colors.white, fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 15, height: 24.43 / 18, letterSpacing: 0.89, ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ), ), ), ), ], ), ), ], ), ), ); }, ), ), ], ), ); }, loading: () => const SizedBox.shrink(), error: (err, stack) => const SizedBox.shrink(), ); } @override Widget build(BuildContext context) { super.build(context); final bannerAsyncValue = ref.watch(bannerListProvider); final bookingsAsyncValue = ref.watch(vendorbookingdetailsProvider); final profileData = ref.watch(profilegetvendorProvider); final vendorServiceAsyncValue = ref.watch(vendorserviceProvider); final expiredPlan = ref.watch(vendorexpiredPlanProvider); final enquiryListAsync = ref.watch(enquriylistProvider); final profiles = profileData.value ?? []; return Scaffold( key: _scaffoldKey, drawer: DrawerMenuVendor( userName: profiles.isNotEmpty ? (profiles.first.name ?? "Vendor") : "Vendor", userImage: profiles.isNotEmpty ? (profiles.first.profilePic1 ?? "") : "", ), backgroundColor: Colors.white, body: SafeArea( child: SingleChildScrollView( child: Column( children: [ // Header section Padding( padding: const EdgeInsets.only(left: 16, right: 24, top: 12), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ GestureDetector( onTap: () { try { _scaffoldKey.currentState?.openDrawer(); } catch (e) { // Handle drawer error silently } }, child: Image.asset( AppAssets.menu, height: 40, errorBuilder: (context, error, stackTrace) { return const Icon(Icons.menu, size: 40); }, ), ), Padding( padding: const EdgeInsets.only(left: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Welcome', style: TextStyle( fontFamily: 'Gilroy-Bold', fontWeight: FontWeight.w700, fontSize: 20, height: 1.0, letterSpacing: 0.2372, color: Colors.black, ), ), SizedBox(height: 7), Text( '👋 ${profiles.isNotEmpty ? (profiles.first.name) : "vendor"}', style: TextStyle( fontFamily: 'Gilroy-Medium', fontWeight: FontWeight.w400, fontSize: 13.48, height: 1.0, letterSpacing: 0.2696, color: Colors.black, ), ), ], ), ), const Spacer(), Consumer( builder: (context, ref, _) { final count = ref.watch(notificationCountProvider); return Stack( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: AppColors.lightGrey, shape: BoxShape.circle, ), child: IconButton( icon: const Icon( Icons.notifications_none, color: Colors.black, ), onPressed: () async { final prefs = await SharedPreferences.getInstance(); final vendorId = prefs.getString('vendor_id') ?? ''; // Reset badge when opened ref .read( notificationCountProvider.notifier, ) .state = 0; Get.to( () => VendorController( child: NotificationPage( type: 2, id: vendorId, ), ), ); }, ), ), if (count > 0) Positioned( right: 4, top: 4, child: Container( padding: const EdgeInsets.all(4), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), constraints: const BoxConstraints( minWidth: 20, minHeight: 20, ), child: Text( '$count', style: const TextStyle( color: Colors.white, fontSize: 12, ), textAlign: TextAlign.center, ), ), ), ], ); }, ), ], ), ), // Banner Carousel _buildBannerSection(bannerAsyncValue), const SizedBox(height: 30), // Expired Plan Section _buildExpiredPlanSection(expiredPlan), const SizedBox(height: 30), // Bookings Section _buildBookingsSection(bookingsAsyncValue), // Enquiry Section _buildEnquirySection(enquiryListAsync), const SizedBox(height: 16), // Services Section _buildServicesSection(vendorServiceAsyncValue), const SizedBox(height: 20), ], ), ), ), ); } }