Initial commit of Flutter project
This commit is contained in:
@@ -1,40 +1,55 @@
|
|||||||
|
import java.util.Properties
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("kotlin-android")
|
id("kotlin-android")
|
||||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
|
||||||
id("dev.flutter.flutter-gradle-plugin")
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val keystoreProperties = Properties().apply {
|
||||||
|
val keystorePropertiesFile = rootProject.file("key.properties")
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystorePropertiesFile.inputStream().use { load(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.example.glowwheels"
|
namespace = "com.example.glowwheels"
|
||||||
compileSdk = flutter.compileSdkVersion
|
compileSdk = 34
|
||||||
ndkVersion = flutter.ndkVersion
|
ndkVersion = "25.1.8937393" // Optional, or remove if not used
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
jvmTarget = "11"
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
|
||||||
applicationId = "com.example.glowwheels"
|
applicationId = "com.example.glowwheels"
|
||||||
// You can update the following values to match your application needs.
|
minSdk = 21
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
targetSdk = 34
|
||||||
minSdk = flutter.minSdkVersion
|
versionCode = 1
|
||||||
targetSdk = flutter.targetSdkVersion
|
versionName = "1.0.0"
|
||||||
versionCode = flutter.versionCode
|
}
|
||||||
versionName = flutter.versionName
|
|
||||||
|
signingConfigs {
|
||||||
|
create("release") {
|
||||||
|
storeFile = File(keystoreProperties["storeFile"] as String)
|
||||||
|
storePassword = keystoreProperties["storePassword"] as String
|
||||||
|
keyAlias = keystoreProperties["keyAlias"] as String
|
||||||
|
keyPassword = keystoreProperties["keyPassword"] as String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
signingConfig = signingConfigs.getByName("release")
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
isMinifyEnabled = false
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
isShrinkResources = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,3 +57,7 @@ android {
|
|||||||
flutter {
|
flutter {
|
||||||
source = "../.."
|
source = "../.."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
android:label="glowwheels"
|
android:label="Glowwheels Vendor"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
20
lib/constants/api.dart
Normal file
20
lib/constants/api.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
class ApiConstants {
|
||||||
|
// Base URL
|
||||||
|
static const String baseUrl = 'https://backend.glowwheels.in/api';
|
||||||
|
|
||||||
|
// Auth
|
||||||
|
static String loginUrl = '$baseUrl/shop/login';
|
||||||
|
|
||||||
|
// Shop
|
||||||
|
static String shopDetails(String shopId) => '$baseUrl/shop/$shopId';
|
||||||
|
|
||||||
|
// Orders
|
||||||
|
static String ordersByShop(String shopId) => '$baseUrl/order/shop/$shopId';
|
||||||
|
static String assignServiceBoy(String orderId) => '$baseUrl/order/assign-serviceboy/$orderId';
|
||||||
|
static String updateOrderStatusByAdmin(String orderId) => '$baseUrl/order/update-status-admin/$orderId';
|
||||||
|
|
||||||
|
// Service Boy
|
||||||
|
static String serviceBoyDetails(String serviceBoyId) => '$baseUrl/service-boy/$serviceBoyId';
|
||||||
|
static String serviceBoysByShop(String shopId) => '$baseUrl/service-boy/shop/$shopId';
|
||||||
|
static String addServiceBoy = '$baseUrl/service-boy/add';
|
||||||
|
}
|
||||||
8
lib/helpers/shopid_helper.dart
Normal file
8
lib/helpers/shopid_helper.dart
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:glowwheels/provider/shop_provider.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
|
||||||
|
String? getShopId(BuildContext context) {
|
||||||
|
return Provider.of<ShopProvider>(context, listen: false).shop?.user.id;
|
||||||
|
}
|
||||||
@@ -5,10 +5,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:glowwheels/models/shop_model.dart';
|
import 'package:glowwheels/models/shop_model.dart';
|
||||||
import 'package:glowwheels/provider/order_provider.dart';
|
import 'package:glowwheels/provider/order_provider.dart';
|
||||||
import 'package:glowwheels/provider/serviceboy_provider.dart';
|
import 'package:glowwheels/provider/serviceboy_provider.dart';
|
||||||
|
import 'package:glowwheels/provider/shop_profile_provider.dart';
|
||||||
import 'package:glowwheels/provider/shop_provider.dart';
|
import 'package:glowwheels/provider/shop_provider.dart';
|
||||||
import 'package:glowwheels/screens/login_screen.dart';
|
import 'package:glowwheels/screens/splash_screen.dart';
|
||||||
import 'package:glowwheels/screens/main_screen.dart';
|
|
||||||
import 'package:glowwheels/screens/order_screen.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:hive_flutter/adapters.dart';
|
import 'package:hive_flutter/adapters.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@@ -17,13 +17,20 @@ void main() async {
|
|||||||
|
|
||||||
await Hive.initFlutter();
|
await Hive.initFlutter();
|
||||||
Hive.registerAdapter(ShopModelAdapter());
|
Hive.registerAdapter(ShopModelAdapter());
|
||||||
|
Hive.registerAdapter(ShopDetailsAdapter());
|
||||||
|
|
||||||
|
if (!Hive.isBoxOpen('shopbox')) {
|
||||||
|
await Hive.openBox<ShopModel>('shopbox');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider(create: (_) => OrderProvider()),
|
ChangeNotifierProvider(create: (_) => OrdersProvider()),
|
||||||
ChangeNotifierProvider(create: (_) => ServiceBoyProvider()),
|
ChangeNotifierProvider(create: (_) => ServiceBoyProvider()),
|
||||||
ChangeNotifierProvider(create: (_) => ShopProvider()),
|
ChangeNotifierProvider(create: (_) => ShopProvider()),
|
||||||
|
ChangeNotifierProvider(create: (_) => ShopProfileProvider()),
|
||||||
],
|
],
|
||||||
child: GlowWheelsApp(),
|
child: GlowWheelsApp(),
|
||||||
),
|
),
|
||||||
@@ -35,7 +42,7 @@ class GlowWheelsApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
home: LoginScreen(),
|
home: SplashDecider(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,229 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:glowwheels/models/serviceboy_model.dart';
|
import 'package:glowwheels/models/serviceboy_model.dart';
|
||||||
|
|
||||||
class Order {
|
class Order {
|
||||||
final String customerName;
|
final String id;
|
||||||
final String mobileNumber;
|
final User user;
|
||||||
final String serviceType;
|
final Shop shop;
|
||||||
final String service;
|
final Service service;
|
||||||
final String price;
|
final String? address;
|
||||||
final String time;
|
|
||||||
final String date;
|
final String date;
|
||||||
final String carName;
|
final String timeSlot;
|
||||||
final String status;
|
final String status;
|
||||||
final String imagePath;
|
final String? serviceBoyId;
|
||||||
ServiceBoy? assignedBoy;
|
final DateTime createdAt;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
final ServiceBoy? assignedServiceBoy;
|
||||||
|
|
||||||
Order({
|
Order({
|
||||||
required this.customerName,
|
required this.id,
|
||||||
required this.mobileNumber,
|
required this.user,
|
||||||
required this.serviceType,
|
required this.shop,
|
||||||
required this.service,
|
required this.service,
|
||||||
required this.price,
|
required this.address,
|
||||||
required this.time,
|
|
||||||
required this.date,
|
required this.date,
|
||||||
required this.carName,
|
required this.timeSlot,
|
||||||
required this.status,
|
required this.status,
|
||||||
required this.imagePath,
|
required this.serviceBoyId,
|
||||||
this.assignedBoy,
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
this.assignedServiceBoy,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
factory Order.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Order(
|
||||||
|
id: json['_id'],
|
||||||
|
user: User.fromJson(json['userId']),
|
||||||
|
shop: Shop.fromJson(json['shopId']),
|
||||||
|
service: Service.fromJson(json['serviceId']),
|
||||||
|
address : json['address'],
|
||||||
|
date: json['date'],
|
||||||
|
timeSlot: json['timeSlot'],
|
||||||
|
status: json['status'],
|
||||||
|
serviceBoyId: json['serviceBoyId'],
|
||||||
|
createdAt: DateTime.parse(json['createdAt']),
|
||||||
|
updatedAt: DateTime.parse(json['updatedAt']),
|
||||||
|
assignedServiceBoy: json['assignedServiceBoy'] != null
|
||||||
|
? ServiceBoy.fromJson(json['assignedServiceBoy'])
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Order copyWith({
|
||||||
|
String? id,
|
||||||
|
User? user,
|
||||||
|
Shop? shop,
|
||||||
|
Service? service,
|
||||||
|
String? date,
|
||||||
|
String? timeSlot,
|
||||||
|
String? status,
|
||||||
|
String? serviceBoyId,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
ServiceBoy? assignedServiceBoy,
|
||||||
|
}) {
|
||||||
|
return Order(
|
||||||
|
id: id ?? this.id,
|
||||||
|
user: user ?? this.user,
|
||||||
|
shop: shop ?? this.shop,
|
||||||
|
service: service ?? this.service,
|
||||||
|
date: date ?? this.date,
|
||||||
|
timeSlot: timeSlot ?? this.timeSlot,
|
||||||
|
status: status ?? this.status,
|
||||||
|
serviceBoyId: serviceBoyId ?? this.serviceBoyId,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
assignedServiceBoy: assignedServiceBoy ?? this.assignedServiceBoy, address: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class User {
|
||||||
|
final String id;
|
||||||
|
final String name;
|
||||||
|
final int phone;
|
||||||
|
|
||||||
|
User({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.phone,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory User.fromJson(Map<String, dynamic> json) {
|
||||||
|
return User(
|
||||||
|
id: json['_id'],
|
||||||
|
name: json['name'],
|
||||||
|
phone: json['phone'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shop {
|
||||||
|
final String id;
|
||||||
|
final String name;
|
||||||
|
final String address;
|
||||||
|
final double latitude;
|
||||||
|
final double longitude;
|
||||||
|
final String phone;
|
||||||
|
final String email;
|
||||||
|
final List<String> images;
|
||||||
|
final int rating;
|
||||||
|
|
||||||
|
Shop({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.address,
|
||||||
|
required this.latitude,
|
||||||
|
required this.longitude,
|
||||||
|
required this.phone,
|
||||||
|
required this.email,
|
||||||
|
required this.images,
|
||||||
|
required this.rating,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Shop.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Shop(
|
||||||
|
id: json['_id'],
|
||||||
|
name: json['name'],
|
||||||
|
address: json['address'],
|
||||||
|
latitude: json['latitude'].toDouble(),
|
||||||
|
longitude: json['longitude'].toDouble(),
|
||||||
|
phone: json['phone'],
|
||||||
|
email: json['email'],
|
||||||
|
images: List<String>.from(json['images']),
|
||||||
|
rating: json['rating'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Service {
|
||||||
|
final String id;
|
||||||
|
final Vehicle vehicle;
|
||||||
|
final Manufacture manufacture;
|
||||||
|
final Model model;
|
||||||
|
final String serviceName;
|
||||||
|
final int price;
|
||||||
|
final String serviceType;
|
||||||
|
|
||||||
|
Service({
|
||||||
|
required this.id,
|
||||||
|
required this.vehicle,
|
||||||
|
required this.manufacture,
|
||||||
|
required this.model,
|
||||||
|
required this.serviceName,
|
||||||
|
required this.price,
|
||||||
|
required this.serviceType,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Service.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Service(
|
||||||
|
id: json['_id'],
|
||||||
|
vehicle: Vehicle.fromJson(json['vehicleId']),
|
||||||
|
manufacture: Manufacture.fromJson(json['manufactureId']),
|
||||||
|
model: Model.fromJson(json['modelId']),
|
||||||
|
serviceName: json['serviceName'],
|
||||||
|
price: json['price'],
|
||||||
|
serviceType: json['serviceType'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vehicle {
|
||||||
|
final String id;
|
||||||
|
final String vehicle;
|
||||||
|
final String image;
|
||||||
|
|
||||||
|
Vehicle({
|
||||||
|
required this.id,
|
||||||
|
required this.vehicle,
|
||||||
|
required this.image,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Vehicle.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Vehicle(
|
||||||
|
id: json['_id'],
|
||||||
|
vehicle: json['vehicle'],
|
||||||
|
image: json['image'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Manufacture {
|
||||||
|
final String id;
|
||||||
|
final String manufacture;
|
||||||
|
final String image;
|
||||||
|
|
||||||
|
Manufacture({
|
||||||
|
required this.id,
|
||||||
|
required this.manufacture,
|
||||||
|
required this.image,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Manufacture.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Manufacture(
|
||||||
|
id: json['_id'],
|
||||||
|
manufacture: json['manufacture'],
|
||||||
|
image: json['image'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Model {
|
||||||
|
final String id;
|
||||||
|
final String model;
|
||||||
|
final String image;
|
||||||
|
|
||||||
|
Model({
|
||||||
|
required this.id,
|
||||||
|
required this.model,
|
||||||
|
required this.image,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Model.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Model(
|
||||||
|
id: json['_id'],
|
||||||
|
model: json['model'],
|
||||||
|
image: json['image'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
42
lib/models/service_boy_response.dart
Normal file
42
lib/models/service_boy_response.dart
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'package:glowwheels/models/service_boy_response.dart';
|
||||||
|
import 'package:glowwheels/models/serviceboy_model.dart';
|
||||||
|
|
||||||
|
class ServiceBoyResponse {
|
||||||
|
final String id;
|
||||||
|
final String shopId;
|
||||||
|
final List<ServiceBoy> serviceBoy;
|
||||||
|
final String createdAt;
|
||||||
|
final String updatedAt;
|
||||||
|
|
||||||
|
ServiceBoyResponse({
|
||||||
|
required this.id,
|
||||||
|
required this.shopId,
|
||||||
|
required this.serviceBoy,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ServiceBoyResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ServiceBoyResponse(
|
||||||
|
id: json['_id'],
|
||||||
|
shopId: json['shopId'],
|
||||||
|
serviceBoy: (json['serviceBoy'] as List)
|
||||||
|
.map((e) => ServiceBoy.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
createdAt: json['createdAt'],
|
||||||
|
updatedAt: json['updatedAt'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'_id': id,
|
||||||
|
'shopId': shopId,
|
||||||
|
'serviceBoy': serviceBoy.map((e) => e.toJson()).toList(),
|
||||||
|
'createdAt': createdAt,
|
||||||
|
'updatedAt': updatedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,27 @@
|
|||||||
class ServiceBoy {
|
class ServiceBoy {
|
||||||
|
final String id;
|
||||||
final String name;
|
final String name;
|
||||||
final String phone;
|
final String phone;
|
||||||
|
|
||||||
ServiceBoy({required this.name, required this.phone});
|
ServiceBoy({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.phone,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ServiceBoy.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ServiceBoy(
|
||||||
|
id: json['_id'],
|
||||||
|
name: json['name'],
|
||||||
|
phone: json['phone'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'_id': id,
|
||||||
|
'name': name,
|
||||||
|
'phone': phone,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,52 +2,74 @@ import 'package:hive/hive.dart';
|
|||||||
|
|
||||||
part 'shop_model.g.dart';
|
part 'shop_model.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: 1)
|
@HiveType(typeId: 0)
|
||||||
class ShopModel {
|
class ShopModel {
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String id;
|
bool success;
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final String image;
|
ShopDetails user;
|
||||||
|
|
||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
final String mobile;
|
String token;
|
||||||
|
|
||||||
@HiveField(3)
|
@HiveField(3)
|
||||||
final String email;
|
String message;
|
||||||
|
|
||||||
@HiveField(4)
|
|
||||||
final String shopName;
|
|
||||||
|
|
||||||
@HiveField(5)
|
|
||||||
final String address; // ✅ NEW
|
|
||||||
|
|
||||||
ShopModel({
|
ShopModel({
|
||||||
required this.id,
|
required this.success,
|
||||||
required this.image,
|
required this.user,
|
||||||
required this.mobile,
|
required this.token,
|
||||||
required this.email,
|
required this.message,
|
||||||
required this.shopName,
|
|
||||||
required this.address,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ShopModel.fromJson(Map<String, dynamic> json) {
|
factory ShopModel.fromJson(Map<String, dynamic> json) => ShopModel(
|
||||||
return ShopModel(
|
success: json['success'],
|
||||||
id: json['id'] ?? '',
|
user: ShopDetails.fromJson(json['user']),
|
||||||
image: json['image'] ?? '',
|
token: json['token'],
|
||||||
mobile: json['mobile'] ?? '',
|
message: json['message'],
|
||||||
email: json['email'] ?? '',
|
);
|
||||||
shopName: json['shopName'] ?? '',
|
|
||||||
address: json['address'] ?? '', // ✅ NEW
|
Map<String, dynamic> toJson() => {
|
||||||
);
|
'success': success,
|
||||||
}
|
'user': user.toJson(),
|
||||||
|
'token': token,
|
||||||
|
'message': message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@HiveType(typeId: 1)
|
||||||
|
class ShopDetails {
|
||||||
|
@HiveField(0)
|
||||||
|
String id;
|
||||||
|
|
||||||
|
@HiveField(1)
|
||||||
|
String name;
|
||||||
|
|
||||||
|
@HiveField(2)
|
||||||
|
String email;
|
||||||
|
|
||||||
|
@HiveField(3)
|
||||||
|
String role;
|
||||||
|
|
||||||
|
ShopDetails({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.email,
|
||||||
|
required this.role,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ShopDetails.fromJson(Map<String, dynamic> json) => ShopDetails(
|
||||||
|
id: json['id'],
|
||||||
|
name: json['name'],
|
||||||
|
email: json['email'],
|
||||||
|
role: json['role'],
|
||||||
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'image': image,
|
'name': name,
|
||||||
'mobile': mobile,
|
|
||||||
'email': email,
|
'email': email,
|
||||||
'shopName': shopName,
|
'role': role,
|
||||||
'address': address, // ✅ NEW
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ part of 'shop_model.dart';
|
|||||||
|
|
||||||
class ShopModelAdapter extends TypeAdapter<ShopModel> {
|
class ShopModelAdapter extends TypeAdapter<ShopModel> {
|
||||||
@override
|
@override
|
||||||
final int typeId = 1;
|
final int typeId = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ShopModel read(BinaryReader reader) {
|
ShopModel read(BinaryReader reader) {
|
||||||
@@ -17,31 +17,25 @@ class ShopModelAdapter extends TypeAdapter<ShopModel> {
|
|||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return ShopModel(
|
return ShopModel(
|
||||||
id: fields[0] as String,
|
success: fields[0] as bool,
|
||||||
image: fields[1] as String,
|
user: fields[1] as ShopDetails,
|
||||||
mobile: fields[2] as String,
|
token: fields[2] as String,
|
||||||
email: fields[3] as String,
|
message: fields[3] as String,
|
||||||
shopName: fields[4] as String,
|
|
||||||
address: fields[5] as String,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, ShopModel obj) {
|
void write(BinaryWriter writer, ShopModel obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(6)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.id)
|
|
||||||
..writeByte(1)
|
|
||||||
..write(obj.image)
|
|
||||||
..writeByte(2)
|
|
||||||
..write(obj.mobile)
|
|
||||||
..writeByte(3)
|
|
||||||
..write(obj.email)
|
|
||||||
..writeByte(4)
|
..writeByte(4)
|
||||||
..write(obj.shopName)
|
..writeByte(0)
|
||||||
..writeByte(5)
|
..write(obj.success)
|
||||||
..write(obj.address);
|
..writeByte(1)
|
||||||
|
..write(obj.user)
|
||||||
|
..writeByte(2)
|
||||||
|
..write(obj.token)
|
||||||
|
..writeByte(3)
|
||||||
|
..write(obj.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -54,3 +48,46 @@ class ShopModelAdapter extends TypeAdapter<ShopModel> {
|
|||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
typeId == other.typeId;
|
typeId == other.typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ShopDetailsAdapter extends TypeAdapter<ShopDetails> {
|
||||||
|
@override
|
||||||
|
final int typeId = 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShopDetails read(BinaryReader reader) {
|
||||||
|
final numOfFields = reader.readByte();
|
||||||
|
final fields = <int, dynamic>{
|
||||||
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
|
};
|
||||||
|
return ShopDetails(
|
||||||
|
id: fields[0] as String,
|
||||||
|
name: fields[1] as String,
|
||||||
|
email: fields[2] as String,
|
||||||
|
role: fields[3] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void write(BinaryWriter writer, ShopDetails obj) {
|
||||||
|
writer
|
||||||
|
..writeByte(4)
|
||||||
|
..writeByte(0)
|
||||||
|
..write(obj.id)
|
||||||
|
..writeByte(1)
|
||||||
|
..write(obj.name)
|
||||||
|
..writeByte(2)
|
||||||
|
..write(obj.email)
|
||||||
|
..writeByte(3)
|
||||||
|
..write(obj.role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => typeId.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is ShopDetailsAdapter &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
typeId == other.typeId;
|
||||||
|
}
|
||||||
|
|||||||
67
lib/models/shop_profile_model.dart
Normal file
67
lib/models/shop_profile_model.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
class ShopProfileModel {
|
||||||
|
final String id;
|
||||||
|
final String name;
|
||||||
|
final String description;
|
||||||
|
final String address;
|
||||||
|
final double latitude;
|
||||||
|
final double longitude;
|
||||||
|
final String phone;
|
||||||
|
final String email;
|
||||||
|
final String password;
|
||||||
|
final String role;
|
||||||
|
final List<String> images;
|
||||||
|
final int rating;
|
||||||
|
final DateTime timestamp;
|
||||||
|
|
||||||
|
ShopProfileModel({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.address,
|
||||||
|
required this.latitude,
|
||||||
|
required this.longitude,
|
||||||
|
required this.phone,
|
||||||
|
required this.email,
|
||||||
|
required this.password,
|
||||||
|
required this.role,
|
||||||
|
required this.images,
|
||||||
|
required this.rating,
|
||||||
|
required this.timestamp,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ShopProfileModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ShopProfileModel(
|
||||||
|
id: json['_id'],
|
||||||
|
name: json['name'],
|
||||||
|
description: json['description'],
|
||||||
|
address: json['address'],
|
||||||
|
latitude: json['latitude'].toDouble(),
|
||||||
|
longitude: json['longitude'].toDouble(),
|
||||||
|
phone: json['phone'],
|
||||||
|
email: json['email'],
|
||||||
|
password: json['password'],
|
||||||
|
role: json['role'],
|
||||||
|
images: List<String>.from(json['images']),
|
||||||
|
rating: json['rating'],
|
||||||
|
timestamp: DateTime.parse(json['timestamp']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'_id': id,
|
||||||
|
'name': name,
|
||||||
|
'description': description,
|
||||||
|
'address': address,
|
||||||
|
'latitude': latitude,
|
||||||
|
'longitude': longitude,
|
||||||
|
'phone': phone,
|
||||||
|
'email': email,
|
||||||
|
'password': password,
|
||||||
|
'role': role,
|
||||||
|
'images': images,
|
||||||
|
'rating': rating,
|
||||||
|
'timestamp': timestamp.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +1,103 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/constants/api.dart';
|
||||||
|
import 'package:glowwheels/models/order_model.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
import '../models/order_model.dart';
|
class OrdersProvider with ChangeNotifier {
|
||||||
import '../models/serviceboy_model.dart';
|
List<Order> _orders = [];
|
||||||
|
bool _isLoading = false;
|
||||||
class OrderProvider with ChangeNotifier {
|
bool _isRefreshing = false;
|
||||||
final List<Order> _orders = [
|
String? _errorMessage;
|
||||||
Order(
|
|
||||||
customerName: "Ankit Ghosh",
|
|
||||||
mobileNumber: "8617015476",
|
|
||||||
serviceType: "Doorstep Service",
|
|
||||||
service: "Foam Wash",
|
|
||||||
price: "₹ 104",
|
|
||||||
time: "10:00 - 11:00 AM",
|
|
||||||
date: "2025-05-28",
|
|
||||||
carName: "Mahindra XUV 700",
|
|
||||||
status: "Confirmed",
|
|
||||||
imagePath: "assets/images/car.jpg",
|
|
||||||
),
|
|
||||||
Order(
|
|
||||||
customerName: "Ravi Kumar",
|
|
||||||
mobileNumber: "9876543210",
|
|
||||||
serviceType: "Workshop",
|
|
||||||
service: "Interior Cleaning",
|
|
||||||
price: "₹ 150",
|
|
||||||
time: "12:00 - 1:00 PM",
|
|
||||||
date: "2025-05-29",
|
|
||||||
carName: "Hyundai Creta",
|
|
||||||
status: "Pending",
|
|
||||||
imagePath: "assets/images/bike.png",
|
|
||||||
),
|
|
||||||
// Add more sample orders if needed
|
|
||||||
];
|
|
||||||
|
|
||||||
List<Order> get orders => _orders;
|
List<Order> get orders => _orders;
|
||||||
|
bool get isLoading => _isLoading;
|
||||||
|
bool get isRefreshing => _isRefreshing;
|
||||||
|
String? get errorMessage => _errorMessage;
|
||||||
|
|
||||||
void assignServiceBoy(int index, ServiceBoy boy) {
|
Future<void> fetchOrders(String shopId, {bool refresh = false}) async {
|
||||||
_orders[index].assignedBoy = boy;
|
final ordersUri = Uri.parse(ApiConstants.ordersByShop(shopId));
|
||||||
|
|
||||||
|
if (refresh) {
|
||||||
|
_isRefreshing = true;
|
||||||
|
} else {
|
||||||
|
_isLoading = true;
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(ordersUri);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = json.decode(response.body);
|
||||||
|
|
||||||
|
if (data['success'] == true) {
|
||||||
|
final List<dynamic> ordersJson = data['orders'];
|
||||||
|
_orders = ordersJson.map((orderJson) => Order.fromJson(orderJson)).toList();
|
||||||
|
_errorMessage = null;
|
||||||
|
} else {
|
||||||
|
_errorMessage = 'Failed to fetch orders';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_errorMessage = 'Server error: ${response.statusCode}';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_errorMessage = 'An error occurred: $e';
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLoading = false;
|
||||||
|
_isRefreshing = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> assignServiceBoyToOrder({
|
||||||
|
required String orderId,
|
||||||
|
required String serviceBoyId,
|
||||||
|
required String shopId,
|
||||||
|
}) async {
|
||||||
|
final assignUri = Uri.parse(ApiConstants.assignServiceBoy(orderId));
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.put(
|
||||||
|
assignUri,
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode({'serviceBoyId': serviceBoyId}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print('Successfully assigned service boy');
|
||||||
|
} else {
|
||||||
|
print('Failed to assign service boy: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error assigning service boy: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateOrderStatus({
|
||||||
|
required String orderId,
|
||||||
|
required String status,
|
||||||
|
}) async {
|
||||||
|
final updateStatusUri = Uri.parse(ApiConstants.updateOrderStatusByAdmin(orderId));
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.put(
|
||||||
|
updateStatusUri,
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode({'status': status}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final index = _orders.indexWhere((o) => o.id == orderId);
|
||||||
|
if (index != -1) {
|
||||||
|
_orders[index] = _orders[index].copyWith(status: status);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print('Failed to update status: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error updating order status: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,152 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../models/serviceboy_model.dart';
|
import 'package:glowwheels/constants/api.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
class ServiceBoyProvider extends ChangeNotifier {
|
import 'package:glowwheels/models/serviceboy_model.dart';
|
||||||
List<ServiceBoy> _serviceBoys = [
|
|
||||||
ServiceBoy(name: 'John Doe', phone: '9875643210'),
|
|
||||||
ServiceBoy(name: 'Amit Raj', phone: '9765432180'),
|
|
||||||
ServiceBoy(name: 'Manoj Sinha', phone: '9543219876'),
|
|
||||||
];
|
|
||||||
|
|
||||||
|
class ServiceBoyProvider with ChangeNotifier {
|
||||||
|
List<ServiceBoy> _serviceBoys = [];
|
||||||
ServiceBoy? _selectedBoy;
|
ServiceBoy? _selectedBoy;
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
List<ServiceBoy> get serviceBoys => _serviceBoys;
|
List<ServiceBoy> get serviceBoys => _serviceBoys;
|
||||||
ServiceBoy? get selectedBoy => _selectedBoy;
|
ServiceBoy? get selectedBoy => _selectedBoy;
|
||||||
|
bool get isLoading => _isLoading;
|
||||||
|
|
||||||
// Add a new service boy
|
/// Fetch service boys by shop ID
|
||||||
void addServiceBoy(ServiceBoy boy) {
|
Future<void> fetchServiceBoys(String shopId) async {
|
||||||
_serviceBoys.add(boy);
|
_isLoading = true;
|
||||||
|
notifyListeners();
|
||||||
|
final serviceBoyListUri = Uri.parse(ApiConstants.serviceBoysByShop(shopId));
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(serviceBoyListUri);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
|
final List<dynamic> boysJson = data['data']['serviceBoy'];
|
||||||
|
_serviceBoys = boysJson.map((json) => ServiceBoy.fromJson(json)).toList();
|
||||||
|
print('fetched service boys');
|
||||||
|
print (_serviceBoys);
|
||||||
|
notifyListeners();
|
||||||
|
} else {
|
||||||
|
print('Failed to fetch service boys: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching service boys: $e');
|
||||||
|
}
|
||||||
|
_isLoading = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edit an existing service boy
|
/// Fetch a single service boy by ID
|
||||||
void editServiceBoy(int index, ServiceBoy updatedBoy) {
|
Future<ServiceBoy?> fetchServiceBoyById(String serviceBoyId) async {
|
||||||
if (index >= 0 && index < _serviceBoys.length) {
|
final serviceBoyUri = Uri.parse(ApiConstants.serviceBoyDetails(serviceBoyId));
|
||||||
_serviceBoys[index] = updatedBoy;
|
|
||||||
notifyListeners();
|
try {
|
||||||
|
final response = await http.get(serviceBoyUri);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
|
final boyJson = data['data']; // Adjust if API wraps in `data`
|
||||||
|
final serviceBoy = ServiceBoy.fromJson(boyJson);
|
||||||
|
return serviceBoy;
|
||||||
|
} else {
|
||||||
|
print('Failed to fetch service boy: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching service boy: $e');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add new service boy to shop
|
||||||
|
Future<void> addServiceBoy(String shopId, String name,String phone) async {
|
||||||
|
print('add api called');
|
||||||
|
print(name+'Phone '+phone.toString());
|
||||||
|
final addServiceBoyUri = Uri.parse(ApiConstants.addServiceBoy);
|
||||||
|
final body = jsonEncode({
|
||||||
|
'shopId': shopId,
|
||||||
|
'serviceBoy': [
|
||||||
|
{
|
||||||
|
'name': name,
|
||||||
|
'phone':phone,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
addServiceBoyUri,
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
print('add service boy response '+response.statusCode.toString());
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
await fetchServiceBoys(shopId); // refresh list
|
||||||
|
} else {
|
||||||
|
print('Failed to add service boy: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error adding service boy: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a service boy
|
/// Edit service boy phone
|
||||||
void deleteServiceBoy(int index) {
|
Future<void> editServiceBoy(
|
||||||
if (index >= 0 && index < _serviceBoys.length) {
|
String serviceBoyId,
|
||||||
_serviceBoys.removeAt(index);
|
String updatedName,
|
||||||
notifyListeners();
|
String updatedPhone,
|
||||||
|
String shopId,
|
||||||
|
) async {
|
||||||
|
final serviceBoyUri = Uri.parse(ApiConstants.serviceBoyDetails(serviceBoyId));
|
||||||
|
|
||||||
|
// Build request body only with non-empty fields
|
||||||
|
final Map<String, dynamic> data = {};
|
||||||
|
if (updatedName.isNotEmpty) data['name'] = updatedName;
|
||||||
|
if (updatedPhone.isNotEmpty) data['phone'] = updatedPhone;
|
||||||
|
|
||||||
|
if (data.isEmpty) {
|
||||||
|
print('No fields to update.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.put(
|
||||||
|
serviceBoyUri,
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(data),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print('Service boy updated successfully');
|
||||||
|
await fetchServiceBoys(shopId); // Refresh list
|
||||||
|
} else {
|
||||||
|
print('Failed to update service boy: ${response.statusCode}');
|
||||||
|
print('Response body: ${response.body}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error updating service boy: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign a selected service boy (for dialogs)
|
|
||||||
|
/// Delete a service boy
|
||||||
|
Future<void> deleteServiceBoy(String serviceBoyId, String shopId) async {
|
||||||
|
final serviceBoyUri = Uri.parse(ApiConstants.serviceBoyDetails(serviceBoyId));
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.delete(serviceBoyUri);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
await fetchServiceBoys(shopId); // refresh list
|
||||||
|
} else {
|
||||||
|
print('Failed to delete service boy: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error deleting service boy: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select a service boy
|
||||||
void selectBoy(ServiceBoy boy) {
|
void selectBoy(ServiceBoy boy) {
|
||||||
_selectedBoy = boy;
|
_selectedBoy = boy;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|||||||
40
lib/provider/shop_profile_provider.dart
Normal file
40
lib/provider/shop_profile_provider.dart
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/constants/api.dart';
|
||||||
|
import 'package:glowwheels/models/shop_profile_model.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
|
||||||
|
class ShopProfileProvider with ChangeNotifier {
|
||||||
|
ShopProfileModel? _shopProfile;
|
||||||
|
bool _isLoading = false;
|
||||||
|
String? _errorMessage;
|
||||||
|
|
||||||
|
ShopProfileModel? get shopProfile => _shopProfile;
|
||||||
|
bool get isLoading => _isLoading;
|
||||||
|
String? get errorMessage => _errorMessage;
|
||||||
|
|
||||||
|
Future<void> fetchShopProfile(String shopId) async {
|
||||||
|
_isLoading = true;
|
||||||
|
_errorMessage = null;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
final shopUri = Uri.parse(ApiConstants.shopDetails(shopId));
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.get(shopUri);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = json.decode(response.body);
|
||||||
|
_shopProfile = ShopProfileModel.fromJson(data);
|
||||||
|
} else {
|
||||||
|
_errorMessage = 'Failed to load shop profile. Status code: ${response.statusCode}';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_errorMessage = 'An error occurred: $e';
|
||||||
|
} finally {
|
||||||
|
_isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,52 +1,98 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/constants/api.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import '../models/shop_model.dart';
|
import '../models/shop_model.dart';
|
||||||
|
|
||||||
class ShopProvider with ChangeNotifier {
|
class ShopProvider with ChangeNotifier {
|
||||||
ShopModel? _shop;
|
ShopModel? _shop;
|
||||||
ShopModel? get shop => _shop;
|
String? _token;
|
||||||
|
|
||||||
final String _boxName = 'shopBox';
|
ShopModel? get shop => _shop;
|
||||||
|
String? get token => _token;
|
||||||
|
|
||||||
|
final String _shopBox = 'shopBox';
|
||||||
|
final String _tokenBox = 'tokenBox';
|
||||||
|
|
||||||
ShopProvider() {
|
ShopProvider() {
|
||||||
_loadOrCreateDummyShop();
|
_loadShopAndTokenFromHive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _loadOrCreateDummyShop() async {
|
|
||||||
final box = await Hive.openBox<ShopModel>(_boxName);
|
|
||||||
|
|
||||||
if (box.isNotEmpty) {
|
void _loadShopAndTokenFromHive() async {
|
||||||
_shop = box.getAt(0);
|
final shopBox = await Hive.openBox<ShopModel>(_shopBox);
|
||||||
} else {
|
_shop = shopBox.get('shop');
|
||||||
// Dummy data
|
|
||||||
_shop = ShopModel(
|
final tokenBox = await Hive.openBox<String>(_tokenBox);
|
||||||
id: '1',
|
_token = tokenBox.get('token');
|
||||||
shopName: "Omkara Car Wash Center",
|
|
||||||
email: "omkara@gmail.com",
|
notifyListeners();
|
||||||
mobile: "8617019854",
|
}
|
||||||
image: "assets/images/shop_image.jpg",
|
String? getShopId(BuildContext context) {
|
||||||
address: "Bidhannagar, Kolkata, pin-700017",
|
return Provider.of<ShopProvider>(context, listen: false).shop?.user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> login(String email, String password) async {
|
||||||
|
final loginUri = Uri.parse(ApiConstants.loginUrl);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
loginUri,
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode({'email': email, 'password': password}),
|
||||||
);
|
);
|
||||||
await box.add(_shop!);
|
|
||||||
|
print('Response code: ${response.statusCode}');
|
||||||
|
print('Response body: ${response.body}');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = jsonDecode(response.body);
|
||||||
|
|
||||||
|
final shopModel = ShopModel.fromJson(data);
|
||||||
|
|
||||||
|
final shopBox = await Hive.openBox<ShopModel>(_shopBox);
|
||||||
|
final tokenBox = await Hive.openBox<String>(_tokenBox);
|
||||||
|
|
||||||
|
await shopBox.put('shop', shopModel);
|
||||||
|
await tokenBox.put('token', shopModel.token);
|
||||||
|
|
||||||
|
_shop = shopModel;
|
||||||
|
_token = shopModel.token;
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print('HTTP Error: ${response.statusCode}');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Login Exception: $e');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setShop(ShopModel shop) async {
|
|
||||||
_shop = shop;
|
|
||||||
notifyListeners();
|
|
||||||
|
|
||||||
final box = await Hive.openBox<ShopModel>(_boxName);
|
|
||||||
await box.clear(); // Keep only one shop
|
|
||||||
await box.add(shop);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> logout() async {
|
Future<void> logout() async {
|
||||||
_shop = null;
|
_shop = null;
|
||||||
|
_token = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
final box = await Hive.openBox<ShopModel>(_boxName);
|
final shopBox = await Hive.openBox<ShopModel>(_shopBox);
|
||||||
await box.clear();
|
await shopBox.clear();
|
||||||
|
|
||||||
|
final tokenBox = await Hive.openBox<String>(_tokenBox);
|
||||||
|
await tokenBox.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setShop(ShopModel shop) {
|
||||||
|
_shop = shop;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setToken(String token) {
|
||||||
|
_token = token;
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,31 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/helpers/shopid_helper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../provider/serviceboy_provider.dart';
|
import '../provider/serviceboy_provider.dart';
|
||||||
import 'add_serviceboy_screen.dart';
|
import 'add_serviceboy_screen.dart';
|
||||||
import 'edit_serviceboy_screen.dart';
|
import 'edit_serviceboy_screen.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
class ServiceBoyScreen extends StatelessWidget {
|
|
||||||
|
class ServiceBoyScreen extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
State<ServiceBoyScreen> createState() => _ServiceBoyScreenState();
|
||||||
|
}
|
||||||
|
late String shopId='';
|
||||||
|
class _ServiceBoyScreenState extends State<ServiceBoyScreen> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
shopId = getShopId(context)!;
|
||||||
|
if (shopId != null) {
|
||||||
|
Provider.of<ServiceBoyProvider>(context, listen: false)
|
||||||
|
.fetchServiceBoys(shopId);
|
||||||
|
} else {
|
||||||
|
print("Shop ID is null");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final provider = Provider.of<ServiceBoyProvider>(context);
|
final provider = Provider.of<ServiceBoyProvider>(context);
|
||||||
@@ -13,7 +34,6 @@ class ServiceBoyScreen extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [Color.fromRGBO(208, 235, 255, 1), Colors.white],
|
colors: [Color.fromRGBO(208, 235, 255, 1), Colors.white],
|
||||||
|
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
),
|
),
|
||||||
@@ -26,54 +46,64 @@ class ServiceBoyScreen extends StatelessWidget {
|
|||||||
SizedBox(height: 40),
|
SizedBox(height: 40),
|
||||||
Text(
|
Text(
|
||||||
'GLOWWHEELS',
|
'GLOWWHEELS',
|
||||||
style: GoogleFonts.inter(
|
style: GoogleFonts.inter(
|
||||||
fontSize: 32,color: Color.fromRGBO(25, 25, 112, 0.87)
|
fontSize: 32,
|
||||||
,fontWeight: FontWeight.w700
|
color: Color.fromRGBO(25, 25, 112, 0.87),
|
||||||
)
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Service Center',
|
'Service Center',
|
||||||
style: GoogleFonts.inter(
|
style: GoogleFonts.inter(
|
||||||
fontSize: 24,color: Color.fromRGBO(25, 25, 112, 0.87)
|
fontSize: 24,
|
||||||
,fontWeight: FontWeight.w400
|
color: Color.fromRGBO(25, 25, 112, 0.87),
|
||||||
)
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
Text(
|
Text(
|
||||||
'Service Boy List',
|
'Service Boy List',
|
||||||
style: GoogleFonts.inter(
|
style: GoogleFonts.inter(
|
||||||
fontSize: 16,color: Color.fromRGBO(33, 33, 33, 1)
|
fontSize: 16,
|
||||||
,fontWeight: FontWeight.w500
|
color: Color.fromRGBO(33, 33, 33, 1),
|
||||||
)
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: provider.serviceBoys.isEmpty
|
child: provider.isLoading
|
||||||
|
? Center(child: CircularProgressIndicator())
|
||||||
|
: provider.serviceBoys.isEmpty
|
||||||
? Center(child: Text("No service boys found"))
|
? Center(child: Text("No service boys found"))
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 16, vertical: 8),
|
||||||
itemCount: provider.serviceBoys.length,
|
itemCount: provider.serviceBoys.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final boy = provider.serviceBoys[index];
|
final boy = provider.serviceBoys[index];
|
||||||
return Card(
|
return Card(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12)),
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 16, vertical: 8),
|
||||||
title: Text(
|
title: Text(
|
||||||
boy.name,
|
boy.name,
|
||||||
style: GoogleFonts.inter(
|
style: GoogleFonts.inter(
|
||||||
fontSize: 18,color: Color.fromRGBO(33, 33, 33, 0.78)
|
fontSize: 18,
|
||||||
,fontWeight: FontWeight.w600
|
color: Color.fromRGBO(33, 33, 33, 0.78),
|
||||||
)
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
boy.phone,
|
boy.phone,
|
||||||
style: GoogleFonts.inter(
|
style: GoogleFonts.inter(
|
||||||
fontSize: 16,color: Color.fromRGBO(33, 33, 33, 0.78)
|
fontSize: 16,
|
||||||
,fontWeight: FontWeight.w400
|
color: Color.fromRGBO(33, 33, 33, 0.78),
|
||||||
)
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -83,7 +113,9 @@ class ServiceBoyScreen extends StatelessWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => EditServiceBoyScreen(serviceBoy: boy),
|
builder: (_) =>
|
||||||
|
EditServiceBoyScreen(
|
||||||
|
serviceBoy: boy),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -92,7 +124,7 @@ class ServiceBoyScreen extends StatelessWidget {
|
|||||||
_buildIconButton(
|
_buildIconButton(
|
||||||
icon: Icons.delete,
|
icon: Icons.delete,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_showDeleteDialog(context, '');
|
_showDeleteDialog(context, boy.id);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -109,7 +141,11 @@ class ServiceBoyScreen extends StatelessWidget {
|
|||||||
shape: CircleBorder(),
|
shape: CircleBorder(),
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
child: Icon(Icons.add, color: Color(0xFF1F1762),size: 25,),
|
child: Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: Color(0xFF1F1762),
|
||||||
|
size: 25,
|
||||||
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(builder: (_) => AddServiceBoyScreen()),
|
MaterialPageRoute(builder: (_) => AddServiceBoyScreen()),
|
||||||
@@ -120,7 +156,8 @@ class ServiceBoyScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildIconButton({required IconData icon, required VoidCallback onPressed}) {
|
Widget _buildIconButton(
|
||||||
|
{required IconData icon, required VoidCallback onPressed}) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 40,
|
height: 40,
|
||||||
width: 40,
|
width: 40,
|
||||||
@@ -141,63 +178,72 @@ class ServiceBoyScreen extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showDeleteDialog(BuildContext context, String id) {
|
void _showDeleteDialog(BuildContext context, String id) {
|
||||||
|
final shopId = getShopId(context);
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (_) => Container(
|
builder: (BuildContext bottomSheetContext) {
|
||||||
padding: EdgeInsets.all(24),
|
return Container(
|
||||||
decoration: BoxDecoration(
|
padding: EdgeInsets.all(24),
|
||||||
color: Colors.white,
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
color: Colors.white,
|
||||||
),
|
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||||
child: Column(
|
),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
Image.asset('assets/images/delete_serviceboy.png'),
|
children: [
|
||||||
SizedBox(height: 16),
|
Image.asset('assets/images/delete_serviceboy.png'),
|
||||||
Text(
|
SizedBox(height: 16),
|
||||||
'Delete Service boy?',
|
Text(
|
||||||
style: TextStyle(
|
'Delete Service boy?',
|
||||||
fontSize: 18,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontSize: 18,
|
||||||
color: Colors.black87,
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: 8),
|
||||||
SizedBox(height: 8),
|
Text(
|
||||||
Text(
|
'Are you sure you want to delete this service boy? You won’t be able to undo this.',
|
||||||
'Are you sure you want to delete this service boy? You won’t be able to undo this.',
|
textAlign: TextAlign.center,
|
||||||
textAlign: TextAlign.center,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: 14,
|
||||||
fontSize: 14,
|
color: Colors.black54,
|
||||||
color: Colors.black54,
|
),
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: 24),
|
||||||
SizedBox(height: 24),
|
SizedBox(
|
||||||
SizedBox(
|
width: double.infinity,
|
||||||
width: double.infinity,
|
child: ElevatedButton(
|
||||||
child: ElevatedButton(
|
style: ElevatedButton.styleFrom(
|
||||||
style: ElevatedButton.styleFrom(
|
backgroundColor: Colors.red[700],
|
||||||
backgroundColor: Colors.red[700],
|
padding: EdgeInsets.symmetric(vertical: 14),
|
||||||
padding: EdgeInsets.symmetric(vertical: 14),
|
shape: RoundedRectangleBorder(
|
||||||
shape: RoundedRectangleBorder(
|
borderRadius: BorderRadius.circular(8),
|
||||||
borderRadius: BorderRadius.circular(8),
|
),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
if (shopId != null) {
|
||||||
|
await Provider.of<ServiceBoyProvider>(
|
||||||
|
bottomSheetContext,
|
||||||
|
listen: false,
|
||||||
|
).deleteServiceBoy(id, shopId);
|
||||||
|
}
|
||||||
|
Navigator.pop(bottomSheetContext); // Correct context
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Confirm',
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
// Provider.of<ServiceBoyProvider>(context, listen: false).deleteServiceBoy(id);
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'Confirm',
|
|
||||||
style: TextStyle(fontSize: 16, color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: 12),
|
||||||
SizedBox(height: 12),
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,41 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:glowwheels/screens/privacy_policy_screen.dart';
|
import 'package:glowwheels/helpers/shopid_helper.dart';
|
||||||
import 'package:glowwheels/screens/profile_details_screen.dart';
|
import 'package:glowwheels/provider/shop_profile_provider.dart';
|
||||||
|
import 'package:glowwheels/provider/shop_provider.dart';
|
||||||
|
import 'package:glowwheels/screens/login_screen.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:glowwheels/screens/terms_condition_screen.dart';
|
|
||||||
import 'package:glowwheels/widgets/profile_header.dart';
|
import 'package:glowwheels/widgets/profile_header.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:glowwheels/screens/profile_details_screen.dart';
|
||||||
class AccountScreen extends StatelessWidget {
|
import 'package:glowwheels/screens/privacy_policy_screen.dart';
|
||||||
|
import 'package:glowwheels/screens/terms_condition_screen.dart';
|
||||||
|
|
||||||
|
class AccountScreen extends StatefulWidget {
|
||||||
const AccountScreen({super.key});
|
const AccountScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AccountScreen> createState() => _AccountScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AccountScreenState extends State<AccountScreen> {
|
||||||
|
bool _isInit = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
if (_isInit) {
|
||||||
|
// Fetch the shop profile once when screen loads
|
||||||
|
final shopId=getShopId(context);
|
||||||
|
Provider.of<ShopProfileProvider>(context, listen: false).fetchShopProfile(shopId!);
|
||||||
|
_isInit = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final shopProfileProvider = Provider.of<ShopProfileProvider>(context);
|
||||||
|
final shopProfile = shopProfileProvider.shopProfile;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@@ -20,7 +46,6 @@ class AccountScreen extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: const [
|
children: const [
|
||||||
ImageIcon(AssetImage("assets/icon/account_icon.png")),
|
ImageIcon(AssetImage("assets/icon/account_icon.png")),
|
||||||
|
|
||||||
SizedBox(width: 8),
|
SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
"Account",
|
"Account",
|
||||||
@@ -33,15 +58,17 @@ class AccountScreen extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Profile Info
|
// Profile Info (shows shimmer skeleton if loading)
|
||||||
ProfileHeader(),
|
ProfileHeader(shopProfile: shopProfile),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// Section Title
|
// Section Title
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text("Options",
|
child: Text(
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
"Options",
|
||||||
|
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
@@ -51,7 +78,7 @@ class AccountScreen extends StatelessWidget {
|
|||||||
title: "Profile Details",
|
title: "Profile Details",
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(builder: (_) => ServiceCenterDetailsScreen()),
|
MaterialPageRoute(builder: (_) => ServiceCenterDetailsScreen()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -60,7 +87,7 @@ class AccountScreen extends StatelessWidget {
|
|||||||
title: "Terms and Conditions",
|
title: "Terms and Conditions",
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(builder: (_) => TermsOfServiceScreen()),
|
MaterialPageRoute(builder: (_) => TermsOfServiceScreen()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -69,14 +96,21 @@ class AccountScreen extends StatelessWidget {
|
|||||||
title: "Privacy Policy",
|
title: "Privacy Policy",
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(builder: (_) => PrivacyPolicyScreen()),
|
MaterialPageRoute(builder: (_) => PrivacyPolicyScreen()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
OptionTile(
|
OptionTile(
|
||||||
icon: "assets/icon/logout_icon.png",
|
icon: "assets/icon/logout_icon.png",
|
||||||
title: "Log Out",
|
title: "Log Out",
|
||||||
onTap: () {},
|
onTap: () async{
|
||||||
|
await Provider.of<ShopProvider>(context, listen: false).logout();
|
||||||
|
|
||||||
|
Navigator.of(context, rootNavigator: true).pushAndRemoveUntil(
|
||||||
|
MaterialPageRoute(builder: (context) => LoginScreen()),
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
},
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -103,19 +137,20 @@ class OptionTile extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Card(
|
||||||
color: isDestructive ? Colors.white : Colors.white,
|
color: Colors.white,
|
||||||
elevation: 0.5,
|
elevation: 0.5,
|
||||||
margin: const EdgeInsets.symmetric(vertical: 6),
|
margin: const EdgeInsets.symmetric(vertical: 6),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: ImageIcon(AssetImage(icon),
|
leading: ImageIcon(AssetImage(icon), color: const Color.fromRGBO(25, 25, 112, 1)),
|
||||||
color: Color.fromRGBO(25, 25, 112, 1)),
|
title: Text(
|
||||||
title: Text(title,
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
style: GoogleFonts.inter(
|
fontSize: 14,
|
||||||
fontSize: 14,color: Color.fromRGBO(41, 45, 50, 1)
|
color: Color.fromRGBO(41, 45, 50, 1),
|
||||||
,fontWeight: FontWeight.w500
|
fontWeight: FontWeight.w500,
|
||||||
),),
|
),
|
||||||
|
),
|
||||||
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
|
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,16 +1,33 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/helpers/shopid_helper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../provider/serviceboy_provider.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
class AddServiceBoyScreen extends StatelessWidget {
|
import '../provider/serviceboy_provider.dart';
|
||||||
|
import '../provider/shop_provider.dart';
|
||||||
|
|
||||||
|
class AddServiceBoyScreen extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_AddServiceBoyScreenState createState() => _AddServiceBoyScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddServiceBoyScreenState extends State<AddServiceBoyScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final TextEditingController nameController = TextEditingController();
|
final TextEditingController nameController = TextEditingController();
|
||||||
final TextEditingController phoneController = TextEditingController();
|
final TextEditingController phoneController = TextEditingController();
|
||||||
|
bool isLoading = false;
|
||||||
|
late String shopId='';
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// TODO: implement initState
|
||||||
|
shopId = getShopId(context)!;
|
||||||
|
}
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final serviceBoyProvider = Provider.of<ServiceBoyProvider>(context, listen: false);
|
||||||
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Color(0xFFF9FAF4), // light background as per screenshot
|
backgroundColor: Color(0xFFF9FAF4),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
@@ -20,12 +37,12 @@ class AddServiceBoyScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'Add Service boy',
|
'Add Service boy',
|
||||||
style: GoogleFonts.nunito(
|
style: GoogleFonts.nunito(
|
||||||
fontSize: 18,color: Color.fromRGBO(26, 26, 26, 1)
|
fontSize: 18,
|
||||||
,fontWeight: FontWeight.w500
|
color: Color.fromRGBO(26, 26, 26, 1),
|
||||||
)
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
//centerTitle: true,
|
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
@@ -33,15 +50,15 @@ class AddServiceBoyScreen extends StatelessWidget {
|
|||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 30,),
|
SizedBox(height: 30),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: nameController,
|
controller: nameController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
|
||||||
hintText: 'Name',
|
hintText: 'Name',
|
||||||
hintStyle: GoogleFonts.nunito(
|
hintStyle: GoogleFonts.nunito(
|
||||||
fontSize: 14,color: Color.fromRGBO(26, 26, 26, 1)
|
fontSize: 14,
|
||||||
,fontWeight: FontWeight.w400
|
color: Color.fromRGBO(26, 26, 26, 1),
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
||||||
@@ -54,9 +71,10 @@ class AddServiceBoyScreen extends StatelessWidget {
|
|||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Mobile Number',
|
hintText: 'Mobile Number',
|
||||||
hintStyle: GoogleFonts.nunito(
|
hintStyle: GoogleFonts.nunito(
|
||||||
fontSize: 14,color: Color.fromRGBO(26, 26, 26, 1)
|
fontSize: 14,
|
||||||
,fontWeight: FontWeight.w400
|
color: Color.fromRGBO(26, 26, 26, 1),
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
||||||
@@ -67,28 +85,69 @@ class AddServiceBoyScreen extends StatelessWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: isLoading
|
||||||
/* if (_formKey.currentState!.validate()) {
|
? null
|
||||||
Provider.of<ServiceBoyProvider>(context, listen: false).addServiceBoy(
|
: () async {
|
||||||
nameController.text,
|
if (_formKey.currentState!.validate()) {
|
||||||
phoneController.text,
|
if (shopId == null) {
|
||||||
);
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
Navigator.pop(context);
|
SnackBar(content: Text('Shop ID is missing')),
|
||||||
}*/
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await serviceBoyProvider.addServiceBoy(
|
||||||
|
shopId,
|
||||||
|
nameController.text.trim(),
|
||||||
|
phoneController.text.trim(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// After successful addition, navigate back
|
||||||
|
Navigator.pop(context);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Service boy added successfully!')),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to add service boy')),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Color(0xFF000B8C), // dark blue
|
backgroundColor: Color(0xFF000B8C),
|
||||||
padding: EdgeInsets.symmetric(vertical: 14),
|
padding: EdgeInsets.symmetric(vertical: 14),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text('Submit', style:
|
child: isLoading
|
||||||
|
? SizedBox(
|
||||||
GoogleFonts.inter(
|
height: 20,
|
||||||
fontSize: 18,color: Colors.white
|
width: 20,
|
||||||
,fontWeight: FontWeight.w600
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 2.5,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
: Text(
|
||||||
|
'Submit',
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,38 +1,81 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/helpers/shopid_helper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../models/serviceboy_model.dart';
|
import '../models/serviceboy_model.dart';
|
||||||
import '../provider/serviceboy_provider.dart';
|
import '../provider/serviceboy_provider.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
class EditServiceBoyScreen extends StatelessWidget {
|
|
||||||
|
class EditServiceBoyScreen extends StatefulWidget {
|
||||||
final ServiceBoy serviceBoy;
|
final ServiceBoy serviceBoy;
|
||||||
|
|
||||||
EditServiceBoyScreen({required this.serviceBoy});
|
EditServiceBoyScreen({required this.serviceBoy});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_EditServiceBoyScreenState createState() => _EditServiceBoyScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditServiceBoyScreenState extends State<EditServiceBoyScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
late final TextEditingController nameController =
|
late final TextEditingController nameController;
|
||||||
TextEditingController(text: serviceBoy.name);
|
late final TextEditingController phoneController;
|
||||||
late final TextEditingController phoneController =
|
|
||||||
TextEditingController(text: serviceBoy.phone);
|
bool isLoading = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
nameController = TextEditingController(text: widget.serviceBoy.name);
|
||||||
|
phoneController = TextEditingController(text: widget.serviceBoy.phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
nameController.dispose();
|
||||||
|
phoneController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _saveDetails(BuildContext context) async {
|
||||||
|
final name = nameController.text.trim();
|
||||||
|
final phone = phoneController.text.trim();
|
||||||
|
|
||||||
|
// Validation: at least one field must be non-empty and different
|
||||||
|
if (name.isEmpty && phone.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Please edit at least one field')),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final shopId = getShopId(context)!;
|
||||||
|
|
||||||
|
setState(() => isLoading = true);
|
||||||
|
|
||||||
|
await Provider.of<ServiceBoyProvider>(context, listen: false)
|
||||||
|
.editServiceBoy(widget.serviceBoy.id, name, phone, shopId);
|
||||||
|
|
||||||
|
setState(() => isLoading = false);
|
||||||
|
Navigator.pop(context); // back to previous screen
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color(0xFFF9FAF4), // light background
|
backgroundColor: const Color(0xFFF9FAF4),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.arrow_back_ios),
|
icon: Icon(Icons.arrow_back_ios),
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'Edit Service boy Details',
|
'Edit Service boy Details',
|
||||||
style: GoogleFonts.nunito(
|
style: GoogleFonts.nunito(
|
||||||
fontSize: 18,color: Color.fromRGBO(26, 26, 26, 1)
|
fontSize: 18,
|
||||||
,fontWeight: FontWeight.w500
|
color: Color.fromRGBO(26, 26, 26, 1),
|
||||||
)
|
fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
|
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
@@ -40,50 +83,40 @@ class EditServiceBoyScreen extends StatelessWidget {
|
|||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 30,),
|
SizedBox(height: 30),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: nameController,
|
controller: nameController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Name',
|
hintText: 'Name',
|
||||||
hintStyle: GoogleFonts.nunito(
|
hintStyle: GoogleFonts.nunito(
|
||||||
fontSize: 14,color: Color.fromRGBO(26, 26, 26, 1)
|
fontSize: 14,
|
||||||
,fontWeight: FontWeight.w400
|
color: Color.fromRGBO(26, 26, 26, 1),
|
||||||
),
|
fontWeight: FontWeight.w400),
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
||||||
),
|
),
|
||||||
validator: (value) => value!.isEmpty ? 'Enter name' : null,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25),
|
SizedBox(height: 25),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: phoneController,
|
controller: phoneController,
|
||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Mobile Number',
|
hintText: 'Mobile Number',
|
||||||
hintStyle: GoogleFonts.nunito(
|
hintStyle: GoogleFonts.nunito(
|
||||||
fontSize: 14,color: Color.fromRGBO(26, 26, 26, 1)
|
fontSize: 14,
|
||||||
,fontWeight: FontWeight.w400
|
color: Color.fromRGBO(26, 26, 26, 1),
|
||||||
),
|
fontWeight: FontWeight.w400),
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
||||||
),
|
),
|
||||||
validator: (value) => value!.isEmpty ? 'Enter phone number' : null,
|
|
||||||
),
|
),
|
||||||
const Spacer(),
|
Spacer(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: isLoading ? null : () => _saveDetails(context),
|
||||||
/* if (_formKey.currentState!.validate()) {
|
|
||||||
Provider.of<ServiceBoyProvider>(context, listen: false)
|
|
||||||
.editServiceBoy(
|
|
||||||
serviceBoy.id,
|
|
||||||
nameController.text,
|
|
||||||
phoneController.text,
|
|
||||||
);
|
|
||||||
Navigator.pop(context);
|
|
||||||
}*/
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: const Color(0xFF000B8C),
|
backgroundColor: const Color(0xFF000B8C),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
@@ -91,10 +124,20 @@ class EditServiceBoyScreen extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text('Save', style:GoogleFonts.inter(
|
child: isLoading
|
||||||
fontSize: 18,color: Colors.white
|
? SizedBox(
|
||||||
,fontWeight: FontWeight.w600
|
width: 24,
|
||||||
)),
|
height: 24,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text('Save',
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w600)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,60 +1,97 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/provider/shop_provider.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import '../screens/main_screen.dart';
|
||||||
|
|
||||||
|
class LoginScreen extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
State<LoginScreen> createState() => _LoginScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LoginScreenState extends State<LoginScreen> {
|
||||||
|
final TextEditingController _emailController = TextEditingController();
|
||||||
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
bool _isLoading = false;
|
||||||
|
bool _obscurePassword = true;
|
||||||
|
|
||||||
|
void _handleLogin(BuildContext context) async {
|
||||||
|
if (!_formKey.currentState!.validate()) return;
|
||||||
|
|
||||||
|
final shopProvider = Provider.of<ShopProvider>(context, listen: false);
|
||||||
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
|
final success = await shopProvider.login(
|
||||||
|
_emailController.text.trim(),
|
||||||
|
_passwordController.text.trim(),
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => MainScreen()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Login failed. Check credentials.')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import 'main_screen.dart';
|
|
||||||
class LoginScreen extends StatelessWidget {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
// Top Banner
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
colors: [
|
colors: [
|
||||||
Color.fromRGBO(80, 166, 242, 0.66),
|
Color.fromRGBO(80, 166, 242, 0.66),
|
||||||
Color.fromRGBO(168, 134, 255, 0.66),
|
Color.fromRGBO(168, 134, 255, 0.66),
|
||||||
Color.fromRGBO(86, 88, 255, 0.3828),
|
Color.fromRGBO(86, 88, 255, 0.38),
|
||||||
Color.fromRGBO(214, 246, 255, 0.66),
|
Color.fromRGBO(214, 246, 255, 0.66),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.only(bottomRight: Radius.elliptical(300, 110), )
|
borderRadius: BorderRadius.only(
|
||||||
|
bottomRight: Radius.elliptical(300, 110),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 30),
|
SizedBox(height: 30),
|
||||||
Image.asset('assets/images/signinlogo.png', height: 250),
|
Image.asset('assets/images/signinlogo.png', height: 250),
|
||||||
const SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Welcome back!", style:
|
Text("Welcome back!",
|
||||||
GoogleFonts.istokWeb(
|
style: GoogleFonts.istokWeb(
|
||||||
fontSize: 18,color: Color.fromRGBO(25, 25, 112, 0.87)
|
fontSize: 18,
|
||||||
,fontWeight: FontWeight.w700
|
color: Color.fromRGBO(25, 25, 112, 0.87),
|
||||||
)),
|
fontWeight: FontWeight.w700,
|
||||||
|
)),
|
||||||
Container(
|
Container(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: Text("Please sign in to continue",
|
child: Text("Please sign in to continue",
|
||||||
style:GoogleFonts.radioCanada(
|
style: GoogleFonts.radioCanada(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
height: 0.9,
|
height: 0.9,
|
||||||
color: Color.fromRGBO(25, 25, 112, 0.87)
|
color: Color.fromRGBO(25, 25, 112, 0.87),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -64,48 +101,68 @@ class LoginScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Login Form
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
child: Form(
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
SizedBox(height: 20),
|
||||||
const SizedBox(height: 20),
|
Text('Enter Email Id', style: GoogleFonts.radioCanada(fontSize: 16)),
|
||||||
Text('Enter Email Id',style:GoogleFonts.radioCanada(
|
SizedBox(height: 5),
|
||||||
fontSize: 16,
|
TextFormField(
|
||||||
fontWeight: FontWeight.w400,
|
controller: _emailController,
|
||||||
//color: Color.fromRGBO(25, 25, 112, 0.87)
|
validator: (value) {
|
||||||
)),
|
if (value == null || value.isEmpty) {
|
||||||
const SizedBox(height: 5),
|
return 'Please enter your email';
|
||||||
TextField(
|
}
|
||||||
|
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
|
||||||
|
return 'Enter a valid email';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
|
||||||
hintText: 'abcd@gmail.com',
|
hintText: 'abcd@gmail.com',
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(15.0)
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
Text('Enter Password',style:GoogleFonts.radioCanada(
|
Text('Enter Password', style: GoogleFonts.radioCanada(fontSize: 16)),
|
||||||
fontSize: 16,
|
SizedBox(height: 5),
|
||||||
fontWeight: FontWeight.w400,
|
TextFormField(
|
||||||
//color: Color.fromRGBO(25, 25, 112, 0.87)
|
controller: _passwordController,
|
||||||
)),
|
obscureText: _obscurePassword,
|
||||||
const SizedBox(height: 5),
|
validator: (value) {
|
||||||
TextField(
|
if (value == null || value.isEmpty) {
|
||||||
obscureText: true,
|
return 'Please enter your password';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Enter your password',
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(15.0)
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_obscurePassword ? Icons.visibility_off : Icons.visibility,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_obscurePassword = !_obscurePassword;
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
suffixIcon: Icon(Icons.remove_red_eye_outlined),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
SizedBox(height: 30),
|
||||||
|
|
||||||
|
// Login Button
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 50,
|
height: 50,
|
||||||
@@ -116,16 +173,16 @@ class LoginScreen extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(25),
|
borderRadius: BorderRadius.circular(25),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: _isLoading ? null : () => _handleLogin(context),
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context)=>MainScreen()));
|
child: _isLoading
|
||||||
},
|
? CircularProgressIndicator(color: Colors.white)
|
||||||
child: Text('Login', style:
|
: Text(
|
||||||
|
'Login',
|
||||||
GoogleFonts.inter(
|
style: GoogleFonts.inter(
|
||||||
fontSize: 18,color: Colors.white
|
fontSize: 16,
|
||||||
,fontWeight: FontWeight.w600
|
color: Colors.white,
|
||||||
)
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -138,4 +195,4 @@ class LoginScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,34 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/helpers/shopid_helper.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:glowwheels/models/order_model.dart';
|
||||||
import '../models/order_model.dart';
|
|
||||||
|
|
||||||
import '../provider/order_provider.dart';
|
import '../provider/order_provider.dart';
|
||||||
import '../widgets/order_card.dart';
|
import '../widgets/order_card.dart';
|
||||||
|
|
||||||
class OrdersScreen extends StatelessWidget {
|
class OrdersScreen extends StatefulWidget {
|
||||||
final TextStyle labelStyle = TextStyle(fontWeight: FontWeight.w500);
|
@override
|
||||||
final TextStyle valueStyle = TextStyle(fontWeight: FontWeight.normal);
|
_OrdersScreenState createState() => _OrdersScreenState();
|
||||||
List<Order> orders = [
|
}
|
||||||
Order(
|
|
||||||
customerName: "Ankit Ghosh",
|
|
||||||
mobileNumber: "8617015476",
|
|
||||||
serviceType: "Doorstep Service",
|
|
||||||
service: "Foam Wash",
|
|
||||||
price: "₹ 104",
|
|
||||||
time: "10:00 - 11:00 AM",
|
|
||||||
date: "2025-05-28",
|
|
||||||
carName: "Mahindra XUV 700",
|
|
||||||
status: "Confirmed",
|
|
||||||
imagePath: "assets/images/car.jpg",
|
|
||||||
),
|
|
||||||
Order(
|
|
||||||
customerName: "Ravi Kumar",
|
|
||||||
mobileNumber: "9876543210",
|
|
||||||
serviceType: "Workshop",
|
|
||||||
service: "Interior Cleaning",
|
|
||||||
price: "₹ 150",
|
|
||||||
time: "12:00 - 1:00 PM",
|
|
||||||
date: "2025-05-29",
|
|
||||||
carName: "Hyundai Creta",
|
|
||||||
status: "Pending",
|
|
||||||
imagePath: "assets/images/bike.png",
|
|
||||||
),
|
|
||||||
// Add more orders...
|
|
||||||
];
|
|
||||||
|
|
||||||
|
class _OrdersScreenState extends State<OrdersScreen> {
|
||||||
|
late String shopId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
shopId = getShopId(context)!;
|
||||||
|
Provider.of<OrdersProvider>(context, listen: false)
|
||||||
|
.fetchOrders(shopId, refresh: false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _refreshOrders() async {
|
||||||
|
shopId = getShopId(context)!;
|
||||||
|
await Provider.of<OrdersProvider>(context, listen: false)
|
||||||
|
.fetchOrders(shopId, refresh: true);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -67,30 +58,52 @@ class OrdersScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Consumer<OrderProvider>(
|
child: Consumer<OrdersProvider>(
|
||||||
builder: (context, orderProvider, _) {
|
builder: (context, orderProvider, _) {
|
||||||
final orders = orderProvider.orders;
|
final orders = orderProvider.orders;
|
||||||
|
|
||||||
|
if (orderProvider.isLoading && !orderProvider.isRefreshing) {
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
if (orders.isEmpty) {
|
if (orders.isEmpty) {
|
||||||
return Center(
|
return RefreshIndicator(
|
||||||
child: Image.asset(
|
onRefresh: _refreshOrders,
|
||||||
'assets/images/noorder.png',
|
child: ListView(
|
||||||
width: 200,
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
height: 200,
|
children: [
|
||||||
|
SizedBox(height: 100),
|
||||||
|
Center(
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/noorder.png',
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
'No orders found',
|
||||||
|
style: GoogleFonts.poppins(fontSize: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListView.builder(
|
return RefreshIndicator(
|
||||||
itemCount: orders.length,
|
onRefresh: _refreshOrders,
|
||||||
itemBuilder: (context, index) {
|
child: ListView.builder(
|
||||||
return OrderCard(order: orders[index],);
|
itemCount: orders.length,
|
||||||
},
|
itemBuilder: (context, index) {
|
||||||
|
return OrderCard(order: orders[index]);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,40 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/helpers/shopid_helper.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../provider/shop_provider.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
import '../provider/shop_profile_provider.dart';
|
||||||
|
|
||||||
|
class ServiceCenterDetailsScreen extends StatefulWidget {
|
||||||
|
const ServiceCenterDetailsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ServiceCenterDetailsScreen> createState() => _ServiceCenterDetailsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ServiceCenterDetailsScreenState extends State<ServiceCenterDetailsScreen> {
|
||||||
|
bool _isInit = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
if (_isInit) {
|
||||||
|
final shopId = getShopId(context);
|
||||||
|
Provider.of<ShopProfileProvider>(context, listen: false).fetchShopProfile(shopId!);
|
||||||
|
_isInit = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ServiceCenterDetailsScreen extends StatelessWidget {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final shop = Provider.of<ShopProvider>(context).shop;
|
final provider = Provider.of<ShopProfileProvider>(context);
|
||||||
|
final shop = provider.shopProfile;
|
||||||
if (shop == null) {
|
|
||||||
return Scaffold(
|
|
||||||
body: Center(child: CircularProgressIndicator()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Color(0xFFEAF5FF),
|
backgroundColor: const Color(0xFFEAF5FF),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [Color.fromRGBO(208, 235, 255, 1), Colors.white],
|
colors: [Color.fromRGBO(208, 235, 255, 1), Colors.white],
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
@@ -35,9 +51,9 @@ class ServiceCenterDetailsScreen extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Navigator.pop(context),
|
onTap: () => Navigator.pop(context),
|
||||||
child: Icon(Icons.arrow_back_ios, color: Colors.black),
|
child: const Icon(Icons.arrow_back_ios, color: Colors.black),
|
||||||
),
|
),
|
||||||
SizedBox(width: 25),
|
const SizedBox(width: 25),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 15.0),
|
padding: const EdgeInsets.only(top: 15.0),
|
||||||
@@ -52,22 +68,25 @@ class ServiceCenterDetailsScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 24),
|
const SizedBox(width: 24),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Main content
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: provider.isLoading
|
||||||
|
? _buildShimmer()
|
||||||
|
: shop == null
|
||||||
|
? const Center(child: Text('Failed to load shop details'))
|
||||||
|
: SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||||
padding: EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
boxShadow: [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black12,
|
color: Colors.black12,
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
@@ -79,36 +98,54 @@ class ServiceCenterDetailsScreen extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Image.asset(
|
child: shop.images != null && shop.images!.isNotEmpty
|
||||||
"assets/images/shop_image.jpg",
|
? Image.network(
|
||||||
|
shop.images[0]!,
|
||||||
height: 230,
|
height: 230,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
errorBuilder: (context, error, stackTrace) =>
|
errorBuilder: (context, error, stackTrace) =>
|
||||||
Icon(Icons.broken_image, size: 100),
|
const Icon(Icons.broken_image, size: 100),
|
||||||
|
loadingBuilder: (context, child, loadingProgress) {
|
||||||
|
if (loadingProgress == null) return child;
|
||||||
|
return Container(
|
||||||
|
height: 230,
|
||||||
|
width: double.infinity,
|
||||||
|
color: Colors.grey[300],
|
||||||
|
child:
|
||||||
|
const Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Image.asset(
|
||||||
|
"assets/images/shop_image.jpg",
|
||||||
|
height: 230,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
shop.shopName,
|
shop.name ?? 'No Shop Name',
|
||||||
style: GoogleFonts.inter(
|
style: GoogleFonts.inter(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
InfoRow(
|
InfoRow(
|
||||||
icon: "assets/icon/location_icon.png",
|
icon: "assets/icon/location_icon.png",
|
||||||
text: shop.address ?? 'Address not available',
|
text: shop.address ?? 'Address not available',
|
||||||
),
|
),
|
||||||
InfoRow(
|
InfoRow(
|
||||||
icon: "assets/icon/contact_icon.png",
|
icon: "assets/icon/contact_icon.png",
|
||||||
text: shop.mobile,
|
text: shop.phone ?? 'Phone not available',
|
||||||
),
|
),
|
||||||
InfoRow(
|
InfoRow(
|
||||||
icon: "assets/icon/Message_icon.png",
|
icon: "assets/icon/Message_icon.png",
|
||||||
text: shop.email,
|
text: shop.email ?? 'Email not available',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -122,6 +159,82 @@ class ServiceCenterDetailsScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildShimmer() {
|
||||||
|
final baseColor = Colors.grey.shade300;
|
||||||
|
final highlightColor = Colors.grey.shade100;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||||
|
child: Shimmer.fromColors(
|
||||||
|
baseColor: baseColor,
|
||||||
|
highlightColor: highlightColor,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
// Image placeholder (same size & rounded corners)
|
||||||
|
Container(
|
||||||
|
height: 230,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: baseColor,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Shop name placeholder (matching width and height)
|
||||||
|
Container(
|
||||||
|
height: 30,
|
||||||
|
width: 220,
|
||||||
|
color: baseColor,
|
||||||
|
margin: const EdgeInsets.only(left: 8),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Location info row shimmer
|
||||||
|
_shimmerInfoRow(width: 200, baseColor: baseColor),
|
||||||
|
|
||||||
|
// Phone info row shimmer
|
||||||
|
_shimmerInfoRow(width: 140, baseColor: baseColor),
|
||||||
|
|
||||||
|
// Email info row shimmer
|
||||||
|
_shimmerInfoRow(width: 180, baseColor: baseColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _shimmerInfoRow({required double width, required Color baseColor}) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 15),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: baseColor,
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Container(
|
||||||
|
height: 16,
|
||||||
|
width: width,
|
||||||
|
color: baseColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InfoRow extends StatelessWidget {
|
class InfoRow extends StatelessWidget {
|
||||||
@@ -129,6 +242,7 @@ class InfoRow extends StatelessWidget {
|
|||||||
final String text;
|
final String text;
|
||||||
|
|
||||||
const InfoRow({
|
const InfoRow({
|
||||||
|
super.key,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.text,
|
required this.text,
|
||||||
});
|
});
|
||||||
@@ -141,7 +255,7 @@ class InfoRow extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ImageIcon(AssetImage(icon)),
|
ImageIcon(AssetImage(icon)),
|
||||||
SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
|
|||||||
54
lib/screens/splash_screen.dart
Normal file
54
lib/screens/splash_screen.dart
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:glowwheels/models/shop_model.dart';
|
||||||
|
import 'package:glowwheels/provider/shop_provider.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'login_screen.dart';
|
||||||
|
import 'main_screen.dart';
|
||||||
|
|
||||||
|
class SplashDecider extends StatefulWidget {
|
||||||
|
const SplashDecider({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SplashDecider> createState() => _SplashDeciderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SplashDeciderState extends State<SplashDecider> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_checkLoginStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _checkLoginStatus() async {
|
||||||
|
final shopBox = await Hive.openBox<ShopModel>('shopBox');
|
||||||
|
final tokenBox = await Hive.openBox<String>('tokenBox');
|
||||||
|
|
||||||
|
final shop = shopBox.get('shop');
|
||||||
|
final token = tokenBox.get('token');
|
||||||
|
|
||||||
|
if (shop != null && token != null && token.isNotEmpty) {
|
||||||
|
// ✅ Set the provider values if needed
|
||||||
|
Provider.of<ShopProvider>(context, listen: false).setShop(shop);
|
||||||
|
Provider.of<ShopProvider>(context, listen: false).setToken(token);
|
||||||
|
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (_) => MainScreen()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (_) => LoginScreen()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Scaffold(
|
||||||
|
body: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,28 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/helpers/shopid_helper.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../models/serviceboy_model.dart';
|
|
||||||
import '../provider/serviceboy_provider.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
class AssignServiceBoyDialog extends StatelessWidget {
|
import 'package:glowwheels/models/serviceboy_model.dart';
|
||||||
final List<ServiceBoy> serviceBoys;
|
import '../provider/serviceboy_provider.dart';
|
||||||
|
import '../provider/order_provider.dart';
|
||||||
|
|
||||||
const AssignServiceBoyDialog({super.key, required this.serviceBoys});
|
class AssignServiceBoyDialog extends StatefulWidget {
|
||||||
|
final List<ServiceBoy> serviceBoys;
|
||||||
|
final String orderId;
|
||||||
|
|
||||||
|
const AssignServiceBoyDialog({
|
||||||
|
Key? key,
|
||||||
|
required this.serviceBoys,
|
||||||
|
required this.orderId,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AssignServiceBoyDialog> createState() => _AssignServiceBoyDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AssignServiceBoyDialogState extends State<AssignServiceBoyDialog> {
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -19,14 +33,17 @@ class AssignServiceBoyDialog extends StatelessWidget {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
title: const Text("Select Service Boy", style: TextStyle(fontWeight: FontWeight.bold)),
|
title: const Text(
|
||||||
|
"Select Service Boy",
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: serviceBoys.length,
|
itemCount: widget.serviceBoys.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final boy = serviceBoys[index];
|
final boy = widget.serviceBoys[index];
|
||||||
final isSelected = assignProvider.selectedBoy == boy;
|
final isSelected = assignProvider.selectedBoy == boy;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
@@ -35,8 +52,9 @@ class AssignServiceBoyDialog extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? Color.fromRGBO(0, 80, 170, 1) : Colors.transparent,
|
color: isSelected ? const Color.fromRGBO(0, 80, 170, 1) : Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -64,28 +82,80 @@ class AssignServiceBoyDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text("CANCEL",style: GoogleFonts.poppins(
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
fontSize: 14,color: Color.fromRGBO(25, 25, 112, 1)
|
child: Text(
|
||||||
,fontWeight: FontWeight.w400
|
"CANCEL",
|
||||||
),),
|
style: GoogleFonts.poppins(
|
||||||
onPressed: () {
|
fontSize: 14,
|
||||||
Navigator.of(context).pop(); // return null
|
color: const Color.fromRGBO(25, 25, 112, 1),
|
||||||
},
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: assignProvider.selectedBoy != null
|
onPressed: assignProvider.selectedBoy != null && !_isLoading
|
||||||
? () {
|
? () async {
|
||||||
Navigator.of(context).pop(assignProvider.selectedBoy);
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
final selectedBoy = assignProvider.selectedBoy!;
|
||||||
|
final orderProvider = Provider.of<OrdersProvider>(context, listen: false);
|
||||||
|
final shopId = getShopId(context);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await orderProvider.assignServiceBoyToOrder(
|
||||||
|
orderId: widget.orderId,
|
||||||
|
serviceBoyId: selectedBoy.id,
|
||||||
|
shopId: shopId!,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context, rootNavigator: true).pop(
|
||||||
|
|
||||||
|
selectedBoy
|
||||||
|
|
||||||
|
); // ✅ return selected boy
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Service boy assigned successfully!')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to assign service boy: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Color(0xFF1B1464),
|
backgroundColor: const Color(0xFF1B1464),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
|
||||||
),
|
),
|
||||||
child: Text("Assign",style: GoogleFonts.inter(
|
child: _isLoading
|
||||||
fontSize: 12,color: Colors.white
|
? const SizedBox(
|
||||||
,fontWeight: FontWeight.w500
|
width: 16,
|
||||||
),),
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
"Assign",
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -94,4 +164,3 @@ class AssignServiceBoyDialog extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:glowwheels/helpers/shopid_helper.dart';
|
||||||
|
import 'package:glowwheels/provider/order_provider.dart';
|
||||||
|
import 'package:glowwheels/provider/serviceboy_provider.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import '../models/order_model.dart';
|
import 'package:glowwheels/models/order_model.dart';
|
||||||
import '../models/serviceboy_model.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:glowwheels/models/serviceboy_model.dart';
|
||||||
import 'assign_serviceboy_dialog.dart';
|
import 'assign_serviceboy_dialog.dart';
|
||||||
|
|
||||||
class OrderCard extends StatefulWidget {
|
class OrderCard extends StatefulWidget {
|
||||||
@@ -16,12 +20,18 @@ class OrderCard extends StatefulWidget {
|
|||||||
|
|
||||||
class _OrderCardState extends State<OrderCard> {
|
class _OrderCardState extends State<OrderCard> {
|
||||||
ServiceBoy? assignedBoy;
|
ServiceBoy? assignedBoy;
|
||||||
|
late String shopId = '';
|
||||||
|
bool isLoading = false;
|
||||||
|
late String orderStatus=widget.order.status;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
assignedBoy = widget.order.assignedServiceBoy;
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
List<ServiceBoy> serviceBoys = [
|
|
||||||
ServiceBoy(name: 'John Doe', phone: '9875643210'),
|
|
||||||
ServiceBoy(name: 'Amit Raj', phone: '9765432180'),
|
|
||||||
ServiceBoy(name: 'Manoj Sinha', phone: '9543219876'),
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -40,18 +50,31 @@ class _OrderCardState extends State<OrderCard> {
|
|||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Image.asset(widget.order.imagePath, width: 100, height: 100, fit: BoxFit.contain),
|
widget.order.service.vehicle.image.isNotEmpty
|
||||||
|
? Image.network(
|
||||||
|
widget.order.service.vehicle.image,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
child: Center(child: Icon(Icons.image_not_supported)),
|
||||||
|
),
|
||||||
SizedBox(width: 16),
|
SizedBox(width: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildRow("Customer Name", widget.order.customerName),
|
_buildRow("Customer Name", widget.order.user.name),
|
||||||
_buildRow("Mobile Number", widget.order.mobileNumber),
|
_buildRow("Mobile Number", widget.order.user.phone.toString()),
|
||||||
_buildRow("Service Type", widget.order.serviceType),
|
_buildRow("Service Type", widget.order.service.serviceType),
|
||||||
_buildRow("Service", widget.order.service),
|
_buildRow("Service", widget.order.service.serviceName),
|
||||||
_buildRow("Price", widget.order.price),
|
_buildRow("Price", widget.order.service.price.toString()),
|
||||||
_buildRow("Service Time", widget.order.time),
|
_buildRow("Service Time", widget.order.timeSlot),
|
||||||
_buildRow("Service Date", widget.order.date),
|
_buildRow("Service Date", widget.order.date),
|
||||||
|
(widget.order.address!=null)?_buildRow("Location", widget.order.address!):SizedBox(),
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -60,7 +83,9 @@ class _OrderCardState extends State<OrderCard> {
|
|||||||
SizedBox(height: 12),
|
SizedBox(height: 12),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.order.carName,
|
widget.order.service.manufacture.manufacture +
|
||||||
|
" " +
|
||||||
|
widget.order.service.model.model,
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -68,46 +93,210 @@ class _OrderCardState extends State<OrderCard> {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
GestureDetector(
|
||||||
widget.order.status,
|
onTap: () async {
|
||||||
style: TextStyle(
|
String? newStatus;
|
||||||
color: widget.order.status == "Confirmed" ? Colors.green : Colors.orange,
|
String? dialogMessage;
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
assignedBoy == null
|
|
||||||
? ElevatedButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final selected = await showDialog<ServiceBoy>(
|
|
||||||
context: context,
|
|
||||||
builder: (_) => AssignServiceBoyDialog(serviceBoys: serviceBoys),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selected != null) {
|
if (orderStatus == "pending") {
|
||||||
setState(() {
|
newStatus = "confirmed";
|
||||||
assignedBoy = selected;
|
dialogMessage = "Are you sure you want to change the status to Confirmed?";
|
||||||
});
|
} else if (orderStatus == "confirmed" && assignedBoy!=null) {
|
||||||
|
newStatus = "completed";
|
||||||
|
dialogMessage = "Are you sure you want to change the status to Completed?";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newStatus != null && dialogMessage != null) {
|
||||||
|
final confirm = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text("Confirm Status Change"),
|
||||||
|
content: Text(dialogMessage!),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
child: const Text("OK"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirm == true) {
|
||||||
|
try {
|
||||||
|
setState(() => isLoading = true);
|
||||||
|
final orderProvider = Provider.of<OrdersProvider>(context, listen: false);
|
||||||
|
await orderProvider.updateOrderStatus(
|
||||||
|
orderId: widget.order.id,
|
||||||
|
status: newStatus,
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
orderStatus = newStatus!;
|
||||||
|
});
|
||||||
|
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Order status updated to $newStatus")),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Failed to update status: $e")),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setState(() => isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
child: Text(
|
||||||
backgroundColor: Color(0xFF1B1464),
|
orderStatus,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
|
style: TextStyle(
|
||||||
),
|
color:orderStatus == "confirmed" || orderStatus == "completed"
|
||||||
child: Text("Assign",style: GoogleFonts.inter(
|
? Colors.green
|
||||||
fontSize: 12,color: Colors.white
|
: orderStatus == "pending"
|
||||||
,fontWeight: FontWeight.w500
|
? Colors.orange
|
||||||
),),
|
: Colors.red,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
|
||||||
)
|
// Visual cue for clickable
|
||||||
: Column(
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
),
|
||||||
children: [
|
|
||||||
Text("Assigned to: ${assignedBoy!.name}",
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
||||||
Text("Phone: ${assignedBoy!.phone}"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
||||||
|
if (assignedBoy == null &&
|
||||||
|
orderStatus.toLowerCase() == "confirmed")
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: isLoading
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
setState(() {
|
||||||
|
isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
final serviceBoyProvider =
|
||||||
|
Provider.of<ServiceBoyProvider>(context, listen: false);
|
||||||
|
|
||||||
|
final shopId = getShopId(context);
|
||||||
|
await serviceBoyProvider.fetchServiceBoys(shopId!);
|
||||||
|
|
||||||
|
final selected = await showDialog<ServiceBoy>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => AssignServiceBoyDialog(
|
||||||
|
serviceBoys: serviceBoyProvider.serviceBoys,
|
||||||
|
orderId: widget.order.id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selected != null) {
|
||||||
|
setState(() {
|
||||||
|
assignedBoy = selected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF1B1464),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(30)),
|
||||||
|
),
|
||||||
|
child: isLoading
|
||||||
|
? SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
"Assign",
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else if (orderStatus.toLowerCase() == "pending")
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async{
|
||||||
|
|
||||||
|
final confirm = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text("Confirm Status Change"),
|
||||||
|
content: Text("Are you sure you want to cancel the order?"!),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
child: const Text("OK"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirm == true) {
|
||||||
|
try {
|
||||||
|
setState(() => isLoading = true);
|
||||||
|
final orderProvider = Provider.of<OrdersProvider>(context, listen: false);
|
||||||
|
await orderProvider.updateOrderStatus(
|
||||||
|
orderId: widget.order.id,
|
||||||
|
status: "cancelled by admin",
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
orderStatus = "cancelled by admin";
|
||||||
|
});
|
||||||
|
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Order status updated to cancelled by admin")),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Failed to update status: $e")),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setState(() => isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"Cancel",
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
fontSize: 12, color: Colors.white, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else if (orderStatus.toLowerCase() == "completed")
|
||||||
|
SizedBox()
|
||||||
|
else if (orderStatus.toLowerCase() == "cancelled by admin")
|
||||||
|
SizedBox()
|
||||||
|
else if (assignedBoy != null)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text("Assigned to: ${assignedBoy!.name}",
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
Text("Phone: ${assignedBoy!.phone}"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -121,18 +310,20 @@ class _OrderCardState extends State<OrderCard> {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
padding: const EdgeInsets.symmetric(vertical: 3),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(flex: 2, child: Text(label, style: GoogleFonts.inter(
|
Expanded(
|
||||||
fontSize: 10,color: Colors.black
|
flex: 2,
|
||||||
,fontWeight: FontWeight.w500
|
child: Text(label,
|
||||||
))),
|
style: GoogleFonts.inter(
|
||||||
Expanded(flex: 3, child: Text(value, style: GoogleFonts.inter(
|
fontSize: 10, color: Colors.black, fontWeight: FontWeight.w500)),
|
||||||
fontSize: 10,color: Colors.black
|
),
|
||||||
,fontWeight: FontWeight.w500
|
Expanded(
|
||||||
))),
|
flex: 3,
|
||||||
|
child: Text(value,
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
fontSize: 10, color: Colors.black, fontWeight: FontWeight.w500)),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,52 +1,66 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
import 'package:glowwheels/models/shop_profile_model.dart';
|
||||||
|
|
||||||
class ProfileHeader extends StatelessWidget {
|
class ProfileHeader extends StatelessWidget {
|
||||||
const ProfileHeader({super.key});
|
final ShopProfileModel? shopProfile;
|
||||||
|
|
||||||
|
const ProfileHeader({super.key, this.shopProfile});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (shopProfile == null) {
|
||||||
|
// Show skeleton loader
|
||||||
|
return _buildSkeleton(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
final name = shopProfile!.name;
|
||||||
|
final phone = shopProfile!.phone;
|
||||||
|
final email = shopProfile!.email;
|
||||||
|
final imageUrl = (shopProfile!.images.isNotEmpty) ? shopProfile!.images.first : null;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// Circular Profile Image
|
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 40,
|
radius: 40,
|
||||||
backgroundImage: AssetImage('assets/images/shop_image.jpg'), // Replace with your asset
|
backgroundImage: imageUrl != null
|
||||||
|
? NetworkImage(imageUrl)
|
||||||
|
: const AssetImage('assets/images/shop_image.jpg') as ImageProvider,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
// Details Column
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: const [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
ImageIcon(AssetImage("assets/icon/account_icon.png")),
|
const ImageIcon(AssetImage("assets/icon/account_icon.png")),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Omkara Car Wash Center',
|
name,
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
ImageIcon(AssetImage("assets/icon/contact_icon.png")),
|
const ImageIcon(AssetImage("assets/icon/contact_icon.png")),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text('+91 9999988888'),
|
Text(phone),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
ImageIcon(AssetImage("assets/icon/Message_icon.png")),
|
const ImageIcon(AssetImage("assets/icon/Message_icon.png")),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(child: Text('loremipsum@gmail.com')),
|
Expanded(child: Text(email)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -56,4 +70,38 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildSkeleton(BuildContext context) {
|
||||||
|
final baseColor = Colors.grey[300]!;
|
||||||
|
final highlightColor = Colors.grey[100]!;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Shimmer.fromColors(
|
||||||
|
baseColor: baseColor,
|
||||||
|
highlightColor: highlightColor,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
decoration: BoxDecoration(color: baseColor, shape: BoxShape.circle),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(height: 20, color: baseColor, margin: const EdgeInsets.only(bottom: 12)),
|
||||||
|
Container(height: 16, width: 150, color: baseColor, margin: const EdgeInsets.only(bottom: 12)),
|
||||||
|
Container(height: 16, width: 200, color: baseColor),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -552,6 +552,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
|
shimmer:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shimmer
|
||||||
|
sha256: "1f1009b5845a1f88f1c5630212279540486f97409e9fc3f63883e71070d107bf"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ dependencies:
|
|||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
hive_flutter: ^1.1.0
|
hive_flutter: ^1.1.0
|
||||||
path_provider: ^2.1.2
|
path_provider: ^2.1.2
|
||||||
|
shimmer: ^2.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user