1999 lines
93 KiB
Dart
1999 lines
93 KiB
Dart
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<VendorHomepage> createState() => _VendorHomepageState();
|
|
}
|
|
|
|
class _VendorHomepageState extends ConsumerState<VendorHomepage>
|
|
with AutomaticKeepAliveClientMixin, WidgetsBindingObserver {
|
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
|
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<String, dynamic> 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: <Widget>[
|
|
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<Widget>((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: <Widget>[
|
|
// 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>[
|
|
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>[
|
|
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: <Widget>[
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
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>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
// Bookings header
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
// ID and View order
|
|
Padding(
|
|
padding: const EdgeInsets.only(
|
|
right: 12,
|
|
left: 12,
|
|
top: 12,
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
Stack(
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
Row(
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
// User Enquiry Header
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
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>[
|
|
BoxShadow(
|
|
color: Color(0x66AEAEAE),
|
|
offset: Offset(0, 2),
|
|
blurRadius: 4,
|
|
),
|
|
],
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
children: <Widget>[
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
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: <Widget>[
|
|
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>[
|
|
BoxShadow(
|
|
color: Color(0xA9A9A940),
|
|
blurRadius: 3.29,
|
|
offset: Offset(0, 0.82),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
Expanded(
|
|
child: Text.rich(
|
|
TextSpan(
|
|
children: <TextSpan>[
|
|
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<bool>(
|
|
context: context,
|
|
builder: (context) =>
|
|
AlertDialog(
|
|
title: const Text(
|
|
'Delete Enquiry',
|
|
),
|
|
content: const Text(
|
|
'Are you sure you want to delete this enquiry?',
|
|
),
|
|
actions: <Widget>[
|
|
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: <Widget>[
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
SizedBox(
|
|
height: 203,
|
|
child: Stack(
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
// Header section
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 16, right: 24, top: 12),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: <Widget>[
|
|
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: <Widget>[
|
|
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),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|