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

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,
);
}
}