You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
621 lines
26 KiB
621 lines
26 KiB
import 'dart:convert';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:bookatanker/common/settings.dart';
|
|
import 'package:table_calendar/table_calendar.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:bookatanker/models/supplier_tankers_model.dart';
|
|
import 'package:bookatanker/supplier/cart_summary.dart';
|
|
|
|
class SupplierScreen extends StatefulWidget {
|
|
var details;
|
|
SupplierScreen({this.details});
|
|
|
|
@override
|
|
State<SupplierScreen> createState() => _SupplierScreenState();
|
|
}
|
|
|
|
class _SupplierScreenState extends State<SupplierScreen> {
|
|
DateTime _focusedDay = DateTime.now();
|
|
DateTime? _selectedDay;
|
|
bool isTankerDataLoading = false;
|
|
bool isCartDataLoading = false;
|
|
List<SupplierTankersModel> tankersList = [];
|
|
bool isSereverIssue = false;
|
|
List cart = [];
|
|
Map<String, int> localQuantities = {};
|
|
Map<String, int> localTotalPrices = {};
|
|
|
|
Future<void> getTankers() async {
|
|
isTankerDataLoading = true;
|
|
try {
|
|
var tankerResponse = await AppSettings.getAllTankers(widget.details.supplier_id);
|
|
|
|
setState(() {
|
|
tankersList =
|
|
((jsonDecode(tankerResponse)['data']) as List).map((dynamic model) {
|
|
return SupplierTankersModel.fromJson(model);
|
|
}).toList();
|
|
|
|
|
|
|
|
isTankerDataLoading = false;
|
|
});
|
|
} catch (e) {
|
|
setState(() {
|
|
isTankerDataLoading = false;
|
|
isSereverIssue = true;
|
|
});
|
|
/* AppSettings.longFailedToast('There is an issue at server side please try after some time');
|
|
Navigator.pop(context);*/
|
|
}
|
|
}
|
|
|
|
|
|
Future<void> getCart() async {
|
|
isCartDataLoading = true;
|
|
try {
|
|
var tankerResponse = await AppSettings.getCartItems();
|
|
|
|
setState(() {
|
|
cart = ((jsonDecode(tankerResponse)['data']['items']));
|
|
|
|
|
|
isCartDataLoading = false;
|
|
});
|
|
} catch (e) {
|
|
setState(() {
|
|
isCartDataLoading = false;
|
|
isSereverIssue = true;
|
|
});
|
|
/* AppSettings.longFailedToast('There is an issue at server side please try after some time');
|
|
Navigator.pop(context);*/
|
|
}
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_selectedDay = _focusedDay;
|
|
getTankers();
|
|
getCart();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: Color(0XFFFFFFFF),
|
|
appBar:AppSettings.SupplierAppBar('Place Order',context),
|
|
body: Padding(
|
|
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
|
|
child: ListView(
|
|
children: [
|
|
// Supplier Card
|
|
Card(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12)),
|
|
elevation: 2,
|
|
color: Colors.white,
|
|
child: Column(
|
|
children: [
|
|
Padding(
|
|
padding: EdgeInsets.fromLTRB(8, 8, 8, 8),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Visibility(
|
|
visible: true,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Visibility(
|
|
visible: widget.details.supplier_name != '',
|
|
child: Text(
|
|
widget.details.supplier_name.toString(),
|
|
style: fontTextStyle(
|
|
16, Color(0XFF2D2E30), FontWeight.w600),
|
|
),
|
|
),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
Image.asset(
|
|
'images/star.png',
|
|
fit: BoxFit.cover,
|
|
width:
|
|
16, // Match the diameter of the CircleAvatar
|
|
height: 16,
|
|
),
|
|
Visibility(
|
|
visible: true,
|
|
child: Text(
|
|
'4.2 (20K+ Ratings)',
|
|
style: fontTextStyle(9,
|
|
Color(0XFF515253), FontWeight.w400),
|
|
),
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
),
|
|
Visibility(
|
|
visible: true,
|
|
child: Text(
|
|
'Drinking | Bore Water',
|
|
style: fontTextStyle(
|
|
12, Color(0XFF4692FD), FontWeight.w500),
|
|
),
|
|
),
|
|
Visibility(
|
|
visible: true,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
widget.details.displayAddress +
|
|
' ' +
|
|
widget.details.distanceInMeters
|
|
.toString() +
|
|
' Km',
|
|
style: fontTextStyle(12, Color(0XFF515253),
|
|
FontWeight.w400)),
|
|
Image.asset(
|
|
'images/heart_outline.png',
|
|
fit: BoxFit.cover,
|
|
width:
|
|
16, // Match the diameter of the CircleAvatar
|
|
height: 16,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Padding(
|
|
padding: EdgeInsets.zero,
|
|
child: Container(
|
|
width: double
|
|
.infinity, // makes it expand within the Card's width
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.vertical(
|
|
bottom: Radius.circular(12),
|
|
), // match Card border
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
Color(0xFFFFE8A3),
|
|
Color(0xFFFFF8DF),
|
|
Color(0xFFFFFFFF),
|
|
],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(vertical: 12),
|
|
alignment: Alignment.center,
|
|
child: Padding(
|
|
padding: EdgeInsets.fromLTRB(12, 0, 12, 0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Image.asset(
|
|
'images/ring.png',
|
|
fit: BoxFit.cover,
|
|
width:
|
|
16, // Match the diameter of the CircleAvatar
|
|
height: 16,
|
|
),
|
|
Text('Best water quality',
|
|
style: fontTextStyle(
|
|
10,
|
|
Color(0XFF2D2E30),
|
|
FontWeight.w400)),
|
|
],
|
|
),
|
|
Row(
|
|
children: [
|
|
Image.asset(
|
|
'images/ring.png',
|
|
fit: BoxFit.cover,
|
|
width:
|
|
16, // Match the diameter of the CircleAvatar
|
|
height: 16,
|
|
),
|
|
Text('Steel casing tankers',
|
|
style: fontTextStyle(
|
|
10,
|
|
Color(0XFF2D2E30),
|
|
FontWeight.w400)),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
)),
|
|
),
|
|
],
|
|
)),
|
|
|
|
Card(
|
|
color: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 0),
|
|
child: Column(
|
|
children: [
|
|
// Header Row
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
DateFormat.yMMM().format(_focusedDay),
|
|
style: fontTextStyle(12, Color(0XFF646566), FontWeight.w600),
|
|
),
|
|
Image.asset(
|
|
'images/calender.png',
|
|
fit: BoxFit.cover,
|
|
width: 16, // Match the diameter of the CircleAvatar
|
|
height: 16,
|
|
),
|
|
],
|
|
),
|
|
Divider(
|
|
color: Colors.grey.shade300,
|
|
),
|
|
// Weekly Calendar
|
|
TableCalendar(
|
|
focusedDay: _focusedDay,
|
|
firstDay: DateTime.now(),
|
|
lastDay: DateTime.now().add(Duration(days: 15)),
|
|
calendarFormat: CalendarFormat.week,
|
|
startingDayOfWeek: StartingDayOfWeek.sunday,
|
|
availableCalendarFormats: const {
|
|
CalendarFormat.week: 'Week',
|
|
},
|
|
selectedDayPredicate: (day) {
|
|
return isSameDay(_selectedDay, day);
|
|
},
|
|
onDaySelected: (selectedDay, focusedDay) {
|
|
setState(() {
|
|
_selectedDay = selectedDay;
|
|
_focusedDay = focusedDay;
|
|
});
|
|
},
|
|
headerVisible: false,
|
|
calendarStyle: CalendarStyle(
|
|
todayDecoration: BoxDecoration(), // No box for today
|
|
todayTextStyle:fontTextStyle(12, Color(0XFF1D7AFC), FontWeight.w400),
|
|
|
|
selectedDecoration: BoxDecoration(
|
|
color: Color(0XFF1D7AFC),
|
|
shape: BoxShape.circle,
|
|
),
|
|
selectedTextStyle: fontTextStyle(12, Color(0XFFFFFFFF), FontWeight.w400),
|
|
weekendTextStyle: TextStyle(color: Colors.black),
|
|
defaultTextStyle: TextStyle(color: Colors.black),
|
|
outsideDaysVisible: false,
|
|
),
|
|
daysOfWeekStyle: DaysOfWeekStyle(
|
|
weekdayStyle: fontTextStyle(12, Color(0XFF343637), FontWeight.w400),
|
|
weekendStyle: fontTextStyle(12, Color(0XFF343637), FontWeight.w400),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)),
|
|
|
|
SizedBox(height: MediaQuery.of(context).size.height * .016),
|
|
|
|
Padding(padding: EdgeInsets.fromLTRB(6, 0, 6, 0),
|
|
child: Container(
|
|
width: double.infinity,
|
|
padding:
|
|
EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: Color(0XFFFCF0E7),
|
|
borderRadius: BorderRadius.circular(6),
|
|
border: Border.all
|
|
(
|
|
color: Color(0XFFEFA168)),
|
|
),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Image.asset(
|
|
'images/warning.png',
|
|
fit: BoxFit.cover,
|
|
width:
|
|
22, // Match the diameter of the CircleAvatar
|
|
height: 22,
|
|
),
|
|
SizedBox(width: MediaQuery.of(context).size.width * .020),
|
|
Expanded(child: Text(
|
|
'The prices shown below are NOT inclusive of the transport charges. Transport charges are calculated based on the distance between the sourcing location and the delivery location.',
|
|
style: fontTextStyle(
|
|
10,
|
|
Color(0XFF444444),
|
|
FontWeight.w400),
|
|
),)
|
|
],
|
|
)
|
|
|
|
|
|
),),
|
|
SizedBox(height: MediaQuery.of(context).size.height * .016),
|
|
// Supply Locations
|
|
Text('SUPPLY LOCATIONS',
|
|
style: fontTextStyle(
|
|
12, Color(0XFF646566), FontWeight.w400),),
|
|
SizedBox(height: MediaQuery.of(context).size.height * .016),
|
|
Card(
|
|
elevation: 2,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
|
color: Color(0XFFFFFFFF),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
...[
|
|
{'name': 'Gandipet', 'type': 'Drinking water', 'km': '4.5 Km'},
|
|
{'name': 'Nizampet', 'type': 'Drinking water', 'km': '7.3 Km'},
|
|
{'name': 'Secunderabad', 'type': 'Bore water', 'km': '12.4 Km'},
|
|
].map((location) => Padding(
|
|
padding: const EdgeInsets.only(bottom: 12.0),
|
|
child: ListBody(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(location['name']!, style: fontTextStyle(12, Color(0XFF2D2E30), FontWeight.w500),),
|
|
Text(location['km']!,style: fontTextStyle(10, Color(0XFF2D2E30), FontWeight.w500),),
|
|
],
|
|
),
|
|
Text(location['type']!, style: fontTextStyle(10, Color(0XFF515253), FontWeight.w400),),
|
|
|
|
],
|
|
),
|
|
)),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
|
|
// Tankers
|
|
SizedBox(height: MediaQuery.of(context).size.height * .016),
|
|
Text('TANKERS',
|
|
style: fontTextStyle(
|
|
12, Color(0XFF646566), FontWeight.w400),),
|
|
SizedBox(height: MediaQuery.of(context).size.height * .016),
|
|
GridView.builder(
|
|
padding: const EdgeInsets.all(0),
|
|
itemCount: tankersList.length,
|
|
shrinkWrap: true,
|
|
physics: NeverScrollableScrollPhysics(),
|
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 2,
|
|
mainAxisSpacing: 12,
|
|
crossAxisSpacing: 12,
|
|
childAspectRatio: 1.6,
|
|
),
|
|
itemBuilder: (context, index) {
|
|
final tanker = tankersList[index];
|
|
final isAvailable = tankersList[index].status == 'Available';
|
|
final isEnabled =tankersList[index].enabled;
|
|
|
|
return Card(
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
|
elevation: 2,
|
|
color: Color(0XFFFFFFFF),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
tankersList[index].capacity+' Litres',
|
|
style: fontTextStyle(12, Color(0XFF2D2E30), FontWeight.w500),
|
|
),
|
|
Text(
|
|
'\u20B9 '+AppSettings.formDouble(tankersList[index].price),
|
|
style: fontTextStyle(12, Color(0XFF515253), FontWeight.w400),
|
|
),
|
|
],
|
|
),
|
|
Text(
|
|
tankersList[index].type_of_water,
|
|
style: fontTextStyle(12, Color(0XFF515253), FontWeight.w400),
|
|
),
|
|
|
|
Expanded(child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Container(
|
|
padding:
|
|
EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(6),
|
|
border: Border.all(
|
|
color: isAvailable ? Color(0XFF098603) : Colors.grey,),
|
|
),
|
|
child: Text(
|
|
tankersList[index].status,
|
|
style: fontTextStyle(
|
|
12,
|
|
isAvailable ? Color(0XFF098603) : Colors.grey,
|
|
FontWeight.w500),
|
|
),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: isEnabled
|
|
? () async {
|
|
AppSettings.preLoaderDialog(context);
|
|
|
|
final product = tankersList[index];
|
|
final productId = product.id;
|
|
final name = product.tanker_name;
|
|
|
|
// Convert price safely from string like "1,000"
|
|
final priceString = product.price;
|
|
final unitPrice = int.parse(priceString.replaceAll(',', ''));
|
|
|
|
// Get current quantity, or 0 if not added yet
|
|
int currentQuantity = localQuantities[productId] ?? 0;
|
|
int newQuantity = currentQuantity + 1;
|
|
|
|
// Calculate total price for UI use (unitPrice * quantity)
|
|
int totalPrice = unitPrice * newQuantity;
|
|
|
|
// Prepare payload to send unit price and quantity only
|
|
final payload = {
|
|
"productId": productId,
|
|
"name": name,
|
|
"quantity": newQuantity,
|
|
"price": unitPrice, // unit price ONLY
|
|
};
|
|
|
|
try {
|
|
final response = await http.post(
|
|
Uri.parse('${AppSettings.host}cart/${AppSettings.customerId}/add'),
|
|
headers: await AppSettings.buildRequestHeaders(),
|
|
body: jsonEncode(payload),
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
setState(() {
|
|
Navigator.of(context, rootNavigator: true).pop();
|
|
|
|
// Update local tracking maps
|
|
localQuantities[productId] = newQuantity;
|
|
localTotalPrices[productId] = totalPrice;
|
|
|
|
// Update or add the cart list entry
|
|
int existingIndex = cart.indexWhere((item) => item['productId'] == productId);
|
|
if (existingIndex != -1) {
|
|
cart[existingIndex]['quantity'] = newQuantity;
|
|
cart[existingIndex]['price'] = unitPrice; // unit price
|
|
cart[existingIndex]['total'] = totalPrice; // total price for UI
|
|
} else {
|
|
cart.add({
|
|
"productId": productId,
|
|
"name": name,
|
|
"quantity": newQuantity,
|
|
"price": unitPrice,
|
|
"total": totalPrice,
|
|
});
|
|
}
|
|
});
|
|
|
|
// Optional: refresh cart from server if you want
|
|
await getCart();
|
|
} else {
|
|
Navigator.of(context, rootNavigator: true).pop();
|
|
print('Failed to add to cart: ${response.body}');
|
|
}
|
|
} catch (e) {
|
|
Navigator.of(context, rootNavigator: true).pop();
|
|
print('Error adding to cart: $e');
|
|
}
|
|
}
|
|
: null,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: isEnabled ? primaryColor : Colors.grey[300],
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
minimumSize: Size(48, 32),
|
|
padding: EdgeInsets.zero,
|
|
),
|
|
child: Text(
|
|
'Add',
|
|
style: fontTextStyle(12, Color(0XFFFFFFFF), FontWeight.w400),
|
|
),
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
],
|
|
))
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
|
|
|
|
],
|
|
),
|
|
),
|
|
bottomNavigationBar: cart.isNotEmpty
|
|
? Padding(
|
|
padding: EdgeInsets.all(0),
|
|
child: Container(
|
|
height: 56,
|
|
width: double.infinity,
|
|
decoration: BoxDecoration(
|
|
color: Color(0XFFF5CD47),
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(12),
|
|
topRight: Radius.circular(12),
|
|
),
|
|
),
|
|
padding: EdgeInsets.symmetric(horizontal: 24),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
"Order updated",
|
|
style: fontTextStyle(12, Color(0XFF232527), FontWeight.w500),
|
|
),
|
|
GestureDetector(
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => CartSummary(details: cart,supplierDetails: widget.details,)),
|
|
);
|
|
},
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
"View summary",
|
|
style: fontTextStyle(12, Color(0XFF232527), FontWeight.w500),
|
|
),
|
|
Image.asset(
|
|
'images/arrow-right.png',
|
|
fit: BoxFit.cover,
|
|
width:
|
|
20, // Match the diameter of the CircleAvatar
|
|
height: 20,
|
|
color: Color(0XFF343637),
|
|
),
|
|
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
|
|
);
|
|
}
|
|
}
|