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.
662 lines
21 KiB
662 lines
21 KiB
import 'dart:convert';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:bookatanker/common/settings.dart';
|
|
import 'package:bookatanker/supplier/my_orders_model.dart';
|
|
import 'cancel_order.dart';
|
|
|
|
class MyOrders extends StatefulWidget {
|
|
final String? navigationFrom;
|
|
const MyOrders({this.navigationFrom, Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<MyOrders> createState() => _MyOrdersState();
|
|
}
|
|
|
|
class _MyOrdersState extends State<MyOrders> with TickerProviderStateMixin {
|
|
bool isOrdersDataLoading = false;
|
|
List<MyOrdersModel> ordersList = [];
|
|
List<MyOrdersModel> upcomingOrders = [];
|
|
List<MyOrdersModel> previousOrders = [];
|
|
bool isPlansLoading = false;
|
|
List<dynamic> plansList = [];
|
|
List<dynamic> activePlans = [];
|
|
List<dynamic> pendingPlans = [];
|
|
late TabController _controller;
|
|
String tabMessage = "Welcome to Tab 1";
|
|
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = TabController(vsync: this, length: 2);
|
|
// Listen for tab changes
|
|
_controller.addListener(() {
|
|
setState(() {
|
|
tabMessage =
|
|
_controller.index == 0 ? "Welcome to Tab 1" : "Welcome to Tab 2";
|
|
});
|
|
});
|
|
getOrdersData();
|
|
getPlansData();
|
|
}
|
|
|
|
Future<void> getPlansData() async {
|
|
setState(() => isPlansLoading = true);
|
|
|
|
try {
|
|
final response = await AppSettings.getCustomerPlans();
|
|
final decoded = jsonDecode(response);
|
|
|
|
if (decoded == null || decoded['data'] == null) {
|
|
setState(() {
|
|
plansList = [];
|
|
isPlansLoading = false;
|
|
});
|
|
return;
|
|
}
|
|
|
|
final List<dynamic> data = decoded['data'];
|
|
|
|
setState(() {
|
|
plansList = data;
|
|
|
|
activePlans = data.where((p) =>
|
|
p['status'] == "processed" ||
|
|
p['status'] == "accepted" ||
|
|
p['status'] == "payment_completed").toList();
|
|
|
|
pendingPlans =
|
|
data.where((p) => p['status'] == "pending").toList();
|
|
|
|
isPlansLoading = false;
|
|
});
|
|
} catch (e) {
|
|
print("Plans error $e");
|
|
setState(() => isPlansLoading = false);
|
|
}
|
|
}
|
|
|
|
Future<void> getOrdersData() async {
|
|
setState(() => isOrdersDataLoading = true);
|
|
try {
|
|
final response = await AppSettings.getAllOrders();
|
|
final decoded = jsonDecode(response);
|
|
|
|
print("🔹 Full API Response: $decoded");
|
|
|
|
if (decoded == null || decoded['data'] == null) {
|
|
print("⚠️ No data key found in API response");
|
|
setState(() {
|
|
isOrdersDataLoading = false;
|
|
ordersList = [];
|
|
});
|
|
return;
|
|
}
|
|
|
|
final List<dynamic> data = decoded['data'];
|
|
if (data.isEmpty) {
|
|
print("⚠️ Empty data array");
|
|
setState(() {
|
|
isOrdersDataLoading = false;
|
|
ordersList = [];
|
|
});
|
|
return;
|
|
}
|
|
|
|
final parsedOrders =
|
|
data.map((model) => MyOrdersModel.fromJson(model)).toList();
|
|
|
|
print("✅ Total orders fetched: ${parsedOrders.length}");
|
|
|
|
setState(() {
|
|
ordersList = parsedOrders;
|
|
|
|
upcomingOrders = parsedOrders.where((order) {
|
|
final status = order.orderStatus.toLowerCase();
|
|
return status == "pending" ||
|
|
status == "advance_paid" ||
|
|
status == "assigned" ||
|
|
status == "deliveryboy_assigned";
|
|
}).toList();
|
|
|
|
previousOrders = parsedOrders.where((order) {
|
|
final status = order.orderStatus.toLowerCase();
|
|
return status == "completed" || status == "cancelled";
|
|
}).toList();
|
|
|
|
isOrdersDataLoading = false;
|
|
});
|
|
|
|
print("📦 Upcoming: ${upcomingOrders.length}, Previous: ${previousOrders.length}");
|
|
} catch (e) {
|
|
print("❌ Error in getOrdersData: $e");
|
|
setState(() => isOrdersDataLoading = false);
|
|
}
|
|
}
|
|
|
|
String capitalizeFirst(String input) {
|
|
if (input.isEmpty) return input;
|
|
return input[0].toUpperCase() + input.substring(1);
|
|
}
|
|
|
|
Widget buildUpcomingOrdersCard(MyOrdersModel order) {
|
|
return Card(
|
|
color: Colors.white,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(10.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Supplier name & status
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Visibility(
|
|
visible: order.supplierName.isNotEmpty,
|
|
child: Text(
|
|
order.supplierName,
|
|
style: fontTextStyle(16, const Color(0xFF2D2E30), FontWeight.w600),
|
|
),
|
|
),
|
|
_statusChip(order.orderStatus, const Color(0xFFFDF3D3), const Color(0xFFF5CD47),
|
|
'images/out_for_delivery.png'),
|
|
],
|
|
),
|
|
const SizedBox(height: 6),
|
|
// Type + Capacity
|
|
Row(
|
|
children: [
|
|
Text("${order.capacity} L - ${order.typeofwater}",
|
|
style: fontTextStyle(12, const Color(0xFF71ABFD), FontWeight.w500)),
|
|
const SizedBox(width: 6),
|
|
Image.asset('images/arrow-right.png', width: 16, height: 16, color: const Color(0xFF71ABFD)),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
// Address + Payment
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Text(order.displayAddress,
|
|
style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w400))),
|
|
_paymentChip(order.paymentStatus),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
// Expected Delivery
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text("Expected delivery: ${order.expectedDateOfDelivery}",
|
|
style: fontTextStyle(10, const Color(0xFF939495), FontWeight.w500)),
|
|
if (order.price.isNotEmpty)
|
|
Text("₹ ${order.price}",
|
|
style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w400)),
|
|
],
|
|
),
|
|
const Divider(height: 20),
|
|
// Buttons
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: ()async{
|
|
final result = await Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => CancelOrderPage(
|
|
order: order,
|
|
status: order.orderStatus,
|
|
),
|
|
),
|
|
);
|
|
|
|
if (result == true) {
|
|
getOrdersData();
|
|
}
|
|
},
|
|
|
|
/* onTap: () => Navigator.push(
|
|
context, MaterialPageRoute(builder: (_) => CancelOrderPage(order:order,status: order.orderStatus,))),*/
|
|
|
|
|
|
child: Text("CANCEL ORDER",
|
|
style: fontTextStyle(12, const Color(0xFFE2483D), FontWeight.w600))),
|
|
const SizedBox(width: 30),
|
|
GestureDetector(
|
|
onTap: () {},
|
|
child: Text("TRACK ORDER",
|
|
style: fontTextStyle(12, const Color(0xFF1D7AFC), FontWeight.w600))),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildPreviousOrdersCard(MyOrdersModel order) {
|
|
final isDelivered = order.orderStatus == 'completed';
|
|
return Card(
|
|
color: Colors.white,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(10.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Header Row
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
if (order.supplierName.isNotEmpty)
|
|
Text(order.supplierName,
|
|
style: fontTextStyle(16, const Color(0xFF2D2E30), FontWeight.w600)),
|
|
_statusChip(
|
|
order.orderStatus,
|
|
isDelivered ? const Color(0xFFC4E8C3) : const Color(0xFFF8D3D0),
|
|
isDelivered ? const Color(0xFF098603) : const Color(0xFFE2483D),
|
|
isDelivered ? 'images/rite.png' : 'images/cancel.png',
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(order.typeofwater,
|
|
style: fontTextStyle(12, const Color(0xFF71ABFD), FontWeight.w500)),
|
|
const SizedBox(height: 4),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Text(order.displayAddress,
|
|
style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w400))),
|
|
_paymentChip(order.paymentStatus),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(order.displayDeliveredDate,
|
|
style: fontTextStyle(10, const Color(0xFF939495), FontWeight.w500)),
|
|
if (order.price.isNotEmpty)
|
|
Text("₹ ${order.price}",
|
|
style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w400)),
|
|
],
|
|
),
|
|
if (isDelivered) ...[
|
|
const Divider(height: 20),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {},
|
|
child: Text("REPEAT ORDER",
|
|
style: fontTextStyle(12, const Color(0xFF4692FD), FontWeight.w600)),
|
|
),
|
|
const SizedBox(width: 30),
|
|
GestureDetector(
|
|
onTap: () {},
|
|
child: Text("RATE ORDER",
|
|
style: fontTextStyle(12, const Color(0xFF646566), FontWeight.w600)),
|
|
),
|
|
],
|
|
),
|
|
]
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _statusChip(String text, Color bg, Color txt, String img) {
|
|
return Container(
|
|
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
|
|
decoration: BoxDecoration(
|
|
color: bg, borderRadius: BorderRadius.circular(4), border: Border.all(color: bg)),
|
|
child: Row(
|
|
children: [
|
|
Image.asset(img, width: 12, height: 12),
|
|
const SizedBox(width: 4),
|
|
Text(capitalizeFirst(text),
|
|
style: fontTextStyle(10, txt, FontWeight.w500)),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _paymentChip(String status) {
|
|
final isPaid = status.toLowerCase() == 'paid';
|
|
return Row(
|
|
children: [
|
|
Image.asset(isPaid ? 'images/paid.png' : 'images/warning.png', width: 16, height: 16),
|
|
const SizedBox(width: 4),
|
|
Text(capitalizeFirst(status),
|
|
style: fontTextStyle(12, const Color(0xFF515253), FontWeight.w400)),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget renderUi() {
|
|
if (ordersList.isEmpty) {
|
|
return const Center(
|
|
child: Text("No Data Available",
|
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)));
|
|
}
|
|
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (upcomingOrders.isNotEmpty) ...[
|
|
Text("UPCOMING ORDERS",
|
|
style: fontTextStyle(12, const Color(0xFF646566), FontWeight.w400)),
|
|
const SizedBox(height: 8),
|
|
ListView.builder(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemCount: upcomingOrders.length,
|
|
itemBuilder: (context, index) =>
|
|
buildUpcomingOrdersCard(upcomingOrders[index]),
|
|
),
|
|
],
|
|
if (previousOrders.isNotEmpty) ...[
|
|
const SizedBox(height: 16),
|
|
Text("PREVIOUS ORDERS",
|
|
style: fontTextStyle(12, const Color(0xFF646566), FontWeight.w400)),
|
|
const SizedBox(height: 8),
|
|
ListView.builder(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemCount: previousOrders.length,
|
|
itemBuilder: (context, index) =>
|
|
buildPreviousOrdersCard(previousOrders[index]),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildPlansCard(var plan) {
|
|
return Card(
|
|
color: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(10),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
"${plan['capacity']} L - ${plan['type_of_water']}",
|
|
style: fontTextStyle(
|
|
14, const Color(0xFF2D2E30), FontWeight.w600),
|
|
),
|
|
|
|
_statusChip(
|
|
plan['status'],
|
|
const Color(0xFFE3F2FD),
|
|
const Color(0xFF1D7AFC),
|
|
'images/rite.png',
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 6),
|
|
|
|
Text(
|
|
"Frequency : ${plan['frequency']}",
|
|
style: fontTextStyle(
|
|
12, const Color(0xFF71ABFD), FontWeight.w500),
|
|
),
|
|
|
|
const SizedBox(height: 4),
|
|
|
|
Text(
|
|
"Start : ${plan['start_date']}",
|
|
style: fontTextStyle(
|
|
10, const Color(0xFF939495), FontWeight.w500),
|
|
),
|
|
|
|
Text(
|
|
"End : ${plan['end_date']}",
|
|
style: fontTextStyle(
|
|
10, const Color(0xFF939495), FontWeight.w500),
|
|
),
|
|
|
|
const Divider(height: 20),
|
|
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {},
|
|
child: Text(
|
|
"VIEW PLAN",
|
|
style: fontTextStyle(
|
|
12, const Color(0xFF1D7AFC), FontWeight.w600),
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 30),
|
|
|
|
GestureDetector(
|
|
onTap: () {},
|
|
child: Text(
|
|
"CANCEL PLAN",
|
|
style: fontTextStyle(
|
|
12, const Color(0xFFE2483D), FontWeight.w600),
|
|
),
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget renderPlansUi() {
|
|
|
|
if (plansList.isEmpty) {
|
|
return const Center(
|
|
child: Text("No Plans Available"),
|
|
);
|
|
}
|
|
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
/// ACTIVE PLANS
|
|
if (activePlans.isNotEmpty) ...[
|
|
Text(
|
|
"ACTIVE PLANS",
|
|
style: fontTextStyle(
|
|
12,
|
|
const Color(0xFF646566),
|
|
FontWeight.w400,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
ListView.builder(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemCount: activePlans.length,
|
|
itemBuilder: (context, index) {
|
|
|
|
final plan = activePlans[index];
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 10),
|
|
child: buildPlansCard(plan),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
|
|
/// SPACE
|
|
const SizedBox(height: 20),
|
|
|
|
/// PENDING PLANS
|
|
if (pendingPlans.isNotEmpty) ...[
|
|
Text(
|
|
"PENDING PLANS",
|
|
style: fontTextStyle(
|
|
12,
|
|
const Color(0xFF646566),
|
|
FontWeight.w400,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
ListView.builder(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemCount: pendingPlans.length,
|
|
itemBuilder: (context, index) {
|
|
|
|
final plan = pendingPlans[index];
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 10),
|
|
child: buildPlansCard(plan),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
|
|
const SizedBox(height: 20),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: Colors.white,
|
|
appBar: AppBar(
|
|
backgroundColor: Colors.white,
|
|
title: Text(
|
|
'Orders',
|
|
style: fontTextStyle(14, Color(0XFF2A2A2A), FontWeight.w500),
|
|
),
|
|
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
|
|
actions: [
|
|
Row(
|
|
children: [
|
|
Padding(
|
|
padding: EdgeInsets.fromLTRB(10, 10, 0, 10),
|
|
child: IconButton(
|
|
icon: Image(
|
|
image: AssetImage('images/customercare_appbar.png')),
|
|
onPressed: () {},
|
|
),
|
|
),
|
|
Padding(
|
|
padding: EdgeInsets.fromLTRB(0, 10, 10, 10),
|
|
child: IconButton(
|
|
icon: Image.asset(
|
|
'images/notification_appbar.png', // Example URL image
|
|
),
|
|
onPressed: () {},
|
|
),
|
|
)
|
|
],
|
|
)
|
|
],
|
|
bottom: PreferredSize(
|
|
preferredSize: Size.fromHeight(50.0),
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: TabBar(
|
|
controller: _controller,
|
|
indicatorColor: Colors.transparent,
|
|
tabs: [
|
|
// Tab 1
|
|
Builder(
|
|
builder: (BuildContext context) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: _controller.index == 0
|
|
? Color(0XFFFE8F2FF)
|
|
: Colors.white, // Selected tab color
|
|
borderRadius: BorderRadius.circular(10),
|
|
border: Border.all(
|
|
color: _controller.index == 0
|
|
? Color(0XFFFE8F2FF)
|
|
: Colors.white,
|
|
width: 2),
|
|
),
|
|
child: Tab(
|
|
child: Center(
|
|
child: Text('Orders',
|
|
style: fontTextStyle(12, Color(0XFF101214),
|
|
FontWeight.w600))),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
// Tab 2
|
|
Builder(
|
|
builder: (BuildContext context) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: _controller.index == 1
|
|
? Color(0XFFFE8F2FF)
|
|
: Colors.white, // Selected tab color
|
|
borderRadius: BorderRadius.circular(10),
|
|
border: Border.all(
|
|
color: _controller.index == 1
|
|
? Color(0XFFFE8F2FF)
|
|
: Colors.white,
|
|
width: 2),
|
|
),
|
|
child: Tab(
|
|
child: Center(
|
|
child: Text('Plans',
|
|
style: fontTextStyle(12, Color(0XFF101214),
|
|
FontWeight.w600))),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
)),
|
|
),
|
|
),
|
|
body: Builder(
|
|
builder: (BuildContext context) {
|
|
return TabBarView(
|
|
controller: _controller,
|
|
children: [
|
|
isOrdersDataLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: renderUi(),
|
|
|
|
isPlansLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: renderPlansUi(),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|