Compare commits

...

5 Commits

Author SHA1 Message Date
Faiz Hashmi 113f158079 05 March,2025 8 months ago
Faiz Hashmi 59875f5198 12 feb,2025 9 months ago
Faiz Hashmi 021618fa6b Changes 9 months ago
Faiz Hashmi 75c46e5194 Merge branch 'master' into faiz_development_common 9 months ago
Faiz Hashmi f9a95579dc Changes 9 months ago

@ -357,7 +357,7 @@
"setupTouchID": "يرجى إعداد معرف اللمس",
"reenableTouchID": "يرجى إعادة تمكين معرف اللمس",
"scanFaceIDAuthenticate": "امسح بصمة الإصبع أو معرف الوجه للتحقق",
"AdDeletedSuccessfully": "تم حذف الإعلان بنجاح!",
"adDeletedSuccessfully": "تم حذف الإعلان بنجاح!",
"yourReservationCancelled": "تم إلغاء حجزك.",
"adDeactivatedSuccessfully": "تم إلغاء تفعيل الإعلان بنجاح!",
"vehicle": {
@ -797,5 +797,16 @@
"continueAsGuest": "الاستمرار كضيف",
"loginToViewAppointments": "الرجاء تسجيل الدخول لعرض المواعيد",
"itemType": "غرض",
"upgradeSubscription": "قم بترقية اشتراكك"
"upgradeSubscription": "قم بترقية اشتراكك",
"searchByAdID": "البحث برقم الإعلان",
"enterAdID": "أدخل رقم الإعلان",
"searchByDemandPrice": "البحث حسب سعر الطلب",
"enterStartPrice": "أدخل سعر البداية",
"enterEndPrice": "أدخل سعر النهاية",
"searchByVehicleType": "البحث حسب نوع المركبة",
"selectVehicleType": "اختر نوع المركبة",
"specialRequestsChats": "دردشات الطلبات الخاصة",
"selectDeliveryOption": "اختر خيار التوصيل",
"deliveryOptions": "خيارات التوصيل",
"selfPickup": "الالتقاط الذاتي"
}

@ -795,5 +795,16 @@
"ok": "Ok",
"continueAsGuest": "Continue as a guest",
"loginToViewAppointments": "Please Login to View Appointments",
"itemType": "Item Type"
"itemType": "Item Type",
"searchByAdID": "Search by Ad ID",
"enterAdID": "Enter Ad ID",
"searchByDemandPrice": "Search by demand price",
"enterStartPrice": "Enter start price",
"enterEndPrice": "Enter end price",
"searchByVehicleType": "Search by vehicle type",
"selectVehicleType": "Select vehicle type",
"specialRequestsChats": "Special Requests Chats",
"selectDeliveryOption": "Select Delivery Option",
"deliveryOptions": "Delivery Options",
"selfPickup": "Self Pickup"
}

