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

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