1870 lines
87 KiB
Dart
1870 lines
87 KiB
Dart
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:lottie/lottie.dart';
|
|
import 'package:nearledaily/helper/logger.dart';
|
|
import 'package:razorpay_flutter/razorpay_flutter.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import '../../constants/color_constants.dart';
|
|
import '../../constants/font_constants.dart';
|
|
import '../../controllers/cart_controller/cart.dart';
|
|
import '../../controllers/order_controller/create_order_controller.dart';
|
|
|
|
import '../../controllers/product/product_controller.dart';
|
|
import '../../domain/provider/tenant/get_tenant_pro.dart';
|
|
|
|
import '../../helper/distance.dart';
|
|
import '../../modules/authentication/auth.dart';
|
|
import '../../modules/orders/create_order.dart';
|
|
import '../../modules/product/product.dart';
|
|
import '../../modules/tenant/get_tenant.dart';
|
|
import '../../widgets/slider_button.dart';
|
|
import '../../widgets/text_widget.dart';
|
|
import '../home_view.dart';
|
|
import '../map_view/location.dart';
|
|
import '../orders/order_succes.dart';
|
|
import 'order_countdown_page.dart';
|
|
|
|
class CartPage extends StatefulWidget {
|
|
const CartPage({super.key});
|
|
|
|
@override
|
|
State<CartPage> createState() => _CartPageState();
|
|
}
|
|
|
|
class _CartPageState extends State<CartPage> {
|
|
DateTime? selectedDate;
|
|
String? selectedTime;
|
|
String? selectedTimeDisplay;
|
|
TenantLocation? selectedLocation;
|
|
|
|
List<TenantLocation> tenantLocations = [];
|
|
|
|
// ── date helpers ──────────────────────────────────────────
|
|
String _fmtDate(DateTime d) =>
|
|
'${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';
|
|
|
|
String _fmtNow() {
|
|
final n = DateTime.now();
|
|
return '${_fmtDate(n)} '
|
|
'${n.hour.toString().padLeft(2, '0')}:'
|
|
'${n.minute.toString().padLeft(2, '0')}:'
|
|
'${n.second.toString().padLeft(2, '0')}';
|
|
}
|
|
// ──────────────────────────────────────────────────────────
|
|
|
|
Future<void> openPreorderPicker() async {
|
|
final picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: selectedDate ?? DateTime.now(),
|
|
firstDate: DateTime.now(),
|
|
lastDate: DateTime.now().add(const Duration(days: 30)),
|
|
);
|
|
|
|
if (picked != null) {
|
|
setState(() {
|
|
selectedDate = picked;
|
|
selectedTime = null;
|
|
selectedTimeDisplay = null;
|
|
});
|
|
}
|
|
|
|
await fetchTenantLocations();
|
|
if (tenantLocations.isEmpty) return;
|
|
|
|
final loc = tenantLocations.first;
|
|
var slots24 = _generateTimeSlots(loc.openTime, loc.closeTime);
|
|
|
|
final now = DateTime.now();
|
|
if (selectedDate != null &&
|
|
selectedDate!.year == now.year &&
|
|
selectedDate!.month == now.month &&
|
|
selectedDate!.day == now.day) {
|
|
slots24 = slots24.where((s) {
|
|
final p = s.split(':');
|
|
final t = DateTime(
|
|
now.year,
|
|
now.month,
|
|
now.day,
|
|
int.parse(p[0]),
|
|
int.parse(p[1]),
|
|
);
|
|
return t.isAfter(now);
|
|
}).toList();
|
|
}
|
|
|
|
showModalBottomSheet(
|
|
backgroundColor: Colors.white,
|
|
context: context,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
|
),
|
|
builder: (_) => Container(
|
|
height: 300,
|
|
padding: const EdgeInsets.all(16),
|
|
child: slots24.isEmpty
|
|
? const Center(child: Text('No slots available'))
|
|
: ListView.separated(
|
|
itemCount: slots24.length,
|
|
separatorBuilder: (_, __) => const Divider(),
|
|
itemBuilder: (_, i) {
|
|
final t24 = slots24[i];
|
|
final t12 = _to12Hour(t24);
|
|
return ListTile(
|
|
title: Text(t12),
|
|
onTap: () {
|
|
setState(() {
|
|
selectedTime = t24;
|
|
selectedTimeDisplay = t12;
|
|
});
|
|
Navigator.pop(context);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> fetchTenantLocations() async {
|
|
final cartController = Get.find<CartController>();
|
|
if (cartController.cartItems.isEmpty) return;
|
|
|
|
int? tenantId = cartController.cartItems.first.product.tenantid;
|
|
try {
|
|
final provider = CustomerTenantsProvider();
|
|
final response = await provider.getTenantLocations(tenantId!);
|
|
if (response != null && response.details.isNotEmpty) {
|
|
setState(() => tenantLocations = response.details);
|
|
} else {
|
|
setState(() => tenantLocations = []);
|
|
}
|
|
} catch (e) {
|
|
// silent
|
|
}
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadProfile();
|
|
_setDefaultDateAndTime();
|
|
|
|
final cartCtrl = Get.find<CartController>();
|
|
|
|
cartCtrl.appliedCoupon.value = "";
|
|
cartCtrl.amt.value = "";
|
|
}
|
|
|
|
Future<void> _setDefaultDateAndTime() async {
|
|
selectedDate = DateTime.now(); // today
|
|
|
|
await fetchTenantLocations();
|
|
if (tenantLocations.isEmpty) {
|
|
setState(() => selectedTime = null);
|
|
return;
|
|
}
|
|
|
|
final first = tenantLocations.first;
|
|
final slots24 = _generateTimeSlots(first.openTime, first.closeTime);
|
|
|
|
final now = DateTime.now();
|
|
final today = DateTime(now.year, now.month, now.day);
|
|
|
|
final future24 = slots24.where((s) {
|
|
final p = s.split(':');
|
|
final t = DateTime(today.year, today.month, today.day,
|
|
int.parse(p[0]), int.parse(p[1]));
|
|
return t.isAfter(now);
|
|
}).toList();
|
|
|
|
setState(() {
|
|
selectedTime = future24.isNotEmpty ? future24.first : null;
|
|
selectedTimeDisplay =
|
|
selectedTime != null ? _to12Hour(selectedTime!) : null;
|
|
});
|
|
}
|
|
|
|
List<String> _generateTimeSlots(String openTime, String closeTime) {
|
|
final fmt = RegExp(r'^(\d{1,2}):(\d{2})$');
|
|
if (!fmt.hasMatch(openTime) || !fmt.hasMatch(closeTime)) return [];
|
|
|
|
final o = fmt.firstMatch(openTime)!;
|
|
final c = fmt.firstMatch(closeTime)!;
|
|
|
|
final openH = int.parse(o.group(1)!);
|
|
final openM = int.parse(o.group(2)!);
|
|
final closeH = int.parse(c.group(1)!);
|
|
final closeM = int.parse(c.group(2)!);
|
|
|
|
DateTime cur = DateTime(0, 0, 0, openH, openM);
|
|
DateTime end = DateTime(0, 0, 0, closeH, closeM);
|
|
|
|
List<String> slots = [];
|
|
while (cur.isBefore(end) || cur.isAtSameMomentAs(end)) {
|
|
slots.add(
|
|
'${cur.hour.toString().padLeft(2, '0')}:${cur.minute.toString().padLeft(2, '0')}');
|
|
cur = cur.add(const Duration(minutes: 60));
|
|
}
|
|
return slots;
|
|
}
|
|
|
|
String _to12Hour(String time24) {
|
|
final parts = time24.split(':');
|
|
int h = int.parse(parts[0]);
|
|
final m = parts[1];
|
|
final period = h >= 12 ? 'PM' : 'AM';
|
|
if (h == 0) h = 12;
|
|
if (h > 12) h -= 12;
|
|
return '$h:$m $period';
|
|
}
|
|
|
|
String name = '', num = '', address = '', suburb = '', city = '', doorNo = '';
|
|
|
|
Future<void> _loadProfile() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final id = prefs.getInt('customerId');
|
|
if (id == null) {
|
|
Get.snackbar('Error', 'Customer ID not found');
|
|
return;
|
|
}
|
|
try {
|
|
final cartCtrl = Get.find<CartController>();
|
|
await cartCtrl.fetchCustomer(id);
|
|
final profile = cartCtrl.customer.value;
|
|
if (profile != null) {
|
|
setState(() {
|
|
name = '${profile.firstName} ${profile.lastName}'.trim();
|
|
num = profile.contactNo ?? '';
|
|
address = profile.address ?? '';
|
|
suburb = profile.suburb ?? '';
|
|
city = profile.city ?? '';
|
|
doorNo = profile.doorNo ?? '';
|
|
});
|
|
}
|
|
} catch (e) {
|
|
// silent
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final cartCtrl = Get.find<CartController>();
|
|
final tenant = cartCtrl.currentTenant.value;
|
|
|
|
return Obx(() {
|
|
final items = cartCtrl.cartItems;
|
|
final dis = double.tryParse(cartCtrl.amt.value.toString()) ?? 0.0;
|
|
final subtotal = items.fold(
|
|
0.0,
|
|
(s, i) {
|
|
final cost = i.product.productcost ?? 0;
|
|
final discount = i.product.discount ?? 0;
|
|
|
|
final priceAfterDiscount = cost - discount;
|
|
|
|
return s + (priceAfterDiscount * i.quantity);
|
|
},
|
|
);
|
|
|
|
final tax = cartCtrl.totalTax;
|
|
final delivery = tenant?.tenantcharge ?? 0.0;
|
|
final total = subtotal + tax + delivery - dis;
|
|
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
Colors.white,
|
|
Color(0xFFF9F7FC),
|
|
Color(0xFFF8F6FC),
|
|
Colors.white
|
|
],
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: SafeArea(
|
|
child: Scaffold(
|
|
backgroundColor: Colors.white,
|
|
appBar: AppBar(
|
|
surfaceTintColor: Colors.transparent,
|
|
scrolledUnderElevation: 0,
|
|
animateColor: false,
|
|
leading: Navigator.of(context).canPop()
|
|
? Row(
|
|
children: [
|
|
IconButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
final nav = Get.find<BottomNavController>();
|
|
nav.currentIndex.value = 0;
|
|
},
|
|
icon:
|
|
const Icon(Icons.arrow_back, color: Colors.black),
|
|
),
|
|
ReusableTextWidget(
|
|
text: "Cart",
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
fontFamily: FontConstants.fontFamily,
|
|
color: Colors.black,
|
|
),
|
|
],
|
|
)
|
|
: const Padding(
|
|
padding: EdgeInsets.all(15.0),
|
|
child: Text(
|
|
'Cart',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black,
|
|
fontSize: 20),
|
|
),
|
|
),
|
|
leadingWidth: double.infinity,
|
|
centerTitle: true,
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
),
|
|
body: items.isEmpty
|
|
? Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
SizedBox(
|
|
height: 120,
|
|
width: 120,
|
|
child: Lottie.asset(
|
|
'assets/lotties/no_slots_lottie.json',
|
|
fit: BoxFit.contain),
|
|
),
|
|
const SizedBox(height: 20),
|
|
const Padding(
|
|
padding: EdgeInsets.all(6.0),
|
|
child: Text(
|
|
'No slots available for today.\nPlease try changing dates to schedule orders.',
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
color: Colors.grey,
|
|
fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(12),
|
|
physics: const ClampingScrollPhysics(),
|
|
child: Column(
|
|
children: [
|
|
SizedBox(height: 13),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
border: Border(
|
|
left: BorderSide(
|
|
color: Colors.black12, width: 0.50),
|
|
right: BorderSide(
|
|
color: Colors.black12, width: 0.50),
|
|
top: BorderSide(
|
|
color: Colors.black12, width: 0.50),
|
|
),
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(20),
|
|
topLeft: Radius.circular(20),
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: ColorConstants.primaryColor
|
|
.withOpacity(0.02),
|
|
blurRadius: 1,
|
|
spreadRadius: 0.50,
|
|
offset: Offset(0, -3),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
children: items
|
|
.map((item) => _cartItemWidget(item, cartCtrl))
|
|
.toList(),
|
|
),
|
|
),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
border: Border(
|
|
left: BorderSide(
|
|
color: Colors.black12, width: 0.50),
|
|
right: BorderSide(
|
|
color: Colors.black12, width: 0.50),
|
|
bottom: BorderSide(
|
|
color: Colors.black12, width: 0.50),
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: ColorConstants.primaryColor
|
|
.withOpacity(0.08),
|
|
blurRadius: 2,
|
|
spreadRadius: 0,
|
|
offset: Offset(0, 3),
|
|
),
|
|
],
|
|
borderRadius: BorderRadius.only(
|
|
bottomRight: Radius.circular(20),
|
|
bottomLeft: Radius.circular(20),
|
|
)),
|
|
child:
|
|
_priceSummary(subtotal, tax, delivery, total, dis)),
|
|
const SizedBox(height: 35),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
});
|
|
}
|
|
|
|
Widget _cartItemWidget(CartItem item, CartController ctrl) {
|
|
final p = item.product;
|
|
final double screenWidth = MediaQuery.of(context).size.width;
|
|
|
|
final double imageSize =
|
|
math.min(100, math.max(80, screenWidth * 0.22));
|
|
|
|
return Slidable(
|
|
key: ValueKey(p.productid),
|
|
endActionPane: ActionPane(
|
|
motion: const DrawerMotion(),
|
|
extentRatio: 0.23,
|
|
children: [
|
|
SlidableAction(
|
|
onPressed: (_) {
|
|
ctrl.removeFromCart(p);
|
|
},
|
|
backgroundColor: Colors.red,
|
|
foregroundColor: Colors.white,
|
|
icon: Icons.delete_outline,
|
|
label: 'Delete',
|
|
borderRadius: const BorderRadius.only(
|
|
topRight: Radius.circular(18),
|
|
bottomRight: Radius.circular(18),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
child: Container(
|
|
margin: const EdgeInsets.symmetric(vertical: 0),
|
|
padding: const EdgeInsets.all(14),
|
|
decoration: const BoxDecoration(),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(16),
|
|
child: SizedBox(
|
|
height: imageSize,
|
|
width: imageSize,
|
|
child: Image.network(
|
|
p.productimage ?? '',
|
|
fit: BoxFit.cover,
|
|
errorBuilder: (_, __, ___) => Container(
|
|
color: Colors.grey[300],
|
|
child: const Icon(Icons.image, size: 24),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(width: screenWidth * 0.03),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
ReusableTextWidget(
|
|
text: p.productname!,
|
|
color: Colors.black87,
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: screenWidth * 0.035,
|
|
fontFamily: FontConstants.fontFamily,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: 6),
|
|
ReusableTextWidget(
|
|
text: p.subcategoryname!,
|
|
color: Colors.black87,
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: screenWidth * 0.034,
|
|
fontFamily: FontConstants.fontFamily,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: 4),
|
|
ReusableTextWidget(
|
|
text: p.productdesc!,
|
|
color: Colors.black45,
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: screenWidth * 0.030,
|
|
fontFamily: FontConstants.fontFamily,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
_qtyButton(
|
|
icon: Icons.remove,
|
|
onTap: () {
|
|
if (item.quantity > 1) {
|
|
item.quantity--;
|
|
ctrl.cartItems.refresh();
|
|
} else {
|
|
ctrl.removeFromCart(p);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Row(
|
|
children: const [
|
|
Icon(Icons.arrow_upward,
|
|
color: Colors.white, size: 18),
|
|
SizedBox(width: 8),
|
|
Text("Removed"),
|
|
],
|
|
),
|
|
backgroundColor: Colors.red,
|
|
duration: const Duration(seconds: 2),
|
|
behavior: SnackBarBehavior.floating,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 6),
|
|
child: Text(
|
|
'${item.quantity}',
|
|
style: TextStyle(
|
|
fontSize: screenWidth * 0.038,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
_qtyButton(
|
|
icon: Icons.add,
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
Colors.white,
|
|
ColorConstants.primaryColor.withOpacity(0.6),
|
|
],
|
|
),
|
|
onTap: () {
|
|
item.quantity++;
|
|
ctrl.cartItems.refresh();
|
|
},
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 18),
|
|
Row(
|
|
children: [
|
|
ReusableTextWidget(
|
|
text:
|
|
'₹${(p.productcost! - (p.discount ?? 0)).toInt()}',
|
|
color: Colors.black87,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: screenWidth * 0.032,
|
|
fontFamily: FontConstants.fontFamily,
|
|
),
|
|
const SizedBox(width: 6),
|
|
if ((p.discount ?? 0) > 0)
|
|
ReusableTextWidget(
|
|
text: '₹${p.productcost?.toInt() ?? 0}',
|
|
color: Colors.red,
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: screenWidth * 0.03,
|
|
fontFamily: FontConstants.fontFamily,
|
|
isUnderText: TextDecoration.lineThrough,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _priceSummary(
|
|
double subtotal, double tax, double delivery, double total, double dis) {
|
|
final cartCtrl = Get.find<CartController>();
|
|
return Column(
|
|
children: [
|
|
const SizedBox(height: 5),
|
|
Container(
|
|
padding: const EdgeInsets.all(14),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(7),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(8),
|
|
onTap: () {
|
|
final cartCtrl = Get.find<CartController>();
|
|
cartCtrl.loadCoupons();
|
|
cartCtrl.showCouponBottomSheet(context);
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8, vertical: 10),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(8),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(0.2),
|
|
spreadRadius: 2,
|
|
blurRadius: 6,
|
|
offset: Offset(0, 3),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Obx(() {
|
|
bool applied = cartCtrl.amt.value.isNotEmpty;
|
|
return Container(
|
|
padding: const EdgeInsets.all(6),
|
|
decoration: BoxDecoration(
|
|
color: applied
|
|
? Colors.green.shade100
|
|
: Colors.orange.shade100,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
applied
|
|
? Icons.check_circle
|
|
: Icons.discount_outlined,
|
|
size: 14,
|
|
color: applied ? Colors.green : Colors.orange,
|
|
),
|
|
);
|
|
}),
|
|
const SizedBox(width: 10),
|
|
Obx(() {
|
|
return Text(
|
|
cartCtrl.amt.value.isEmpty
|
|
? 'Apply a promo code'
|
|
: 'Discount Applied: ₹${cartCtrl.amt.value}',
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: cartCtrl.amt.value.isEmpty
|
|
? Colors.black.withOpacity(0.65)
|
|
: Colors.green.shade700,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
);
|
|
}),
|
|
const Spacer(),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 16, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: ColorConstants.primaryColor,
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: const [
|
|
ReusableTextWidget(
|
|
text: "Apply",
|
|
color: Colors.white,
|
|
fontFamily: FontConstants.fontFamily,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
maxLines: 1,
|
|
),
|
|
SizedBox(width: 6),
|
|
Icon(
|
|
Icons.arrow_forward_ios,
|
|
size: 14,
|
|
color: Colors.white,
|
|
),
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(height: 12),
|
|
_priceRow('Subtotal', subtotal),
|
|
_priceRow('Discount', dis),
|
|
_priceRow('GST', tax),
|
|
_priceRow('Other Charges', delivery),
|
|
const Divider(),
|
|
_priceRow('Total', total, isBold: true),
|
|
const SizedBox(height: 12),
|
|
|
|
SliderCheckoutButton(
|
|
onConfirmed: () async {
|
|
final cartCtrl = Get.find<CartController>();
|
|
if (cartCtrl.cartItems.any((i) => i.quantity <= 0)) {
|
|
Get.snackbar('', 'Invalid Quantity',
|
|
backgroundColor: Colors.white,
|
|
colorText: Colors.black);
|
|
|
|
print('Invalid quantity in cart items');
|
|
return;
|
|
}
|
|
final orderCtrl = Get.find<OrderController>();
|
|
final loc = await Get.to(() => LocationPage());
|
|
|
|
if (loc != null && loc is Authentication) {
|
|
final tenant =
|
|
Get.find<CartController>().currentTenant.value;
|
|
final tenantLat =
|
|
double.tryParse(tenant?.latitude ?? '0') ?? 0;
|
|
final tenantLon =
|
|
double.tryParse(tenant?.longitude ?? '0') ?? 0;
|
|
final selLat =
|
|
double.tryParse(loc.latitude.toString()) ?? 0;
|
|
final selLon =
|
|
double.tryParse(loc.longitude.toString()) ?? 0;
|
|
final dist = calculateDistance(
|
|
tenantLat, tenantLon, selLat, selLon);
|
|
|
|
if (dist <= 5) {
|
|
showModalBottomSheet(
|
|
backgroundColor: Colors.white,
|
|
context: context,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(
|
|
top: Radius.circular(16))),
|
|
builder: (ctx) => SafeArea(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
ReusableTextWidget(
|
|
text: 'Confirm Order',
|
|
color: Colors.black.withOpacity(0.7),
|
|
fontFamily: FontConstants.fontFamily,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
maxLines: 1,
|
|
),
|
|
const SizedBox(height: 20),
|
|
|
|
// ── CASH ON DELIVERY ──────────────────────────
|
|
Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 14, vertical: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: Colors.grey.shade300,
|
|
width: 1.5,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color:
|
|
Colors.black.withOpacity(0.04),
|
|
blurRadius: 8,
|
|
offset: Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque, // ← ADD THIS
|
|
child: Row(children: [
|
|
Container(
|
|
padding: EdgeInsets.all(10),
|
|
decoration: BoxDecoration(
|
|
color:
|
|
Colors.green.withOpacity(0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.money,
|
|
color: Colors.green,
|
|
size: 22,
|
|
),
|
|
),
|
|
SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
ReusableTextWidget(
|
|
text: 'Cash on Delivery',
|
|
color: Colors.black,
|
|
fontFamily:
|
|
FontConstants.fontFamily,
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
SizedBox(height: 4),
|
|
Text(
|
|
'Pay when order arrives',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey.shade600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Icon(
|
|
Icons.arrow_forward_ios,
|
|
size: 18,
|
|
color: Colors.grey.shade400,
|
|
),
|
|
]),
|
|
onTap: () async {
|
|
Navigator.pop(ctx);
|
|
|
|
final items = cartCtrl.cartItems
|
|
.map((i) => OrderItem(
|
|
productid:
|
|
i.product.productid,
|
|
productname:
|
|
i.product.productname,
|
|
productdescription: '',
|
|
orderqty: i.quantity,
|
|
price: i.product.productcost
|
|
?.toDouble() ??
|
|
0,
|
|
unitid: int.tryParse(i
|
|
.product.unitvalue
|
|
.toString()) ??
|
|
1,
|
|
unitname: i.product.productunit
|
|
?.toString()
|
|
.split('.')
|
|
.last ??
|
|
'unit',
|
|
productsumprice: total,
|
|
tax: tax,
|
|
discount: dis,
|
|
tenantfee: delivery,
|
|
))
|
|
.toList();
|
|
|
|
final order = CreateOrder(
|
|
applocationid:
|
|
tenant?.applocationid,
|
|
applocation: '',
|
|
tenantid: cartCtrl.cartItems.first
|
|
.product.tenantid,
|
|
partnerid: 60,
|
|
locationid: int.tryParse(
|
|
cartCtrl.pastLocationId ?? ""),
|
|
categoryid: cartCtrl.cartItems.first
|
|
.product.categoryid,
|
|
subcategoryid: cartCtrl
|
|
.cartItems.first.product.subcategoryid,
|
|
moduleid: 2,
|
|
configid: 1,
|
|
orderdate: _fmtNow(), // ✅ FIXED
|
|
deliverydate: '${_fmtDate(DateTime.now())} ${TimeOfDay.now().hour.toString().padLeft(2, '0')}:${TimeOfDay.now().minute.toString().padLeft(2, '0')}:00', orderstatus: 'created',
|
|
deliverycharge: tenant?.tenantcharge,
|
|
customerid: int.tryParse(
|
|
loc.customerid ?? '0'),
|
|
pickupcustomer:
|
|
tenant?.tenantname ?? '',
|
|
pickupcontactno:
|
|
tenant?.primarycontact,
|
|
pickupaddress: tenant?.address,
|
|
pickuplocationid: tenant?.locationid,
|
|
pickupcity: tenant?.city,
|
|
deliverycustomer: name,
|
|
deliverycontactno: num,
|
|
deliveryaddress: loc.address,
|
|
deliverylocationid: loc.locationid,
|
|
deliverylat: loc.latitude ?? '',
|
|
deliverylong: loc.longitude ?? '',
|
|
paymenttype: 42,
|
|
items: items,
|
|
);
|
|
|
|
|
|
print(order.toJson());
|
|
print(items.map((i) => i.toJson()).toList());
|
|
print('customer details');
|
|
// ✅ Navigate to countdown page
|
|
Get.to(() => OrderCountdownPage(
|
|
order: order,
|
|
orderCtrl: orderCtrl,
|
|
cartCtrl: cartCtrl,
|
|
customerName: name,
|
|
));
|
|
|
|
},
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
// ── PAY ONLINE (RAZORPAY) ─────────────────────
|
|
Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 14, vertical: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: Colors.grey.shade300,
|
|
width: 1.5,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color:
|
|
Colors.black.withOpacity(0.04),
|
|
blurRadius: 8,
|
|
offset: Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque, // ← ADD THIS
|
|
child: Row(children: [
|
|
Container(
|
|
padding: EdgeInsets.all(10),
|
|
decoration: BoxDecoration(
|
|
color: Colors.deepPurple
|
|
.withOpacity(0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.account_balance_wallet,
|
|
color: Colors.deepPurple,
|
|
size: 22,
|
|
),
|
|
),
|
|
SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
ReusableTextWidget(
|
|
text: 'Pay Online',
|
|
color: Colors.black,
|
|
fontFamily:
|
|
FontConstants.fontFamily,
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
SizedBox(height: 4),
|
|
Text(
|
|
'UPI / Cards / Net Banking',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey.shade600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Icon(
|
|
Icons.arrow_forward_ios,
|
|
size: 18,
|
|
color: Colors.grey.shade400,
|
|
),
|
|
]),
|
|
onTap: () {
|
|
Navigator.pop(ctx);
|
|
|
|
final Razorpay razorpay = Razorpay();
|
|
|
|
razorpay.on(
|
|
Razorpay.EVENT_PAYMENT_SUCCESS,
|
|
(PaymentSuccessResponse
|
|
response) async {
|
|
razorpay.clear();
|
|
if (!context.mounted) return;
|
|
|
|
final items = cartCtrl.cartItems
|
|
.map((i) => OrderItem(
|
|
productid:
|
|
i.product.productid,
|
|
productname:
|
|
i.product.productname,
|
|
productdescription: '',
|
|
orderqty: i.quantity,
|
|
price: i.product
|
|
.productcost
|
|
?.toDouble() ??
|
|
0,
|
|
unitid: int.tryParse(i
|
|
.product.unitvalue
|
|
.toString()) ??
|
|
1,
|
|
unitname: i.product
|
|
.productunit
|
|
?.toString()
|
|
.split('.')
|
|
.last ??
|
|
'unit',
|
|
productsumprice: total,
|
|
tax: tax,
|
|
discount: dis,
|
|
tenantfee: delivery,
|
|
))
|
|
.toList();
|
|
|
|
final order = CreateOrder(
|
|
applocationid:
|
|
tenant?.applocationid,
|
|
applocation: '',
|
|
tenantid: cartCtrl.cartItems.first
|
|
.product.tenantid,
|
|
partnerid: 60,
|
|
locationid: int.tryParse(
|
|
cartCtrl.pastLocationId ??
|
|
""),
|
|
categoryid: cartCtrl
|
|
.cartItems.first.product.categoryid,
|
|
subcategoryid: cartCtrl
|
|
.cartItems.first.product.subcategoryid,
|
|
moduleid: 6,
|
|
configid: 1,
|
|
orderdate: _fmtNow(), // ✅ FIXED
|
|
deliverydate: '${_fmtDate(DateTime.now())} ${TimeOfDay.now().hour.toString().padLeft(2, '0')}:${TimeOfDay.now().minute.toString().padLeft(2, '0')}:00', orderstatus: 'created',
|
|
deliverycharge:
|
|
tenant?.tenantcharge,
|
|
customerid: int.tryParse(
|
|
loc.customerid ?? '0'),
|
|
pickupcustomer:
|
|
tenant?.tenantname ?? '',
|
|
pickupcontactno:
|
|
tenant?.primarycontact,
|
|
pickupaddress: tenant?.address,
|
|
pickuplocationid:
|
|
tenant?.locationid,
|
|
pickupcity: tenant?.city,
|
|
deliverycustomer: name,
|
|
deliverycontactno: num,
|
|
deliveryaddress: loc.address,
|
|
deliverylocationid:
|
|
loc.locationid,
|
|
deliverylat: loc.latitude ?? '',
|
|
deliverylong: loc.longitude ?? '',
|
|
paymenttype: 2,
|
|
items: items,
|
|
);
|
|
|
|
await orderCtrl.createOrder(
|
|
CreateOrderRequest(
|
|
orders: order));
|
|
|
|
if (!context.mounted) return;
|
|
|
|
if (!orderCtrl.isLoading.value) {
|
|
Get.to(OrderSuccessView());
|
|
print(order);
|
|
print(items);
|
|
cartCtrl.clearCart();
|
|
await cartCtrl.notifyAdmin(
|
|
title:
|
|
'Nearle Deals - New Order',
|
|
body:
|
|
'A new order has been placed successfully by $name!');
|
|
}
|
|
});
|
|
|
|
razorpay.on(
|
|
Razorpay.EVENT_PAYMENT_ERROR,
|
|
(PaymentFailureResponse
|
|
response) {
|
|
razorpay.clear();
|
|
if (!context.mounted) return;
|
|
ScaffoldMessenger.of(context)
|
|
.showSnackBar(
|
|
SnackBar(
|
|
content: Text(response.message ??
|
|
'Something went wrong'),
|
|
backgroundColor: Colors.red,
|
|
behavior:
|
|
SnackBarBehavior.floating,
|
|
),
|
|
);
|
|
});
|
|
|
|
razorpay.on(
|
|
Razorpay.EVENT_EXTERNAL_WALLET,
|
|
(ExternalWalletResponse
|
|
response) {
|
|
razorpay.clear();
|
|
});
|
|
|
|
razorpay.open({
|
|
'key': 'rzp_test_pUSj1Pz4LFLb',
|
|
'amount': (total * 100).toInt(),
|
|
'name': 'Nearle Deals',
|
|
'description': 'Order Payment',
|
|
'prefill': {
|
|
'contact': num,
|
|
'name': name,
|
|
},
|
|
'external': {
|
|
'wallets': ['paytm']
|
|
},
|
|
'theme': {
|
|
'full_screen': true,
|
|
},
|
|
});
|
|
},
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
ListTile(
|
|
title: ReusableTextWidget(
|
|
text: 'Cancel',
|
|
color: Colors.red,
|
|
fontFamily: FontConstants.fontFamily,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
maxLines: 1,
|
|
),
|
|
onTap: () => Navigator.pop(ctx),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
} else {
|
|
showDialog(
|
|
context: Get.context!,
|
|
builder: (_) => AlertDialog(
|
|
backgroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16)),
|
|
title: Column(
|
|
children: [
|
|
Lottie.asset('assets/lotties/location.json',
|
|
height: 100, repeat: true),
|
|
const SizedBox(height: 8),
|
|
const Text('Not Serviceable',
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 17,
|
|
color: Colors.black87)),
|
|
],
|
|
),
|
|
content: const Text(
|
|
'Sorry, we don\'t serve this area yet.',
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontSize: 15, color: Colors.black54)),
|
|
actionsAlignment: MainAxisAlignment.center,
|
|
actions: [
|
|
ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.redAccent,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius:
|
|
BorderRadius.circular(10))),
|
|
onPressed: () =>
|
|
Navigator.of(context).pop(),
|
|
child: const Padding(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 20, vertical: 8),
|
|
child: Text('OK',
|
|
style: TextStyle(
|
|
color: Colors.white, fontSize: 15)),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
},
|
|
),
|
|
|
|
// GestureDetector(
|
|
// onTap: () async {
|
|
// final cartCtrl = Get.find<CartController>();
|
|
// if (cartCtrl.cartItems.any((i) => i.quantity <= 0)) {
|
|
// Get.snackbar('', 'Invalid Quantity',
|
|
// backgroundColor: Colors.white,
|
|
// colorText: Colors.black);
|
|
//
|
|
// print('Invalid quantity in cart items');
|
|
// return;
|
|
// }
|
|
// final orderCtrl = Get.find<OrderController>();
|
|
// final loc = await Get.to(() => LocationPage());
|
|
//
|
|
// if (loc != null && loc is Authentication) {
|
|
// final tenant =
|
|
// Get.find<CartController>().currentTenant.value;
|
|
// final tenantLat =
|
|
// double.tryParse(tenant?.latitude ?? '0') ?? 0;
|
|
// final tenantLon =
|
|
// double.tryParse(tenant?.longitude ?? '0') ?? 0;
|
|
// final selLat =
|
|
// double.tryParse(loc.latitude.toString()) ?? 0;
|
|
// final selLon =
|
|
// double.tryParse(loc.longitude.toString()) ?? 0;
|
|
// final dist = calculateDistance(
|
|
// tenantLat, tenantLon, selLat, selLon);
|
|
//
|
|
// if (dist <= 5) {
|
|
// showModalBottomSheet(
|
|
// backgroundColor: Colors.white,
|
|
// context: context,
|
|
// shape: const RoundedRectangleBorder(
|
|
// borderRadius: BorderRadius.vertical(
|
|
// top: Radius.circular(16))),
|
|
// builder: (ctx) => SafeArea(
|
|
// child: Padding(
|
|
// padding: const EdgeInsets.all(16.0),
|
|
// child: Column(
|
|
// mainAxisSize: MainAxisSize.min,
|
|
// children: [
|
|
// ReusableTextWidget(
|
|
// text: 'Confirm Order',
|
|
// color: Colors.black.withOpacity(0.7),
|
|
// fontFamily: FontConstants.fontFamily,
|
|
// fontSize: 18,
|
|
// fontWeight: FontWeight.bold,
|
|
// textAlign: TextAlign.center,
|
|
// overflow: TextOverflow.ellipsis,
|
|
// maxLines: 1,
|
|
// ),
|
|
// const SizedBox(height: 20),
|
|
//
|
|
// // ── CASH ON DELIVERY ──────────────────────────
|
|
// Container(
|
|
// padding: EdgeInsets.symmetric(
|
|
// horizontal: 14, vertical: 16),
|
|
// decoration: BoxDecoration(
|
|
// color: Colors.white,
|
|
// borderRadius: BorderRadius.circular(16),
|
|
// border: Border.all(
|
|
// color: Colors.grey.shade300,
|
|
// width: 1.5,
|
|
// ),
|
|
// boxShadow: [
|
|
// BoxShadow(
|
|
// color:
|
|
// Colors.black.withOpacity(0.04),
|
|
// blurRadius: 8,
|
|
// offset: Offset(0, 4),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// child: GestureDetector(
|
|
// behavior: HitTestBehavior.opaque, // ← ADD THIS
|
|
// child: Row(children: [
|
|
// Container(
|
|
// padding: EdgeInsets.all(10),
|
|
// decoration: BoxDecoration(
|
|
// color:
|
|
// Colors.green.withOpacity(0.1),
|
|
// shape: BoxShape.circle,
|
|
// ),
|
|
// child: Icon(
|
|
// Icons.money,
|
|
// color: Colors.green,
|
|
// size: 22,
|
|
// ),
|
|
// ),
|
|
// SizedBox(width: 12),
|
|
// Expanded(
|
|
// child: Column(
|
|
// crossAxisAlignment:
|
|
// CrossAxisAlignment.start,
|
|
// children: [
|
|
// ReusableTextWidget(
|
|
// text: 'Cash on Delivery',
|
|
// color: Colors.black,
|
|
// fontFamily:
|
|
// FontConstants.fontFamily,
|
|
// fontSize: 15,
|
|
// fontWeight: FontWeight.w600,
|
|
// ),
|
|
// SizedBox(height: 4),
|
|
// Text(
|
|
// 'Pay when order arrives',
|
|
// style: TextStyle(
|
|
// fontSize: 12,
|
|
// color: Colors.grey.shade600,
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// Icon(
|
|
// Icons.arrow_forward_ios,
|
|
// size: 18,
|
|
// color: Colors.grey.shade400,
|
|
// ),
|
|
// ]),
|
|
// onTap: () async {
|
|
// Navigator.pop(ctx);
|
|
//
|
|
// final items = cartCtrl.cartItems
|
|
// .map((i) => OrderItem(
|
|
// productid:
|
|
// i.product.productid,
|
|
// productname:
|
|
// i.product.productname,
|
|
// productdescription: '',
|
|
// orderqty: i.quantity,
|
|
// price: i.product.productcost
|
|
// ?.toDouble() ??
|
|
// 0,
|
|
// unitid: int.tryParse(i
|
|
// .product.unitvalue
|
|
// .toString()) ??
|
|
// 1,
|
|
// unitname: i.product.productunit
|
|
// ?.toString()
|
|
// .split('.')
|
|
// .last ??
|
|
// 'unit',
|
|
// productsumprice: total,
|
|
// tax: tax,
|
|
// discount: dis,
|
|
// tenantfee: delivery,
|
|
// ))
|
|
// .toList();
|
|
//
|
|
// final order = CreateOrder(
|
|
// applocationid:
|
|
// tenant?.applocationid,
|
|
// applocation: '',
|
|
// tenantid: cartCtrl.cartItems.first
|
|
// .product.tenantid,
|
|
// partnerid: 60,
|
|
// locationid: int.tryParse(
|
|
// cartCtrl.pastLocationId ?? ""),
|
|
// categoryid: cartCtrl.cartItems.first
|
|
// .product.categoryid,
|
|
// subcategoryid: cartCtrl
|
|
// .cartItems.first.product.subcategoryid,
|
|
// moduleid: 6,
|
|
// configid: 1,
|
|
// orderdate: _fmtNow(), // ✅ FIXED
|
|
// deliverydate: '${_fmtDate(DateTime.now())} ${TimeOfDay.now().hour.toString().padLeft(2, '0')}:${TimeOfDay.now().minute.toString().padLeft(2, '0')}:00', orderstatus: 'created',
|
|
// deliverycharge: tenant?.tenantcharge,
|
|
// customerid: int.tryParse(
|
|
// loc.customerid ?? '0'),
|
|
// pickupcustomer:
|
|
// tenant?.tenantname ?? '',
|
|
// pickupcontactno:
|
|
// tenant?.primarycontact,
|
|
// pickupaddress: tenant?.address,
|
|
// pickuplocationid: tenant?.locationid,
|
|
// pickupcity: tenant?.city,
|
|
// deliverycustomer: name,
|
|
// deliverycontactno: num,
|
|
// deliveryaddress: loc.address,
|
|
// deliverylocationid: loc.locationid,
|
|
// deliverylat: loc.latitude ?? '',
|
|
// deliverylong: loc.longitude ?? '',
|
|
// paymenttype: 42,
|
|
// items: items,
|
|
// );
|
|
//
|
|
//
|
|
// print(order.toJson());
|
|
// print(items.map((i) => i.toJson()).toList());
|
|
// print('orders');
|
|
// // ✅ Navigate to countdown page
|
|
// Get.to(() => OrderCountdownPage(
|
|
// order: order,
|
|
// orderCtrl: orderCtrl,
|
|
// cartCtrl: cartCtrl,
|
|
// customerName: name,
|
|
// ));
|
|
//
|
|
// },
|
|
// ),
|
|
// ),
|
|
//
|
|
// const SizedBox(height: 10),
|
|
//
|
|
// // ── PAY ONLINE (RAZORPAY) ─────────────────────
|
|
// Container(
|
|
// padding: EdgeInsets.symmetric(
|
|
// horizontal: 14, vertical: 16),
|
|
// decoration: BoxDecoration(
|
|
// color: Colors.white,
|
|
// borderRadius: BorderRadius.circular(16),
|
|
// border: Border.all(
|
|
// color: Colors.grey.shade300,
|
|
// width: 1.5,
|
|
// ),
|
|
// boxShadow: [
|
|
// BoxShadow(
|
|
// color:
|
|
// Colors.black.withOpacity(0.04),
|
|
// blurRadius: 8,
|
|
// offset: Offset(0, 4),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// child: GestureDetector(
|
|
// behavior: HitTestBehavior.opaque, // ← ADD THIS
|
|
// child: Row(children: [
|
|
// Container(
|
|
// padding: EdgeInsets.all(10),
|
|
// decoration: BoxDecoration(
|
|
// color: Colors.deepPurple
|
|
// .withOpacity(0.1),
|
|
// shape: BoxShape.circle,
|
|
// ),
|
|
// child: Icon(
|
|
// Icons.account_balance_wallet,
|
|
// color: Colors.deepPurple,
|
|
// size: 22,
|
|
// ),
|
|
// ),
|
|
// SizedBox(width: 12),
|
|
// Expanded(
|
|
// child: Column(
|
|
// crossAxisAlignment:
|
|
// CrossAxisAlignment.start,
|
|
// children: [
|
|
// ReusableTextWidget(
|
|
// text: 'Pay Online',
|
|
// color: Colors.black,
|
|
// fontFamily:
|
|
// FontConstants.fontFamily,
|
|
// fontSize: 15,
|
|
// fontWeight: FontWeight.w600,
|
|
// ),
|
|
// SizedBox(height: 4),
|
|
// Text(
|
|
// 'UPI / Cards / Net Banking',
|
|
// style: TextStyle(
|
|
// fontSize: 12,
|
|
// color: Colors.grey.shade600,
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// Icon(
|
|
// Icons.arrow_forward_ios,
|
|
// size: 18,
|
|
// color: Colors.grey.shade400,
|
|
// ),
|
|
// ]),
|
|
// onTap: () {
|
|
// Navigator.pop(ctx);
|
|
//
|
|
// final Razorpay razorpay = Razorpay();
|
|
//
|
|
// razorpay.on(
|
|
// Razorpay.EVENT_PAYMENT_SUCCESS,
|
|
// (PaymentSuccessResponse
|
|
// response) async {
|
|
// razorpay.clear();
|
|
// if (!context.mounted) return;
|
|
//
|
|
// final items = cartCtrl.cartItems
|
|
// .map((i) => OrderItem(
|
|
// productid:
|
|
// i.product.productid,
|
|
// productname:
|
|
// i.product.productname,
|
|
// productdescription: '',
|
|
// orderqty: i.quantity,
|
|
// price: i.product
|
|
// .productcost
|
|
// ?.toDouble() ??
|
|
// 0,
|
|
// unitid: int.tryParse(i
|
|
// .product.unitvalue
|
|
// .toString()) ??
|
|
// 1,
|
|
// unitname: i.product
|
|
// .productunit
|
|
// ?.toString()
|
|
// .split('.')
|
|
// .last ??
|
|
// 'unit',
|
|
// productsumprice: total,
|
|
// tax: tax,
|
|
// discount: dis,
|
|
// tenantfee: delivery,
|
|
// ))
|
|
// .toList();
|
|
//
|
|
// final order = CreateOrder(
|
|
// applocationid:
|
|
// tenant?.applocationid,
|
|
// applocation: '',
|
|
// tenantid: cartCtrl.cartItems.first
|
|
// .product.tenantid,
|
|
// partnerid: 60,
|
|
// locationid: int.tryParse(
|
|
// cartCtrl.pastLocationId ??
|
|
// ""),
|
|
// categoryid: cartCtrl
|
|
// .cartItems.first.product.categoryid,
|
|
// subcategoryid: cartCtrl
|
|
// .cartItems.first.product.subcategoryid,
|
|
// moduleid: 6,
|
|
// configid: 1,
|
|
// orderdate: _fmtNow(), // ✅ FIXED
|
|
// deliverydate: '${_fmtDate(DateTime.now())} ${TimeOfDay.now().hour.toString().padLeft(2, '0')}:${TimeOfDay.now().minute.toString().padLeft(2, '0')}:00', orderstatus: 'created',
|
|
// deliverycharge:
|
|
// tenant?.tenantcharge,
|
|
// customerid: int.tryParse(
|
|
// loc.customerid ?? '0'),
|
|
// pickupcustomer:
|
|
// tenant?.tenantname ?? '',
|
|
// pickupcontactno:
|
|
// tenant?.primarycontact,
|
|
// pickupaddress: tenant?.address,
|
|
// pickuplocationid:
|
|
// tenant?.locationid,
|
|
// pickupcity: tenant?.city,
|
|
// deliverycustomer: name,
|
|
// deliverycontactno: num,
|
|
// deliveryaddress: loc.address,
|
|
// deliverylocationid:
|
|
// loc.locationid,
|
|
// deliverylat: loc.latitude ?? '',
|
|
// deliverylong: loc.longitude ?? '',
|
|
// paymenttype: 2,
|
|
// items: items,
|
|
// );
|
|
//
|
|
// await orderCtrl.createOrder(
|
|
// CreateOrderRequest(
|
|
// orders: order));
|
|
//
|
|
// if (!context.mounted) return;
|
|
//
|
|
// if (!orderCtrl.isLoading.value) {
|
|
// Get.to(OrderSuccessView());
|
|
// print(order);
|
|
// print(items);
|
|
// cartCtrl.clearCart();
|
|
// await cartCtrl.notifyAdmin(
|
|
// title:
|
|
// 'Nearle Deals - New Order',
|
|
// body:
|
|
// 'A new order has been placed successfully by $name!');
|
|
// }
|
|
// });
|
|
//
|
|
// razorpay.on(
|
|
// Razorpay.EVENT_PAYMENT_ERROR,
|
|
// (PaymentFailureResponse
|
|
// response) {
|
|
// razorpay.clear();
|
|
// if (!context.mounted) return;
|
|
// ScaffoldMessenger.of(context)
|
|
// .showSnackBar(
|
|
// SnackBar(
|
|
// content: Text(response.message ??
|
|
// 'Something went wrong'),
|
|
// backgroundColor: Colors.red,
|
|
// behavior:
|
|
// SnackBarBehavior.floating,
|
|
// ),
|
|
// );
|
|
// });
|
|
//
|
|
// razorpay.on(
|
|
// Razorpay.EVENT_EXTERNAL_WALLET,
|
|
// (ExternalWalletResponse
|
|
// response) {
|
|
// razorpay.clear();
|
|
// });
|
|
//
|
|
// razorpay.open({
|
|
// 'key': 'rzp_test_pUSj1Pz4LFLb',
|
|
// 'amount': (total * 100).toInt(),
|
|
// 'name': 'Nearle Deals',
|
|
// 'description': 'Order Payment',
|
|
// 'prefill': {
|
|
// 'contact': num,
|
|
// 'name': name,
|
|
// },
|
|
// 'external': {
|
|
// 'wallets': ['paytm']
|
|
// },
|
|
// 'theme': {
|
|
// 'full_screen': true,
|
|
// },
|
|
// });
|
|
// },
|
|
// ),
|
|
// ),
|
|
//
|
|
// const SizedBox(height: 10),
|
|
// ListTile(
|
|
// title: ReusableTextWidget(
|
|
// text: 'Cancel',
|
|
// color: Colors.red,
|
|
// fontFamily: FontConstants.fontFamily,
|
|
// fontSize: 18,
|
|
// fontWeight: FontWeight.bold,
|
|
// textAlign: TextAlign.center,
|
|
// overflow: TextOverflow.ellipsis,
|
|
// maxLines: 1,
|
|
// ),
|
|
// onTap: () => Navigator.pop(ctx),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// );
|
|
// } else {
|
|
// showDialog(
|
|
// context: Get.context!,
|
|
// builder: (_) => AlertDialog(
|
|
// backgroundColor: Colors.white,
|
|
// shape: RoundedRectangleBorder(
|
|
// borderRadius: BorderRadius.circular(16)),
|
|
// title: Column(
|
|
// children: [
|
|
// Lottie.asset('assets/lotties/location.json',
|
|
// height: 100, repeat: true),
|
|
// const SizedBox(height: 8),
|
|
// const Text('Not Serviceable',
|
|
// textAlign: TextAlign.center,
|
|
// style: TextStyle(
|
|
// fontWeight: FontWeight.w600,
|
|
// fontSize: 17,
|
|
// color: Colors.black87)),
|
|
// ],
|
|
// ),
|
|
// content: const Text(
|
|
// 'Sorry, we don\'t serve this area yet.',
|
|
// textAlign: TextAlign.center,
|
|
// style: TextStyle(
|
|
// fontSize: 15, color: Colors.black54)),
|
|
// actionsAlignment: MainAxisAlignment.center,
|
|
// actions: [
|
|
// ElevatedButton(
|
|
// style: ElevatedButton.styleFrom(
|
|
// backgroundColor: Colors.redAccent,
|
|
// shape: RoundedRectangleBorder(
|
|
// borderRadius:
|
|
// BorderRadius.circular(10))),
|
|
// onPressed: () =>
|
|
// Navigator.of(context).pop(),
|
|
// child: const Padding(
|
|
// padding: EdgeInsets.symmetric(
|
|
// horizontal: 20, vertical: 8),
|
|
// child: Text('OK',
|
|
// style: TextStyle(
|
|
// color: Colors.white, fontSize: 15)),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// );
|
|
// }
|
|
// }
|
|
// },
|
|
// child: Container(
|
|
// width: double.infinity,
|
|
// padding: const EdgeInsets.symmetric(vertical: 16),
|
|
// decoration: BoxDecoration(
|
|
// gradient: LinearGradient(
|
|
// colors: [
|
|
// ColorConstants.primaryColor,
|
|
// ColorConstants.primaryColor.withOpacity(0.8)
|
|
// ],
|
|
// begin: Alignment.topLeft,
|
|
// end: Alignment.bottomRight,
|
|
// ),
|
|
// borderRadius: BorderRadius.circular(14),
|
|
// ),
|
|
// child: Obx(() {
|
|
// final oc = Get.find<OrderController>();
|
|
// return oc.isLoading.value
|
|
// ? const Center(
|
|
// child: Text('Please wait...',
|
|
// style: TextStyle(
|
|
// color: Colors.white,
|
|
// fontSize: 16,
|
|
// fontWeight: FontWeight.w500)),
|
|
// )
|
|
// : Center(
|
|
// child: Text('Proceed to Checkout',
|
|
// style: TextStyle(
|
|
// color: Colors.white,
|
|
// fontSize: 16,
|
|
// fontWeight: FontWeight.w500)),
|
|
// );
|
|
// }),
|
|
// ),
|
|
// )
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _qtyButton({
|
|
required IconData icon,
|
|
required VoidCallback onTap,
|
|
Gradient? gradient,
|
|
}) {
|
|
return InkWell(
|
|
onTap: onTap,
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Container(
|
|
width: 32,
|
|
height: 32,
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: ColorConstants.primaryColor.withOpacity(0.25),
|
|
),
|
|
gradient: gradient,
|
|
color: gradient == null ? Colors.white : null,
|
|
),
|
|
child: Icon(icon, size: 18, color: Colors.black87),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _priceRow(String title, double value, {bool isBold = false}) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
|
child: LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final baseWidth = 392.0;
|
|
final screenWidth = MediaQuery.of(context).size.width;
|
|
final scale = screenWidth / baseWidth;
|
|
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontWeight: isBold ? FontWeight.w700 : FontWeight.w500,
|
|
fontSize: 13 * scale,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
Text(
|
|
'₹${value.toStringAsFixed(2)}',
|
|
style: TextStyle(
|
|
fontWeight: isBold ? FontWeight.w800 : FontWeight.w600,
|
|
fontSize: (isBold ? 14 : 13) * scale,
|
|
color: isBold ? Colors.black : Colors.black87,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildProductItem(Product product) {
|
|
return Container(
|
|
width: 70,
|
|
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Stack(
|
|
clipBehavior: Clip.none,
|
|
children: [
|
|
Container(
|
|
height: 70,
|
|
width: 70,
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey.shade200,
|
|
borderRadius: BorderRadius.circular(14),
|
|
image: product.productimage != null
|
|
? DecorationImage(
|
|
image: NetworkImage(product.productimage!),
|
|
fit: BoxFit.cover,
|
|
)
|
|
: null,
|
|
),
|
|
),
|
|
Positioned(
|
|
bottom: -8,
|
|
right: -8,
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
// TODO: Add to cart
|
|
},
|
|
child: Container(
|
|
height: 26,
|
|
width: 26,
|
|
decoration: BoxDecoration(
|
|
color: Colors.green,
|
|
shape: BoxShape.circle,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.25),
|
|
blurRadius: 6,
|
|
),
|
|
],
|
|
),
|
|
child: const Icon(
|
|
Icons.add,
|
|
color: Colors.white,
|
|
size: 18,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
product.productname ?? '',
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} |