@ -125,6 +125,7 @@ class ApiConsts {
static String vehicleAdsSpecialServicesGet = "${baseUrlServices}api/Common/SpecialService_Get";
static String vehicleAdsSingleStepCreate = "${baseUrlServices}api/Advertisement/AdsSingleStep_Create";
static String vehicleAdsSingleStepUpdate = "${baseUrlServices}api/Advertisement/AdsSingleStep_Update";
static String vehicleAdsSingleStepUpdateExtend = "${baseUrlServices}api/Advertisement/AdsSingleStep_Update_Extend";
static String vehicleAdsGet = "${baseUrlServices}api/Advertisement/Ads_Get";
static String myAdsReserveGet = "${baseUrlServices}api/Advertisement/AdsReserve_Get";
static String reserveAdsBankDetailsGet = "${baseUrlServices}api/Advertisement/MCBankAccountAd_Get";
@ -137,6 +138,9 @@ class ApiConsts {
static String adsReserveCreate = "${baseUrlServices}api/Advertisement/AdsReserve_Create";
static String reserveAdPaymentOnDealDoneCreate = "${baseUrlServices}api/Advertisement/ReserveAdPaymentOnDealDone_Create";
static String adsExtendDurationCreate = "${baseUrlServices}api/Advertisement/Ads_ExtendDuration_Create";
static String adsSingleStepDraftCreate = "${baseUrlServices}api/Advertisement/AdsSingleStepDraft_Create";
static String adsSingleStepDraftUpdate = "${baseUrlServices}api/Advertisement/AdsSingleStepDraft_Update";
static String getMyDraftAds = "${baseUrlServices}api/Advertisement/AdsDraft_Get";
//Subscription
static String getMySubscriptions = "${baseUrlServices}api/ServiceProviders/ProviderSubscription_Get";
@ -175,9 +179,12 @@ class ApiConsts {
static String requestOffersSpsGet = "${baseUrlServices}api/RequestManagement/Request_OfferSPs_Get";
static String getServiceRequestsForProvider = "${baseUrlServices}api/RequestManagement/Request_ServiceProvider";
//Shipping
static String shippingRequestStatusUpdate = "${baseUrlServices}api/RequestManagement/ShippingRequestStatus_Update";
static String shippingRequestStatusGet = "${baseUrlServices}api/RequestManagement/ShippingRequestStatus_Get";
static String selfPickupRequestStatusGet = "${baseUrlServices}api/RequestManagement/SelfPickUpRequestStatus_Get";
static String selfPickupRequestStatusUpdate = "${baseUrlServices}api/RequestManagement/SelfPickUpRequestStatus_Update";
//Chat
static String chatHubUrl = "$baseUrlServices/McHub";
@ -186,6 +193,7 @@ class ApiConsts {
static String getChatMessagesForRequests = "${baseUrlServices}api/RequestManagement/ReqOfferChat_Get";
static String getChatMessagesForAds = "${baseUrlServices}api/Advertisement/AdsChat_Get";
static String getChatBuyersForAds = "${baseUrlServices}api/Advertisement/AdsChatBuyer_Get";
static String reqChatUnreadGet = "${baseUrlServices}api/RequestManagement/Req_ChatCount_Get";
//Settings Options
static String getAllFAQs = "${baseUrlServices}api/Common/FAQ_Get";
@ -222,6 +230,7 @@ class GlobalConsts {
static String welcomeVideoUrl = "welcomeVideoUrl";
static String doNotShowWelcomeVideo = "doNotShowWelcomeVideo";
static String demandAmountError = "Amount Cannot be Empty";
static String deliveryOptionSelectionError = "At least one delivery option should be selected.";
static String reservationCancelError = "Cancellation Reason Cannot be Empty";
static String descriptionError = "Description should be more than 5 letters.";
static String acceptingThisOffer = "I am accepting this offer.";
@ -415,45 +424,45 @@ class SignalrConsts {
static String receiveMessageGeneral = "ReceiveMessageGeneral";
}
class GuestConsts {
UserInfo userInfo = UserInfo.fromJson({
"id": -1,
"userID": null,
"firstName": "Guest",
"lastName": "User",
"companyName": null,
"accountStatus": "2",
"activityStatus": "Offline",
"accountStatusText": null,
"subscriptionDate": null,
"mobileNo": "966123456789",
"email": "mowatter@gmail.com",
"userImageUrl": "https://ms.hmg.com/api/ProfileImage?imageName=User_Default.png",
"roleID": 4,
"roleName": "Customer",
"isEmailVerified": false,
"serviceProviderBranch": [],
"isVerified": true,
"userRoles": [],
"isProviderDealership": false,
"isProviderIndividual": false,
"isProvider": false,
"providerID": null,
"isCustomer": true,
"customerID": 25,
"countryID": 1,
"countryName": "Saudi Arabia",
"cityID": 1,
"cityName": "Riyadh",
"dealershipUserID": null,
"serviceProviderBranchID": null,
"createdOn": "2024-12-24T09:20:47.6733333",
"genderID": 1,
"genderName": "Male",
"serviceProviderPayment": [],
"deviceType": "1",
"deviceToken": null,
});
}
UserInfo userInfo = UserInfo.fromJson(
{
"id": -1,
"userID": null,
"firstName": "Guest",
"lastName": "User",
"companyName": null,
"accountStatus": "2",
"activityStatus": "Offline",
"accountStatusText": null,
"subscriptionDate": null,
"mobileNo": "966123456789",
"email": "mowatter@gmail.com",
"userImageUrl": "https://ms.hmg.com/api/ProfileImage?imageName=User_Default.png",
"roleID": 4,
"roleName": "Customer",
"isEmailVerified": false,
"serviceProviderBranch": [],
"isVerified": true,
"userRoles": [],
"isProviderDealership": false,
"isProviderIndividual": false,
"isProvider": false,
"providerID": null,
"isCustomer": true,
"customerID": 25,
"countryID": 1,
"countryName": "Saudi Arabia",
"cityID": 1,
"cityName": "Riyadh",
"dealershipUserID": null,
"serviceProviderBranchID": null,
"createdOn": "2024-12-24T09:20:47.6733333",
"genderID": 1,
"genderName": "Male",
"serviceProviderPayment": [],
"deviceType": "1",
"deviceToken": null,
},
);
}

@ -15,6 +15,7 @@ import 'package:mc_common_app/views/payments/payment_methods_view.dart';
import 'package:mc_common_app/views/profile/profile_view.dart';
import 'package:mc_common_app/views/requests/create_request_page.dart';
import 'package:mc_common_app/views/requests/offer_list_page.dart';
import 'package:mc_common_app/views/requests/providers_chat_list_page.dart';
import 'package:mc_common_app/views/requests/request_detail_page.dart';
import 'package:mc_common_app/views/requests/requests_filter_view.dart';
import 'package:mc_common_app/views/requests/review_request_offer.dart';
@ -151,6 +152,7 @@ class AppRoutes {
static const String offersListPage = "/offersListPage";
static const String reviewRequestOffer = "/reviewRequestOffer";
static const String providerAcceptedRequestsView = "/providerAcceptedRequestsView";
static const String providersChatListPage = "/providersChatListPage";
//Setting Options
static const String settingOptionsFaqs = "/settingOptionsFaqs";
@ -221,6 +223,7 @@ class AppRoutes {
AppRoutes.offersListPage: (context) => OfferListPage(requestId: ModalRoute.of(context)!.settings.arguments as int),
AppRoutes.reviewRequestOffer: (context) => const ReviewRequestOffer(),
AppRoutes.requestsFilterView: (context) => const RequestsFilterView(),
AppRoutes.providersChatListPage: (context) => const ProvidersChatListPage(),
//MediaViewer
AppRoutes.mediaViewerScreen: (context) => MediaViewerScreen(images: ModalRoute.of(context)!.settings.arguments as List<MessageImageModel>),

@ -975,7 +975,7 @@ extension ShippingStatusEnumExt on int {
return ShippingRequestStatusEnum.inTransit;
} else if (this == 3) {
return ShippingRequestStatusEnum.outForDelivery;
} else if (this == 4) {
} else if (this == 4) {
return ShippingRequestStatusEnum.delivered;
}
return ShippingRequestStatusEnum.pending;
@ -1003,6 +1003,83 @@ extension ShippingStatusEnumToInt on ShippingRequestStatusEnum {
}
}
extension SelfPickupStatusEnumExt on int {
SelfPickupRequestStatusEnum toSelfPickupStatusEnum() {
if (this == -1) {
return SelfPickupRequestStatusEnum.allRequests;
} else if (this == 0) {
return SelfPickupRequestStatusEnum.allRequests;
} else if (this == 1) {
return SelfPickupRequestStatusEnum.preparingToCollect;
} else if (this == 2) {
return SelfPickupRequestStatusEnum.readyToCollect;
} else if (this == 3) {
return SelfPickupRequestStatusEnum.collected;
}
return SelfPickupRequestStatusEnum.allRequests;
}
}
extension SelfPickupStatusEnumToInt on SelfPickupRequestStatusEnum {
int getIdFromSelfPickupStatusEnum() {
switch (this) {
case SelfPickupRequestStatusEnum.allRequests:
return 0;
case SelfPickupRequestStatusEnum.preparingToCollect:
return 1;
case SelfPickupRequestStatusEnum.readyToCollect:
return 2;
case SelfPickupRequestStatusEnum.collected:
return 3;
default:
return -1;
}
}
}
extension RequestDeliveryOptionEnumExt on int {
RequestDeliveryOptionEnum toRequestDeliveryOptionEnum() {
if (this == 1) {
return RequestDeliveryOptionEnum.delivery;
} else if (this == 2) {
return RequestDeliveryOptionEnum.selfPickup;
} else if (this == 3) {
return RequestDeliveryOptionEnum.both;
}
return RequestDeliveryOptionEnum.none;
}
}
extension RequestDeliveryOptionEnumToIntExt on RequestDeliveryOptionEnum {
int getIdRequestDeliveryOptionEnum() {
switch (this) {
case RequestDeliveryOptionEnum.delivery:
return 1;
case RequestDeliveryOptionEnum.selfPickup:
return 2;
case RequestDeliveryOptionEnum.both:
return 3;
default:
return 0;
}
}
}
extension RequestDeliveryOptionEnumTosTRINGExt on RequestDeliveryOptionEnum {
String getStringFromRequestDeliveryOptionEnum() {
switch (this) {
case RequestDeliveryOptionEnum.delivery:
return "Delivery";
case RequestDeliveryOptionEnum.selfPickup:
return "Self Pickup";
case RequestDeliveryOptionEnum.both:
return "Delivery, Self Pickup";
default:
return "Self Pickup";
}
}
}
extension CapitalizeFirstLetter on String {
String capitalizeFirstLetter() {
if (isEmpty) {

@ -373,7 +373,7 @@ class CodegenLoader extends AssetLoader{
"setupTouchID": "يرجى إعداد معرف اللمس",
"reenableTouchID": "يرجى إعادة تمكين معرف اللمس",
"scanFaceIDAuthenticate": "امسح بصمة الإصبع أو معرف الوجه للتحقق",
"AdDeletedSuccessfully": "تم حذف الإعلان بنجاح!",
"adDeletedSuccessfully": "تم حذف الإعلان بنجاح!",
"yourReservationCancelled": "تم إلغاء حجزك.",
"adDeactivatedSuccessfully": "تم إلغاء تفعيل الإعلان بنجاح!",
"vehicle": {
@ -813,7 +813,18 @@ class CodegenLoader extends AssetLoader{
"continueAsGuest": "الاستمرار كضيف",
"loginToViewAppointments": "الرجاء تسجيل الدخول لعرض المواعيد",
"itemType": "غرض",
"upgradeSubscription": "قم بترقية اشتراكك"
"upgradeSubscription": "قم بترقية اشتراكك",
"searchByAdID": "البحث برقم الإعلان",
"enterAdID": "أدخل رقم الإعلان",
"searchByDemandPrice": "البحث حسب سعر الطلب",
"enterStartPrice": "أدخل سعر البداية",
"enterEndPrice": "أدخل سعر النهاية",
"searchByVehicleType": "البحث حسب نوع المركبة",
"selectVehicleType": "اختر نوع المركبة",
"specialRequestsChats": "دردشات الطلبات الخاصة",
"selectDeliveryOption": "اختر خيار التوصيل",
"deliveryOptions": "خيارات التوصيل",
"selfPickup": "الالتقاط الذاتي"
};
static const Map<String,dynamic> en_US = {
"firstTimeLogIn": "First Time Log In",
@ -1612,7 +1623,18 @@ static const Map<String,dynamic> en_US = {
"ok": "Ok",
"continueAsGuest": "Continue as a guest",
"loginToViewAppointments": "Please Login to View Appointments",
"itemType": "Item Type"
"itemType": "Item Type",
"searchByAdID": "Search by Ad ID",
"enterAdID": "Enter Ad ID",
"searchByDemandPrice": "Search by demand price",
"enterStartPrice": "Enter start price",
"enterEndPrice": "Enter end price",
"searchByVehicleType": "Search by vehicle type",
"selectVehicleType": "Select vehicle type",
"specialRequestsChats": "Special Requests Chats",
"selectDeliveryOption": "Select Delivery Option",
"deliveryOptions": "Delivery Options",
"selfPickup": "Self Pickup"
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": ar_SA, "en_US": en_US};
}

@ -337,7 +337,7 @@ abstract class LocaleKeys {
static const setupTouchID = 'setupTouchID';
static const reenableTouchID = 'reenableTouchID';
static const scanFaceIDAuthenticate = 'scanFaceIDAuthenticate';
static const AdDeletedSuccessfully = 'AdDeletedSuccessfully';
static const adDeletedSuccessfully = 'adDeletedSuccessfully';
static const yourReservationCancelled = 'yourReservationCancelled';
static const adDeactivatedSuccessfully = 'adDeactivatedSuccessfully';
static const vehicle_selectVehicleType = 'vehicle.selectVehicleType';
@ -777,5 +777,16 @@ abstract class LocaleKeys {
static const loginToViewAppointments = 'loginToViewAppointments';
static const itemType = 'itemType';
static const upgradeSubscription = 'upgradeSubscription';
static const searchByAdID = 'searchByAdID';
static const enterAdID = 'enterAdID';
static const searchByDemandPrice = 'searchByDemandPrice';
static const enterStartPrice = 'enterStartPrice';
static const enterEndPrice = 'enterEndPrice';
static const searchByVehicleType = 'searchByVehicleType';
static const selectVehicleType = 'selectVehicleType';
static const specialRequestsChats = 'specialRequestsChats';
static const selectDeliveryOption = 'selectDeliveryOption';
static const deliveryOptions = 'deliveryOptions';
static const selfPickup = 'selfPickup';
}

@ -28,3 +28,17 @@ class AdsBankDetailsModel {
return data;
}
}
class AdExtensionOrderResponseModel {
final int adsID;
final bool isPaymentRequired;
AdExtensionOrderResponseModel({required this.adsID, required this.isPaymentRequired});
factory AdExtensionOrderResponseModel.fromJson(Map<String, dynamic> json) {
return AdExtensionOrderResponseModel(
adsID: json['adsID'] as int,
isPaymentRequired: json['isPaymentRequired'] as bool,
);
}
}

@ -28,6 +28,7 @@ class AppointmentListModel {
String? branchName;
int? branchId;
int? servicePaymentStatus;
bool? isPaymentRequiredAtBooking;
double? totalAmount;
double? remainingAmount;
bool? isSelected;
@ -61,6 +62,7 @@ class AppointmentListModel {
this.branchName,
this.branchId,
this.servicePaymentStatus,
this.isPaymentRequiredAtBooking,
this.totalAmount,
this.remainingAmount,
this.isSelected,
@ -102,6 +104,7 @@ class AppointmentListModel {
branchName = json['branchName'];
branchId = json['branchID'];
servicePaymentStatus = json['servicePaymentStatus'];
isPaymentRequiredAtBooking = json['isPaymentRequiredAtBooking'];
totalAmount = json['amountTotal'];
remainingAmount = json['amountRem'];
isSelected = false;

@ -84,6 +84,7 @@ class ReqOffer {
String? manufacturedOn;
double? price;
bool? isDeliveryAvailable;
RequestDeliveryOptionEnum? requestDeliveryOption;
RequestsTypeEnum? requestsTypeEnum;
RequestOfferStatusEnum? requestOfferStatusEnum;
List<MessageImageModel>? reqOfferImages;
@ -100,6 +101,7 @@ class ReqOffer {
this.manufacturedOn,
this.price,
this.isDeliveryAvailable,
this.requestDeliveryOption,
this.requestOfferStatusEnum,
this.requestsTypeEnum,
this.reqOfferImages,
@ -117,6 +119,7 @@ class ReqOffer {
manufacturedOn = json['offeredItemCreatedOn'] ?? json["createdOn"];
price = json['price'];
isDeliveryAvailable = json['isDeliveryAvailable'];
requestDeliveryOption = ((json['offerDeliveryOption'] ?? 0) as int).toRequestDeliveryOptionEnum();
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();
requestsTypeEnum = RequestsTypeEnum.serviceRequest;
// if (isForReqOfferImagesURLs) {

@ -1,50 +1,30 @@
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/utils/enums.dart';
class OffersModel {
int? id;
int? requestID;
int? serviceProviderID;
ServiceProvider? serviceProvider;
int? offerStatus;
RequestOfferStatusEnum? requestOfferStatusEnum;
String? offerStatusText;
String? comment;
double? price;
OffersModel(
{this.id,
this.requestID,
this.serviceProviderID,
this.serviceProvider,
this.offerStatus,
this.offerStatusText,
this.comment,
this.price});
OffersModel({this.id, this.requestID, this.serviceProviderID, this.serviceProvider, this.offerStatus, this.offerStatusText, this.comment, this.price});
OffersModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestID = json['requestID'];
serviceProviderID = json['serviceProviderID'];
serviceProvider = json['serviceProvider'] != null
? ServiceProvider.fromJson(json['serviceProvider'])
: null;
serviceProvider = json['serviceProvider'] != null ? ServiceProvider.fromJson(json['serviceProvider']) : null;
offerStatus = json['offerStatus'];
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();
offerStatusText = json['offerStatusText'];
comment = json['comment'];
price = json['price'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['requestID'] = requestID;
data['serviceProviderID'] = serviceProviderID;
if (serviceProvider != null) {
data['serviceProvider'] = serviceProvider!.toJson();
}
data['offerStatus'] = offerStatus;
data['offerStatusText'] = offerStatusText;
data['comment'] = comment;
data['price'] = price;
return data;
}
}
class ServiceProvider {
@ -78,32 +58,32 @@ class ServiceProvider {
ServiceProvider(
{this.providerId,
this.providerGUID,
this.firstName,
this.lastName,
this.name,
this.gender,
this.genderName,
this.mobileNo,
this.email,
this.isEmailVerfied,
this.isCompleted,
this.city,
this.cityName,
this.country,
this.countryName,
this.accountStatus,
this.accountStatusText,
this.activityStatus,
this.activityStatusText,
this.bankName,
this.iBanNo,
this.isActive,
this.subscriptionDate,
this.companyName,
this.currency,
this.branch,
this.requestOffer});
this.providerGUID,
this.firstName,
this.lastName,
this.name,
this.gender,
this.genderName,
this.mobileNo,
this.email,
this.isEmailVerfied,
this.isCompleted,
this.city,
this.cityName,
this.country,
this.countryName,
this.accountStatus,
this.accountStatusText,
this.activityStatus,
this.activityStatusText,
this.bankName,
this.iBanNo,
this.isActive,
this.subscriptionDate,
this.companyName,
this.currency,
this.branch,
this.requestOffer});
ServiceProvider.fromJson(Map<String, dynamic> json) {
providerId = json['providerId'];

@ -0,0 +1,60 @@
class OffersUnreadChatModel {
final int reqTotal;
final List<OffersUnreadChatDataModel> reqChatUnread;
OffersUnreadChatModel({
required this.reqTotal,
required this.reqChatUnread,
});
factory OffersUnreadChatModel.fromJson(Map<String, dynamic> json) {
return OffersUnreadChatModel(
reqTotal: json['total'],
reqChatUnread: (json['reqChatCountData'] as List).map((e) => OffersUnreadChatDataModel.fromJson(e)).toList(),
);
}
}
class OffersUnreadChatDataModel {
String reqTitle;
int reqTotal;
int requestID;
String senderUserID;
int unreadMessagesCount;
String lastChatTime;
String lastChatText;
String customerName;
OffersUnreadChatDataModel({
required this.reqTitle,
required this.reqTotal,
required this.requestID,
required this.senderUserID,
required this.unreadMessagesCount,
required this.lastChatTime,
required this.lastChatText,
required this.customerName,
});
factory OffersUnreadChatDataModel.fromJson(Map<String, dynamic> json) {
return OffersUnreadChatDataModel(
reqTitle: json['reqTitle'],
reqTotal: json['reqTotal'],
requestID: json['requestID'],
senderUserID: json['senderUserID'],
unreadMessagesCount: json['unreadMessagesCount'],
lastChatTime: json['lastChatTime'],
lastChatText: json['lastChatText'],
customerName: json['customerName'],
);
}
}
// "reqTitle": "Spare_Parts / Ford / Edge / 2016",
// I/flutter (16941): "reqTotal": 1,
// I/flutter (16941): "requestID": 145,
// I/flutter (16941): "senderUserID": "b56bc6bd-e45c-4644-b7c4-08dd02460895",
// I/flutter (16941): "unreadMessagesCount": 0,
// I/flutter (16941): "lastChatTime": "2025-02-12T10:33:42.1433333",
// I/flutter (16941): "lastChatText": "I am accepting this offer.",
// I/flutter (16941): "customerName": "Faiz 100"

@ -66,6 +66,9 @@ class ServiceProvidersOffers {
RequestOfferStatusEnum? requestOfferStatusEnum;
int? offerCount;
int? requestId;
String? providerAddress;
String? providerLatitude;
String? providerLongitude;
List<ChatMessageModel>? chatMessages;
ServiceProvidersOffers({
@ -80,6 +83,9 @@ class ServiceProvidersOffers {
this.providerUserId,
this.createdOn,
this.requestOfferStatusEnum,
this.providerAddress,
this.providerLatitude,
this.providerLongitude,
});
ServiceProvidersOffers.fromJson(Map<String, dynamic> json, int? reqId) {
@ -93,6 +99,9 @@ class ServiceProvidersOffers {
requestId = reqId;
createdOn = json['createdOn'];
requestOfferStatusEnum = ((json['offerStatusLast']) as int).toRequestOfferStatusEnum();
providerAddress = json['address'] ?? "";
providerLatitude = json['latititude'] ?? "0.0";
providerLongitude = json['langitude'] ?? "0.0";
chatMessages = [];
}

@ -0,0 +1,158 @@
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/utils/enums.dart';
class ProviderOffersChatsModel {
int? id;
int? requestID;
int? serviceProviderID;
ServiceProviderModel? serviceProvider;
int? offerStatus;
RequestOfferStatusEnum? requestOfferStatusEnum;
String? offerStatusText;
String? comment;
String? customerName;
double? price;
String? serviceItem;
String? offeredItemCreatedBy;
String? offeredItemCreatedByName;
String? offeredItemCreatedOn;
String? reqOfferImages;
bool? isDeliveryAvailable;
String? createdOn;
ProviderOffersChatsModel({
this.id,
this.requestID,
this.serviceProviderID,
this.serviceProvider,
this.offerStatus,
this.requestOfferStatusEnum,
this.offerStatusText,
this.comment,
this.customerName,
this.price,
this.serviceItem,
this.offeredItemCreatedBy,
this.offeredItemCreatedByName,
this.offeredItemCreatedOn,
this.reqOfferImages,
this.isDeliveryAvailable,
this.createdOn,
});
ProviderOffersChatsModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestID = json['requestID'];
serviceProviderID = json['serviceProviderID'];
serviceProvider = json['serviceProvider'] != null ? ServiceProviderModel.fromJson(json['serviceProvider']) : null;
offerStatus = json['offerStatus'];
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();
offerStatusText = json['offerStatusText'];
comment = json['comment'];
customerName = json['customerName'];
price = json['price'];
serviceItem = json['serviceItem'];
offeredItemCreatedBy = json['offeredItemCreatedBy'];
offeredItemCreatedByName = json['offeredItemCreatedByName'];
offeredItemCreatedOn = json['offeredItemCreatedOn'];
reqOfferImages = json['reqOfferImages'];
isDeliveryAvailable = json['isDeliveryAvailable'];
createdOn = json['createdOn'];
}
}
class ServiceProviderModel {
int? providerId;
String? providerGUID;
String? firstName;
String? lastName;
String? name;
int? gender;
String? genderName;
String? mobileNo;
String? email;
bool? isEmailVerified;
bool? isCompleted;
int? city;
String? cityName;
int? country;
String? countryName;
int? accountStatus;
String? accountStatusText;
int? activityStatus;
String? activityStatusText;
String? bankName;
String? iBanNo;
bool? isActive;
String? subscriptionDate;
String? createdOn;
String? companyName;
String? currency;
String? branch;
bool? isChatted;
bool? isDealership;
ServiceProviderModel({
this.providerId,
this.providerGUID,
this.firstName,
this.lastName,
this.name,
this.gender,
this.genderName,
this.mobileNo,
this.email,
this.isEmailVerified,
this.isCompleted,
this.city,
this.cityName,
this.country,
this.countryName,
this.accountStatus,
this.accountStatusText,
this.activityStatus,
this.activityStatusText,
this.bankName,
this.iBanNo,
this.isActive,
this.subscriptionDate,
this.createdOn,
this.companyName,
this.currency,
this.branch,
this.isChatted,
this.isDealership,
});
ServiceProviderModel.fromJson(Map<String, dynamic> json) {
providerId = json['providerId'];
providerGUID = json['providerGUID'];
firstName = json['firstName'];
lastName = json['lastName'];
name = json['name'];
gender = json['gender'];
genderName = json['genderName'];
mobileNo = json['mobileNo'];
email = json['email'];
isEmailVerified = json['isEmailVerfied'];
isCompleted = json['isCompleted'];
city = json['city'];
cityName = json['cityName'];
country = json['country'];
countryName = json['countryName'];
accountStatus = json['accountStatus'];
accountStatusText = json['accountStatusText'];
activityStatus = json['activityStatus'];
activityStatusText = json['activityStatusText'];
bankName = json['bankName'];
iBanNo = json['iBanNo'];
isActive = json['isActive'];
subscriptionDate = json['subscriptionDate'];
createdOn = json['createdOn'];
companyName = json['companyName'];
currency = json['currency'];
branch = json['branch'];
isChatted = json['isChatted'];
isDealership = json['isDealership'];
}
}

@ -78,7 +78,7 @@ class ServiceModel {
: List<ItemData>.from(json["branchServiceItems"]!.map((x) => ItemData.fromJson(x))),
isExpandedOrSelected: false,
providerServiceId: 0,
providerServiceName: "",
providerServiceName: json['serviceName'] ?? "",
isHomeSelected: false,
isActive: json["isActive"] ?? true,
servicelocationInfo: CurrentLocationInfoModel(address: '', distanceToBranch: 0.0, homeChargesInCurrentService: 0.0, latitude: 0.0, longitude: 0.0),

@ -23,7 +23,7 @@ abstract class AdsRepo {
Future<List<SpecialServiceModel>> getSpecialServices({required int specialServiceType, required int cityId, required int countryId});
Future<GenericRespModel> createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew});
Future<GenericRespModel> createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew, required bool isExtendAdEditEnabled});
Future<List<AdDetailsModel>> getAllAds({
required bool isMyAds,
@ -33,13 +33,20 @@ abstract class AdsRepo {
int? page,
});
Future<List<AdDetailsModel>> getMyDraftAds({int? page});
Future<List<AdDetailsModel>> getExploreAdsBasedOnFilters({
List<String>? adIdsList,
List<String>? cityIdsList,
List<String>? vehicleModelYearIdsList,
List<String>? vehicleTypeIdsList,
List<String>? vehicleBrandIdsList,
List<String>? createdByRolesIdsList,
List<String>? vehicleAdConditionIdsList,
List<String>? vehicleAdCreatedDateList,
List<String>? mobileNumbersList,
int? startPriceDemand,
int? endPriceDemand,
int page,
bool isMyAds = false,
});
@ -120,9 +127,10 @@ class AdsRepoImp implements AdsRepo {
}
@override
Future<GenericRespModel> createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew}) async {
Future<GenericRespModel> createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew, required bool isExtendAdEditEnabled}) async {
List vehiclePostingImages = [];
log("isExtendAdEditEnabled: $isExtendAdEditEnabled");
log("adsCreationPayloadModel.ads!.adsDurationID,: ${adsCreationPayloadModel.ads!.adsDurationID}");
adsCreationPayloadModel.vehiclePosting!.vehiclePostingImages?.forEach((element) {
var imageMap = {
@ -195,7 +203,11 @@ class AdsRepoImp implements AdsRepo {
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
isCreateNew ? ApiConsts.vehicleAdsSingleStepCreate : ApiConsts.vehicleAdsSingleStepUpdate,
isExtendAdEditEnabled
? ApiConsts.vehicleAdsSingleStepUpdateExtend
: isCreateNew
? ApiConsts.vehicleAdsSingleStepCreate
: ApiConsts.vehicleAdsSingleStepUpdate,
postParams,
token: token,
);
@ -203,6 +215,27 @@ class AdsRepoImp implements AdsRepo {
return Future.value(adsGenericModel);
}
@override
Future<List<AdDetailsModel>> getMyDraftAds({int? page}) async {
var draftAdsParams = {
"PageSize": "30",
"PageIndex": page != null ? page.toString() : "0",
"userID": appState.getUser.data!.userInfo!.userId ?? "",
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),
ApiConsts.vehicleAdsGet,
queryParameters: draftAdsParams,
);
List<AdDetailsModel> vehicleAdsDetails = List.generate(
adsGenericModel.data.length,
(index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true, adsGenericModel.totalItemsCount ?? 1),
);
return vehicleAdsDetails;
}
@override
Future<List<AdDetailsModel>> getAllAds({
required isMyAds,
@ -251,23 +284,33 @@ class AdsRepoImp implements AdsRepo {
@override
Future<List<AdDetailsModel>> getExploreAdsBasedOnFilters({
List<String>? adIdsList,
List<String>? cityIdsList,
List<String>? vehicleModelYearIdsList,
List<String>? vehicleTypeIdsList,
List<String>? vehicleBrandIdsList,
List<String>? createdByRolesIdsList,
List<String>? vehicleAdConditionIdsList,
List<String>? vehicleAdCreatedDateList,
List<String>? mobileNumbersList,
int? startPriceDemand,
int? endPriceDemand,
int? page,
bool isMyAds = false,
}) async {
var parameters = {
"AdsIDs": adIdsList ?? [],
"CityIDs": cityIdsList ?? [],
"VehicleTypeIDs": vehicleTypeIdsList ?? [],
"VehicleBrandIDs": vehicleBrandIdsList ?? [],
"VehicleModelYearIDs": vehicleModelYearIdsList ?? [],
"CreatedByRoles": createdByRolesIdsList ?? [],
"AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS
"VehicleNew": vehicleAdConditionIdsList ?? [],
"CreatedOn": (vehicleAdCreatedDateList != null && vehicleAdCreatedDateList.isNotEmpty) ? vehicleAdCreatedDateList.first.toString() : "",
"VehicleNew": vehicleAdConditionIdsList != null && vehicleAdConditionIdsList.isNotEmpty ? vehicleAdConditionIdsList.first.toString() : null,
"CreatedOn": (vehicleAdCreatedDateList != null && vehicleAdCreatedDateList.isNotEmpty) ? vehicleAdCreatedDateList.first.toString() : null,
"MobileNumbers": mobileNumbersList ?? [],
"StartPriceDemand": startPriceDemand?.toString(),
"EndPriceDemand": endPriceDemand?.toString(),
"isActive": "true", //only Active ADS
"isExplore": (!isMyAds).toString(),
"PageSize": "30",
@ -280,6 +323,8 @@ class AdsRepoImp implements AdsRepo {
});
}
parameters.removeWhere((key, value) => value == null);
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),

@ -5,11 +5,10 @@ import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/requests_models/offers_model.dart';
import 'package:mc_common_app/models/requests_models/offers_unread_chat_model.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
import 'package:mc_common_app/models/requests_models/providers_offers_chat_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/utils/enums.dart';
@ -30,7 +29,9 @@ abstract class RequestRepo {
required List requestImages,
});
Future<List<OffersModel>> getOffersByRequest({required int requestId, int serviceProviderId = 0});
Future<List<ProviderOffersChatsModel>> getOffersChatByProvider({int requestId = 0, required int serviceProviderId});
Future<OffersUnreadChatModel> getOffersChatsUnreadList({required String userId});
Future<ProviderOffersModel> getOffersFromProvidersByRequest({required int requestId});
@ -38,6 +39,7 @@ abstract class RequestRepo {
Future<List<RequestModel>> getRequestsBasedOnFilters({
required int requestTypeId,
int requestId = 0,
int requestStatusId = 0,
int reqOfferStatus = 0,
int? cityId = 0,
@ -62,6 +64,7 @@ abstract class RequestRepo {
required int offerId,
required String offerPrice,
required bool isDeliveryAvailable,
required int offerDeliveryOption,
required String serviceItemName,
required String manufacturedByName,
required String manufacturedOn,
@ -146,6 +149,7 @@ class RequestRepoImp implements RequestRepo {
Future<List<RequestModel>> getRequestsBasedOnFilters({
required int requestTypeId,
int requestStatusId = 0,
int requestId = 0,
int reqOfferStatus = 0,
int? cityId = 0,
int? vehicleYearId = 0,
@ -275,7 +279,7 @@ class RequestRepoImp implements RequestRepo {
}
@override
Future<List<OffersModel>> getOffersByRequest({required int requestId, int serviceProviderId = 0}) async {
Future<List<ProviderOffersChatsModel>> getOffersChatByProvider({int requestId = 0, required int serviceProviderId}) async {
var queryParameters = {
"RequestID": requestId.toString(),
"ServiceProviderID": serviceProviderId.toString(),
@ -286,13 +290,29 @@ class RequestRepoImp implements RequestRepo {
queryParameters: queryParameters,
token: appState.getUser.data!.accessToken,
);
List<OffersModel> offersList = List.generate(
List<ProviderOffersChatsModel> offersList = List.generate(
genericRespModel.data.length,
(index) => OffersModel.fromJson(genericRespModel.data[index]),
(index) => ProviderOffersChatsModel.fromJson(genericRespModel.data[index]),
);
return offersList;
}
@override
Future<OffersUnreadChatModel> getOffersChatsUnreadList({required String userId}) async {
var queryParameters = {
"ReceiverUserID": userId.toString(),
};
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.reqChatUnreadGet,
queryParameters: queryParameters,
token: appState.getUser.data!.accessToken,
);
OffersUnreadChatModel offersUnreadChatModel = OffersUnreadChatModel.fromJson(genericRespModel.data);
return offersUnreadChatModel;
}
@override
Future<ProviderOffersModel> getOffersFromProvidersByRequest({required int requestId}) async {
final customerId = appState.getUser.data!.userInfo!.customerId;
@ -353,6 +373,7 @@ class RequestRepoImp implements RequestRepo {
required int offerId,
required String offerPrice,
required bool isDeliveryAvailable,
required int offerDeliveryOption,
required String serviceItemName,
required String manufacturedByName,
required String manufacturedOn,
@ -372,7 +393,8 @@ class RequestRepoImp implements RequestRepo {
"offeredItemCreatedByName": manufacturedByName.toString(),
"offeredItemCreatedOn": manufacturedOn.toString(),
"reqOfferImages": requestImages,
"isDeliveryAvailable": isDeliveryAvailable
"isDeliveryAvailable": isDeliveryAvailable,
"offerDeliveryOption": offerDeliveryOption
};
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),

@ -14,9 +14,13 @@ import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
abstract class ShippingRepo {
Future<List<ShippingRequestModel>> getSelfPickupRequestListByStatus({SelfPickupRequestStatusEnum? selfPickupRequestStatus, int? requestId});
Future<List<ShippingRequestModel>> getShippingRequestListByStatus({ShippingRequestStatusEnum? shippingStatusEnum, int? requestId});
Future<GenericRespModel> updateShippingRequestStatus({required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId, String? comment});
Future<GenericRespModel> updateSelfPickupRequestStatus({required SelfPickupRequestStatusEnum selfPickupStatusEnum, required int shippingRequestId, String? comment});
}
class ShippingRepoImp extends ShippingRepo {
@ -48,6 +52,31 @@ class ShippingRepoImp extends ShippingRepo {
return list;
}
@override
Future<List<ShippingRequestModel>> getSelfPickupRequestListByStatus({SelfPickupRequestStatusEnum? selfPickupRequestStatus, int? requestId}) async {
String token = appState.getUser.data!.accessToken ?? "";
Map queryParameters = {
"requestID": "${requestId ?? 0}",
"selfPickUpStatus": "${selfPickupRequestStatus != null ? selfPickupRequestStatus.getIdFromSelfPickupStatusEnum() : -1}", // -1 to get all requests
};
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.selfPickupRequestStatusGet,
queryParameters,
token: token,
);
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<ShippingRequestModel> list = List.generate(genericRespModel.data.length, (index) => ShippingRequestModel.fromJson(genericRespModel.data[index]));
return list;
}
@override
Future<GenericRespModel> updateShippingRequestStatus({required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId, String? comment}) async {
String token = appState.getUser.data!.accessToken ?? "";
@ -70,4 +99,27 @@ class ShippingRepoImp extends ShippingRepo {
return genericRespModel;
}
@override
Future<GenericRespModel> updateSelfPickupRequestStatus({required SelfPickupRequestStatusEnum selfPickupStatusEnum, required int shippingRequestId, String? comment}) async {
String token = appState.getUser.data!.accessToken ?? "";
Map<String, String> queryParameters = {
"id": "$shippingRequestId",
"selfPickUpStatus": "${selfPickupStatusEnum.getIdFromSelfPickupStatusEnum()}",
"comment": comment ?? "",
};
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.selfPickupRequestStatusUpdate,
queryParameters,
token: token,
);
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
}
return genericRespModel;
}
}

@ -19,6 +19,8 @@ class AppEnums {
static const int conditionEnumId = -1; // to get the Condition Filter Enums
static const int serviceDeliveryTypeEnumId = 24; // to get the serviceDeliveryType
static const int shippingStatusEnumId = 30; // to get the Shipping Filter Enums
static const int selfPickupStatusEnumId = 33; // to get the SelfPickup Status Enums
static const int deliveryOptionEnumId = 32; // to get the SelfPickup Status Enums
}
enum DashboardRouteEnum {
@ -223,10 +225,9 @@ enum SubscriptionActionTypeEnum {
ads,
users,
branches,
subscription
subscription,
}
enum ShippingRequestStatusEnum {
allRequests,
pending,
@ -236,10 +237,22 @@ enum ShippingRequestStatusEnum {
delivered,
}
enum SelfPickupRequestStatusEnum {
allRequests,
preparingToCollect,
readyToCollect,
collected,
}
enum RequestDeliveryOptionEnum {
none,
delivery,
selfPickup,
both,
}
enum InviteTypeEnum {
whatsapp,
sms,
email,
}

@ -80,9 +80,11 @@ class AdVM extends BaseVM {
String adPhoneNumberError = "";
// Edit Variables Amir
bool isExtendAdEditEnabled = false;
bool isAdEditEnabled = false;
AdDetailsModel? previousAdDetails;
List<AdDetailsModel> myDraftAds = [];
List<AdDetailsModel> exploreAds = [];
List<AdDetailsModel> exploreAdsFilteredList = [];
List<AdDetailsModel> myAdsFilteredList = [];
@ -179,6 +181,7 @@ class AdVM extends BaseVM {
if (myAdsStatusEnums.isEmpty) {
myAdsStatusEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.myAdsFilterEnumId);
}
if (vehicleBrandsForFilters.isEmpty) {
vehicleBrandsForFilters = await commonRepo.getVehicleBrands(vehicleTypeId: -1); // to get all the brands
}
@ -340,6 +343,44 @@ class AdVM extends BaseVM {
setState(ViewState.idle);
}
bool hasMoreDataForMyDraftsAds = false;
int pageIndexForMyDrafts = 1;
Future<void> getMyDraftAds() async {
pageIndexForMyAds = 1;
hasMoreDataForMyAds = true;
setState(ViewState.busy);
myDraftAds = await adsRepo.getMyDraftAds();
notifyListeners();
setState(ViewState.idle);
}
Future<void> fetchMoreDraftAds({required AdPostStatus adsStatus}) async {
if (isLoadingMore) return;
hasMoreDataForMyDraftsAds = true;
isLoadingMore = true;
notifyListeners();
try {
final List<AdDetailsModel> newAds = await adsRepo.getMyDraftAds(page: pageIndexForMyDrafts);
if (newAds.isEmpty) {
hasMoreDataForMyDraftsAds = false;
} else {
myDraftAds.addAll(newAds);
pageIndexForMyDrafts++;
if (myDraftAds.length < myDraftAds.last.totalItemsCount!) {
hasMoreDataForMyDraftsAds = true;
} else {
hasMoreDataForMyDraftsAds = false;
}
}
} catch (error) {
isLoadingMore = false;
Utils.showToast(error.toString());
}
isLoadingMore = false;
notifyListeners();
}
Future<void> getMyAds() async {
pageIndexForMyAds = 1;
hasMoreDataForMyAds = true;
@ -418,7 +459,7 @@ class AdVM extends BaseVM {
Utils.showToast(respModel.message ?? LocaleKeys.adMarkedAsSold.tr());
updateIsExploreAds(false);
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.sold); //pending for review
navigateReplaceWithName(context, AppRoutes.dashboard);
navigateReplaceWithName(context, AppRoutes.dashboard, arguments: DashboardRouteEnum.fromAdsSubmit);
}
Future<void> deleteMyAd(BuildContext context, {required int adId}) async {
@ -431,10 +472,10 @@ class AdVM extends BaseVM {
return;
}
Utils.hideLoading(context);
Utils.showToast(LocaleKeys.AdDeletedSuccessfully.tr());
Utils.showToast(LocaleKeys.adDeletedSuccessfully.tr());
updateIsExploreAds(false);
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.active); //pending for review
navigateReplaceWithName(context, AppRoutes.dashboard);
navigateReplaceWithName(context, AppRoutes.dashboard, arguments: DashboardRouteEnum.fromAdsSubmit);
}
Future<void> cancelMyAdReservation(BuildContext context, {required int adId, required String reason}) async {
@ -450,7 +491,7 @@ class AdVM extends BaseVM {
Utils.showToast(LocaleKeys.yourReservationCancelled.tr());
updateIsExploreAds(false);
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.active); //pending for review
navigateReplaceWithName(context, AppRoutes.dashboard);
navigateReplaceWithName(context, AppRoutes.dashboard, arguments: DashboardRouteEnum.fromAdsSubmit);
}
Future<void> deactivateTheAd(BuildContext context, {required int adId, String? comment}) async {
@ -467,7 +508,7 @@ class AdVM extends BaseVM {
updateDeactivateAdReasonDescription('');
updateIsExploreAds(false);
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.cancelled); //Cacncelled
navigateReplaceWithName(context, AppRoutes.dashboard);
navigateReplaceWithName(context, AppRoutes.dashboard, arguments: DashboardRouteEnum.fromAdsSubmit);
}
bool isFetchingLists = false;
@ -570,7 +611,16 @@ class AdVM extends BaseVM {
SelectionModel vehicleTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateSelectionVehicleTypeId(SelectionModel id) async {
void updateSelectionVehicleTypeId(SelectionModel id, {bool isForSearch = false}) async {
if (isForSearch) {
VehicleTypeModel vehicleTypeModel = vehicleTypesForFilters.firstWhere((element) => element.id == id.selectedId);
DropValue vehicleTypeVValue = DropValue(vehicleTypeModel.id ?? 0, vehicleTypeModel.vehicleTypeName ?? "", "");
if (!ifAlreadyExist(list: vehicleTypesAdSearchHistory, value: vehicleTypeVValue)) {
addToVehicleTypesAdSearchHistory(value: vehicleTypeVValue);
}
notifyListeners();
return;
}
vehicleTypeId = id;
await getVehicleBrandsByVehicleTypeId();
notifyListeners();
@ -580,7 +630,7 @@ class AdVM extends BaseVM {
Future<void> updateSelectionVehicleBrandId(SelectionModel id, {bool isForSearch = false}) async {
if (isForSearch) {
VehicleBrandsModel brand = vehicleBrands.firstWhere((element) => element.id == id.selectedId);
VehicleBrandsModel brand = vehicleBrandsForFilters.firstWhere((element) => element.id == id.selectedId);
DropValue brandValue = DropValue(brand.id ?? 0, brand.vehicleBrandDescription ?? "", "");
if (!ifAlreadyExist(list: vehicleBrandsAdSearchHistory, value: brandValue)) {
addToVehicleBrandsAdSearchHistory(value: brandValue);
@ -1218,6 +1268,7 @@ class AdVM extends BaseVM {
return;
}
isAdEditEnabled = false;
isExtendAdEditEnabled = false;
Utils.hideLoading(context);
currentProgressStep = AdCreationSteps.vehicleDetails;
resetValues();
@ -1544,6 +1595,7 @@ class AdVM extends BaseVM {
Utils.showToast("${LocaleKeys.error.tr()}: ${genericRespModel.message}");
} else {
resetSpecialServiceBottomSheet();
pop(context);
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.ads);
}
} catch (e) {
@ -1559,18 +1611,19 @@ class AdVM extends BaseVM {
for (var value in specialServiceCards) {
adsSelectedServices.add(value.serviceSelectedId!.selectedId);
}
log("selectionDurationStartDate: $selectionDurationStartDate");
if (selectionDurationStartDate.isNotEmpty) {
// Define the format of the input string
DateTime startDate = DateTime.parse(
"${selectionDurationStartDate.split('-')[2]}-${selectionDurationStartDate.split('-')[1]}-${selectionDurationStartDate.split('-')[0]}",
);
DateTime startDate = DateHelper.parseStringToDate(selectionDurationStartDate);
DateTime currentDate = DateTime.now();
if (startDate.isBefore(currentDate)) {
selectionDurationStartDate = DateHelper.formatAsYearMonthDay(currentDate);
}
}
log("selectionDurationStartDate: $selectionDurationStartDate");
Ads ads = Ads(
id: isAdEditEnabled ? previousAdDetails!.id : null,
adsDurationID: vehicleAdDurationId.selectedId == -1 ? 0 : vehicleAdDurationId.selectedId,
@ -1629,7 +1682,8 @@ class AdVM extends BaseVM {
);
AdsCreationPayloadModel adsCreationPayloadModel = AdsCreationPayloadModel(ads: ads, vehiclePosting: vehiclePosting);
GenericRespModel respModel = await adsRepo.createOrUpdateAd(adsCreationPayloadModel: adsCreationPayloadModel, isCreateNew: !isAdEditEnabled);
GenericRespModel respModel = await adsRepo.createOrUpdateAd(adsCreationPayloadModel: adsCreationPayloadModel, isCreateNew: !isAdEditEnabled, isExtendAdEditEnabled: isExtendAdEditEnabled);
Utils.showToast(respModel.message.toString());
@ -1685,6 +1739,8 @@ class AdVM extends BaseVM {
List<EnumsModel> vehicleConditionsEnum = [];
List<EnumsModel> adOwnerEnumsFilter = [];
List<VehicleTypeModel> vehicleTypesForFilters = [];
Future<void> populateDataForAdFilter() async {
setState(ViewState.busy);
pageIndexForExploreAds = 1;
@ -1701,6 +1757,10 @@ class AdVM extends BaseVM {
vehicleConditionsEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.conditionEnumId);
}
if (vehicleTypesForFilters.isEmpty) {
vehicleTypesForFilters = await commonRepo.getVehicleTypes();
}
if (vehicleBrandsForFilters.isEmpty) {
vehicleBrandsForFilters = await commonRepo.getVehicleBrands(vehicleTypeId: -1); // to get all the brands
}
@ -1725,6 +1785,127 @@ class AdVM extends BaseVM {
return false;
}
ifAlreadyExistForStringValue({required List<String> list, required String value}) {
int index = list.indexWhere((element) {
return element == value;
});
if (index != -1) {
return true;
}
return false;
}
//Ad Demand Price
String adFilterDemandStartPrice = "";
void updateAdFilterDemandStartPrice(String title) {
adFilterDemandStartPrice = title;
}
String adFilterDemandEndPrice = "";
void updateAdFilterDemandEndPrice(String title) {
adFilterDemandEndPrice = title;
}
// Ads Mobile Number Filter
String currentAdMobilePhoneFilter = '';
void onCurrentAdMobilePhoneFilterChanged(var value) {
currentAdMobilePhoneFilter = value;
notifyListeners();
}
List<String> adFilterMobilePhoneSearchHistory = [];
void removeAdFilterMobilePhoneSearchHistory({bool isClear = false, required int index}) {
if (isClear) {
adFilterMobilePhoneSearchHistory.clear();
notifyListeners();
return;
}
adFilterMobilePhoneSearchHistory.removeAt(index);
if (adFilterMobilePhoneSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter - 1);
}
notifyListeners();
}
void addAdFilterMobilePhoneSearchHistory({required String value}) {
if (adFilterMobilePhoneSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter + 1);
}
if (!ifAlreadyExistForStringValue(list: adFilterMobilePhoneSearchHistory, value: value)) {
adFilterMobilePhoneSearchHistory.add(value);
currentAdMobilePhoneFilter = "";
}
notifyListeners();
}
// Ad ID
List<String> adsFilterAdIDSearchHistory = [];
String currentAdIDFilter = '';
void onCurrentAdIDFilterChanged(var value) {
currentAdIDFilter = value;
notifyListeners();
}
void removeAdFilterAdIDSearchHistory({bool isClear = false, required int index}) {
if (isClear) {
adsFilterAdIDSearchHistory.clear();
notifyListeners();
return;
}
adsFilterAdIDSearchHistory.removeAt(index);
if (adsFilterAdIDSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter - 1);
}
notifyListeners();
}
void addAdFilterAdIDSearchHistory({required String value}) {
if (adsFilterAdIDSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter + 1);
}
if (!ifAlreadyExistForStringValue(list: adsFilterAdIDSearchHistory, value: value)) {
adsFilterAdIDSearchHistory.add(value);
currentAdIDFilter = "";
}
notifyListeners();
}
//TYPES
List<DropValue> vehicleTypesAdSearchHistory = [];
void removeVehicleTypesAdSearchHistory({bool isClear = false, required int index}) {
if (isClear) {
vehicleTypesAdSearchHistory.clear();
notifyListeners();
return;
}
vehicleTypesAdSearchHistory.removeAt(index);
if (vehicleTypesAdSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter - 1);
}
notifyListeners();
}
void addToVehicleTypesAdSearchHistory({required DropValue value}) {
if (vehicleTypesAdSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter + 1);
}
vehicleTypesAdSearchHistory.add(value);
notifyListeners();
}
//BRANDS
List<DropValue> vehicleBrandsAdSearchHistory = [];
@ -1861,11 +2042,14 @@ class AdVM extends BaseVM {
if (vehicleAdCreatedDateSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter + 1);
}
if (vehicleAdCreatedDateSearchHistory.isEmpty) {
vehicleAdCreatedDateSearchHistory.add(value);
} else {
vehicleAdCreatedDateSearchHistory.first = value;
}
vehicleAdCreatedDateSearchHistory.add(value);
// if (vehicleAdCreatedDateSearchHistory.isEmpty) {
// vehicleAdCreatedDateSearchHistory.add(value);
// } else {
// vehicleAdCreatedDateSearchHistory.first = value;
// }
notifyListeners();
}
@ -1902,12 +2086,16 @@ class AdVM extends BaseVM {
}
void clearAdsFilters() {
vehicleBrandsAdSearchHistory.clear();
adsFilterAdIDSearchHistory.clear();
vehicleTypesAdSearchHistory.clear();
vehicleAdOwnerSearchHistory.clear();
vehicleLocationAdSearchHistory.clear();
vehicleYearAdSearchHistory.clear();
vehicleAdCreatedDateSearchHistory.clear();
vehicleAdConditionSearchHistory.clear();
adFilterMobilePhoneSearchHistory.clear();
adFilterDemandStartPrice = '';
adFilterDemandEndPrice = '';
adsFiltersCounter = 0;
getExploreAds();
notifyListeners();
@ -1928,12 +2116,25 @@ class AdVM extends BaseVM {
exploreAdsFilteredList.clear();
setState(ViewState.busy);
}
List<String> adIdsList = [];
if (adsFilterAdIDSearchHistory.isNotEmpty) {
for (var element in adsFilterAdIDSearchHistory) {
adIdsList.add(element.toString());
}
}
List<String> cityIdsList = [];
if (vehicleLocationAdSearchHistory.isNotEmpty) {
for (var element in vehicleLocationAdSearchHistory) {
cityIdsList.add(element.id.toString());
}
}
List<String> typesIdsList = [];
if (vehicleTypesAdSearchHistory.isNotEmpty) {
for (var element in vehicleTypesAdSearchHistory) {
typesIdsList.add(element.id.toString());
}
}
List<String> brandsIdsList = [];
if (vehicleBrandsAdSearchHistory.isNotEmpty) {
for (var element in vehicleBrandsAdSearchHistory) {
@ -1969,13 +2170,43 @@ class AdVM extends BaseVM {
}
}
List<String> phoneNumbersList = [];
if (adFilterMobilePhoneSearchHistory.isNotEmpty) {
for (var element in adFilterMobilePhoneSearchHistory) {
phoneNumbersList.add(element.toString());
}
}
int? startPriceDemand;
int? endPriceDemand;
if (adFilterDemandStartPrice.isNotEmpty) {
try {
startPriceDemand = int.parse(adFilterDemandStartPrice);
} catch (e) {
startPriceDemand = null;
}
}
if (adFilterDemandEndPrice.isNotEmpty) {
try {
endPriceDemand = int.parse(adFilterDemandEndPrice);
} catch (e) {
endPriceDemand = null;
}
}
List<AdDetailsModel> list = await adsRepo.getExploreAdsBasedOnFilters(
adIdsList: adIdsList,
cityIdsList: cityIdsList,
createdByRolesIdsList: adOwnerIdsList,
vehicleTypeIdsList: typesIdsList,
vehicleBrandIdsList: brandsIdsList,
vehicleModelYearIdsList: vehicleYearIdsList,
vehicleAdConditionIdsList: conditionsIdsList,
vehicleAdCreatedDateList: createdDatesList,
mobileNumbersList: phoneNumbersList,
startPriceDemand: startPriceDemand,
endPriceDemand: endPriceDemand,
page: pageIndex,
);
@ -1992,6 +2223,7 @@ class AdVM extends BaseVM {
void onEditUpdateAdPressed({required BuildContext context, required AdDetailsModel previousDetails, required bool isFromExtendAd}) {
isAdEditEnabled = true;
isExtendAdEditEnabled = isFromExtendAd;
previousAdDetails = previousDetails;
autoFillSelectedVehicleType();
autoFillSelectedVehicleAdsDuration();
@ -1999,22 +2231,25 @@ class AdVM extends BaseVM {
navigateWithName(context, AppRoutes.selectAdTypeView, arguments: [AppState().currentAppType == AppType.provider, isFromExtendAd, previousDetails.id]);
}
Future<bool> createAdExtensionOrder(BuildContext context, {required int adId, required int adsDurationId}) async {
Future<List<bool>> createAdExtensionOrder(BuildContext context, {required int adId, required int adsDurationId}) async {
// [0] Means API response [1] means isPaymentRequired
try {
Utils.showLoading(context);
GenericRespModel respModel = await adsRepo.createAdExtensionOrder(adID: adId, adsDurationId: adsDurationId, specialServiceIds: []);
if (respModel.messageStatus != 1) {
if (respModel.messageStatus != 1 && respModel.data == null) {
Utils.hideLoading(context);
Utils.showToast(respModel.message ?? LocaleKeys.somethingWrong.tr());
return false;
return [false, false]; // api fail response
}
Utils.hideLoading(context);
return respModel.messageStatus == 1;
final AdExtensionOrderResponseModel adExtensionOrderResponseModel = AdExtensionOrderResponseModel.fromJson(respModel.data);
return [true, adExtensionOrderResponseModel.isPaymentRequired];
} catch (e) {
Utils.hideLoading(context);
Utils.showToast(e.toString());
return false;
return [false, false]; // api fail response
}
}

@ -307,7 +307,9 @@ class AppointmentsVM extends BaseVM {
}
void removeServiceInCurrentAppointment(int index) {
int serviceId = servicesInCurrentAppointment.elementAt(index).serviceProviderServiceId ?? -1;
int serviceId = servicesInCurrentAppointment
.elementAt(index)
.serviceProviderServiceId ?? -1;
allSelectedItemsInAppointments.removeWhere((element) => element.serviceProviderServiceId == serviceId);
servicesInCurrentAppointment[index].serviceItems!.clear();
servicesInCurrentAppointment.removeAt(index);
@ -370,13 +372,12 @@ class AppointmentsVM extends BaseVM {
if (shouldPopulateUpcoming) {
myUpComingAppointments = myFilteredAppointmentsForProvider
.where((element) =>
(element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
(element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.toList();
log("myUpComingAppointments: ${myUpComingAppointments.length}");
}
log("myUpComingAppointments: ${myUpComingAppointments.length}");
if (isNeedCustomerFilter) {
myFilteredAppointmentsForProvider = findAppointmentsBasedOnCustomers(myFilteredAppointmentsForProvider);
}
@ -391,17 +392,16 @@ class AppointmentsVM extends BaseVM {
}
int index = appointmentsFilterOptions.indexWhere((element) => element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum());
appointmentsFilterOptions[index].isSelected = true;
isAppointmentLoading = true;
notifyListeners();
myFilteredAppointmentsForCustomers = await getAppointmentsBasedOnFiltersForCustomer();
myFilteredAppointmentsForCustomers = await getAppointmentsBasedOnFiltersForCustomer(appointmentStatusEnum: appointmentStatusEnum);
isAppointmentLoading = false;
if (shouldPopulateUpcoming) {
myUpComingAppointments = myFilteredAppointmentsForCustomers
.where((element) =>
(element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
(element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.toList();
}
@ -416,14 +416,19 @@ class AppointmentsVM extends BaseVM {
List<AppointmentListModel> groupedList = uniqueCustomerIDs.map((id) {
List<AppointmentListModel> list = appointments.where((item) => item.customerID == id).toList();
list.sort((a, b) => DateHelper.parseStringToDate(DateHelper.formatDateT(b.appointmentDate!))
.millisecondsSinceEpoch
.compareTo(DateHelper.parseStringToDate(DateHelper.formatDateT(a.appointmentDate!)).millisecondsSinceEpoch));
list.sort((a, b) =>
DateHelper
.parseStringToDate(DateHelper.formatDateT(b.appointmentDate!))
.millisecondsSinceEpoch
.compareTo(DateHelper
.parseStringToDate(DateHelper.formatDateT(a.appointmentDate!))
.millisecondsSinceEpoch));
AppointmentListModel model = list.first;
model.customerAppointmentList = list;
return model;
}).toList();
return groupedList;
}
@ -502,7 +507,7 @@ class AppointmentsVM extends BaseVM {
void updateCheckBoxInMergeRequest(int currentIndex) {
myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected =
!(myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false);
!(myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false);
int count = countSelected(myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList ?? []);
if (count > 1) {
@ -514,7 +519,10 @@ class AppointmentsVM extends BaseVM {
}
int countSelected(List<AppointmentListModel> appointments) {
return appointments.where((appointment) => appointment.isSelected == true).toList().length;
return appointments
.where((appointment) => appointment.isSelected == true)
.toList()
.length;
}
updateSelectedAppointmentDate({required int dateIndex, required int scheduleIndex}) {
@ -690,13 +698,14 @@ class AppointmentsVM extends BaseVM {
Column(
children: List.generate(
selectedService.serviceItems!.length,
(index) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"${selectedService.serviceItems![index].price} ${LocaleKeys.sar.tr()}".toText(fontSize: 12, isBold: true),
],
),
(index) =>
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"${selectedService.serviceItems![index].price} ${LocaleKeys.sar.tr()}".toText(fontSize: 12, isBold: true),
],
),
),
),
Row(
@ -915,7 +924,10 @@ class AppointmentsVM extends BaseVM {
return InfoBottomSheet(
title: LocaleKeys.reportComplaint.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
padding: EdgeInsets.only(bottom: MediaQuery
.of(context)
.viewInsets
.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1250,9 +1262,6 @@ class AppointmentsVM extends BaseVM {
ifAlreadyExistForStringValue({required List<String> list, required String value}) {
int index = list.indexWhere((element) {
log("element: $element");
log("value: $value");
return element == value;
});
@ -1814,7 +1823,7 @@ class AppointmentsVM extends BaseVM {
notifyListeners();
}
Future<List<AppointmentListModel>> getAppointmentsBasedOnFiltersForCustomer() async {
Future<List<AppointmentListModel>> getAppointmentsBasedOnFiltersForCustomer({required AppointmentStatusEnum appointmentStatusEnum}) async {
try {
setState(ViewState.busy);
List<String> providersIdsList = [];
@ -1854,6 +1863,7 @@ class AppointmentsVM extends BaseVM {
serviceIdsList: servicesIdsList,
itemIdsList: itemIdsList,
branchIdsList: branchesIdsList,
appointmentStatusEnum: appointmentStatusEnum,
);
setState(ViewState.idle);

@ -362,6 +362,7 @@ class ChatVM extends BaseVM {
required int requestId,
required String offerPrice,
required bool isDeliveryAvailable,
required RequestDeliveryOptionEnum offerDeliveryOption,
required String serviceItemName,
required String manufacturedByName,
required String manufacturedOn,
@ -385,10 +386,11 @@ class ChatVM extends BaseVM {
"RequestID": requestId,
"Price": double.parse(offerPrice),
"IsDeliveryAvailable": isDeliveryAvailable,
"OfferDeliveryOption": offerDeliveryOption.getIdRequestDeliveryOptionEnum(),
"ServiceItem": serviceItemName,
"ReqOfferImages": offerImages,
"OfferedItemCreatedByName": manufacturedByName,
"OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime
// "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime
"ServiceProviderID": providerId,
"OfferStatus": RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
"Comment": message,
@ -651,6 +653,8 @@ class ChatVM extends BaseVM {
required int serviceProviderID,
required int requestOfferID,
required String offerPrice,
required bool isDeliveryAvailable,
required RequestDeliveryOptionEnum offerDeliveryOption,
required String serviceItemName,
required String manufacturedByName,
required String manufacturedOn,
@ -675,6 +679,9 @@ class ChatVM extends BaseVM {
"Price": double.parse(offerPrice),
"ServiceItem": serviceItemName,
"OfferedItemCreatedByName": manufacturedByName,
"IsDeliveryAvailable": isDeliveryAvailable,
"OfferDeliveryOption": offerDeliveryOption.getIdRequestDeliveryOptionEnum(),
// We have commented this because Backend Team is using their server time for this. It was the time offer created by
// "OfferedItemCreatedOn": manufacturedOn,
"ServiceProviderID": serviceProviderID,
"OfferStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum(),

@ -64,10 +64,11 @@ class DashboardVmCustomer extends BaseVM {
RequestsVM requestsVM = Provider.of<RequestsVM>(context, listen: false);
ChatVM chatVM = Provider.of<ChatVM>(context, listen: false);
appointmentsVM.populateBranchesFilterList();
appointmentsVM.populateAppointmentsFilterList();
adVM.populateAdsFilterList();
requestsVM.populateDataForRequestsFilter();
await appointmentsVM.populateBranchesFilterList();
await appointmentsVM.populateAppointmentsFilterList();
await adVM.populateAdsFilterList();
await requestsVM.populateDataForRequestsFilter();
await requestsVM.populateDeliveryOptionEnums();
await appointmentsVM.applyFilterOnAppointmentsVMForCustomers(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, shouldPopulateUpcoming: true);
await appointmentsVM.applyFilterOnBranches(index: 0); // to get all branches!
await appointmentsVM.getMyRecentBranches(); // to get my recent branches

@ -79,20 +79,23 @@ class DashboardVMProvider extends BaseVM {
final serviceVM = context.read<ServiceVM>();
final adVM = context.read<AdVM>();
final subscriptionsVM = context.read<SubscriptionsVM>();
requestsVM.populateDataForRequestsFilter();
appointmentVM.populateAppointmentsFilterList();
shippingManagementVM.populateShippingRequestFilterList();
await requestsVM.populateDataForRequestsFilter();
await requestsVM.populateDeliveryOptionEnums();
await appointmentVM.populateAppointmentsFilterList();
await adVM.populateAdsFilterList();
await serviceVM.populateBranchServiceFilters();
await serviceVM.getBranchAndServices();
await requestsVM.getRequests();
await subscriptionsVM.getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
if (dashboardRouteEnum != DashboardRouteEnum.fromAdsPayment && dashboardRouteEnum != DashboardRouteEnum.fromAdsSubmit) {
await adVM.getMyAds();
await adVM.getExploreAds();
}
await shippingManagementVM.populateShippingRequestFilterList();
await shippingManagementVM.populateSelfPickupRequestFilterList();
await appointmentVM.applyFilterOnAppointmentsVMForProviders(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, shouldPopulateUpcoming: true);
adVM.populateAdsFilterList();
await requestsVM.getRequests();
await subscriptionsVM.getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
await adVM.getVehicleTypes();
await adVM.getVehicleAdsDuration();
await chatVM.buildHubConnection(context);

@ -217,7 +217,7 @@ class PaymentVM extends ChangeNotifier {
context.read<DashboardVmCustomer>().onNavbarTapped(1);
}
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
}
Future<void> onVisaCardSelected(BuildContext context, PaymentTypes paymentType) async {
currentPaymentType = paymentType;

@ -18,7 +18,9 @@ import 'package:mc_common_app/models/general_models/enums_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/requests_models/offers_model.dart';
import 'package:mc_common_app/models/requests_models/offers_unread_chat_model.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
import 'package:mc_common_app/models/requests_models/providers_offers_chat_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/repositories/common_repo.dart';
import 'package:mc_common_app/repositories/request_repo.dart';
@ -84,6 +86,27 @@ class RequestsVM extends BaseVM {
notifyListeners();
}
RequestDeliveryOptionEnum? selectedDeliveryOptionEnum;
void updateSelectedDeliveryOptionEnum(RequestDeliveryOptionEnum value) {
for (var value in deliveryOptionsForReviewRequest) {
value.isSelected = false;
}
int index = deliveryOptionsForReviewRequest.indexWhere((element) => element.id.toRequestDeliveryOptionEnum() == value);
deliveryOptionsForReviewRequest[index].isSelected = true;
selectedDeliveryOptionEnum = value;
selectedDeliveryOptionEnumError = "";
notifyListeners();
}
String selectedDeliveryOptionEnumError = "";
void updateSelectedDeliveryOptionEnumError(var value) {
selectedDeliveryOptionEnumError = value;
notifyListeners();
}
Future<void> getRequests({bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
applyFilterOnRequestsVM(requestsTypeEnum: RequestsTypeEnum.specialCarRequest);
@ -784,32 +807,32 @@ class RequestsVM extends BaseVM {
});
try {
GenericRespModel respModel = await requestRepo.createRequest(
requestTypeId: requestTypeId.selectedId,
vehicleTypeId: vehicleTypeId.selectedId,
brand: brand,
model: model,
year: vehicleYearId.selectedOption,
isNew: vehicleConditionId.selectedId == 1,
// 1 for new, 2 for Used
countryID: vehicleCountryId.selectedId,
cityID: vehicleCityId.selectedId,
price: price.isEmpty ? "1.0" : price,
description: description,
address: address,
isSpecialServiceNeeded: false,
requestImages: requestImages,
);
Utils.hideLoading(context);
if (respModel.messageStatus == 1) {
log("requestTypeId.selectedId.toRequestTypeEnum(): ${requestTypeId.selectedId.toRequestTypeEnum()}");
Utils.showToast(LocaleKeys.requestSuccessfullyCreated.tr());
Navigator.pop(context);
await applyFilterOnRequestsVM(requestsTypeEnum: requestTypeId.selectedId.toRequestTypeEnum());
resetRequestCreationForm();
} else {
Utils.showToast(respModel.message.toString());
}
GenericRespModel respModel = await requestRepo.createRequest(
requestTypeId: requestTypeId.selectedId,
vehicleTypeId: vehicleTypeId.selectedId,
brand: brand,
model: model,
year: vehicleYearId.selectedOption,
isNew: vehicleConditionId.selectedId == 1,
// 1 for new, 2 for Used
countryID: vehicleCountryId.selectedId,
cityID: vehicleCityId.selectedId,
price: price.isEmpty ? "1.0" : price,
description: description,
address: address,
isSpecialServiceNeeded: false,
requestImages: requestImages,
);
Utils.hideLoading(context);
if (respModel.messageStatus == 1) {
log("requestTypeId.selectedId.toRequestTypeEnum(): ${requestTypeId.selectedId.toRequestTypeEnum()}");
Utils.showToast(LocaleKeys.requestSuccessfullyCreated.tr());
Navigator.pop(context);
await applyFilterOnRequestsVM(requestsTypeEnum: requestTypeId.selectedId.toRequestTypeEnum());
resetRequestCreationForm();
} else {
Utils.showToast(respModel.message.toString());
}
} catch (e, s) {
Utils.hideLoading(context);
log(e.toString());
@ -842,20 +865,43 @@ class RequestsVM extends BaseVM {
return isValid;
}
Future<List<OffersModel>> getOffersByRequest({required int requestId, required BuildContext context}) async {
Future<void> getOffersChatByProvider({required int providerId, required BuildContext context}) async {
List<ProviderOffersChatsModel> providerOffersChatsList = [];
try {
Utils.showLoading(context);
List<OffersModel> respModel = await requestRepo.getOffersByRequest(requestId: requestId);
List<ProviderOffersChatsModel> respModel = await requestRepo.getOffersChatByProvider(serviceProviderId: providerId);
Utils.hideLoading(context);
return respModel;
providerOffersChatsList.clear();
providerOffersChatsList = respModel;
notifyListeners();
} catch (e) {
Utils.showToast(e.toString());
Utils.hideLoading(context);
return [];
return;
}
}
OffersUnreadChatModel? offersUnreadChatModel;
Future<void> getOffersChatsUnreadList({required String userId, required BuildContext context}) async {
try {
Utils.showLoading(context);
OffersUnreadChatModel respModel = await requestRepo.getOffersChatsUnreadList(userId: userId);
Utils.hideLoading(context);
offersUnreadChatModel = respModel;
log("offersUnreadChatModel: ${offersUnreadChatModel!.reqChatUnread.length}");
notifyListeners();
} catch (e) {
logger.e(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
return;
}
}
String offerPriceError = "";
String offerDescriptionError = "";
String offerPrice = "";
@ -902,6 +948,47 @@ class RequestsVM extends BaseVM {
}
}
String deliveryOptionSelectionError = "";
List<EnumsModel> myDeliveryOptionsEnum = [];
List<FilterListModel> myDeliveryOptionsFilterOptions = [];
Future<void> populateDeliveryOptionEnums() async {
if (myDeliveryOptionsFilterOptions.isNotEmpty) return;
try {
notifyListeners();
myDeliveryOptionsEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.deliveryOptionEnumId);
for (int i = 0; i < myDeliveryOptionsEnum.length; i++) {
myDeliveryOptionsFilterOptions.add(FilterListModel(title: myDeliveryOptionsEnum[i].enumValueStr, isSelected: false, id: myDeliveryOptionsEnum[i].enumValue));
}
notifyListeners();
} catch (e) {
logger.e(e.toString());
}
}
List<FilterListModel> deliveryOptionsForReviewRequest = [];
void populateRequestDeliveryOptions() {
deliveryOptionsForReviewRequest.clear();
deliveryOptionsForReviewRequest.addAll(myDeliveryOptionsFilterOptions.where((element) => element.id != RequestDeliveryOptionEnum.both.getIdRequestDeliveryOptionEnum()).toList());
notifyListeners();
}
Future<void> applyFilterOnDeliveryOption({required RequestDeliveryOptionEnum requestDeliveryOptionEnum}) async {
if (myDeliveryOptionsFilterOptions.isEmpty) return;
for (var value in myDeliveryOptionsFilterOptions) {
value.isSelected = false;
}
if (requestDeliveryOptionEnum != RequestDeliveryOptionEnum.none) {
myDeliveryOptionsFilterOptions[requestDeliveryOptionEnum.getIdRequestDeliveryOptionEnum() - 1].isSelected = true; // -1 to match with the index
}
notifyListeners();
}
List<OfferRequestCommentModel> providerDeliveryActionsList = [
OfferRequestCommentModel(
index: 0,
@ -969,19 +1056,40 @@ class RequestsVM extends BaseVM {
} else {
offerDescriptionError = "";
}
if (currentSelectedRequest != null && currentSelectedRequest!.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeEnum()) {
int index = myDeliveryOptionsFilterOptions.indexWhere((option) => option.isSelected);
if (index == -1) {
deliveryOptionSelectionError = GlobalConsts.deliveryOptionSelectionError;
log("indexindex: ${index}");
isValidated = false;
}
notifyListeners();
} else {
deliveryOptionSelectionError = "";
}
notifyListeners();
log("isValidatedisValidated: ${isValidated}");
return isValidated;
}
void resetSendOfferBottomSheet() {
offerPrice = "";
offerPriceError = "";
offerDescription = "";
offerDescriptionError = "";
deliveryOptionSelectionError = "";
serviceItem = "";
serviceItemCreatedOn = "";
itemManufacturer = "";
isDeliveryAvailableStatus = false;
if (myDeliveryOptionsFilterOptions.isNotEmpty) {
myDeliveryOptionsFilterOptions.forEach((option) => option.isSelected = false);
}
pickedVehicleImages.clear();
notifyListeners();
}
@ -1050,6 +1158,7 @@ class RequestsVM extends BaseVM {
required int offerId,
required String offerPrice,
required bool isDeliveryAvailable,
required RequestDeliveryOptionEnum offerDeliveryOption,
required String serviceItemName,
required String manufacturedByName,
required String manufacturedOn,
@ -1072,6 +1181,7 @@ class RequestsVM extends BaseVM {
offerId: offerId,
offerPrice: offerPrice,
isDeliveryAvailable: isDeliveryAvailable,
offerDeliveryOption: offerDeliveryOption.getIdRequestDeliveryOptionEnum(),
serviceItemName: serviceItemName,
manufacturedByName: manufacturedByName,
manufacturedOn: manufacturedOn,
@ -1115,6 +1225,7 @@ class RequestsVM extends BaseVM {
required String manufacturedOn,
required RequestModel requestModel,
required bool isDeliveryAvailable,
required RequestDeliveryOptionEnum offerDeliveryOption,
required int requestIndex,
required bool isFromChatScreen,
required int? offerId,
@ -1134,6 +1245,7 @@ class RequestsVM extends BaseVM {
requestId: requestId,
offerPrice: offerPrice,
isDeliveryAvailable: isDeliveryAvailable,
offerDeliveryOption: offerDeliveryOption,
manufacturedByName: manufacturedByName,
manufacturedOn: manufacturedOn,
serviceItemName: serviceItemName,
@ -1168,6 +1280,7 @@ class RequestsVM extends BaseVM {
requestID: requestModel.id,
price: double.parse(offerPrice),
isDeliveryAvailable: isDeliveryAvailable,
requestDeliveryOption: offerDeliveryOption,
manufacturedByName: manufacturedByName,
manufacturedOn: manufacturedOn,
serviceItemName: serviceItemName,
@ -1177,6 +1290,7 @@ class RequestsVM extends BaseVM {
reqOfferImages: images,
),
);
context.read<ChatVM>().onNewMessageReceivedForRequestOffer(messages: [chatMessageModel], requestsVM: this, isMyOwnOffer: true);
if (!isFromChatScreen) {

@ -21,8 +21,11 @@ class ShippingManagementVM extends BaseVM {
List<ShippingRequestModel> shippingRequestsList = [];
List<EnumsModel> shippingStatusEnums = [];
List<EnumsModel> selfPickupStatusEnums = [];
List<FilterListModel> selfPickupRequestsFilterOptions = [];
List<FilterListModel> selfPickupRequestsStatusesList = [];
List<EnumsModel> shippingStatusEnums = [];
List<FilterListModel> shippingRequestFilterOptions = [];
List<FilterListModel> shippingRequestStatusesList = [];
@ -32,15 +35,41 @@ class ShippingManagementVM extends BaseVM {
requestStatusComments = value;
}
bool isSelfPickupTapped = false;
void updateIsSelfPickupTapped(var value) {
isSelfPickupTapped = value;
notifyListeners();
}
resetFilters() {
if (shippingRequestFilterOptions.isEmpty) return;
for (var value in shippingRequestFilterOptions) {
value.isSelected = false;
}
shippingRequestFilterOptions[0].isSelected = true;
if (selfPickupRequestsFilterOptions.isEmpty) return;
for (var value in selfPickupRequestsFilterOptions) {
value.isSelected = false;
}
selfPickupRequestsFilterOptions[0].isSelected = true;
requestStatusComments = "";
}
Future<void> populateSelfPickupRequestFilterList() async {
selfPickupStatusEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.selfPickupStatusEnumId);
selfPickupRequestsFilterOptions.clear();
selfPickupRequestsStatusesList.clear();
for (int i = 0; i < selfPickupStatusEnums.length; i++) {
selfPickupRequestsFilterOptions.add(FilterListModel(title: selfPickupStatusEnums[i].enumValueStrDes, isSelected: false, id: selfPickupStatusEnums[i].enumValue));
selfPickupRequestsStatusesList.add(FilterListModel(title: selfPickupStatusEnums[i].enumValueStrDes, isSelected: false, id: selfPickupStatusEnums[i].enumValue));
}
selfPickupRequestsFilterOptions.insert(0, FilterListModel(title: Utils.getNameByShippingRequestStatusEnum(ShippingRequestStatusEnum.allRequests), isSelected: true, id: -1));
notifyListeners();
}
Future<void> populateShippingRequestFilterList() async {
// if (shippingRequestFilterOptions.isNotEmpty && shippingRequestStatusesList.isNotEmpty) return;
@ -78,6 +107,22 @@ class ShippingManagementVM extends BaseVM {
notifyListeners();
}
Future<void> applyFiltersOnSelfPickupRequests({required SelfPickupRequestStatusEnum selfPickupRequestStatusEnum}) async {
for (var value in selfPickupRequestsFilterOptions) {
value.isSelected = false;
}
if (selfPickupRequestStatusEnum == SelfPickupRequestStatusEnum.allRequests) {
selfPickupRequestsFilterOptions[0].isSelected = true;
await getSelfPickupRequestsListByFilters();
notifyListeners();
return;
}
int index = selfPickupRequestsFilterOptions.indexWhere((element) => element.id == selfPickupRequestStatusEnum.getIdFromSelfPickupStatusEnum());
selfPickupRequestsFilterOptions[index].isSelected = true; // +1 to match with the index 0 index has all requests
await getSelfPickupRequestsListByFilters(selfPickupStatusEnum: selfPickupRequestStatusEnum);
notifyListeners();
}
void updateSelectionInShippingRequestStatuses(int index) {
for (var value in shippingRequestStatusesList) {
value.isSelected = false;
@ -99,6 +144,19 @@ class ShippingManagementVM extends BaseVM {
}
}
Future<void> getSelfPickupRequestsListByFilters({SelfPickupRequestStatusEnum? selfPickupStatusEnum}) async {
setState(ViewState.busy);
try {
shippingRequestsList = await shippingRepo.getSelfPickupRequestListByStatus(selfPickupRequestStatus: selfPickupStatusEnum);
setState(ViewState.idle);
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
setState(ViewState.idle);
}
}
Future<bool> onUpdateShippingStatusTapped({required BuildContext context, required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId}) async {
Utils.showLoading(context);
try {

@ -370,7 +370,32 @@ class _AdsDetailViewState extends State<AdsDetailView> {
],
),
],
).paddingOnly(top: 5, bottom: 2).toWhiteContainer(width: double.infinity, allPading: 12);
).paddingOnly(top: 2, bottom: 2).toWhiteContainer(width: double.infinity, allPading: 12);
}
Widget buildSpecialServicesContainer() {
if (widget.adDetails.specialservice == null || widget.adDetails.specialservice!.isEmpty) {
return const SizedBox();
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.specialServices.tr().toText(fontSize: 16),
3.height,
Column(
children: List.generate(
widget.adDetails.specialservice!.length,
(index) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: ("${widget.adDetails.specialservice![index].name}").toText(fontSize: 14, color: MyColors.lightTextColor)),
("${widget.adDetails.specialservice![index].price} ${LocaleKeys.sar.tr()} ").toText(fontSize: 14, color: MyColors.lightTextColor),
],
)),
),
],
).paddingOnly(bottom: 2).toWhiteContainer(width: double.infinity, allPading: 12);
}
@override
@ -385,9 +410,30 @@ class _AdsDetailViewState extends State<AdsDetailView> {
onPressed: () {
return deleteAdBottomSheet(context);
},
).toContainer(
margin: const EdgeInsets.fromLTRB(0, 8, 21, 8),
paddingAll: 0,
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
height: 40,
width: 42,
);
} else if ((widget.adDetails.adPostStatus != AdPostStatus.pendingForPost)) {
actionWidget = IconButton(
icon: const Icon(Icons.chat_outlined, color: Colors.black),
onPressed: () => adVM.onMessagesButtonPressed(
context: context,
adDetailsModel: widget.adDetails,
)).toContainer(
margin: const EdgeInsets.fromLTRB(0, 8, 21, 8),
paddingAll: 0,
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
height: 40,
width: 42,
);
} else {
actionWidget = IconButton(icon: const Icon(Icons.chat_outlined, color: Colors.black), onPressed: () => adVM.onMessagesButtonPressed(context: context, adDetailsModel: widget.adDetails));
}
return Scaffold(
appBar: CustomAppBar(
@ -395,17 +441,7 @@ class _AdsDetailViewState extends State<AdsDetailView> {
profileImageUrl: MyAssets.bnCar,
isRemoveBackButton: false,
isDrawerEnabled: false,
actions: [
actionWidget.toContainer(
margin: const EdgeInsets.fromLTRB(0, 8, 21, 8),
paddingAll: 0,
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
height: 40,
width: 42,
),
],
actions: [actionWidget],
onTap: () {},
),
body: Container(
@ -427,6 +463,10 @@ class _AdsDetailViewState extends State<AdsDetailView> {
buildPersonalInformationCard(context: context),
],
if (widget.adDetails.isMyAd ?? false) ...[
if (widget.adDetails.specialservice != null && widget.adDetails.specialservice!.isNotEmpty) ...[
12.height,
buildSpecialServicesContainer(),
],
12.height,
buildAdStartEndDates(),
],

@ -75,6 +75,33 @@ class _AdsFilterViewState extends State<AdsFilterView> {
ListView(
children: [
20.height,
SearchEntityWidget(
title: LocaleKeys.searchByAdID.tr(),
isForString: true,
actionWidget: TxtField(
value: adVM.currentAdIDFilter,
onChanged: (value) => adVM.onCurrentAdIDFilterChanged(value),
hint: LocaleKeys.enterAdID.tr(),
postfixWidget: Consumer(
builder: (BuildContext context, AdVM adVM, Widget? child) {
if (adVM.currentAdIDFilter.isEmpty) {
return const SizedBox();
}
return IconButton(
onPressed: () => adVM.addAdFilterAdIDSearchHistory(value: adVM.currentAdIDFilter),
icon: const Icon(Icons.done, color: MyColors.lightIconColor),
);
},
),
),
historyContentString: adVM.adsFilterAdIDSearchHistory,
onHistoryItemDeleted: (index) {
adVM.removeAdFilterAdIDSearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) => null,
historyContent: const [], // ignore in the case of TEXT FIELD
),
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
SearchEntityWidget(
title: LocaleKeys.searchByCity.tr(),
actionWidget: Builder(builder: (context) {
@ -95,6 +122,27 @@ class _AdsFilterViewState extends State<AdsFilterView> {
onHistoryItemTapped: (DropValue value) => null,
),
const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7),
SearchEntityWidget(
title: LocaleKeys.searchByVehicleType.tr(),
actionWidget: Builder(builder: (context) {
List<DropValue> vehicleTypesDrop = [];
for (var element in adVM.vehicleTypesForFilters) {
vehicleTypesDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleTypeName ?? "", ""));
}
return DropdownField(
(DropValue value) => adVM.updateSelectionVehicleTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: vehicleTypesDrop,
dropdownValue: adVM.vehicleTypeId.selectedId != -1 ? DropValue(adVM.vehicleTypeId.selectedId, adVM.vehicleTypeId.selectedOption, "") : null,
hint: LocaleKeys.selectVehicleType.tr(),
errorValue: adVM.vehicleTypeId.errorValue,
);
}),
historyContent: adVM.vehicleTypesAdSearchHistory,
onHistoryItemDeleted: (index) => adVM.removeVehicleTypesAdSearchHistory(index: index),
onHistoryItemTapped: (DropValue value) => null,
),
const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7),
SearchEntityWidget(
title: LocaleKeys.searchByBrandName.tr(),
actionWidget: Builder(builder: (context) {
@ -160,6 +208,60 @@ class _AdsFilterViewState extends State<AdsFilterView> {
onHistoryItemTapped: (DropValue value) => null,
),
const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7),
LocaleKeys.searchByDemandPrice.tr().toText(fontSize: 16, isBold: true),
8.height,
Row(
children: [
Expanded(
child: SearchEntityWidget(
title: '',
isForString: false,
actionWidget: TxtField(
onChanged: (value) => adVM.updateAdFilterDemandStartPrice(value),
value: adVM.adFilterDemandStartPrice,
hint: LocaleKeys.enterStartPrice.tr(),
keyboardType: TextInputType.number,
numbersOnly: true,
postfixWidget: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.sar.tr().toText(fontWeight: MyFonts.Medium, fontSize: 15, color: borderColor, textAlign: TextAlign.center),
],
),
),
historyContentString: const [],
onHistoryItemDeleted: (index) {},
onHistoryItemTapped: (DropValue value) => null,
historyContent: const [], // ignore in the case of TEXT FIELD
),
),
20.width,
Expanded(
child: SearchEntityWidget(
title: '',
isForString: false,
actionWidget: TxtField(
onChanged: (value) => adVM.updateAdFilterDemandEndPrice(value),
value: adVM.adFilterDemandEndPrice,
hint: LocaleKeys.enterEndPrice.tr(),
keyboardType: TextInputType.number,
numbersOnly: true,
postfixWidget: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.sar.tr().toText(fontWeight: MyFonts.Medium, fontSize: 15, color: borderColor, textAlign: TextAlign.center),
],
),
),
historyContentString: [],
onHistoryItemDeleted: (index) {},
onHistoryItemTapped: (DropValue value) => null,
historyContent: const [], // ignore in the case of TEXT FIELD
),
),
],
),
const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7),
SearchEntityWidget(
title: LocaleKeys.searchByAdOwner.tr(),
actionWidget: Builder(
@ -204,7 +306,34 @@ class _AdsFilterViewState extends State<AdsFilterView> {
historyContent: adVM.vehicleAdCreatedDateSearchHistory,
onHistoryItemDeleted: (index) => adVM.removeVehicleAdCreatedDateSearchHistory(index: index),
onHistoryItemTapped: (DropValue value) => null,
)
),
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
SearchEntityWidget(
title: LocaleKeys.searchByMobileNumber.tr(),
isForString: true,
actionWidget: TxtField(
onChanged: (value) => adVM.onCurrentAdMobilePhoneFilterChanged(value),
value: adVM.currentAdMobilePhoneFilter,
hint: LocaleKeys.enterMobileNumber.tr(),
postfixWidget: Consumer(
builder: (BuildContext context, AdVM adVM, Widget? child) {
if (adVM.currentAdMobilePhoneFilter.isEmpty) {
return const SizedBox();
}
return IconButton(
onPressed: () => adVM.addAdFilterMobilePhoneSearchHistory(value: adVM.currentAdMobilePhoneFilter),
icon: const Icon(Icons.done, color: MyColors.lightIconColor),
);
},
),
),
historyContentString: adVM.adFilterMobilePhoneSearchHistory,
onHistoryItemDeleted: (index) {
adVM.removeAdFilterMobilePhoneSearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) => null,
historyContent: const [], // ignore in the case of TEXT FIELD
),
],
).expand(),
Container(

@ -2,6 +2,7 @@ import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
@ -12,6 +13,7 @@ import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/subscriptions_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
@ -167,11 +169,18 @@ class AdDurationSelectionSheet extends StatelessWidget {
if (adVM.isFetchingLists) {
return;
} else {
final subscriptionsVM = context.read<SubscriptionsVM>();
if (isFromExtendAd && !isUpdateAdSelected) {
bool status = await adVM.createAdExtensionOrder(context, adId: adsID, adsDurationId: adVM.vehicleAdDurationId.selectedId);
log("hereStatus: $status");
if (status) {
List<bool> statuses =
await adVM.createAdExtensionOrder(context, adId: adsID, adsDurationId: adVM.vehicleAdDurationId.selectedId); // [0] Means API response [1] means isPaymentRequired
if (statuses[0] && statuses[1]) {
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.extendAds);
} else if (statuses[0] == true && statuses[1] == false) {
pop(context);
pop(context);
pop(context);
await subscriptionsVM.getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
adVM.applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.active);
}
} else {
navigateReplaceWithName(context, AppRoutes.createAdView);

@ -82,10 +82,15 @@ class SelectAdTypeView extends StatelessWidget {
}
if (AppState().currentAppType == AppType.provider) {
if (isFromExtendAd && !adVM.isAdEditEnabled) {
bool status = await adVM.createAdExtensionOrder(context, adId: adsId, adsDurationId: adVM.vehicleAdDurationId.selectedId);
log("hereStatus: $status");
if (status) {
List<bool> statuses =
await adVM.createAdExtensionOrder(context, adId: adsId, adsDurationId: adVM.vehicleAdDurationId.selectedId); // [0] Means API response [1] means isPaymentRequired
if (statuses[0] && statuses[1]) {
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.extendAds);
} else if (statuses[0] == true && statuses[1] == false) {
pop(context);
pop(context);
pop(context);
adVM.applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.active);
}
} else {
navigateReplaceWithName(context, AppRoutes.createAdView);

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
@ -7,6 +9,7 @@ import 'package:mc_common_app/models/appointments_models/appointment_list_model.
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
@ -79,7 +82,7 @@ class AppointmentDetailView extends StatelessWidget {
}
}
Widget buildBottomActionButton({required AppointmentStatusEnum appointmentStatusEnum, required BuildContext context}) {
Widget buildBottomActionButton({required AppointmentStatusEnum appointmentStatusEnum, required BuildContext context, required bool shouldShowConfirmButton}) {
switch (appointmentStatusEnum) {
case AppointmentStatusEnum.booked:
return Align(
@ -87,13 +90,15 @@ class AppointmentDetailView extends StatelessWidget {
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => buildCancelAppointmentReasonsBottomSheet(context: context), text: LocaleKeys.cancel.tr()),
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
text: LocaleKeys.confirm.tr()),
if (shouldShowConfirmButton) ...[
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
text: LocaleKeys.confirm.tr()),
]
],
),
);
@ -134,13 +139,15 @@ class AppointmentDetailView extends StatelessWidget {
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => buildCancelAppointmentReasonsBottomSheet(context: context), text: LocaleKeys.cancel.tr()),
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
text: LocaleKeys.confirm.tr()),
if (shouldShowConfirmButton) ...[
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
text: LocaleKeys.confirm.tr()),
],
],
),
);
@ -256,6 +263,12 @@ class AppointmentDetailView extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
bool shouldShowConfirmButton = false;
final currentDateTime = DateTime.now();
if ((appointmentListModel.isPaymentRequiredAtBooking ?? false) && appointmentListModel.appointmentDate != null && appointmentListModel.appointmentDate!.isNotEmpty) {
final DateTime appointmentDatetime = DateHelper.parseStringToDate(appointmentListModel.appointmentDate!);
shouldShowConfirmButton = (appointmentListModel.isPaymentRequiredAtBooking ?? false) && (appointmentDatetime.isAfter(currentDateTime));
}
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.appointment.tr(),
@ -337,7 +350,7 @@ class AppointmentDetailView extends StatelessWidget {
},
icon: MyAssets.scheduleAppointmentIcon.buildSvg(),
),
if (appointmentListModel.appointmentStatusEnum == AppointmentStatusEnum.booked) ...[
if (appointmentListModel.appointmentStatusEnum == AppointmentStatusEnum.booked && (shouldShowConfirmButton)) ...[
10.width,
CardButtonWithIcon(
title: LocaleKeys.payforAppointment.tr(),
@ -353,7 +366,7 @@ class AppointmentDetailView extends StatelessWidget {
],
],
).toWhiteContainer(width: double.infinity, allPading: 12),
buildBottomActionButton(appointmentStatusEnum: appointmentListModel.appointmentStatusEnum!, context: context),
buildBottomActionButton(appointmentStatusEnum: appointmentListModel.appointmentStatusEnum!, context: context, shouldShowConfirmButton: shouldShowConfirmButton),
],
),
),

@ -11,6 +11,7 @@ import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_provider.dart';
import 'package:mc_common_app/view_models/payment_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/views/advertisement/ads_buyer_chats_view.dart';
@ -175,30 +176,30 @@ class _ChatViewState extends State<ChatView> {
child: chatMessages.isEmpty
? Center(child: LocaleKeys.noChatMessage.tr().toText(fontSize: 16, color: MyColors.lightTextColor, textAlign: TextAlign.center)).paddingAll(22)
: ListView.separated(
controller: chatVM.scrollController,
itemCount: chatMessages.length,
separatorBuilder: (BuildContext context, int index) => 20.height,
itemBuilder: (BuildContext context, int index) {
ChatMessageModel chatMessageModel = chatMessages[index];
return ChatMessageCustomWidget(
chatMessageModel: chatMessageModel,
requestModel: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel! : null,
requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus,
chatTypeEnum: chatTypeEnum,
requestsTypeEnum: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel!.requestType.toRequestTypeEnum() : RequestsTypeEnum.specialCarRequest,
);
},
).horPaddingMain(),
controller: chatVM.scrollController,
itemCount: chatMessages.length,
separatorBuilder: (BuildContext context, int index) => 20.height,
itemBuilder: (BuildContext context, int index) {
ChatMessageModel chatMessageModel = chatMessages[index];
return ChatMessageCustomWidget(
chatMessageModel: chatMessageModel,
requestModel: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel! : null,
requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus,
chatTypeEnum: chatTypeEnum,
requestsTypeEnum: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel!.requestType.toRequestTypeEnum() : RequestsTypeEnum.specialCarRequest,
);
},
).horPaddingMain(),
),
10.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (chatTypeEnum == ChatTypeEnum.requestOffer &&
if (
AppState().currentAppType == AppType.customer && chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestType.toRequestTypeEnum() == RequestsTypeEnum.serviceRequest &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress &&
requestVM.currentSelectedOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted &&
AppState().currentAppType == AppType.customer) ...[
requestVM.currentSelectedOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted ) ...[
Expanded(
child: ShowFillButton(
maxWidth: double.infinity,
@ -233,88 +234,93 @@ class _ChatViewState extends State<ChatView> {
// ),
// ]
//
else ...[
if (AppState().currentAppType == AppType.provider &&
chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted &&
chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.local_offer_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() {
requestVM.resetSendOfferBottomSheet();
RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(
requestIndex: chatViewArgumentsForRequest!.requestIndex,
requestModel: chatViewArgumentsForRequest!.requestModel!,
);
buildSendOfferBottomSheet(
context: context,
requestDetailPageArguments: requestDetailArguments,
isFromChatScreen: true,
offerId: null, // null means creating new offer
);
},
else
...[
if (AppState().currentAppType == AppType.provider &&
chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted &&
chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.local_offer_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() {
requestVM.resetSendOfferBottomSheet();
RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(
requestIndex: chatViewArgumentsForRequest!.requestIndex,
requestModel: chatViewArgumentsForRequest!.requestModel!,
);
context.read<DashboardVMProvider>().checkUserSubscription(SubscriptionActionTypeEnum.subscription, context, callback: () {
buildSendOfferBottomSheet(
context: context,
requestDetailPageArguments: requestDetailArguments,
isFromChatScreen: true,
offerId: null, // null means creating new offer
);
});
},
),
),
),
],
if (chatVM.pickedImagesForMessage.isNotEmpty) ...[
Expanded(
flex: 8,
child: PickedFilesContainer(
pickedFiles: chatVM.pickedImagesForMessage,
onCrossPressedPrimary: chatVM.removeImageFromList,
onAddFilePressed: () => chatVM.pickMultipleImages(),
],
if (chatVM.pickedImagesForMessage.isNotEmpty) ...[
Expanded(
flex: 8,
child: PickedFilesContainer(
pickedFiles: chatVM.pickedImagesForMessage,
onCrossPressedPrimary: chatVM.removeImageFromList,
onAddFilePressed: () => chatVM.pickMultipleImages(),
),
),
),
] else if (chatTypeEnum == ChatTypeEnum.requestOffer) ...[
] else
if (chatTypeEnum == ChatTypeEnum.requestOffer) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.photo_library_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(() => chatVM.pickMultipleImages()),
),
],
if (chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 8,
child: TxtField(
isNeedLabelOnTop: false,
value: chatVM.chatMessageText,
hint: LocaleKeys.typeMessageHere.tr(),
keyboardType: TextInputType.text,
isNeedBorder: false,
onChanged: (v) => chatVM.updateChatMessageText(v),
),
),
],
Expanded(
flex: 1,
child: const Icon(
Icons.photo_library_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(() => chatVM.pickMultipleImages()),
),
],
if (chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 8,
child: TxtField(
value: chatVM.chatMessageText,
hint: LocaleKeys.typeMessageHere.tr(),
keyboardType: TextInputType.text,
isNeedBorder: false,
onChanged: (v) => chatVM.updateChatMessageText(v),
),
),
],
Expanded(
flex: 1,
child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress(
() async {
ChatMessageTypeEnum chatMessageTypeEnum = ChatMessageTypeEnum.freeText;
child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress(
() async {
ChatMessageTypeEnum chatMessageTypeEnum = ChatMessageTypeEnum.freeText;
if (chatVM.pickedImagesForMessage.isNotEmpty) {
chatMessageTypeEnum = ChatMessageTypeEnum.image;
}
final status = await onMessageSend(chatMessageType: chatMessageTypeEnum);
if (chatVM.pickedImagesForMessage.isNotEmpty) {
chatMessageTypeEnum = ChatMessageTypeEnum.image;
}
final status = await onMessageSend(chatMessageType: chatMessageTypeEnum);
if (status) {
chatVM.scrollChatDown();
if (chatMessageTypeEnum == ChatMessageTypeEnum.freeText) {
chatVM.clearChatMessageText();
} else if (chatMessageTypeEnum == ChatMessageTypeEnum.image) {
chatVM.clearPickedImagesForMessage();
if (status) {
chatVM.scrollChatDown();
if (chatMessageTypeEnum == ChatMessageTypeEnum.freeText) {
chatVM.clearChatMessageText();
} else if (chatMessageTypeEnum == ChatMessageTypeEnum.image) {
chatVM.clearPickedImagesForMessage();
}
}
}
},
},
),
),
),
],
],
],
).toContainer(isShadowEnabled: true)
],

@ -128,6 +128,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
isDeliveryAvailable: chatMessageModel.reqOffer!.isDeliveryAvailable ?? false,
offerDeliveryOption: chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.selfPickup,
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
@ -227,6 +229,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
isDeliveryAvailable: chatMessageModel.reqOffer!.isDeliveryAvailable ?? false,
offerDeliveryOption: chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.selfPickup,
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
@ -374,6 +378,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
isDeliveryAvailable: chatMessageModel.reqOffer!.isDeliveryAvailable ?? false,
offerDeliveryOption: chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.selfPickup,
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
@ -428,6 +434,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
isDeliveryAvailable: chatMessageModel.reqOffer!.isDeliveryAvailable ?? false,
offerDeliveryOption: chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.selfPickup,
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
@ -500,6 +508,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Widget buildOfferDetailsInChatMessage({required RequestStatusEnum requestStatusEnum, required ChatMessageModel chatMessageModel, required BuildContext context}) {
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer;
if (requestStatusEnum != RequestStatusEnum.submitted && requestOfferStatusEnum != RequestOfferStatusEnum.accepted) {
return Column(
children: [
@ -744,6 +753,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
requestVM.updateOfferDescription((widget.chatMessageModel.chatText ?? "").toString());
requestVM.updateIsDeliveryAvailableStatus((offer.isDeliveryAvailable ?? false));
requestVM.applyFilterOnDeliveryOption(requestDeliveryOptionEnum: offer.requestDeliveryOption ?? RequestDeliveryOptionEnum.none);
if (offer.reqOfferImages != null && offer.reqOfferImages!.isNotEmpty) {
for (var element in offer.reqOfferImages!) {
if (element.imageUrl != null || element.imageStr != null) {
@ -886,6 +896,27 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
);
}
Widget buildDeliveryOptionsWidget({required RequestDeliveryOptionEnum deliveryOptionId}) {
Widget finalWidget = const SizedBox();
String finalText = "";
if (deliveryOptionId == RequestDeliveryOptionEnum.none) {
finalText = RequestDeliveryOptionEnum.selfPickup.getStringFromRequestDeliveryOptionEnum();
} else {
finalText = deliveryOptionId.getStringFromRequestDeliveryOptionEnum();
}
finalWidget = Directionality(
textDirection: TextDirection.ltr,
child: "${LocaleKeys.deliveryOptions.tr()} : $finalText".toText(
fontSize: 10,
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontWeight: MyFonts.Medium,
),
);
return finalWidget;
}
Widget messageWidgetBasedOnType({required ChatMessageTypeEnum? chatMessageTypeEnum}) {
Widget messageTypeWidget = const SizedBox();
if (chatMessageTypeEnum == null) {
@ -908,14 +939,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
buildFreeTextDetailsInMessage(chatMessageTypeEnum: chatMessageTypeEnum),
if (widget.requestsTypeEnum == RequestsTypeEnum.serviceRequest) ...[
2.height,
Directionality(
textDirection: TextDirection.ltr,
child: "${LocaleKeys.deliveryAvailable.tr()} : ${(widget.chatMessageModel.reqOffer!.isDeliveryAvailable ?? false) ? LocaleKeys.yes.tr() : LocaleKeys.no.tr()}".toText(
fontSize: 10,
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontWeight: MyFonts.Medium,
),
),
buildDeliveryOptionsWidget(deliveryOptionId: widget.chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.none),
],
if (widget.chatMessageModel.reqOffer!.reqOfferImages != null && widget.chatMessageModel.reqOffer!.reqOfferImages!.isNotEmpty) ...[
5.height,

@ -80,6 +80,18 @@ class MyRequestsFragment extends StatelessWidget {
title: LocaleKeys.manageRequests.tr(),
isRemoveBackButton: true,
actions: [
if (AppState().currentAppType == AppType.provider) ...[
Padding(
padding: const EdgeInsets.only(top: 8, bottom: 8, right: 15),
child: const Icon(Icons.messenger_outline_rounded, color: Colors.black, size: 18).toContainer(
borderRadius: 80,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
),
).onPress(() {
navigateWithName(context, AppRoutes.providersChatListPage);
})
],
Padding(
padding: EdgeInsets.only(top: requestsVM.requestsFiltersCounter > 0 ? 20 : 0, right: 21),
child: Badge(
@ -92,7 +104,7 @@ class MyRequestsFragment extends StatelessWidget {
),
).onPress(() {
navigateWithName(context, AppRoutes.requestsFilterView);
})
}),
],
).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
@ -201,8 +213,8 @@ class MyRequestsFragment extends StatelessWidget {
color: MyColors.white,
),
).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
})
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
})
: null,
);
});

@ -0,0 +1,192 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/requests_models/offers_model.dart';
import 'package:mc_common_app/models/requests_models/offers_unread_chat_model.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
import 'package:mc_common_app/models/requests_models/providers_offers_chat_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class ProvidersChatListPage extends StatefulWidget {
const ProvidersChatListPage({super.key});
@override
State<ProvidersChatListPage> createState() => _ProvidersChatListPageState();
}
class _ProvidersChatListPageState extends State<ProvidersChatListPage> {
@override
void initState() {
_onRefresh();
super.initState();
}
_onRefresh() async {
scheduleMicrotask(() async {
RequestsVM requestsVM = context.read<RequestsVM>();
await requestsVM.getOffersChatsUnreadList(userId: AppState().getUser.data!.userInfo!.userId ?? "", context: context);
});
}
Future<void> onChatTapped({required BuildContext context, required int requestID}) async {
final requestsVM = context.read<RequestsVM>();
final chatVM = context.read<ChatVM>();
int requestIndex = requestsVM.myFilteredRequests.indexWhere((request) => request.id == requestID);
log("requestIndex1: $requestIndex");
if (requestIndex == -1) {
RequestsTypeEnum requestTypeEnum = requestsVM.requestsTypeFilterOptions.firstWhere((element) => element.isSelected).id.toRequestTypeEnum();
if (requestTypeEnum == RequestsTypeEnum.specialCarRequest) {
requestTypeEnum = RequestsTypeEnum.serviceRequest;
} else {
requestTypeEnum = RequestsTypeEnum.specialCarRequest;
}
await requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: requestTypeEnum);
requestIndex = requestsVM.myFilteredRequests.indexWhere((request) => request.id == requestID);
}
log("requestIndex2: $requestIndex");
if (requestIndex != -1) {
RequestModel request = requestsVM.myFilteredRequests[requestIndex];
requestsVM.updateCurrentSelectedRequest(request);
// if (request.requestStatus == RequestStatusEnum.pending || request.requestStatus == RequestStatusEnum.cancelled || request.requestStatus == RequestStatusEnum.expired) {
// Utils.showToast("${LocaleKeys.requests.tr()} ${request.requestStatusName}");
// return;
// }
RequestDetailPageArguments requestDetailPageArguments = RequestDetailPageArguments(requestIndex: requestIndex, requestModel: request);
ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest(
chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: requestDetailPageArguments.requestModel.customerID,
senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestId: requestDetailPageArguments.requestModel.id,
providerIndex: -1,
// This will be only sent in case of customer
requestModel: requestDetailPageArguments.requestModel,
requestIndex: requestDetailPageArguments.requestIndex, // This will be only sent in case of provider
);
ChatViewArguments chatViewArguments = ChatViewArguments(
chatTypeEnum: ChatTypeEnum.requestOffer,
chatViewArgumentsForRequest: chatViewArgumentsForRequest,
);
await chatVM
.getRequestsChatMessagesForProvider(
customerId: requestDetailPageArguments.requestModel.customerId,
context: navigatorKey.currentState!.overlay!.context,
requestOfferId: 0,
requestId: requestDetailPageArguments.requestModel.id,
customerRequestIndex: requestDetailPageArguments.requestIndex,
)
.whenComplete(() => navigateWithName(navigatorKey.currentState!.overlay!.context, AppRoutes.chatView, arguments: chatViewArguments));
}
}
@override
Widget build(BuildContext context) {
return Consumer<RequestsVM>(builder: (context, RequestsVM requestsVM, Widget? child) {
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.specialRequestChat.tr()),
body: RefreshIndicator(
onRefresh: () async {
_onRefresh();
},
child: requestsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: requestsVM.offersUnreadChatModel == null || requestsVM.offersUnreadChatModel!.reqChatUnread.isEmpty
? Center(
child: LocaleKeys.noOffersShow.tr().toText(
fontSize: 16,
color: MyColors.lightTextColor,
),
)
: ListView.separated(
itemCount: requestsVM.offersUnreadChatModel!.reqChatUnread.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
OffersUnreadChatDataModel offersUnreadChatDataModel = requestsVM.offersUnreadChatModel!.reqChatUnread[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Utils.statusContainerChip(
// text: Utils.getNameByRequestOfferStatusEnum(providerOffersChatsModel.requestOfferStatusEnum!),
// chipColor: Utils.getChipColorByRequestOfferStatusEnum(providerOffersChatsModel.requestOfferStatusEnum!),
// ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(offersUnreadChatDataModel.customerName).toText(
fontSize: 16,
isBold: true,
),
if (offersUnreadChatDataModel.unreadMessagesCount > 0) ...[
Center(
child: "${offersUnreadChatDataModel.unreadMessagesCount}".toText(
color: Colors.white,
isBold: true,
fontSize: 10,
),
).toContainer(
backgroundColor: MyColors.cancelledColor,
borderRadius: 100,
paddingAll: 1,
width: 22,
height: 22,
),
],
],
),
4.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: offersUnreadChatDataModel.lastChatText.toText(color: MyColors.lightTextColor, fontSize: 14),
),
4.height,
if (offersUnreadChatDataModel.lastChatTime.isNotEmpty) ...[
DateTime.parse(offersUnreadChatDataModel.lastChatTime).getTimeAgo().toText(color: MyColors.lightTextColor, fontSize: 12),
],
],
),
4.height,
const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18),
],
),
],
).onPress(() async {
requestsVM.offersUnreadChatModel!.reqChatUnread[index].unreadMessagesCount = 0;
await onChatTapped(context: context, requestID: offersUnreadChatDataModel.requestID ?? 0);
}).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 16.height,
),
),
);
});
}
}

@ -19,6 +19,7 @@ import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/filters_list.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
@ -96,7 +97,7 @@ Future buildSendOfferBottomSheet({
],
12.height,
TxtField(
maxLines: 5,
maxLines: 3,
value: requestsVM.offerDescription,
errorValue: requestsVM.offerDescriptionError,
keyboardType: TextInputType.text,
@ -105,32 +106,49 @@ Future buildSendOfferBottomSheet({
),
12.height,
if (requestDetail.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeEnum()) ...[
LocaleKeys.deliveryAvailable.tr().toText(fontSize: 16),
8.height,
Container(
width: 50,
height: 30,
decoration: BoxDecoration(
color: requestsVM.isDeliveryAvailableStatus ? MyColors.darkPrimaryColor : MyColors.white,
borderRadius: BorderRadius.circular(25.0),
border: Border.all(color: MyColors.black, width: 1),
),
child: Transform.scale(
scale: 0.8,
child: CupertinoSwitch(
activeColor: MyColors.darkPrimaryColor,
trackColor: MyColors.white,
thumbColor: MyColors.grey98Color,
value: requestsVM.isDeliveryAvailableStatus,
onChanged: (value) {
requestsVM.updateIsDeliveryAvailableStatus(value);
},
),
LocaleKeys.selectDeliveryOption.tr().toText(fontSize: 16),
10.height,
// Container(
// width: 50,
// height: 30,
// decoration: BoxDecoration(
// color: requestsVM.isDeliveryAvailableStatus ? MyColors.darkPrimaryColor : MyColors.white,
// borderRadius: BorderRadius.circular(25.0),
// border: Border.all(color: MyColors.black, width: 1),
// ),
// child: Transform.scale(
// scale: 0.8,
// child: CupertinoSwitch(
// activeTrackColor: MyColors.darkPrimaryColor,
// inactiveTrackColor: MyColors.white,
// thumbColor: MyColors.grey98Color,
// value: requestsVM.isDeliveryAvailableStatus,
// onChanged: (value) {
// requestsVM.updateIsDeliveryAvailableStatus(value);
// },
// ),
// ),
// ),
FiltersList(
filterList: requestsVM.myDeliveryOptionsFilterOptions,
onFilterTapped: (index, selectedFilterId) {
requestsVM.applyFilterOnDeliveryOption(requestDeliveryOptionEnum: selectedFilterId.toRequestDeliveryOptionEnum());
},
needLeftPadding: false,
).paddingOnly(bottom: 21),
if (requestsVM.deliveryOptionSelectionError.isNotEmpty) ...[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
requestsVM.deliveryOptionSelectionError.toText(),
],
),
),
]
],
12.height,
if (requestsVM.pickedVehicleImages.isEmpty) ...[
5.height,
DottedRectContainer(
onTap: () => context.read<RequestsVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
@ -139,7 +157,7 @@ Future buildSendOfferBottomSheet({
),
],
if (requestsVM.pickedVehicleImages.isNotEmpty) ...[
16.height,
// 14.height,
PickedFilesContainer(
pickedFiles: requestsVM.pickedVehicleImages,
onCrossPressedPrimary: requestsVM.removeImageFromList,
@ -159,6 +177,12 @@ Future buildSendOfferBottomSheet({
"${AppState().getUser.data!.userInfo!.firstName} ${AppState().getUser.data!.userInfo!.lastName}",
);
requestsVM.updateServiceItemCreatedOn(DateHelper.formatAsYearMonthDay(DateTime.now()));
RequestDeliveryOptionEnum requestDeliveryOptionEnum = RequestDeliveryOptionEnum.none;
int index = requestsVM.myDeliveryOptionsFilterOptions.indexWhere((option) => option.isSelected);
if (index != -1) {
requestDeliveryOptionEnum = requestsVM.myDeliveryOptionsFilterOptions[index].id.toRequestDeliveryOptionEnum();
}
if (offerId == null) {
requestsVM.onSendOfferPressed(
context: context,
@ -169,6 +193,7 @@ Future buildSendOfferBottomSheet({
offerPrice: requestsVM.offerPrice,
requestModel: requestDetail,
isDeliveryAvailable: requestsVM.isDeliveryAvailableStatus,
offerDeliveryOption: requestDeliveryOptionEnum,
requestIndex: requestDetailPageArguments.requestIndex,
isFromChatScreen: isFromChatScreen,
manufacturedByName: requestsVM.itemManufacturer,
@ -182,6 +207,7 @@ Future buildSendOfferBottomSheet({
offerId: offerId,
offerPrice: requestsVM.offerPrice,
isDeliveryAvailable: requestsVM.isDeliveryAvailableStatus,
offerDeliveryOption: requestDeliveryOptionEnum,
serviceItemName: requestsVM.serviceItem,
manufacturedByName: requestsVM.itemManufacturer,
manufacturedOn: requestsVM.serviceItemCreatedOn,

@ -26,6 +26,107 @@ class RequestDetailPage extends StatelessWidget {
const RequestDetailPage({super.key, required this.requestDetailPageArguments});
Widget buildRequestDetailsContainer(BuildContext context) {
final requestDetail = requestDetailPageArguments.requestModel;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
requestDetail.vehicleTypeName.toText(fontSize: 16, letterSpacing: -0.64),
showItem("${LocaleKeys.brand.tr()}: ", requestDetail.brand),
showItem("${LocaleKeys.model.tr()}: ", requestDetail.model),
showItem("${LocaleKeys.year.tr()}: ", "${requestDetail.year}"),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.start,
children: [
"${requestDetail.city ?? ""} ${requestDetail.countryName}".toText(
color: MyColors.lightTextColor,
),
if (requestDetail.createdOn != null) ...[
DateTime.parse(requestDetail.createdOn!).getTimeAgo().toText(
color: MyColors.lightTextColor,
),
],
],
),
],
),
if (requestDetail.customerName.isNotEmpty) ...[
showItem("${LocaleKeys.customerName.tr()}: ", requestDetail.customerName),
],
showItem("${LocaleKeys.description.tr()}: ", requestDetail.description),
16.height,
if (AppState().currentAppType == AppType.provider &&
(requestDetailPageArguments.requestModel.requestStatus == RequestStatusEnum.inProgress || requestDetailPageArguments.requestModel.requestStatus == RequestStatusEnum.submitted)) ...[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
("${LocaleKeys.reportComplaint.tr()}?").toText(fontSize: 14, color: MyColors.darkTextColor),
],
).onPress(
() {
int customerID = requestDetailPageArguments.requestModel.customerId;
int complainType = 2;
String customerName = requestDetailPageArguments.requestModel.customerName;
context.read<AppointmentsVM>().buildComplaintBottomSheet(
customerID: customerID,
customerName: customerName,
complainType: complainType,
context: context,
);
},
),
],
// showItem("${LocaleKeys.priceRange.tr()}:", ""),
// Row(
// children: [
// Expanded(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// Row(
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// "${requestDetail.price.toInt()}".toText(fontSize: 19, isBold: true, letterSpacing: -1.16),
// 2.width,
// LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, letterSpacing: -0.4),
// ],
// ),
// Row(
// children: [
// Utils.statusContainerChip(
// text: (requestDetail.requestStatusName),
// chipColor: MyColors.grey98Color.withOpacity(0.1),
// textColor: MyColors.lightTextColor,
// ),
// ],
// ),
// ],
// ),
// ),
// // const Icon(Icons.arrow_forward)
// ],
// ),
],
);
}
Widget buildRequestDetailActionFooter({
required int requestId,
required RequestStatusEnum requestStatus,
@ -209,107 +310,6 @@ class RequestDetailPage extends StatelessWidget {
);
}
Widget buildRequestDetailsContainer(BuildContext context) {
final requestDetail = requestDetailPageArguments.requestModel;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
requestDetail.vehicleTypeName.toText(fontSize: 16, letterSpacing: -0.64),
showItem("${LocaleKeys.brand.tr()}: ", requestDetail.brand),
showItem("${LocaleKeys.model.tr()}: ", requestDetail.model),
showItem("${LocaleKeys.year.tr()}: ", "${requestDetail.year}"),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.start,
children: [
"${requestDetail.city ?? ""} ${requestDetail.countryName}".toText(
color: MyColors.lightTextColor,
),
if (requestDetail.createdOn != null) ...[
DateTime.parse(requestDetail.createdOn!).getTimeAgo().toText(
color: MyColors.lightTextColor,
),
],
],
),
],
),
if (requestDetail.customerName.isNotEmpty) ...[
showItem("${LocaleKeys.customerName.tr()}: ", requestDetail.customerName),
],
showItem("${LocaleKeys.description.tr()}: ", requestDetail.description),
16.height,
if (AppState().currentAppType == AppType.provider &&
(requestDetailPageArguments.requestModel.requestStatus == RequestStatusEnum.inProgress || requestDetailPageArguments.requestModel.requestStatus == RequestStatusEnum.submitted)) ...[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
("${LocaleKeys.reportComplaint.tr()}?").toText(fontSize: 14, color: MyColors.darkTextColor),
],
).onPress(
() {
int customerID = requestDetailPageArguments.requestModel.customerId;
int complainType = 2;
String customerName = requestDetailPageArguments.requestModel.customerName;
context.read<AppointmentsVM>().buildComplaintBottomSheet(
customerID: customerID,
customerName: customerName,
complainType: complainType,
context: context,
);
},
),
],
// showItem("${LocaleKeys.priceRange.tr()}:", ""),
// Row(
// children: [
// Expanded(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// Row(
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// "${requestDetail.price.toInt()}".toText(fontSize: 19, isBold: true, letterSpacing: -1.16),
// 2.width,
// LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, letterSpacing: -0.4),
// ],
// ),
// Row(
// children: [
// Utils.statusContainerChip(
// text: (requestDetail.requestStatusName),
// chipColor: MyColors.grey98Color.withOpacity(0.1),
// textColor: MyColors.lightTextColor,
// ),
// ],
// ),
// ],
// ),
// ),
// // const Icon(Icons.arrow_forward)
// ],
// ),
],
);
}
Widget showItem(String title, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,

@ -1,3 +1,6 @@
import 'dart:async';
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
@ -9,17 +12,15 @@ import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/user_models/user.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_review_containers.dart';
import 'package:mc_common_app/views/location_views/pick_location_page.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/filters_list.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
@ -36,6 +37,9 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
@override
void initState() {
final requestVM = context.read<RequestsVM>();
if (requestVM.acceptedRequestOffer != null && requestVM.acceptedRequestOffer!.requestDeliveryOption == RequestDeliveryOptionEnum.both) {
populateRequestDeliveryOptions();
}
if (requestVM.currentSelectedRequest != null && requestVM.currentSelectedRequest!.address.isEmpty) {
requestVM.addressSparePartRequestDelivery = "";
@ -44,6 +48,18 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
super.initState();
}
@override
void dispose() {
super.dispose();
}
populateRequestDeliveryOptions() {
scheduleMicrotask(() {
final requestVM = context.read<RequestsVM>();
requestVM.populateRequestDeliveryOptions();
});
}
Widget buildPersonalInformation() {
UserInfo userInfo = AppState().getUser.data!.userInfo!;
return Column(
@ -90,7 +106,21 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
Widget buildLocationInformation(BuildContext context) {
final requestVM = context.watch<RequestsVM>();
String address = "";
if (requestVM.acceptedRequestOffer != null && requestVM.acceptedRequestOffer!.requestDeliveryOption == RequestDeliveryOptionEnum.delivery) {
address = requestVM.currentSelectedRequest!.address;
} else if (requestVM.acceptedRequestOffer != null && requestVM.acceptedRequestOffer!.requestDeliveryOption == RequestDeliveryOptionEnum.selfPickup) {
address = requestVM.currentSelectedOffer!.providerAddress ?? "";
} else {
if (requestVM.selectedDeliveryOptionEnum == RequestDeliveryOptionEnum.delivery) {
address = requestVM.currentSelectedRequest!.address;
} else if (requestVM.selectedDeliveryOptionEnum == RequestDeliveryOptionEnum.selfPickup) {
address = requestVM.currentSelectedOffer!.providerAddress ?? "";
} else {
address = "";
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -104,17 +134,48 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
// ],
// ),
8.height,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleDetailWidget(text: requestVM.currentSelectedRequest!.address ?? "", type: LocaleKeys.location.tr()),
// 16.height,
// SingleDetailWidget(
// text: requestVM.additionalAddressSparePartRequestDelivery.isNotEmpty ? requestVM.additionalAddressSparePartRequestDelivery : "N/A",
// type: LocaleKeys.additionalAddressDetails.tr(),
// ),
],
),
FiltersList(
filterList: requestVM.deliveryOptionsForReviewRequest,
onFilterTapped: (index, selectedFilterId) {
requestVM.updateSelectedDeliveryOptionEnum(selectedFilterId.toRequestDeliveryOptionEnum());
},
needLeftPadding: false,
).paddingOnly(bottom: 8),
if (requestVM.selectedDeliveryOptionEnumError.isNotEmpty) ...[
requestVM.selectedDeliveryOptionEnumError.toText(fontSize: 12, color: Colors.red),
],
if (requestVM.selectedDeliveryOptionEnum != null) ...[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleDetailWidget(text: address, type: LocaleKeys.location.tr()),
if (requestVM.selectedDeliveryOptionEnum == RequestDeliveryOptionEnum.selfPickup) ...[
Row(
children: [
LocaleKeys.openMapLocation.tr().toText(
fontSize: 12,
isBold: true,
color: MyColors.primaryColor,
isUnderLine: true,
),
4.width,
Image.asset(
MyAssets.icRightUpPng,
height: 6,
width: 6,
color: MyColors.primaryColor,
),
],
).onPress(() async {
double latitude, longitude = 0.0;
latitude = double.parse(requestVM.currentSelectedOffer!.providerLatitude ?? "0.0");
longitude = double.parse(requestVM.currentSelectedOffer!.providerLongitude ?? "0.0");
await Utils.openLocationInMaps(latitude: latitude, longitude: longitude);
})
],
],
),
],
],
);
}
@ -161,8 +222,9 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
// SingleDetailWidget(text: "${requestVM.acceptedRequestOffer!.price.toString()} ${LocaleKeys.sar.tr()}", type: LocaleKeys.offerPrice.tr()),
16.height,
SingleDetailWidget(text: requestVM.acceptedRequestOfferProviderName ?? "", type: LocaleKeys.providerName.tr()),
16.height,
SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()),
SingleDetailWidget(text: LocaleKeys.online.tr(), type: LocaleKeys.paymentType.tr()),
],
),
),
@ -180,21 +242,18 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
SingleDetailWidget(text: manufacturedOnFormattedDate, type: LocaleKeys.manufacturedOn.tr()),
],
16.height,
// SingleDetailWidget(text: "${requestVM.currentSelectedRequest!.price.toString()} ${LocaleKeys.sar.tr()}", type: LocaleKeys.totalPrice.tr()),
SingleDetailWidget(text: "${requestVM.acceptedRequestOffer!.price.toString()} ${LocaleKeys.sar.tr()}", type: LocaleKeys.offerPrice.tr()),
if (requestCreatedOn.isNotEmpty) ...[
16.height,
SingleDetailWidget(text: requestCreatedOn, type: LocaleKeys.requestCreatedOn.tr()),
],
16.height,
SingleDetailWidget(text: LocaleKeys.online.tr(), type: LocaleKeys.paymentType.tr()),
],
),
),
],
),
16.height,
SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()),
],
);
}
@ -320,8 +379,13 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
title: LocaleKeys.payNow.tr(),
isBold: false,
onPressed: () {
final requestVM = context.read<RequestsVM>();
// context.read<PaymentVM>().updateRequestId(id: requestVM.currentSelectedRequest!.id);
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.request);
if (requestVM.acceptedRequestOffer!.requestDeliveryOption == RequestDeliveryOptionEnum.both && requestVM.selectedDeliveryOptionEnum == null) {
requestVM.updateSelectedDeliveryOptionEnumError(GlobalConsts.deliveryOptionSelectionError);
} else {
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.request);
}
},
),
],

@ -108,7 +108,9 @@ class _SettingOptionsMoreState extends State<SettingOptionsMore> {
onTap: () {
navigateWithName(context, AppRoutes.shippingManagementView);
},
),
).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
}),
],
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.settings, size: 20),

@ -1,7 +1,9 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
@ -39,7 +41,14 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
if (shippingViewModel.shippingRequestFilterOptions.isNotEmpty) {
await shippingViewModel.populateShippingRequestFilterList();
}
await shippingViewModel.getShippingRequestsListByFilters();
if (shippingViewModel.selfPickupRequestsFilterOptions.isNotEmpty) {
await shippingViewModel.populateSelfPickupRequestFilterList();
}
if (shippingViewModel.isSelfPickupTapped) {
await shippingViewModel.getSelfPickupRequestsListByFilters();
} else {
await shippingViewModel.getShippingRequestsListByFilters();
}
});
super.initState();
}
@ -146,27 +155,78 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
Widget build(BuildContext context) {
return Consumer(builder: (BuildContext context, ShippingManagementVM shippingVm, Widget? child) {
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.shippingManagement.tr()),
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Column(
children: [
16.height,
appBar: CustomAppBar(title: LocaleKeys.shippingManagement.tr()),
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Column(
children: [
16.height,
Row(
children: [
Expanded(
child: ShowFillButton(
isFilled: !shippingVm.isSelfPickupTapped,
maxHeight: 55,
fontSize: 15,
title: LocaleKeys.delivery.tr(),
txtColor: !shippingVm.isSelfPickupTapped ? MyColors.white : MyColors.darkTextColor,
onPressed: () async {
shippingVm.updateIsSelfPickupTapped(false);
int index = shippingVm.shippingRequestFilterOptions.indexWhere((element) => element.isSelected);
int id = shippingVm.shippingRequestFilterOptions[index].id;
await shippingVm.getShippingRequestsListByFilters(shippingStatusEnum: id.toShippingStatusEnum());
},
),
),
12.width,
Expanded(
child: ShowFillButton(
isFilled: shippingVm.isSelfPickupTapped,
txtColor: shippingVm.isSelfPickupTapped ? MyColors.white : MyColors.darkTextColor,
maxHeight: 55,
fontSize: 15,
title: LocaleKeys.selfPickup.tr(),
onPressed: () async {
shippingVm.updateIsSelfPickupTapped(true);
int index = shippingVm.selfPickupRequestsFilterOptions.indexWhere((element) => element.isSelected);
int id = shippingVm.selfPickupRequestsFilterOptions[index].id;
await shippingVm.getSelfPickupRequestsListByFilters(selfPickupStatusEnum: id.toSelfPickupStatusEnum());
},
),
),
],
).horPaddingMain(),
16.height,
if (shippingVm.isSelfPickupTapped) ...[
FiltersList(
filterList: shippingVm.selfPickupRequestsFilterOptions,
onFilterTapped: (index, selectedFilterId) {
shippingVm.applyFiltersOnSelfPickupRequests(selfPickupRequestStatusEnum: selectedFilterId.toSelfPickupStatusEnum());
},
),
] else ...[
FiltersList(
filterList: shippingVm.shippingRequestFilterOptions,
onFilterTapped: (index, selectedFilterId) {
shippingVm.applyFiltersOnShippingRequests(shippingRequestStatusEnum: selectedFilterId.toShippingStatusEnum());
},
),
8.height,
Expanded(
child: RefreshIndicator(
],
8.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async {
int index = shippingVm.shippingRequestFilterOptions.indexWhere((element) => element.isSelected);
int id = shippingVm.shippingRequestFilterOptions[index].id;
await shippingVm.getShippingRequestsListByFilters(shippingStatusEnum: id.toShippingStatusEnum());
if (shippingVm.isSelfPickupTapped) {
int index = shippingVm.selfPickupRequestsFilterOptions.indexWhere((element) => element.isSelected);
int id = shippingVm.selfPickupRequestsFilterOptions[index].id;
await shippingVm.getSelfPickupRequestsListByFilters(selfPickupStatusEnum: id.toSelfPickupStatusEnum());
} else {
int index = shippingVm.shippingRequestFilterOptions.indexWhere((element) => element.isSelected);
int id = shippingVm.shippingRequestFilterOptions[index].id;
await shippingVm.getShippingRequestsListByFilters(shippingStatusEnum: id.toShippingStatusEnum());
}
},
child: (shippingVm.state == ViewState.busy)
? const Center(child: CircularProgressIndicator())
@ -220,10 +280,12 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
},
separatorBuilder: (context, index) => 16.height,
),
)),
],
),
));
),
),
],
),
),
);
});
}
}

@ -30,8 +30,10 @@ class SearchEntityWidget extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText(fontSize: 16, isBold: true),
8.height,
if (title.isNotEmpty) ...[
title.toText(fontSize: 16, isBold: true),
8.height,
],
actionWidget,
10.height,
if (isForString && historyContentString != null && historyContentString!.isNotEmpty) ...[

@ -36,6 +36,7 @@ class TxtField extends StatelessWidget {
Widget? preFixWidget;
bool numbersOnly;
bool allowOnlyOneDigit;
bool isNeedLabelOnTop;
TxtField({
super.key,
@ -66,6 +67,7 @@ class TxtField extends StatelessWidget {
this.onPostFixPressed,
this.numbersOnly = false,
this.allowOnlyOneDigit = false,
this.isNeedLabelOnTop = true,
});
TextEditingController controller = TextEditingController();
@ -77,7 +79,7 @@ class TxtField extends StatelessWidget {
return Column(
children: [
if (hint != null) ...[
if (isNeedLabelOnTop && hint != null) ...[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
@ -149,7 +151,7 @@ class TxtField extends StatelessWidget {
prefixIcon: prefixData != null ? Icon(prefixData, color: borderColor) : preFixWidget,
labelStyle: const TextStyle(color: borderColor, fontSize: 13, fontWeight: MyFonts.Medium),
hintStyle: const TextStyle(color: borderColor, fontSize: 13, fontWeight: MyFonts.Medium),
// hintText: hint ?? "",
hintText: isNeedLabelOnTop ? null : hint ?? "",
contentPadding: prefixData == null
? EdgeInsets.only(
left: 12,

Loading…
Cancel
Save