diff --git a/images/bore-water.png b/images/bore-water.png new file mode 100644 index 0000000..f1020aa Binary files /dev/null and b/images/bore-water.png differ diff --git a/images/building.png b/images/building.png new file mode 100644 index 0000000..2d675de Binary files /dev/null and b/images/building.png differ diff --git a/images/drinking-water.png b/images/drinking-water.png new file mode 100644 index 0000000..e5c8a2a Binary files /dev/null and b/images/drinking-water.png differ diff --git a/lib/common/dashboard.dart b/lib/common/dashboard.dart index e77df8c..60a7118 100644 --- a/lib/common/dashboard.dart +++ b/lib/common/dashboard.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/orders/all_orders.dart'; +import 'package:supplier_new/set_rates/set_rates.dart'; import '../login/login.dart'; @@ -664,24 +666,6 @@ class HomeScreen extends StatefulWidget { class _HomeScreenState extends State { - Widget _quickActionButton(String label) { - return Container( - width: 70, - height: 70, - decoration: BoxDecoration( - color: Colors.grey.shade100, - borderRadius: BorderRadius.circular(12), - ), - child: Center( - child: Text( - label, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500), - ), - ), - ); - } - @override Widget build(BuildContext context) { return SingleChildScrollView( @@ -689,32 +673,30 @@ class _HomeScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - - // Profile Completion + /// Profile Completion Section Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "Complete your Profile", - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), - Text( - "Edit Profile", - style: TextStyle( - color: Colors.blue, - fontSize: 14, - fontWeight: FontWeight.w500, + TextButton( + onPressed: () {}, + child: const Text( + "Edit Profile", + style: TextStyle(color: Colors.blue, fontSize: 14), ), - ), + ) ], ), - const SizedBox(height: 8), + const SizedBox(height: 4), LinearProgressIndicator( value: 0.4, - backgroundColor: Colors.grey.shade200, - color: Colors.blue, + backgroundColor: Colors.grey[300], + valueColor: AlwaysStoppedAnimation(Colors.deepPurple), minHeight: 6, - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(8), ), const SizedBox(height: 6), const Text( @@ -722,165 +704,249 @@ class _HomeScreenState extends State { style: TextStyle(fontSize: 12, color: Colors.grey), ), + const SizedBox(height: 16), + + /// Orders + Revenue + GridView.count( + crossAxisCount: 2, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisSpacing: 12, + mainAxisSpacing: 12, + childAspectRatio: 1.5, + children: [ + DashboardCard( + title: "Today's Orders", + value: "12", + subtitle: "+2 from yesterday", + icon: Icons.shopping_cart_outlined, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AllOrders()), + ); + }, + ), + DashboardCard( + title: "Today's Revenue", + value: "₹24,890", + subtitle: "↑ 8% from yesterday", + icon: Icons.currency_rupee, + ), + ], + ), + + const SizedBox(height: 12), + + /// Vehicles + Drivers + GridView.count( + crossAxisCount: 2, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisSpacing: 12, + mainAxisSpacing: 12, + childAspectRatio: 1.5, + children: const [ + DashboardCard( + title: "Active Vehicles", + value: "12/14", + subtitle: "2 in maintenance", + icon: Icons.local_shipping_outlined, + ), + DashboardCard( + title: "Available Drivers", + value: "2/10", + subtitle: "6 on delivery, 2 offline", + icon: Icons.person_outline, + ), + ], + ), + const SizedBox(height: 20), - // Quick Actions Title + /// Quick Actions const Text( "Quick Actions", - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), ), const SizedBox(height: 12), - // Set Rates Card - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.blue.shade600, - borderRadius: BorderRadius.circular(16), - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - Text( - "Set Rates", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - SizedBox(height: 6), - Text( - "Set your water rates and delivery\nrates for the day in just a few taps", - style: TextStyle(fontSize: 12, color: Colors.white70), - ), - ], + GestureDetector( + onTap: (){ + Navigator.push( + context, + new MaterialPageRoute( + builder: (__) => new SetRatesScreen())); + }, + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.deepPurple, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + const Icon(Icons.currency_rupee, color: Colors.white), + const SizedBox(width: 12), + const Expanded( + child: Text( + "Set Rates\nSet your water rates and delivery rates for the day in just a few taps", + style: TextStyle(color: Colors.white, fontSize: 14), + ), ), - ), - const Icon(Icons.currency_rupee, color: Colors.white, size: 28), - ], + Icon(Icons.arrow_forward_ios, color: Colors.white, size: 18), + ], + ), ), ), const SizedBox(height: 16), - // Quick Action Buttons + /// Requests Section Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _quickActionButton("Orders"), - _quickActionButton("Plans"), - _quickActionButton("Drivers"), - _quickActionButton("Fleet"), - ], - ), - - const SizedBox(height: 24), - - // Ongoing Plans Title - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: const [ - Text( - "Ongoing Plans", - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + Expanded( + child: RequestCard( + title: "Order Requests", + subtitle: "1 new request", + ), + ), + SizedBox(width: 12), + Expanded( + child: RequestCard( + title: "Plan Requests", + subtitle: "2 new request", + ), ), - Icon(Icons.arrow_forward_ios, size: 14, color: Colors.grey), ], - ), + ) + ], + ), + ); + } +} - const SizedBox(height: 12), +class DashboardCard extends StatelessWidget { + final String title; + final String value; + final String subtitle; + final IconData icon; + final VoidCallback? onTap; // 👈 add callback + + const DashboardCard({ + super.key, + required this.title, + required this.value, + required this.subtitle, + required this.icon, + this.onTap, // 👈 optional + }); - // Ongoing Plan Card - Container( - width: double.infinity, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: [ - BoxShadow( - color: Colors.black12, - blurRadius: 4, - spreadRadius: 2, - offset: Offset(0, 2), - ) - ], + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, // 👈 handle tap + borderRadius: BorderRadius.circular(16), // ripple effect follows shape + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade300, + blurRadius: 6, + offset: const Offset(0, 2), ), - child: Column( + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - ClipRRect( - borderRadius: - const BorderRadius.vertical(top: Radius.circular(16)), - child: Image.asset( - "images/business.png", // replace with your asset - height: 30, - width: 30, - fit: BoxFit.cover, + Text( + title, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, ), ), - Padding( - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Status - Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: Colors.amber.shade200, - borderRadius: BorderRadius.circular(8), - ), - child: const Text( - "Pending", - style: TextStyle( - fontSize: 12, fontWeight: FontWeight.w500), - ), - ), - const SizedBox(height: 6), - const Text( - "Rajpushpa Atria", - style: TextStyle( - fontSize: 16, fontWeight: FontWeight.bold), - ), - const Text( - "Kokapet", - style: TextStyle(color: Colors.grey, fontSize: 13), - ), - const SizedBox(height: 6), - const Text( - "15,000L - Bore Water", - style: TextStyle(fontSize: 14), - ), - const Text( - "12:00 PM, 23/07/2025", - style: TextStyle(fontSize: 13, color: Colors.grey), - ), - const SizedBox(height: 10), - ElevatedButton( - onPressed: () {}, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), - ), - child: const Text("Assign"), - ) - ], - ), - ) + Align( + alignment: Alignment.topRight, + child: Icon(icon, color: Colors.deepPurple), + ), ], ), - ) - ], + const SizedBox(height: 6), + Text( + value, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + subtitle, + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), + ], + ), ), ); } } +class RequestCard extends StatelessWidget { + final String title; + final String subtitle; -// Screens for each bottom navigation item + const RequestCard({ + super.key, + required this.title, + required this.subtitle, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.grey.shade300), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + ), + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: Colors.green[50], + borderRadius: BorderRadius.circular(8), + ), + child: Text( + subtitle, + style: const TextStyle(color: Colors.green, fontSize: 12), + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/common/settings.dart b/lib/common/settings.dart index 1ef0b27..ffd3fc6 100644 --- a/lib/common/settings.dart +++ b/lib/common/settings.dart @@ -137,6 +137,8 @@ class AppSettings{ static String resetTokenUrl = host + 'reset_token'; static String verifyPhnUrl = host + 'phone'; static String uploadPicUrl = host + 'uploads-user'; + static String getOrderRequestsFromUsersUrl = host + 'getuserRequestbookingsforsupplier'; + // Shared preferences save,get and clear data @@ -340,6 +342,30 @@ class AppSettings{ return response.body; } + static Future getOrderRequestsFromUsers(payload) async { + var uri = Uri.parse(getOrderRequestsFromUsersUrl); + //uri = uri.replace(query: 'customerId=$customerId'); + + var response = await http.post(uri, body: json.encode(payload), headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return response.body; + } else if (response.statusCode == 401) { + bool status = await AppSettings.resetToken(); + if (status) { + response = await http.post(uri, headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return response.body; + } else { + return ''; + } + } else { + return ''; + } + } else { + return ''; + } + } + /*Apis ends here*/ diff --git a/lib/orders/all_orders.dart b/lib/orders/all_orders.dart new file mode 100644 index 0000000..1d974c1 --- /dev/null +++ b/lib/orders/all_orders.dart @@ -0,0 +1,403 @@ +import 'package:flutter/material.dart'; +import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/orders/order_requests.dart'; +import 'package:supplier_new/orders/orders_model.dart'; +import 'package:supplier_new/orders/search_order_appbar.dart'; +import 'package:intl/intl.dart'; + +class AllOrders extends StatefulWidget { + const AllOrders({super.key}); + + @override + State createState() => _AllOrdersState(); +} + +class _AllOrdersState extends State { + final TextEditingController searchController = TextEditingController(); + + + + + + @override + Widget build(BuildContext context) { + final List orders = [ + OrdersModel( + date: DateTime(2025, 8, 24), + imageAsset: "images/building.png",// sample image + status: "completed", + title: "Rajpushpa Atria", + location: "Kokapet", + quantity: "15,000L - Bore Water", + time: "01:00 AM, Today", + extraInfo: "Delivered by Suresh", + ), + OrdersModel( + date: DateTime(2025, 8, 24), + imageAsset: "images/building.png",// sample image + status: "in-progress", + title: "Rajpushpa Atria", + location: "Kokapet", + quantity: "15,000L - Bore Water", + time: "01:00 AM, 21/07/2025", + extraInfo: "Track Delivery", + ), + OrdersModel( + date: DateTime(2025, 8, 23), + imageAsset: "images/building.png",// sample image + status: "cancelled", + title: "Lakeview Towers", + location: "Madhapur", + quantity: "8,000L - Tanker Water", + time: "09:30 PM, Yesterday", + extraInfo: "Cancelled by user", + ), + ]; + + // ✅ Group orders by date + final Map> groupedOrders = {}; + for (var order in orders) { + final formattedDate = DateFormat("dd MMM yyyy").format(order.date); + groupedOrders.putIfAbsent(formattedDate, () => []).add(order); + } + return Scaffold( + backgroundColor: Color(0XFFF2F2F2), + appBar: SearchOrderAppBar( + controller: searchController, + onBack: () => Navigator.pop(context), + onHelp: () { + // Show help dialog or navigate + print("Help tapped"); + }, + ), + body: SingleChildScrollView( + child: + Padding( + padding: EdgeInsets.all(16), + child: Column( + children: [ + SizedBox(height: MediaQuery.of(context).size.height * .042), + /// Total Orders + Column( + children: [ + Text( + "12", + style:fontTextStyle(64, Color(0XFFFF2D2E30), FontWeight.w700), + ), + Text( + "Today's Total Orders", + style: fontTextStyle(24, Color(0XFFFF2D2E30), FontWeight.w600), + ), + SizedBox(height: MediaQuery.of(context).size.height * .042), + /// Bore Water + Drinking Water + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + OrderCategoryCard( + image: Image.asset( + 'images/bore-water.png', + fit: BoxFit.contain, + height: 40, + width: 40, + ), + value: "16", + label: "Bore Water", + ), + OrderCategoryCard( + image: Image.asset( + 'images/drinking-water.png', + height: 40, + width: 40, + fit: BoxFit.contain, + ), + value: "08", + label: "Drinking Water", + ), + ], + ), + + SizedBox(height: MediaQuery.of(context).size.height * .024), + /// Button + Container( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor:Color(0XFF8270DB), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(32), + ), + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 16), + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => OrderRequestsPage()), + ); + }, + child: Text( + "View Order Requests", + style: fontTextStyle(16, Color(0XFFFFFFFFFF), FontWeight.w500), + ), + ), + ) + ], + ), + + const SizedBox(height: 20), + + /// Filters Row + SingleChildScrollView( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + FilterChipWidget(label: "Building"), + const SizedBox(width: 8), + FilterChipWidget(label: "Status"), + const SizedBox(width: 8), + FilterChipWidget(label: "Date"), + const SizedBox(width: 8), + FilterChipWidget(label: "Amount"), + ], + ), + ), + + const SizedBox(height: 20), + + /// Order Card Example + /// Order List + ListView( + padding: const EdgeInsets.all(12), + shrinkWrap: true, // ✅ Important + physics: NeverScrollableScrollPhysics(), // ✅ Prevent nested scrolling + children: groupedOrders.entries.map((entry) { + final date = entry.key; + final ordersForDate = entry.value; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Date heading + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + date, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), + ), + + // Orders list for this date + ...ordersForDate.map((order) => OrderCard(order: order)), + const SizedBox(height: 12), + ], + ); + }).toList(), + ), + + ], + ), + ) + ), + ); + } +} + +/// Category Card (Bore Water, Drinking Water) +class OrderCategoryCard extends StatelessWidget { + final Image image; + final String value; + final String label; + + const OrderCategoryCard({ + super.key, + required this.image, + required this.value, + required this.label, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SizedBox( + width: 40, + height: 40, + child: image, // ✅ Replaced Icon with Image + ), + SizedBox(height: MediaQuery.of(context).size.height * .008), + Text( + value, + style: fontTextStyle(20, Color(0XFFFF515253), FontWeight.w700), + ), + SizedBox(height: MediaQuery.of(context).size.height * .008), + Text(label, style: fontTextStyle(16, Color(0XFFFF515253), FontWeight.w400),), + ], + ); + } +} + +/// Filter Chip +class FilterChipWidget extends StatelessWidget { + final String label; + const FilterChipWidget({super.key, required this.label}); + + @override + Widget build(BuildContext context) { + return ChoiceChip( + label: Text(label), + selected: false, + onSelected: (_) {}, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + backgroundColor: Colors.white, + side: BorderSide(color: Colors.grey.shade300), + ); + } +} + +class OrderCard extends StatelessWidget { + final OrdersModel order; + + const OrderCard({super.key, required this.order}); + + Color _getStatusColor() { + switch (order.status.toLowerCase()) { + case "completed": + return Colors.green; + case "in-progress": + return Colors.blue; + case "cancelled": + return Colors.red; + default: + return Colors.grey; + } + } + + @override + Widget build(BuildContext context) { + final statusColor = _getStatusColor(); + + return Container( + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 5, + offset: const Offset(0, 3), + ) + ], + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Image + ClipRRect( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(12), + bottomLeft: Radius.circular(12), + ), + child: order.imageAsset != null + ? Image.asset( + order.imageAsset!, + height: 100, + width: 100, + fit: BoxFit.cover, + ) + : Image.network( + order.imageUrl!, + height: 100, + width: 100, + fit: BoxFit.cover, + ), + ), + + // Right side content + Expanded( + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Status chip + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 2, + ), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + order.status, + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: statusColor, + ), + ), + ), + const SizedBox(height: 6), + + Text( + order.title, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), + Text( + order.location, + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade600, + ), + ), + const SizedBox(height: 4), + + Text( + order.quantity, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + ), + ), + Text( + order.time, + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade600, + ), + ), + const SizedBox(height: 4), + + Text( + order.extraInfo, + style: TextStyle( + fontSize: 12, + color: order.status == "in-progress" + ? Colors.blue + : Colors.grey.shade600, + fontWeight: order.status == "in-progress" + ? FontWeight.w600 + : FontWeight.w400, + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/orders/order_requests.dart b/lib/orders/order_requests.dart new file mode 100644 index 0000000..a358a6a --- /dev/null +++ b/lib/orders/order_requests.dart @@ -0,0 +1,294 @@ +import 'package:flutter/material.dart'; + +class OrderRequestsPage extends StatefulWidget { + const OrderRequestsPage({super.key}); + + @override + State createState() => _OrderRequestsPageState(); +} + +class _OrderRequestsPageState extends State { + final TextEditingController searchController = TextEditingController(); + + // ✅ Sample orders list + final List allOrders = [ + OrderModel( + status: "New", + title: "Green Valley Apartments", + location: "Gachibowli", + description: "10,000 L - Drinking water", + price: "₹3,400", + ), + OrderModel( + status: "Expires in 15m", + title: "Lakeview Towers", + location: "Madhapur", + description: "8,000 L - Borewell water", + price: "₹2,700", + ), + OrderModel( + status: "Expired", + title: "Sunrise Residency", + location: "Kukatpally", + description: "12,000 L - Tanker water", + price: "₹4,000", + ), + OrderModel( + status: "Rejected", + title: "Skyline Apartments", + location: "Kompally", + description: "5,000 L - Drinking water", + price: "₹1,600", + ), + OrderModel( + status: "Pending", + title: "Skyline Apartments", + location: "Kompally", + description: "5,000 L - Drinking water", + price: "₹1,600", + ), + OrderModel( + status: "Rejected", + title: "Elite Towers", + location: "Hitech City", + description: "20,000 L - Borewell water", + price: "₹6,000", + ), + ]; + + @override + Widget build(BuildContext context) { + // ✅ Split orders into rejected and others + final rejectedOrders = + allOrders.where((o) => o.status.toLowerCase() == "rejected").toList(); + final otherOrders = + allOrders.where((o) => o.status.toLowerCase() != "rejected").toList(); + + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () => Navigator.pop(context), + ), + title: const Text( + "Order Requests", + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + centerTitle: false, + actions: [ + IconButton( + icon: const Icon(Icons.help_outline, color: Colors.black), + onPressed: () {}, + ), + const SizedBox(width: 8), + ], + ), + body: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + /// Search bar + Row( + children: [ + Expanded( + child: Container( + height: 42, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24), + border: Border.all(color: Colors.grey.shade300), + color: Colors.white, + ), + child: TextField( + controller: searchController, + decoration: const InputDecoration( + hintText: "Search", + prefixIcon: Icon(Icons.search, color: Colors.grey), + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 10), + ), + ), + ), + ), + const SizedBox(width: 10), + Container( + height: 42, + width: 42, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade300), + color: Colors.white, + ), + child: IconButton( + icon: const Icon(Icons.sort, size: 20, color: Colors.black), + onPressed: () {}, + ), + ), + ], + ), + + const SizedBox(height: 16), + + /// Orders List + Expanded( + child: ListView( + children: [ + // Active / Other Orders + ...otherOrders.map((o) => OrderCard(order: o)), + + // Rejected Orders Section + if (rejectedOrders.isNotEmpty) ...[ + const SizedBox(height: 12), + const Text( + "Rejected Requests", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + const SizedBox(height: 8), + ...rejectedOrders.map((o) => OrderCard(order: o)), + ], + ], + ), + ), + ], + ), + ), + ); + } +} + +/// Order Model +class OrderModel { + final String status; + final String title; + final String location; + final String description; + final String price; + + OrderModel({ + required this.status, + required this.title, + required this.location, + required this.description, + required this.price, + }); +} + +/// Order Card widget +class OrderCard extends StatelessWidget { + final OrderModel order; + + const OrderCard({super.key, required this.order}); + + Color _getStatusColor() { + switch (order.status.toLowerCase()) { + case "new": + return Colors.blue; + case "expires in 15m": + case "expires in 5m": + return Colors.orange; + case "expired": + return Colors.grey; + case "rejected": + return Colors.red; + default: + return Colors.black; + } + } + + @override + Widget build(BuildContext context) { + final statusColor = _getStatusColor(); + + return Container( + margin: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade300), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// Left content + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Status chip + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + order.status, + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: statusColor, + ), + ), + ), + const SizedBox(height: 6), + + // Title + Text( + order.title, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), + + // Location + Text( + order.location, + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade600, + ), + ), + + const SizedBox(height: 4), + + // Description + Text( + order.description, + style: const TextStyle( + fontSize: 12, + color: Colors.blue, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + + /// Price + Text( + order.price, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ); + } +} diff --git a/lib/orders/orders_model.dart b/lib/orders/orders_model.dart new file mode 100644 index 0000000..d703708 --- /dev/null +++ b/lib/orders/orders_model.dart @@ -0,0 +1,23 @@ +class OrdersModel { + final DateTime date; + final String? imageUrl; // Network image + final String? imageAsset; + final String status; + final String title; + final String location; + final String quantity; + final String time; + final String extraInfo; + + OrdersModel({ + required this.date, + this.imageUrl, + this.imageAsset, + required this.status, + required this.title, + required this.location, + required this.quantity, + required this.time, + required this.extraInfo, + }); +} \ No newline at end of file diff --git a/lib/orders/search_order_appbar.dart b/lib/orders/search_order_appbar.dart new file mode 100644 index 0000000..d484bdd --- /dev/null +++ b/lib/orders/search_order_appbar.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; + +class SearchOrderAppBar extends StatelessWidget implements PreferredSizeWidget { + final TextEditingController controller; + final VoidCallback onBack; + final VoidCallback onHelp; + + const SearchOrderAppBar({ + super.key, + required this.controller, + required this.onBack, + required this.onHelp, + }); + + @override + Widget build(BuildContext context) { + return AppBar( + backgroundColor: Colors.white, + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: onBack, + ), + titleSpacing: 0, + title: Container( + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(24), + border: Border.all(color: Colors.grey.shade400), + ), + child: TextField( + controller: controller, + decoration: const InputDecoration( + hintText: "Search order", + hintStyle: TextStyle(fontSize: 14, color: Colors.grey), + prefixIcon: Icon(Icons.search, color: Colors.grey), + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 10), + ), + ), + ), + actions: [ + IconButton( + icon: const Icon(Icons.help_outline, color: Colors.black), + onPressed: onHelp, + ), + const SizedBox(width: 8), + ], + ); + } + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +} diff --git a/lib/set_rates/set_rates.dart b/lib/set_rates/set_rates.dart new file mode 100644 index 0000000..f74cf08 --- /dev/null +++ b/lib/set_rates/set_rates.dart @@ -0,0 +1,169 @@ +import 'package:flutter/material.dart'; + +class SetRatesScreen extends StatefulWidget { + const SetRatesScreen({super.key}); + + @override + State createState() => _SetRatesScreenState(); +} + +class _SetRatesScreenState extends State + with SingleTickerProviderStateMixin { + late TabController _tabController; + + final TextEditingController drinking5000 = TextEditingController(); + final TextEditingController drinking10000 = TextEditingController(); + final TextEditingController drinking15000 = TextEditingController(); + final TextEditingController bore5000 = TextEditingController(); + final TextEditingController bore10000 = TextEditingController(); + final TextEditingController bore15000 = TextEditingController(); + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 3, vsync: this); + // Prefill values + drinking5000.text = "₹500"; + drinking10000.text = "₹500"; + drinking15000.text = "₹500"; + bore5000.text = "₹500"; + bore10000.text = "₹500"; + bore15000.text = "₹500"; + } + + Widget buildTextField(String label, TextEditingController controller) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: TextField( + controller: controller, + decoration: InputDecoration( + labelText: label, + labelStyle: const TextStyle(fontSize: 14, color: Colors.black87), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + keyboardType: TextInputType.number, + ), + ); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: AppBar( + title: const Text( + "Set Rates", + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), + ), + centerTitle: true, + actions: [ + TextButton( + onPressed: () {}, + child: const Text( + "HELP", + style: TextStyle(color: Colors.blue, fontSize: 14), + ), + ) + ], + ), + body: Column( + children: [ + const SizedBox(height: 16), + const CircleAvatar( + radius: 40, + backgroundColor: Color(0xFFE0E0E0), + child: Icon(Icons.water_drop_outlined, + size: 40, color: Colors.deepPurple), + ), + const SizedBox(height: 16), + const Text( + "What’s today’s water price?", + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), + ), + const SizedBox(height: 4), + const Text( + "Set your daily rate so customers know what to expect", + style: TextStyle(fontSize: 13, color: Colors.black54), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + TabBar( + controller: _tabController, + labelColor: Colors.black, + unselectedLabelColor: Colors.grey, + indicatorColor: Colors.deepPurple, + tabs: const [ + Tab(text: "Water Type"), + Tab(text: "Delivery Fee"), + Tab(text: "Pump"), + ], + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + // Water Type Tab + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "DRINKING WATER", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 14), + ), + buildTextField("5,000L (in ₹)", drinking5000), + buildTextField("10,000L (in ₹)", drinking10000), + buildTextField("15,000L (in ₹)", drinking15000), + const SizedBox(height: 20), + const Text( + "BORE WATER", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 14), + ), + buildTextField("5,000L (in ₹)", bore5000), + buildTextField("10,000L (in ₹)", bore10000), + buildTextField("15,000L (in ₹)", bore15000), + const SizedBox(height: 30), + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.deepPurple, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + ), + onPressed: () { + // Handle save logic + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Rates saved successfully")), + ); + }, + child: const Text( + "Save", + style: + TextStyle(fontSize: 16, color: Colors.white), + ), + ), + ) + ], + ), + ), + // Delivery Fee Tab + const Center(child: Text("Delivery Fee Settings")), + // Pump Tab + const Center(child: Text("Pump Settings")), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 9ac7938..171d04d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -352,6 +352,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.1+1" + intl: + dependency: "direct main" + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" js: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 058bd9b..7777d99 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: flutter_secure_storage: ^9.0.0 firebase_messaging: ^14.9.1 image_picker: ^1.1.2 + intl: ^0.17.0 dev_dependencies: flutter_test: