Fixing JIRA Issues

aamir_dev
Faiz Hashmi 12 months ago
parent d414327147
commit 7326886660

@ -698,7 +698,6 @@
"explore": "استكشاف",
"manageRequests": "إدارة الطلبات",
"serviceNotAvailableAtHomeLocation": "الخدمة المختارة غير متوفرة في الموقع المحدد.",
"noAvailableItems": "لا توجد عناصر متاحة.",
"wantToRescheduleAppointment": "أرغب في إعادة جدولة الموعد.",
"noNeedForService": "لا أحتاج هذه الخدمة بعد الآن.",
"testTheService": "اختبر الخدمة.",
@ -706,5 +705,20 @@
"customerLocation": "موقع العميل",
"deliveryAvailable": "التوصيل متاح",
"viewed": "تم المشاهدة",
"itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا."
"itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا.",
"reactivateAd": "إعادة تنشيط الإعلان",
"dealOutsideApp": "تمت الصفقة خارج التطبيق مع عميل آخر.",
"noAgreementCustomer": "لا يوجد اتفاق من جانب العميل.",
"dealNotCompleted": "لم تكتمل الصفقة",
"changedDesireToSell": "غيرت رغبتي في بيع المركبة.",
"vehicleDealOutsideApp": "تمت صفقة المركبة خارج التطبيق.",
"markAdAsSoldDesc": "سيتم وضع علامة 'تم البيع' على هذا الإعلان، ولن يتمكن المستخدمون الآخرون من التواصل معك.",
"ownerInformation": "معلومات المالك",
"acceptedRequests": "الطلبات المقبولة",
"specialRequestChat": "دردشة الطلب الخاص",
"companyName": "اسم الشركة",
"noAvailableItems": "لا توجد عناصر متاحة.",
"serviceDeliveryType": "نوع تقديم الخدمة",
"noImagesToShow": "لا توجد صور للعرض",
"updateGroupServices": "تحديث خدمات المجموعة"
}

@ -626,7 +626,7 @@
"priceRange": "Price Range",
"cancelOffer": "Cancel Offer",
"pleaseSpecify": "Please Specify",
"customerNotResponding": "Customer Not Responding",
"customerNotResponding": "The customer is not responding.",
"cancelRequestPrompt": "Do you want to cancel this request?",
"requestPermanentlyCancelled": "Your request will be permanently cancelled. You cannot undo this action.",
"awaitingResponseFromCustomer": "Awaiting Response From Customer",
@ -695,7 +695,6 @@
"explore": "Explore",
"manageRequests": "Manage Requests",
"serviceNotAvailableAtHomeLocation": "The Selected Service is not available at Home Location.",
"noAvailableItems": "There are no available items.",
"wantToRescheduleAppointment": "I want to reschedule the appointment.",
"noNeedForService": "I do not need this service anymore.",
"testTheService": "Test the service.",
@ -703,6 +702,21 @@
"customerLocation": "Customer Location",
"deliveryAvailable": "Delivery Available",
"viewed": "Viewed",
"itemNoLongerAvailable": "This item is no longer available."
"itemNoLongerAvailable": "This item is no longer available.",
"reactivateAd": "Reactivate Ad",
"dealOutsideApp": "The deal was done outside the app with another customer.",
"noAgreementCustomer": "There is no agreement from the customer side.",
"dealNotCompleted": "Deal not completed",
"changedDesireToSell": "I changed my desire to sell the vehicle.",
"vehicleDealOutsideApp": "The vehicle deal was done outside the app.",
"markAdAsSoldDesc": "This ad will be marked as sold, and other users will no longer be able to contact you.",
"ownerInformation": "Owner Information",
"noItemsToShow": "There are no Items no show.",
"acceptedRequests": "Accepted Requests",
"specialRequestChat": "Special Request Chat",
"companyName": "Company Name",
"noAvailableItems": "There are no available items.",
"serviceDeliveryType": "Service Delivery Type",
"noImagesToShow": "No Images to Show",
"updateGroupServices": "Update Group Services"
}

@ -14,6 +14,8 @@ class ApiConsts {
static String basicComplete = "${baseUrlServices}api/Register/BasicComplete";
static String refreshToken = "${baseUrlServices}api/Account/RefreshToken";
static String signalRUrl = "$baseUrl/McHub";
//User
static String Login_V1 = "${baseUrlServices}api/Account/Login_V1";
static String Login_V2_OTP = "${baseUrlServices}api/Account/Login_V2_OTP";
@ -96,6 +98,7 @@ class ApiConsts {
static String updateSchedule = "${baseUrlServices}api/ServiceProviders/BranchAppointmentSchedule_Update";
static String createGroup = "${baseUrlServices}api/ServiceProviders/BranchScheduleGroupService_Create";
static String updateGroup = "${baseUrlServices}api/ServiceProviders/BranchScheduleGroupService_Update";
static String checkGroupServiceInBranchSchedule = "${baseUrlServices}api/ServiceProviders/BranchScheduleGroupService_Check";
//Advertisement APIs
static String vehicleTypeGet = "${baseUrlServices}api/ServiceProviders/VehicleType_Get";
@ -227,7 +230,8 @@ class GlobalConsts {
static String homeLocationEmptyError = "Home location cannot be empty";
static String fillAllFields = "Please fill out all the fields.";
static String requestTypeCannotBeEmpty = "Request type cannot be empty.";
static String reserveAdPriceInfo = "Some dummy description to explain the following concept. This price will be for 24 hours and if a user cancels the reservations before 24 hours then the amount will be automatically refunded to the buyer.";
static String reserveAdPriceInfo =
"Some dummy description to explain the following concept. This price will be for 24 hours and if a user cancels the reservations before 24 hours then the amount will be automatically refunded to the buyer.";
static String appInvitationMessageEn = "🚗 Hey , check out MOWATER to book car services, buy/sell vehicles, and more—join here! [Invite Link]";
static String getAppInvitationLink() {

@ -18,6 +18,7 @@ import 'package:mc_common_app/views/requests/offer_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';
import 'package:mc_common_app/views/setting_options/provider_accepted_requests_view.dart';
import 'package:mc_common_app/views/setting_options/provider_license_page.dart';
import 'package:mc_common_app/views/setting_options/setting_option_help.dart';
import 'package:mc_common_app/views/setting_options/setting_options_app_info.dart';
@ -145,6 +146,7 @@ class AppRoutes {
static const String createRequestPage = "/createRequestPage";
static const String offersListPage = "/offersListPage";
static const String reviewRequestOffer = "/reviewRequestOffer";
static const String providerAcceptedRequestsView = "/providerAcceptedRequestsView";
//Setting Options
static const String settingOptionsFaqs = "/settingOptionsFaqs";
@ -208,7 +210,7 @@ class AppRoutes {
//Requests
AppRoutes.requestsDetailPage: (context) => RequestDetailPage(requestDetailPageArguments: ModalRoute.of(context)!.settings.arguments as RequestDetailPageArguments),
AppRoutes.createRequestPage: (context) => const CreateRequestPage(),
AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments),
AppRoutes.offersListPage: (context) => OfferListPage(requestId: ModalRoute.of(context)!.settings.arguments as int),
AppRoutes.reviewRequestOffer: (context) => const ReviewRequestOffer(),
AppRoutes.requestsFilterView: (context) => const RequestsFilterView(),
@ -216,7 +218,7 @@ class AppRoutes {
AppRoutes.mediaViewerScreen: (context) => MediaViewerScreen(images: ModalRoute.of(context)!.settings.arguments as List<MessageImageModel>),
// ChatsList Provider
AppRoutes.generalChatsListForProvider: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments),
AppRoutes.generalChatsListForProvider: (context) => OfferListPage(requestId: ModalRoute.of(context)!.settings.arguments as int),
//Shipping
AppRoutes.shippingManagementView: (context) => const ShippingManagementView(),

@ -238,6 +238,8 @@ extension AdPostEnum on int {
return AdPostStatus.buyingService;
} else if (this == 11) {
return AdPostStatus.reserveCancel;
} else if (this == 12) {
return AdPostStatus.deActive;
} else if (this == 0) {
return AdPostStatus.allAds;
} else {
@ -264,15 +266,21 @@ extension AppointmentStatusToInt on AppointmentStatusEnum {
case AppointmentStatusEnum.rescheduled:
return 5;
case AppointmentStatusEnum.upcoming:
return 6;
case AppointmentStatusEnum.workStarted:
return 7;
case AppointmentStatusEnum.visitCompleted:
return 8;
case AppointmentStatusEnum.today:
return 9;
case AppointmentStatusEnum.past:
return 10;
case AppointmentStatusEnum.upcoming:
return 11;
case AppointmentStatusEnum.allAppointments:
return 0;
@ -334,14 +342,18 @@ extension AppointmentStatusToString on AppointmentStatusEnum {
case AppointmentStatusEnum.visitCompleted:
return "Visit Completed";
case AppointmentStatusEnum.past:
return "Past";
case AppointmentStatusEnum.today:
return "Today";
default:
return "Booked";
}
}
}
//TODO: Need to verify Enum on upcoming and inprogress with the database
//TODO: 6 is service Deactivated
extension AppointmentEnum on int {
AppointmentStatusEnum toAppointmentStatusEnum() {
if (this == 1) {
@ -354,12 +366,16 @@ extension AppointmentEnum on int {
return AppointmentStatusEnum.cancelled;
} else if (this == 5) {
return AppointmentStatusEnum.rescheduled;
} else if (this == 6) {
return AppointmentStatusEnum.upcoming;
} else if (this == 7) {
return AppointmentStatusEnum.workStarted;
} else if (this == 8) {
return AppointmentStatusEnum.visitCompleted;
} else if (this == 9) {
return AppointmentStatusEnum.today;
} else if (this == 10) {
return AppointmentStatusEnum.past;
} else if (this == 11) {
return AppointmentStatusEnum.upcoming;
} else {
return AppointmentStatusEnum.allAppointments;
}
@ -409,7 +425,7 @@ extension AppointmentPaymentEnum on int {
}
extension RequestTypeTypEnum on int {
RequestsTypeEnum toRequestTypeStatusEnum() {
RequestsTypeEnum toRequestTypeEnum() {
if (this == 1) {
return RequestsTypeEnum.specialCarRequest;
} else if (this == 2) {
@ -420,7 +436,7 @@ extension RequestTypeTypEnum on int {
}
extension RequestTypeStatusToInt on RequestsTypeEnum {
int getIdFromRequestTypeStatusEnum() {
int getIdFromRequestTypeEnum() {
switch (this) {
case RequestsTypeEnum.specialCarRequest:
return 1;
@ -439,6 +455,7 @@ extension AdPostStatusToInt on AdPostStatus {
switch (this) {
case AdPostStatus.pendingForReview:
return 1;
case AdPostStatus.pendingForPayment:
return 2;
@ -468,6 +485,10 @@ extension AdPostStatusToInt on AdPostStatus {
case AdPostStatus.reserveCancel:
return 11;
case AdPostStatus.deActive:
return 12;
default:
return 0;
}
@ -910,8 +931,10 @@ extension SubscriptionTypeEnumToString on SubscriptionTypeEnum {
extension ShippingStatusEnumExt on int {
ShippingRequestStatusEnum toShippingStatusEnum() {
if (this == 0) {
if (this == -1) {
return ShippingRequestStatusEnum.allRequests;
} else if (this == 0) {
return ShippingRequestStatusEnum.pending;
} else if (this == 1) {
return ShippingRequestStatusEnum.initiated;
} else if (this == 2) {
@ -921,13 +944,15 @@ extension ShippingStatusEnumExt on int {
} else if (this == 4) {
return ShippingRequestStatusEnum.delivered;
}
return ShippingRequestStatusEnum.initiated;
return ShippingRequestStatusEnum.pending;
}
}
extension ShippingStatusEnumToInt on ShippingRequestStatusEnum {
int getIdFromShippingStatusEnum() {
switch (this) {
case ShippingRequestStatusEnum.pending:
return 0;
case ShippingRequestStatusEnum.initiated:
return 1;
case ShippingRequestStatusEnum.inTransit:
@ -936,7 +961,6 @@ extension ShippingStatusEnumToInt on ShippingRequestStatusEnum {
return 3;
case ShippingRequestStatusEnum.delivered:
return 4;
default:
return 0;
}

@ -714,7 +714,6 @@ class CodegenLoader extends AssetLoader{
"explore": "استكشاف",
"manageRequests": "إدارة الطلبات",
"serviceNotAvailableAtHomeLocation": "الخدمة المختارة غير متوفرة في الموقع المحدد.",
"noAvailableItems": "لا توجد عناصر متاحة.",
"wantToRescheduleAppointment": "أرغب في إعادة جدولة الموعد.",
"noNeedForService": "لا أحتاج هذه الخدمة بعد الآن.",
"testTheService": "اختبر الخدمة.",
@ -722,7 +721,22 @@ class CodegenLoader extends AssetLoader{
"customerLocation": "موقع العميل",
"deliveryAvailable": "التوصيل متاح",
"viewed": "تم المشاهدة",
"itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا."
"itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا.",
"reactivateAd": "إعادة تنشيط الإعلان",
"dealOutsideApp": "تمت الصفقة خارج التطبيق مع عميل آخر.",
"noAgreementCustomer": "لا يوجد اتفاق من جانب العميل.",
"dealNotCompleted": "لم تكتمل الصفقة",
"changedDesireToSell": "غيرت رغبتي في بيع المركبة.",
"vehicleDealOutsideApp": "تمت صفقة المركبة خارج التطبيق.",
"markAdAsSoldDesc": "سيتم وضع علامة 'تم البيع' على هذا الإعلان، ولن يتمكن المستخدمون الآخرون من التواصل معك.",
"ownerInformation": "معلومات المالك",
"acceptedRequests": "الطلبات المقبولة",
"specialRequestChat": "دردشة الطلب الخاص",
"companyName": "اسم الشركة",
"noAvailableItems": "لا توجد عناصر متاحة.",
"serviceDeliveryType": "نوع تقديم الخدمة",
"noImagesToShow": "لا توجد صور للعرض",
"updateGroupServices": "تحديث خدمات المجموعة"
};
static const Map<String,dynamic> en_US = {
"firstTimeLogIn": "First Time Log In",
@ -1352,7 +1366,7 @@ static const Map<String,dynamic> en_US = {
"priceRange": "Price Range",
"cancelOffer": "Cancel Offer",
"pleaseSpecify": "Please Specify",
"customerNotResponding": "Customer Not Responding",
"customerNotResponding": "The customer is not responding.",
"cancelRequestPrompt": "Do you want to cancel this request?",
"requestPermanentlyCancelled": "Your request will be permanently cancelled. You cannot undo this action.",
"awaitingResponseFromCustomer": "Awaiting Response From Customer",
@ -1421,7 +1435,6 @@ static const Map<String,dynamic> en_US = {
"explore": "Explore",
"manageRequests": "Manage Requests",
"serviceNotAvailableAtHomeLocation": "The Selected Service is not available at Home Location.",
"noAvailableItems": "There are no available items.",
"wantToRescheduleAppointment": "I want to reschedule the appointment.",
"noNeedForService": "I do not need this service anymore.",
"testTheService": "Test the service.",
@ -1429,7 +1442,23 @@ static const Map<String,dynamic> en_US = {
"customerLocation": "Customer Location",
"deliveryAvailable": "Delivery Available",
"viewed": "Viewed",
"itemNoLongerAvailable": "This item is no longer available."
"itemNoLongerAvailable": "This item is no longer available.",
"reactivateAd": "Reactivate Ad",
"dealOutsideApp": "The deal was done outside the app with another customer.",
"noAgreementCustomer": "There is no agreement from the customer side.",
"dealNotCompleted": "Deal not completed",
"changedDesireToSell": "I changed my desire to sell the vehicle.",
"vehicleDealOutsideApp": "The vehicle deal was done outside the app.",
"markAdAsSoldDesc": "This ad will be marked as sold, and other users will no longer be able to contact you.",
"ownerInformation": "Owner Information",
"noItemsToShow": "There are no Items no show.",
"acceptedRequests": "Accepted Requests",
"specialRequestChat": "Special Request Chat",
"companyName": "Company Name",
"noAvailableItems": "There are no available items.",
"serviceDeliveryType": "Service Delivery Type",
"noImagesToShow": "No Images to Show",
"updateGroupServices": "Update Group Services"
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": ar_SA, "en_US": en_US};
}

@ -677,7 +677,6 @@ abstract class LocaleKeys {
static const explore = 'explore';
static const manageRequests = 'manageRequests';
static const serviceNotAvailableAtHomeLocation = 'serviceNotAvailableAtHomeLocation';
static const noAvailableItems = 'noAvailableItems';
static const wantToRescheduleAppointment = 'wantToRescheduleAppointment';
static const noNeedForService = 'noNeedForService';
static const testTheService = 'testTheService';
@ -686,5 +685,19 @@ abstract class LocaleKeys {
static const deliveryAvailable = 'deliveryAvailable';
static const viewed = 'viewed';
static const itemNoLongerAvailable = 'itemNoLongerAvailable';
static const reactivateAd = 'reactivateAd';
static const dealOutsideApp = 'dealOutsideApp';
static const noAgreementCustomer = 'noAgreementCustomer';
static const dealNotCompleted = 'dealNotCompleted';
static const changedDesireToSell = 'changedDesireToSell';
static const vehicleDealOutsideApp = 'vehicleDealOutsideApp';
static const markAdAsSoldDesc = 'markAdAsSoldDesc';
static const ownerInformation = 'ownerInformation';
static const acceptedRequests = 'acceptedRequests';
static const specialRequestChat = 'specialRequestChat';
static const companyName = 'companyName';
static const noAvailableItems = 'noAvailableItems';
static const serviceDeliveryType = 'serviceDeliveryType';
static const noImagesToShow = 'noImagesToShow';
static const updateGroupServices = 'updateGroupServices';
}

@ -45,6 +45,8 @@ class AdDetailsModel {
String? phoneNo;
String? whatsAppNo;
String? adOwnerName;
String? adOwnerEmail;
AdOwnerDetails? adOwnerDetails;
String? warrantyYears;
CreatedByRoleEnum? createdByRoleEnum;
List<ChatMessageModel>? adMessages;
@ -87,6 +89,8 @@ class AdDetailsModel {
this.phoneNo,
this.whatsAppNo,
this.adOwnerName,
this.adOwnerEmail,
this.adOwnerDetails,
this.warrantyYears,
this.createdByRoleEnum,
this.modifiedOn,
@ -138,7 +142,9 @@ class AdDetailsModel {
phoneNo = json['vehicle'] != null ? (json['vehicle']['mobileNo'] ?? "") : "";
whatsAppNo = json['vehicle'] != null ? (json['vehicle']['whatsAppNo'] ?? "") : "";
warrantyYears = json['vehicle'] != null ? (json['vehicle']['warantyYears'] != null ? ((json['vehicle']['warantyYears']).toString()) : "") : "";
adOwnerName = json['adOwnerName'] ?? "";
adOwnerName = json['vehicle'] != null ? (json['vehicle']['adOwnerName'] ?? "") : "";
adOwnerEmail = json['vehicle'] != null ? (json['vehicle']['adOwnerEmail'] ?? "") : "";
adOwnerDetails = (json['vehicle'] != null && json['vehicle']['aDsUser'] != null) ? (AdOwnerDetails.fromJson(json['vehicle']['aDsUser'])) : null;
adPostStatus = (json['statusID'] as int).toAdPostEnum();
adReserveStatus = AdReserveStatus.defaultStatus;
createdByRoleEnum = (json['createdByRole'] as int).toCreatedByRoleEnum();
@ -155,7 +161,7 @@ class Vehicle {
bool? isActive;
bool? isFinanceAvailable;
int? status;
String? statustext;
String? statusText;
Category? category;
Category? color;
Condition? condition;
@ -174,14 +180,15 @@ class Vehicle {
int? countryID;
String? currency;
Vehicle({this.id,
Vehicle(
{this.id,
this.cityID,
this.cityName,
this.demandAmount,
this.isActive,
this.isFinanceAvailable,
this.status,
this.statustext,
this.statusText,
this.category,
this.color,
this.condition,
@ -208,7 +215,7 @@ class Vehicle {
isActive = json['isActive'];
isFinanceAvailable = json['isFinanceAvailable'];
status = json['status'];
statustext = json['statustext'];
statusText = json['statustext'];
category = json['category'] != null ? Category.fromJson(json['category']) : null;
color = json['color'] != null ? Category.fromJson(json['color']) : null;
condition = json['condition'] != null ? Condition.fromJson(json['condition']) : null;
@ -417,3 +424,28 @@ class DamageReport {
return data;
}
}
class AdOwnerDetails {
String? name;
String? email;
String? mobileNo;
String? profilePic;
AdOwnerDetails({this.name, this.email, this.mobileNo, this.profilePic});
AdOwnerDetails.fromJson(Map<String, dynamic> json) {
name = json['name'];
email = json['email'];
mobileNo = json['mobileNo'];
profilePic = json['profilePic'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['name'] = name;
data['email'] = email;
data['mobileNo'] = mobileNo;
data['profilePic'] = profilePic;
return data;
}
}

@ -11,6 +11,7 @@ class CustomTimeDateSlotModel {
}
class ServiceAppointmentScheduleModel {
String? scheduleName;
List<ServiceSlotList>? serviceSlotList;
List<ServiceModel>? servicesListInAppointment;
int? selectedDateIndex;
@ -23,6 +24,7 @@ class ServiceAppointmentScheduleModel {
AppointmentTypeEnum? appointmentTypeEnum;
ServiceAppointmentScheduleModel({
this.scheduleName,
this.serviceSlotList,
this.servicesListInAppointment,
this.selectedDateIndex,
@ -90,6 +92,8 @@ class ServiceAppointmentScheduleModel {
}
ServiceAppointmentScheduleModel.fromJson(Map<String, dynamic> json, {bool isForAppointment = false}) {
scheduleName = json['scheduleName'];
if (json['serviceSlotList'] != null) {
serviceSlotList = <ServiceSlotList>[];
json['serviceSlotList'].forEach((v) {

@ -34,7 +34,7 @@ class ProviderOffersModel {
this.serviceProviders,
});
ProviderOffersModel.fromJson(Map<String, dynamic> json) {
ProviderOffersModel.fromJson(Map<String, dynamic> json, int? reqId) {
id = json['id'];
customerID = json['customerID'];
requestType = json['requestType'];
@ -49,7 +49,7 @@ class ProviderOffersModel {
if (json['serviceProviders'] != null) {
serviceProviders = <ServiceProvidersOffers>[];
json['serviceProviders'].forEach((v) {
serviceProviders!.add(ServiceProvidersOffers.fromJson(v));
serviceProviders!.add(ServiceProvidersOffers.fromJson(v, reqId));
});
}
}
@ -65,6 +65,7 @@ class ServiceProvidersOffers {
String? createdOn;
RequestOfferStatusEnum? requestOfferStatusEnum;
int? offerCount;
int? requestId;
List<ChatMessageModel>? chatMessages;
ServiceProvidersOffers({
@ -74,13 +75,14 @@ class ServiceProvidersOffers {
this.email,
this.companyName,
this.offerCount,
this.requestId,
this.chatMessages,
this.providerUserId,
this.createdOn,
this.requestOfferStatusEnum,
});
ServiceProvidersOffers.fromJson(Map<String, dynamic> json) {
ServiceProvidersOffers.fromJson(Map<String, dynamic> json, int? reqId) {
providerId = json['providerID'];
providerUserId = json['providerUserID'];
name = json['name'];
@ -88,6 +90,7 @@ class ServiceProvidersOffers {
email = json['email'];
companyName = json['companyName'];
offerCount = json['offerCount'];
requestId = reqId;
createdOn = json['createdOn'];
requestOfferStatusEnum = ((json['offerStatusLast']) as int).toRequestOfferStatusEnum();
chatMessages = [];

@ -1,27 +1,80 @@
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/utils/enums.dart';
class ShippingRequestModel {
int? id;
int? requestID;
RequestModel? request;
Request? request;
int? shippingStatus;
ShippingRequestStatusEnum? shippingStatusEnum;
String? deliveredOn;
String? comment;
String? createdOn;
int? customerID;
String? customerName;
ShippingRequestModel({this.id, this.requestID, this.request, this.shippingStatus, this.deliveredOn, this.comment, this.createdOn});
ShippingRequestModel({this.id, this.requestID, this.request, this.shippingStatus, this.deliveredOn, this.comment, this.createdOn, this.customerID, this.customerName});
ShippingRequestModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestID = json['requestID'];
request = json['request'];
request = json['request'] != null ? Request.fromJson(json['request']) : null;
shippingStatus = json['shippingStatus'];
shippingStatusEnum = json['shippingStatus'] != null ? (json['shippingStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.initiated;
deliveredOn = json['deliveredOn'];
comment = json['comment'];
createdOn = json['createdOn'];
customerID = json['customerID'];
customerName = json['customerName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['requestID'] = requestID;
if (request != null) {
data['request'] = request!.toJson();
}
data['shippingStatus'] = shippingStatus;
data['deliveredOn'] = deliveredOn;
data['comment'] = comment;
data['createdOn'] = createdOn;
data['customerID'] = customerID;
data['customerName'] = customerName;
return data;
}
}
class Request {
int? requestType;
String? brand;
String? model;
int? year;
bool? isNew;
String? description;
double? price;
Request({this.requestType, this.brand, this.model, this.year, this.isNew, this.description, this.price});
Request.fromJson(Map<String, dynamic> json) {
requestType = json['requestType'];
brand = json['brand'];
model = json['model'];
year = json['year'];
isNew = json['isNew'];
description = json['description'];
price = json['price'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['requestType'] = requestType;
data['brand'] = brand;
data['model'] = model;
data['year'] = year;
data['isNew'] = isNew;
data['description'] = description;
data['price'] = price;
return data;
}
}

@ -67,7 +67,8 @@ class AdsRepoImp implements AdsRepo {
var params = {
"SpecialServiceType": specialServiceType.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsSpecialServicesGet, queryParameters: params);
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsSpecialServicesGet, queryParameters: params);
List<SpecialServiceModel> vehicleAdsDuration = List.generate(adsGenericModel.data.length, (index) => SpecialServiceModel.fromJson(adsGenericModel.data[index]));
return vehicleAdsDuration;
}
@ -89,7 +90,14 @@ class AdsRepoImp implements AdsRepo {
List vehiclePostingDamageParts = [];
adsCreationPayloadModel.vehiclePosting!.vehiclePostingDamageParts?.forEach((element) {
var imageMap = {"id": element.id ?? 0, "comment": element.comment, "vehicleImageBase64": element.vehicleImageBase64, "vehicleDamagePartID": element.vehicleDamagePartID, "vehiclePostingID": element.vehiclePostingID ?? 0, "isActive": true};
var imageMap = {
"id": element.id ?? 0,
"comment": element.comment,
"vehicleImageBase64": element.vehicleImageBase64,
"vehicleDamagePartID": element.vehicleDamagePartID,
"vehiclePostingID": element.vehiclePostingID ?? 0,
"isActive": true
};
vehiclePostingDamageParts.add(imageMap);
});
var postParams = {
@ -148,13 +156,15 @@ class AdsRepoImp implements AdsRepo {
};
if (isMyAds && adPostStatus != null) {
onlyMyAdsParams.addAll({
"AdsStatuses": ["${adPostStatus.getIdFromAdPostStatusEnum()}"]
"AdsStatuses": ["${adPostStatus.getIdFromAdPostStatusEnum()}"],
"PageSize": "30",
});
}
var allAdsParams = {
"AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS
"isActive": "true", //only Active ADS
"isExplore": "true"
"isExplore": "true",
"PageSize": "30",
};
if (!isMyAds && createdByRoleEnum != null) {
@ -186,7 +196,8 @@ class AdsRepoImp implements AdsRepo {
"CreatedByRoles": createdByRolesIdsList ?? [],
"AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS
"isActive": "true", //only Active ADS
"isExplore": "true"
"isExplore": "true",
"PageSize": "30",
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
@ -222,7 +233,11 @@ class AdsRepoImp implements AdsRepo {
}
Future<List<AdDetailsModel>> getAdsPerSpecificIds({required List<String> ids, required List<MyReservedAdsRespModel> reservedAds}) async {
var params = {"AdsIDs": ids, "isActive": "true"};
var params = {
"AdsIDs": ids,
"isActive": "true",
"PageSize": "30",
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),
@ -243,6 +258,7 @@ class AdsRepoImp implements AdsRepo {
Future<List<AdDetailsModel>> getMyAds() async {
var params = {
"userID": appState.getUser.data!.userInfo!.userId ?? "",
"PageSize": "30",
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,

@ -132,7 +132,8 @@ class AppointmentRepoImp implements AppointmentRepo {
if (adsGenericModel.data == null) {
return [];
}
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleModel = List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index], isForAppointment: true));
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleModel =
List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index], isForAppointment: true));
return serviceAppointmentScheduleModel;
}
@ -149,8 +150,19 @@ class AppointmentRepoImp implements AppointmentRepo {
serviceItemIds.add(item.id!);
}
}
int slotId = 0;
int index = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.indexWhere((element) => element.isSelected);
if (index == -1) {
index = 0;
}
slotId = schedule.selectedCustomTimeDateSlotModel!.availableSlots![index].slotId;
mapList.add({
"serviceSlotID": schedule.selectedCustomTimeDateSlotModel!.date!.slotId,
"serviceSlotID": slotId,
"serviceProviderID": serviceProviderID,
"customerID": customerId,
"serviceItemID": serviceItemIds,

@ -51,7 +51,7 @@ class ChatRepoImp implements ChatRepo {
@override
Future<HubConnection> getHubConnection() async {
final userId = AppState().getUser.data!.userInfo!.userId ?? "";
final hubUrl = "https://ms.hmg.com/McHub?userID=$userId";
final hubUrl = "${ApiConsts.signalRUrl}?userID=$userId";
logger.i("Connecting with Hub ($hubUrl)");
HubConnection hub;

@ -38,6 +38,7 @@ abstract class RequestRepo {
Future<List<RequestModel>> getRequestsBasedOnFilters({
required int requestTypeId,
int requestStatusId = 0,
int reqOfferStatus = 0,
int? cityId = 0,
int? vehicleYearId = 0,
String? vehicleModel = "",
@ -131,6 +132,7 @@ class RequestRepoImp implements RequestRepo {
Future<List<RequestModel>> getRequestsBasedOnFilters({
required int requestTypeId,
int requestStatusId = 0,
int reqOfferStatus = 0,
int? cityId = 0,
int? vehicleYearId = 0,
String? vehicleModel = "",
@ -146,6 +148,7 @@ class RequestRepoImp implements RequestRepo {
"doPagination": true,
"requestType": requestTypeId.toString(),
"requestStatus": requestStatusId.toString(),
"reqOfferStatus": reqOfferStatus.toString(),
"brand": vehicleBrand.toString(),
"requestedDate": requestedDate,
"model": vehicleModel.toString(),
@ -291,7 +294,7 @@ class RequestRepoImp implements RequestRepo {
);
ProviderOffersModel providerOffersModel = ProviderOffersModel();
if (genericRespModel.data != null && genericRespModel.data.length > 0) {
providerOffersModel = ProviderOffersModel.fromJson(genericRespModel.data.first);
providerOffersModel = ProviderOffersModel.fromJson(genericRespModel.data.first, requestId);
}
return providerOffersModel;
}

@ -11,6 +11,8 @@ abstract class ScheduleRepo {
Future<GenericRespModel> createSchedule(Map map);
Future<GenericRespModel> checkServiceGroupInBranchSchedule(Map map);
Future<GenericRespModel> addServicesInSchedule(Map map);
Future<GenericRespModel> updateSchedule(Map map);
@ -36,6 +38,12 @@ class ScheduleRepoImp implements ScheduleRepo {
return await injector.get<ApiClient>().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.createSchedule, map, token: t);
}
@override
Future<GenericRespModel> checkServiceGroupInBranchSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.checkGroupServiceInBranchSchedule, map, token: t);
}
@override
Future<GenericRespModel> addServicesInSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? "";

@ -61,11 +61,21 @@ class SettingOptionsRepoImp extends SettingOptionsRepo {
@override
Future<List<ContactInfoModel>> getAllContactInfos() async {
int channel = 0;
if (AppState().currentAppType == AppType.provider) {
channel = 2;
} else {
channel = 3;
}
final params = {"Channel": channel.toString()};
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.getContactInfo,
queryParameters: params,
token: token,
);

@ -16,23 +16,15 @@ class AppEnums {
static const int appointmentsFilterEnumId = 13; // Appointments Filter Enums
static const int requestStatusesFilterEnumId = 15; // Appointments Filter Enums
static const int conditionEnumId = -1; // to get the Condition Filter Enums
static const int shippingStatusEnumId = -2; // to get the Shipping Filter Enums
static const int shippingStatusEnumId = 30; // to get the Shipping Filter Enums
}
enum DashboardRouteEnum {
fromAdsPayment,
fromAdsSubmit,
none,
}
enum VehicleType {
car,
motorCycle,
@ -70,6 +62,7 @@ enum AdPostStatus {
reserved,
buyingService,
reserveCancel,
deActive,
allAds,
}
@ -173,9 +166,11 @@ enum AppointmentStatusEnum {
cancelled,
rescheduled,
allAppointments,
upcoming,
workStarted,
visitCompleted,
today,
past,
upcoming,
}
enum AppointmentPaymentStatusEnum {
@ -228,6 +223,7 @@ enum SubscriptionActionTypeEnum {
enum ShippingRequestStatusEnum {
allRequests,
pending,
initiated,
inTransit,
outForDelivery,

@ -29,7 +29,14 @@ class Utils {
static bool get isLoading => _isLoadingVisible;
static void showToast(String message) {
Fluttertoast.showToast(msg: message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.black54, textColor: Colors.white, fontSize: 16.0);
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 2,
backgroundColor: Colors.black54,
textColor: Colors.white,
fontSize: 16.0);
}
static Future<void> openNumberViaCaller({required String phoneNumber}) async {
@ -84,7 +91,11 @@ class Utils {
return "";
}
return ("${timeOfDay.hour.toString().length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute.toString().length == 1 ? "0" : ""}${timeOfDay.minute}").toString();
return ("${timeOfDay.hour
.toString()
.length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute
.toString()
.length == 1 ? "0" : ""}${timeOfDay.minute}").toString();
}
static dynamic getNotNullValue(List<dynamic> list, int index) {
@ -234,6 +245,9 @@ class Utils {
case AdPostStatus.reserved:
return MyColors.primaryColor;
case AdPostStatus.deActive:
return MyColors.textColor;
case AdPostStatus.buyingService:
case AdPostStatus.reserveCancel:
case AdPostStatus.allAds:
@ -313,6 +327,9 @@ class Utils {
case ShippingRequestStatusEnum.delivered:
return "Delivered";
case ShippingRequestStatusEnum.pending:
return "Pending";
}
}
@ -332,6 +349,9 @@ class Utils {
case ShippingRequestStatusEnum.delivered:
return MyColors.greenColor;
case ShippingRequestStatusEnum.pending:
return MyColors.pendingColor;
}
}
@ -379,7 +399,12 @@ class Utils {
}
}
static statusContainerChip({required String text, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), Color chipColor = MyColors.greenColor, Color textColor = MyColors.white}) {
static statusContainerChip({
required String text,
EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6),
Color chipColor = MyColors.greenColor,
Color textColor = MyColors.white,
}) {
return Container(
decoration: BoxDecoration(
color: chipColor,
@ -562,7 +587,8 @@ class Utils {
}
static String getAdsPaymentBrowserForm({required int paymentId, required int adId}) {
return '<html> <head></head><body><form id="paymentForm" action="${ApiConsts.paymentWebViewUrl}" method="post"><input type="hidden" name="PaymentType" value="$paymentId"><input type="hidden" name="AdsID" value="$adId"></form><script type="text/javascript"> document.getElementById("paymentForm").submit(); </script></body></html>';
return '<html> <head></head><body><form id="paymentForm" action="${ApiConsts
.paymentWebViewUrl}" method="post"><input type="hidden" name="PaymentType" value="$paymentId"><input type="hidden" name="AdsID" value="$adId"></form><script type="text/javascript"> document.getElementById("paymentForm").submit(); </script></body></html>';
}
// BOTTOM SHEETS

@ -190,11 +190,12 @@ class AdVM extends BaseVM {
}
myAdsFilterOptions.insert(0, FilterListModel(title: "All Ads", isSelected: true, id: 0));
log("myAdsFilterOptions: ${myAdsFilterOptions.last.id}");
for (int i = 0; i < exploreAdsEnums.length; i++) {
exploreAdsFilterOptions.add(FilterListModel(title: "${exploreAdsEnums[i].enumValueStr} Ads", isSelected: false, id: exploreAdsEnums[i].enumValue));
}
exploreAdsFilterOptions.insert(0, FilterListModel(title: "All Ads", isSelected: true, id: 0));
notifyListeners();
}
@ -207,8 +208,7 @@ class AdVM extends BaseVM {
}
exploreAdsFilterOptions[index].isSelected = true;
if (createdByRoleFilter == CreatedByRoleEnum.allAds) {
exploreAdsFilteredList = exploreAds;
notifyListeners();
getExploreAds();
return;
}
setState(ViewState.busy);
@ -232,12 +232,12 @@ class AdVM extends BaseVM {
myAdsFilterOptions[index].isSelected = true;
if (adPostStatusEnum.getIdFromAdPostStatusEnum() == 0) {
myAdsFilteredList = myAds;
notifyListeners();
getMyAds();
return;
}
// this means if the filter is reserved ads
if (adPostStatusEnum == AdPostStatus.reserved) {
log("reserved: ${myReservedAds.length}");
myAdsFilteredList = myReservedAds;
for (var ad in myAdsFilteredList) {
ad.isReservedByMe = true;
@ -591,26 +591,16 @@ class AdVM extends BaseVM {
OfferRequestCommentModel(
index: 0,
isSelected: true,
title: LocaleKeys.changedMind.tr(),
title: LocaleKeys.vehicleDealOutsideApp.tr(),
),
OfferRequestCommentModel(
index: 1,
isSelected: false,
title: LocaleKeys.veryHighPrice.tr(),
title: LocaleKeys.changedDesireToSell.tr(),
),
OfferRequestCommentModel(
index: 2,
isSelected: false,
title: LocaleKeys.alreadySold.tr(),
),
OfferRequestCommentModel(
index: 3,
isSelected: false,
title: LocaleKeys.customerNotResponding.tr(),
),
OfferRequestCommentModel(
index: 4,
isSelected: false,
title: LocaleKeys.otherVar.tr(),
),
];
@ -626,6 +616,7 @@ class AdVM extends BaseVM {
value.isSelected = false;
}
selectedDeActivateAdCommentModel = deActivateAdModelList[index];
log("selectedDeActivateAdCommentModel: ${selectedDeActivateAdCommentModel.index}");
deActivateAdModelList[index].isSelected = true;
notifyListeners();
}
@ -684,10 +675,12 @@ class AdVM extends BaseVM {
SelectionModel vehicleSellerTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateSelectionVehicleSellerTypeId(SelectionModel id) {
void updateSelectionVehicleSellerTypeId(SelectionModel id, {bool needRefresh = true}) {
vehicleSellerTypeId = id;
if (needRefresh) {
notifyListeners();
}
}
// SelectionModel vehicleDamagePartId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
@ -925,12 +918,12 @@ class AdVM extends BaseVM {
vehicleTransmissionId.errorValue = "";
}
if (vehicleSellerTypeId.selectedId == -1) {
vehicleSellerTypeId.errorValue = LocaleKeys.vehicle_vehicleSellerType.tr();
isValidated = false;
} else {
vehicleSellerTypeId.errorValue = "";
}
// if (vehicleSellerTypeId.selectedId == -1) {
// vehicleSellerTypeId.errorValue = LocaleKeys.vehicle_vehicleSellerType.tr();
// isValidated = false;
// } else {
// vehicleSellerTypeId.errorValue = "";
// }
if (vehicleCountryId.selectedId == -1) {
vehicleCountryId.errorValue = LocaleKeys.vehicle_selectAny.tr();
@ -1483,7 +1476,7 @@ class AdVM extends BaseVM {
vehicleConditionID: vehicleConditionId.selectedId,
vehicleMileageID: vehicleMileageId.selectedId,
vehicleTransmissionID: vehicleTransmissionId.selectedId,
vehicleSellerTypeID: vehicleSellerTypeId.selectedId,
vehicleSellerTypeID: vehicleSellerTypeId.selectedId == -1 ? 1 : vehicleSellerTypeId.selectedId,
cityID: vehicleCityId.selectedId,
price: int.parse(vehicleDemandAmount),
vehicleVIN: vehicleVin,
@ -1843,7 +1836,8 @@ class AdVM extends BaseVM {
updateSelectionVehicleColorId(SelectionModel(selectedId: previousAdDetails!.vehicle!.color!.id!, selectedOption: previousAdDetails!.vehicle!.color!.label ?? ""));
updateSelectionVehicleConditionId(SelectionModel(selectedId: previousAdDetails!.vehicle!.condition!.id!, selectedOption: previousAdDetails!.vehicle!.condition!.label ?? ""));
updateSelectionVehicleCategoryId(SelectionModel(selectedId: previousAdDetails!.vehicle!.category!.id!, selectedOption: previousAdDetails!.vehicle!.category!.label ?? ""));
updateSelectionVehicleMileageId(SelectionModel(selectedId: previousAdDetails!.vehicle!.mileage!.id!, selectedOption: "${previousAdDetails!.vehicle!.mileage!.mileageStart} - ${previousAdDetails!.vehicle!.mileage!.mileageEnd}"));
updateSelectionVehicleMileageId(SelectionModel(
selectedId: previousAdDetails!.vehicle!.mileage!.id!, selectedOption: "${previousAdDetails!.vehicle!.mileage!.mileageStart} - ${previousAdDetails!.vehicle!.mileage!.mileageEnd}"));
updateSelectionVehicleTransmissionId(SelectionModel(selectedId: previousAdDetails!.vehicle!.transmission!.id!, selectedOption: previousAdDetails!.vehicle!.transmission!.label ?? ""));
updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: previousAdDetails!.vehicle!.sellertype!.id!, selectedOption: previousAdDetails!.vehicle!.sellertype!.label ?? ""));
int indexCountry = vehicleCountries.indexWhere((element) => element.id == previousAdDetails!.vehicle!.countryID);

@ -298,9 +298,17 @@ class AppointmentsVM extends BaseVM {
}
resetAfterBookingAppointment() {
// allSelectedItemsInAppointments.clear();
allSelectedItemsInAppointments.clear();
currentServiceSelection = null;
for (var element in servicesInCurrentAppointment) {
if (element.serviceItems != null) {
element.serviceItems!.clear();
element.serviceItems = [];
}
}
servicesInCurrentAppointment.clear();
// serviceAppointmentScheduleList.clear();
servicesInCurrentAppointment = [];
log("here servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}");
}
List<EnumsModel> myAppointmentsEnum = [];
@ -315,13 +323,11 @@ class AppointmentsVM extends BaseVM {
}
appointmentsFilterOptions.insert(0, FilterListModel(title: "All Appointments", isSelected: true, id: 0));
// TODO: THIS SHOULD REMOVED AND ADDED IN THE ENUMS API
appointmentsFilterOptions.add(FilterListModel(title: "Work In Progress", isSelected: false, id: 7));
appointmentsFilterOptions.add(FilterListModel(title: "Visit Completed", isSelected: false, id: 8));
notifyListeners();
}
applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) {
log("appointmentStatusEnum: ${appointmentStatusEnum}");
// isNeedCustomerFilter IS ONLY FOR THE PROVIDER APP
if (appointmentsFilterOptions.isEmpty) return;
for (var value in appointmentsFilterOptions) {
@ -405,7 +411,9 @@ class AppointmentsVM extends BaseVM {
myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters();
// myFilteredAppointments = myAppointments;
myUpComingAppointments = myAppointments
.where((element) => (element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) && (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.where((element) =>
(element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.toList();
setState(ViewState.idle);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
@ -433,39 +441,47 @@ class AppointmentsVM extends BaseVM {
myAppointments = await appointmentRepo.getMyAppointmentsForProvider(map);
myFilteredAppointments = myAppointments;
myUpComingAppointments = myAppointments
.where((element) => (element.appointmentStatusEnum == AppointmentStatusEnum.confirmed || element.appointmentStatusEnum == AppointmentStatusEnum.booked) && (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.where((element) =>
(element.appointmentStatusEnum == AppointmentStatusEnum.confirmed || element.appointmentStatusEnum == AppointmentStatusEnum.booked) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.toList();
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
setState(ViewState.idle);
}
updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum, bool isNeedToRebuild = false}) async {
Future<bool> updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum, bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
try {
GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentStatus(appointmentId: appointmentId, appointmentStatusEnum: appointmentStatusEnum);
if (genericRespModel.messageStatus == 1) {
Utils.showToast(LocaleKeys.appointmentStatusUpdated.tr());
return true;
} else {
Utils.showToast(genericRespModel.message.toString());
return false;
}
} catch (e) {
Utils.showToast(e.toString());
return false;
}
}
updateAppointmentPaymentStatus(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
Future<bool> updateAppointmentPaymentStatus(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
try {
GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentPaymentStatus(map);
if (genericRespModel.messageStatus == 1) {
Utils.showToast(LocaleKeys.paymentStatusUpdated.tr());
return true;
} else {
Utils.showToast(genericRespModel.message.toString());
return false;
}
} catch (e) {
Utils.showToast(e.toString());
return false;
}
}
@ -484,7 +500,8 @@ class AppointmentsVM extends BaseVM {
bool inNeedToEnableMergeButton = false;
void updateCheckBoxInMergeRequest(int currentIndex) {
myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected = !(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false);
myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected =
!(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false);
int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList ?? []);
if (count > 1) {
@ -540,10 +557,12 @@ class AppointmentsVM extends BaseVM {
void onItemUpdateOrSelected(int index, bool selected, int itemId) {
int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
log("index: $index");
log("selected: $selected");
log("itemId: $itemId");
log("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}");
// log("currentServiceSelection!.allSelectedItemsInAppointments: ${allSelectedItemsInAppointments.length}");
// log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}");
// log("index: $index");
// log("selected: $selected");
// log("itemId: $itemId");
// log("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}");
serviceItemsFromApi[index].isUpdateOrSelected = selected;
serviceItemsFromApi[index].isHomeSelected = isHomeTapped;
@ -554,23 +573,25 @@ class AppointmentsVM extends BaseVM {
selectSubServicesError = "";
currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]);
allSelectedItemsInAppointments.add(serviceItemsFromApi[index]);
for (var element in allSelectedItemsInAppointments) {
if (!ifItemAlreadySelected(element.id!)) {
// for (var element in allSelectedItemsInAppointments) {
if (!ifItemAlreadySelected(serviceItemsFromApi[index].id!)) {
if (serviceIndex != -1) {
servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
}
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
}
}
// }
} else {
log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}");
// log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}");
selectedSubServicesCounter = selectedSubServicesCounter - 1;
currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId);
log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}");
// log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}");
allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId);
if (serviceIndex != -1) {
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0");
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0");
servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId);
}
}
@ -714,7 +735,9 @@ class AppointmentsVM extends BaseVM {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
(selectedService.isHomeSelected ? "${(selectedService.currentTotalServicePrice) + ((double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms))}" : "${selectedService.currentTotalServicePrice}")
(selectedService.isHomeSelected
? "${(selectedService.currentTotalServicePrice) + ((double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms))}"
: "${selectedService.currentTotalServicePrice}")
.toText(fontSize: 29, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),

@ -19,12 +19,13 @@ import 'package:mc_common_app/services/common_services.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:provider/provider.dart';
import 'package:signalr_core/signalr_core.dart';
import 'package:easy_localization/easy_localization.dart';
class ChatVM extends ChangeNotifier {
class ChatVM extends BaseVM {
final ChatRepo chatRepo;
final RequestRepo requestRepo;
final CommonAppServices commonServices;
@ -75,34 +76,48 @@ class ChatVM extends ChangeNotifier {
}
}
List<int> indexesForCancelSpecialCarOffer = [0, 1, 6, 7];
List<int> indexesForCancelSparePartOffer = [2, 7];
List<int> indexesForRejectOffer = [3, 4, 5, 7];
List<OfferRequestCommentModel> offerRejectModelList = [
OfferRequestCommentModel(
index: 0,
isSelected: true,
title: LocaleKeys.itemNoLongerAvailable.tr(),
title: LocaleKeys.dealOutsideApp.tr(),
),
OfferRequestCommentModel(
index: 1,
isSelected: false,
title: LocaleKeys.noAgreementCustomer.tr(),
),
OfferRequestCommentModel(
index: 2,
isSelected: false,
title: LocaleKeys.itemNoLongerAvailable.tr(),
),
OfferRequestCommentModel(
index: 3,
isSelected: true,
title: LocaleKeys.changedMind.tr(),
),
OfferRequestCommentModel(
index: 2,
index: 4,
isSelected: false,
title: LocaleKeys.veryHighPrice.tr(),
),
OfferRequestCommentModel(
index: 3,
index: 5,
isSelected: false,
title: LocaleKeys.alreadySold.tr(),
),
OfferRequestCommentModel(
index: 4,
index: 6,
isSelected: false,
title: LocaleKeys.customerNotResponding.tr(),
),
OfferRequestCommentModel(
index: 5,
index: 7,
isSelected: false,
title: LocaleKeys.otherVar.tr(),
),
@ -154,7 +169,8 @@ class ChatVM extends ChangeNotifier {
Future<void> onNewMessageReceivedForRequestOffer({required List<ChatMessageModel> messages, bool isMyOwnOffer = false, required RequestsVM requestsVM}) async {
if (AppState().currentAppType == AppType.customer) {
for (var currentMessage in messages) {
log("currentMessage: ${currentMessage.reqOffer!.reqOfferImages!.first.imageUrl}");
logger.i(currentMessage);
int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == currentMessage.senderUserID);
if (providerIndex != -1) {
if (currentMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer) {
@ -197,17 +213,17 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
}
void subscribeToReceiveRequestOfferMessages(BuildContext context) {
void _subscribeToReceiveRequestOfferMessages(BuildContext context) {
hubConnection!.on(SignalrConsts.receiveMessageRequestOffer, (List<Object?>? arguments) {
logger.i(arguments);
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];
for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>, isForReqOfferImagesURLs: true);
log("chatMessagechatMessage: ${chatMessage.reqOffer!.reqOfferImages!.first.imageUrl}");
chat.add(chatMessage);
}
onNewMessageReceivedForRequestOffer(messages: chat, requestsVM: context.read<RequestsVM>());
logger.i(arguments);
// Utils.showToast(arguments.toString());
});
}
@ -227,22 +243,67 @@ class ChatVM extends ChangeNotifier {
return status;
}
int _retryCount = 0;
final int _maxRetries = 5; // Maximum number of retries
bool _isReconnecting = false;
Future<void> _reconnectWithBackoff(BuildContext context) async {
if (_retryCount >= _maxRetries) {
logger.e("Maximum retry attempts reached. Stopping reconnection.");
return;
}
final delay = Duration(seconds: 2 * (_retryCount + 1)); // Exponential backoff
logger.i("Retrying connection in ${delay.inSeconds} seconds...");
await Future.delayed(delay);
_retryCount++;
await buildHubConnection(context); // Try reconnecting
}
Future<void> buildHubConnection(BuildContext context) async {
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
if (_isReconnecting) {
logger.i("Reconnection already in progress");
return;
}
if (_isReconnecting) {
logger.i("Reconnection already in progress");
return;
}
try {
_isReconnecting = true;
hubConnection = await chatRepo.getHubConnection();
await hubConnection!.start();
subscribeToReceiveRequestOfferMessages(context);
subscribeToReceiveAdMessages(context);
subscribeToReceiveGeneralMessages(context);
hubConnection!.onclose((exception) {
// Reset retry count on successful connection
_retryCount = 0;
_isReconnecting = false;
// Subscribe to messages
_subscribeToReceiveRequestOfferMessages(context);
_subscribeToReceiveAdMessages(context);
_subscribeToReceiveGeneralMessages(context);
// Configure handlers
hubConnection!.onclose((exception) async {
logger.i("onClose: ${exception.toString()}");
buildHubConnection(context);
await _reconnectWithBackoff(context);
});
hubConnection!.onreconnecting((exception) {
logger.i("onReconnecting: ${exception?.toString()}");
});
hubConnection!.onreconnected((connectionId) {
logger.i("onReconnected: ${connectionId.toString()}");
});
hubConnection!.onreconnecting((exception) => () => logger.i("onReconnecting: ${exception.toString()}"));
hubConnection!.onreconnected((connectionId) => () => logger.i("onReconnected: ${connectionId.toString()}"));
} catch (e) {
logger.i("Error: ${e.toString()}");
logger.e("Error during hub connection setup: ${e.toString()}");
_isReconnecting = false; // Reset on failure
}
notifyListeners();
@ -251,6 +312,12 @@ class ChatVM extends ChangeNotifier {
}
}
Future<void> closeHubConnection() async {
if (hubConnection != null && hubConnection!.state == HubConnectionState.connected) {
hubConnection!.stop();
}
}
Future<bool> onOfferSendForRequest({
required String receiverId,
required ChatMessageTypeEnum chatMessageType,
@ -424,18 +491,24 @@ class ChatVM extends ChangeNotifier {
List<ServiceProvidersOffers> serviceProviderOffersList = [];
Future<void> getOffersFromProvidersByRequest({required int requestId, required BuildContext context}) async {
Future<void> getOffersFromProvidersByRequest({required int requestId, bool isNeedLoading = true}) async {
try {
Utils.showLoading(context);
if (isNeedLoading) {
setState(ViewState.busy);
}
ProviderOffersModel providerOffersModel = await requestRepo.getOffersFromProvidersByRequest(requestId: requestId);
Utils.hideLoading(context);
if (isNeedLoading) {
setState(ViewState.idle);
}
serviceProviderOffersList.clear();
serviceProviderOffersList = providerOffersModel.serviceProviders ?? [];
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
if (isNeedLoading) {
setState(ViewState.idle);
}
}
}
@ -611,7 +684,7 @@ class ChatVM extends ChangeNotifier {
}
}
void subscribeToReceiveAdMessages(BuildContext context) {
void _subscribeToReceiveAdMessages(BuildContext context) {
hubConnection!.on(SignalrConsts.receiveMessageAds, (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];
@ -727,7 +800,7 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
}
void subscribeToReceiveGeneralMessages(BuildContext context) {
void _subscribeToReceiveGeneralMessages(BuildContext context) {
hubConnection!.on(SignalrConsts.receiveMessageGeneral, (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];

@ -73,7 +73,8 @@ class PaymentVM extends ChangeNotifier {
return;
}
Future<void> verifyPayments({required BuildContext context, required int id, List<int>? appointmentIds, required int paymentTypeId, required Function() onVerified, bool isForAppointment = false}) async {
Future<void> verifyPayments(
{required BuildContext context, required int id, List<int>? appointmentIds, required int paymentTypeId, required Function() onVerified, bool isForAppointment = false}) async {
try {
Utils.showLoading(context);
bool isPaid = await paymentRepo.verifyPayment(
@ -85,12 +86,13 @@ class PaymentVM extends ChangeNotifier {
Utils.hideLoading(context);
// TODO: VERIFY THIS WITH ZAHOOR
// if (!isPaid) {
if (!isPaid) {
// Utils.showToast(LocaleKeys.paymentFailed.tr());
// pop(context);
// return;
// }
return;
} else {
Utils.showToast(LocaleKeys.paymentSuccessful.tr());
}
onVerified();
} catch (e) {
Utils.showToast(e.toString());

@ -86,6 +86,7 @@ class RequestsVM extends BaseVM {
Future<void> getRequests({bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
applyFilterOnRequestsVM(requestsTypeEnum: RequestsTypeEnum.specialCarRequest);
isOfferSent = false;
setState(ViewState.idle);
}
@ -111,7 +112,7 @@ class RequestsVM extends BaseVM {
requestStatusIdApi = requestStatusId.selectedId;
}
// try {
try {
myFilteredRequests = await requestRepo.getRequestsBasedOnFilters(
requestTypeId: requestTypeIdApi,
cityId: requestCityIdApi,
@ -124,12 +125,50 @@ class RequestsVM extends BaseVM {
);
setState(ViewState.idle);
return true;
// } catch (e, s) {
// logger.i("e: ${e.toString()}");
// Utils.showToast("Need to ask Zahoor Bhai to add params for this API: /api/RequestManagement/Request_ServiceProvider");
// setState(ViewState.idle);
// return false;
// }
} catch (e, s) {
logger.i("e: ${e.toString()}");
setState(ViewState.idle);
return false;
}
}
List<RequestModel> providersAcceptedRequestsList = [];
List<FilterListModel> acceptedRequestsTypeFilterOptions = [];
Future<List<RequestModel>> getProvidersAcceptedRequests() async {
setState(ViewState.busy);
if (acceptedRequestsTypeFilterOptions.isEmpty) {
for (int i = 0; i < myRequestsTypeEnum.length; i++) {
acceptedRequestsTypeFilterOptions.add(FilterListModel(title: myRequestsTypeEnum[i].enumValueStr, isSelected: false, id: myRequestsTypeEnum[i].enumValue));
}
acceptedRequestsTypeFilterOptions[0].isSelected = true;
}
providersAcceptedRequestsList.clear();
try {
int requestTypeIdApi = acceptedRequestsTypeFilterOptions.firstWhere((element) => element.isSelected).id;
providersAcceptedRequestsList = await requestRepo.getRequestsBasedOnFilters(
requestTypeId: requestTypeIdApi,
reqOfferStatus: RequestOfferStatusEnum.accepted.getIdFromRequestOfferStatusEnum(),
);
setState(ViewState.idle);
return providersAcceptedRequestsList;
} catch (e) {
logger.e(e.toString());
setState(ViewState.idle);
return [];
}
}
applyFilterOnProvidersAcceptedRequests({required RequestsTypeEnum requestsTypeEnum}) async {
if (acceptedRequestsTypeFilterOptions.isEmpty) return;
for (var value in acceptedRequestsTypeFilterOptions) {
value.isSelected = false;
}
acceptedRequestsTypeFilterOptions[requestsTypeEnum.getIdFromRequestTypeEnum() - 1].isSelected = true; // -1 to match with the index
await getProvidersAcceptedRequests();
notifyListeners();
}
Future<List<RequestModel>> getServiceRequestsForProviders() async {
@ -166,8 +205,9 @@ class RequestsVM extends BaseVM {
for (var value in requestsTypeFilterOptions) {
value.isSelected = false;
}
requestsTypeFilterOptions[requestsTypeEnum.getIdFromRequestTypeStatusEnum() - 1].isSelected = true; // -1 to match with the index
requestsTypeFilterOptions[requestsTypeEnum.getIdFromRequestTypeEnum() - 1].isSelected = true; // -1 to match with the index
await getRequestsBasedOnFilters();
isOfferSent = false;
notifyListeners();
}
@ -220,9 +260,10 @@ class RequestsVM extends BaseVM {
requestStatusesEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.requestStatusesFilterEnumId);
}
if (requestsTypeFilterOptions.isEmpty) {
if (requestsTypeFilterOptions.isEmpty || acceptedRequestsTypeFilterOptions.isEmpty) {
for (int i = 0; i < myRequestsTypeEnum.length; i++) {
requestsTypeFilterOptions.add(FilterListModel(title: myRequestsTypeEnum[i].enumValueStr, isSelected: false, id: myRequestsTypeEnum[i].enumValue));
acceptedRequestsTypeFilterOptions.add(FilterListModel(title: myRequestsTypeEnum[i].enumValueStr, isSelected: false, id: myRequestsTypeEnum[i].enumValue));
}
}
@ -721,7 +762,8 @@ class RequestsVM extends BaseVM {
}
} catch (e, s) {
Utils.hideLoading(context);
log(s.toString());
log(e.toString());
Utils.showToast(e.toString());
}
}
}
@ -904,6 +946,13 @@ class RequestsVM extends BaseVM {
return requestImages;
}
bool isOfferSent = false;
updateIsOfferSent(var value) {
isOfferSent = value;
notifyListeners();
}
Future<void> onSendOfferPressed({
required BuildContext context,
required String receiverId,
@ -946,6 +995,8 @@ class RequestsVM extends BaseVM {
return;
}
updateIsOfferSent(true);
final senderName = AppState().getUser.data!.userInfo!.firstName;
final senderId = AppState().getUser.data!.userInfo!.userId;
resetSendOfferBottomSheet();

@ -304,6 +304,8 @@ class ServiceVM extends BaseVM {
branches = await branchRepo.getBranchAndServices();
if (branches!.data == null) {
homePageBranches = [];
} else if (branches!.data!.serviceProviderBranch == null) {
homePageBranches = [];
} else {
homePageBranches = branches!.data!.serviceProviderBranch!.where((element) => element.branchStatus == BranchStatusEnum.approvedOrActive).toList();
}

@ -42,7 +42,7 @@ class ShippingManagementVM extends BaseVM {
}
Future<void> populateShippingRequestFilterList() async {
if (shippingRequestFilterOptions.isNotEmpty && shippingRequestStatusesList.isNotEmpty) return;
// if (shippingRequestFilterOptions.isNotEmpty && shippingRequestStatusesList.isNotEmpty) return;
shippingStatusEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.shippingStatusEnumId);
shippingRequestFilterOptions.clear();
@ -51,8 +51,8 @@ class ShippingManagementVM extends BaseVM {
shippingRequestFilterOptions.add(FilterListModel(title: shippingStatusEnums[i].enumValueStr, isSelected: false, id: shippingStatusEnums[i].enumValue));
shippingRequestStatusesList.add(FilterListModel(title: shippingStatusEnums[i].enumValueStr, isSelected: false, id: shippingStatusEnums[i].enumValue));
}
shippingRequestFilterOptions.insert(0, FilterListModel(title: Utils.getNameByShippingRequestStatusEnum(0.toShippingStatusEnum()), isSelected: true, id: 0));
shippingRequestFilterOptions.insert(0, FilterListModel(title: Utils.getNameByShippingRequestStatusEnum(0.toShippingStatusEnum()), isSelected: false, id: 0));
shippingRequestFilterOptions.insert(0, FilterListModel(title: Utils.getNameByShippingRequestStatusEnum(ShippingRequestStatusEnum.allRequests), isSelected: true, id: -1));
notifyListeners();
}
@ -66,7 +66,9 @@ class ShippingManagementVM extends BaseVM {
notifyListeners();
return;
}
shippingRequestFilterOptions[shippingRequestStatusEnum.getIdFromShippingStatusEnum()].isSelected = true; // -1 to match with the index
log("shippingRequestStatusEnum.getIdFromShippingStatusEnum(): ${shippingRequestStatusEnum.getIdFromShippingStatusEnum()}");
log("shippingRequestFilterOptions: ${shippingRequestFilterOptions[shippingRequestStatusEnum.getIdFromShippingStatusEnum() + 1].title}");
shippingRequestFilterOptions[shippingRequestStatusEnum.getIdFromShippingStatusEnum() + 1].isSelected = true; // +1 to match with the index 0 index has all requests
await getShippingRequestsListByFilters(shippingStatusEnum: shippingRequestStatusEnum);
notifyListeners();
}
@ -93,12 +95,12 @@ class ShippingManagementVM extends BaseVM {
}
Future<bool> onUpdateShippingStatusTapped({required BuildContext context, required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId}) async {
Utils.showLoading(context);
try {
GenericRespModel? genericRespModel = await shippingRepo.updateShippingRequestStatus(shippingStatusEnum: shippingStatusEnum, shippingRequestId: shippingRequestId);
GenericRespModel? genericRespModel = await shippingRepo.updateShippingRequestStatus(shippingStatusEnum: shippingStatusEnum, shippingRequestId: shippingRequestId, comment: requestStatusComments);
Utils.showToast(genericRespModel.message.toString());
Utils.hideLoading(context);
requestStatusComments = "";
return genericRespModel.messageStatus == 1;
} catch (e) {
logger.i(e.toString());

@ -36,6 +36,7 @@ import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/shared_prefrence.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/service_view_model.dart';
import 'package:mc_common_app/view_models/subscriptions_view_model.dart';
import 'package:mc_common_app/views/location_views/map_selection_widget.dart';
@ -435,7 +436,6 @@ class UserVM extends BaseVM {
}
} else {
Utils.showToast("Please verify your credentials and role type.");
}
} else {
Utils.showToast(verifiedUser.message ?? "");
@ -560,7 +560,8 @@ class UserVM extends BaseVM {
return await userRepo.getAllCountries();
}
Future<void> performBasicOtpRegisterPage(BuildContext context, {required String countryCode, required String phoneNum, required int role, bool isNeedToPassToken = false, VoidCallback? reloadPage}) async {
Future<void> performBasicOtpRegisterPage(BuildContext context,
{required String countryCode, required String phoneNum, required int role, bool isNeedToPassToken = false, VoidCallback? reloadPage}) async {
Utils.showLoading(context);
BasicOtpRespModel basicOtp = await userRepo.basicOtp(countryCode + phoneNum, roleId: role, isNeedToPassToken: isNeedToPassToken);
Utils.hideLoading(context);
@ -676,8 +677,10 @@ class UserVM extends BaseVM {
print(value.body);
print("Logout");
}
SharedPrefManager.setPhoneOrEmail("");
SharedPrefManager.setUserPassword("");
context.read<ChatVM>().closeHubConnection();
if (AppState().getUser.data!.userInfo!.userLocalImage != null) {
AppState().getUser.data!.userInfo!.userLocalImage = null;
AppState().getUser.data!.userInfo!.userImageUrl = null;

@ -17,7 +17,7 @@ import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class AdDuration extends StatelessWidget {
const AdDuration({Key? key}) : super(key: key);
const AdDuration({super.key});
void onAddSpecialServiceButtonTapped(BuildContext context, AdVM adVM) {
showModalBottomSheet(

@ -1,11 +1,16 @@
import 'dart:developer';
import 'package:flutter/cupertino.dart';
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/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/advertisment_models/vehicle_details_models.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart';
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
@ -26,7 +31,8 @@ class VehicleDetails extends StatelessWidget {
children: [
LocaleKeys.vehicleDetail.tr().toText(fontSize: 18, isBold: true),
8.height,
Builder(builder: (context) {
Builder(
builder: (context) {
List<DropValue> vehicleBrandsDrop = [];
for (var element in adVM.vehicleBrands) {
vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleBrandDescription ?? "", ""));
@ -38,7 +44,8 @@ class VehicleDetails extends StatelessWidget {
hint: LocaleKeys.vehicleBrand.tr(),
errorValue: adVM.vehicleBrandId.errorValue,
);
}),
},
),
if (adVM.vehicleBrandId.selectedId != -1) ...[
if (adVM.isFetchingLists) ...[
const Row(
@ -49,6 +56,32 @@ class VehicleDetails extends StatelessWidget {
).paddingAll(10),
] else ...[
8.height,
if (AppState().currentAppType == AppType.provider) ...[
Builder(builder: (context) {
List<DropValue> vehicleSellerTypesDrop = [];
for (var element in adVM.vehicleSellerTypes) {
vehicleSellerTypesDrop.add(DropValue(element.id?.toInt() ?? 0, element.sellerType ?? "", ""));
}
DropValue? model;
if (AppState().userType == UserType.providerDealer) {
model = vehicleSellerTypesDrop.firstWhere((element) => element.id == 2);
} else {
model = vehicleSellerTypesDrop.firstWhere((element) => element.id == 1);
}
adVM.updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: model.id, selectedOption: model.value), needRefresh: false);
return DropdownField(
(DropValue value) => adVM.updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
isSelectAble: false,
list: vehicleSellerTypesDrop,
dropdownValue: adVM.vehicleSellerTypeId.selectedId != -1 ? DropValue(adVM.vehicleSellerTypeId.selectedId, adVM.vehicleSellerTypeId.selectedOption, "") : null,
hint: LocaleKeys.vehicleSellerType.tr(),
errorValue: adVM.vehicleSellerTypeId.errorValue,
);
}),
8.height,
],
Builder(builder: (context) {
List<DropValue> vehicleModelsDrop = [];
for (var element in adVM.vehicleModels) {
@ -148,20 +181,7 @@ class VehicleDetails extends StatelessWidget {
);
}),
8.height,
Builder(builder: (context) {
List<DropValue> vehicleSellerTypesDrop = [];
for (var element in adVM.vehicleSellerTypes) {
vehicleSellerTypesDrop.add(DropValue(element.id?.toInt() ?? 0, element.sellerType ?? "", ""));
}
return DropdownField(
(DropValue value) => adVM.updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleSellerTypesDrop,
dropdownValue: adVM.vehicleSellerTypeId.selectedId != -1 ? DropValue(adVM.vehicleSellerTypeId.selectedId, adVM.vehicleSellerTypeId.selectedOption, "") : null,
hint: LocaleKeys.vehicleSellerType.tr(),
errorValue: adVM.vehicleSellerTypeId.errorValue,
);
}),
8.height,
Builder(builder: (context) {
List<DropValue> vehicleCountriesDrop = [];
for (var element in adVM.vehicleCountries) {

@ -1,6 +1,7 @@
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/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
@ -28,7 +29,7 @@ import 'package:easy_localization/easy_localization.dart';
class AdsDetailView extends StatefulWidget {
final AdDetailsModel adDetails;
const AdsDetailView({Key? key, required this.adDetails}) : super(key: key);
const AdsDetailView({super.key, required this.adDetails});
@override
State<AdsDetailView> createState() => _AdsDetailViewState();
@ -130,6 +131,12 @@ class _AdsDetailViewState extends State<AdsDetailView> {
"${widget.adDetails.vehicle!.transmission!.label}".toText(fontSize: 12),
],
),
Row(
children: [
("${LocaleKeys.financeAvailable.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
((widget.adDetails.vehicle!.isFinanceAvailable ?? false) ? LocaleKeys.yes.tr() : LocaleKeys.no.tr()).toText(fontSize: 12),
],
),
8.height,
("${LocaleKeys.description.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.vehicleDescription}".toText(fontSize: 14),
@ -158,6 +165,43 @@ class _AdsDetailViewState extends State<AdsDetailView> {
).toWhiteContainer(width: double.infinity, allPading: 12);
}
Widget showItem(String title, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"$title: ".toText(color: MyColors.lightTextColor, letterSpacing: -0.48),
3.width,
Flexible(child: value.toText(isBold: true, overflow: TextOverflow.ellipsis)),
],
);
}
Widget buildPersonalInformationCard({required BuildContext context}) {
if (widget.adDetails.adOwnerDetails == null) {
return const SizedBox();
}
if ((widget.adDetails.adOwnerDetails!.name == null || widget.adDetails.adOwnerDetails!.name!.isEmpty) &&
(widget.adDetails.adOwnerDetails!.mobileNo == null || widget.adDetails.adOwnerDetails!.mobileNo!.isEmpty) &&
(widget.adDetails.adOwnerDetails!.email == null || widget.adDetails.adOwnerDetails!.email!.isEmpty)) {
return const SizedBox();
}
String name = widget.adDetails.adOwnerDetails!.name ?? "";
String phone = widget.adDetails.adOwnerDetails!.mobileNo ?? "";
String email = widget.adDetails.adOwnerDetails!.email ?? "";
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
(LocaleKeys.ownerInformation.tr()).toText(fontSize: 16),
name.isNotEmpty ? showItem(LocaleKeys.name.tr(), "$name ") : const SizedBox(),
phone.isNotEmpty ? showItem(LocaleKeys.phoneNumber.tr(), "$phone ") : const SizedBox(),
email.isNotEmpty ? showItem(LocaleKeys.emailAddress.tr(), "$email ") : const SizedBox(),
],
).toWhiteContainer(width: double.infinity, allPading: 12);
}
Widget buildBankDetailsIfAvailable() {
return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) {
if (adVM.adsBankDetailsModel != null) {
@ -294,36 +338,35 @@ class _AdsDetailViewState extends State<AdsDetailView> {
}
Widget buildAdStartEndDates() {
String startDate = "";
String endDate = "";
if (widget.adDetails.startdate != null) {
startDate = "${DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.startdate!))} at ${DateHelper.formatAsTime(DateTime.parse(widget.adDetails.startdate!))}";
}
if (widget.adDetails.enddate != null) {
endDate = "${DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.enddate!))} at ${DateHelper.formatAsTime(DateTime.parse(widget.adDetails.enddate!))}";
}
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
("${LocaleKeys.startDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
widget.adDetails.startdate != null
? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.startdate!)).toText(
fontSize: 10,
)
: const SizedBox(),
startDate.isNotEmpty ? startDate.toText(fontSize: 10) : const SizedBox(),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
("${LocaleKeys.endDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
widget.adDetails.enddate != null ? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.enddate!)).toText(fontSize: 10) : const SizedBox(),
],
),
endDate.isNotEmpty ? endDate.toText(fontSize: 10) : const SizedBox(),
],
),
3.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
(LocaleKeys.extendAd.tr()).toText(fontSize: 12, color: MyColors.darkPrimaryColor, isUnderLine: true).onPress(() async => await onExtendAdPressed()),
],
)
),
],
).paddingOnly(top: 5, bottom: 2).toWhiteContainer(width: double.infinity, allPading: 12);
}
@ -331,7 +374,10 @@ class _AdsDetailViewState extends State<AdsDetailView> {
@override
Widget build(BuildContext context) {
Widget actionWidget = const SizedBox();
if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active) && (widget.adDetails.adPostStatus != AdPostStatus.pendingForPost)) {
if ((widget.adDetails.isMyAd ?? false) &&
(widget.adDetails.adPostStatus != AdPostStatus.reserved) &&
(widget.adDetails.adPostStatus != AdPostStatus.active) &&
(widget.adDetails.adPostStatus != AdPostStatus.pendingForPost)) {
actionWidget = IconButton(
icon: const Icon(Icons.delete_outline, color: MyColors.redColor),
onPressed: () {
@ -351,7 +397,6 @@ class _AdsDetailViewState extends State<AdsDetailView> {
actionWidget.toContainer(
margin: const EdgeInsets.fromLTRB(0, 8, 21, 8),
paddingAll: 0,
padding: const EdgeInsets.only(right: 21),
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
@ -375,6 +420,10 @@ class _AdsDetailViewState extends State<AdsDetailView> {
if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) ...[
buildDamagePartDetails(),
],
if (!(widget.adDetails.isMyAd ?? false)) ...[
12.height,
buildPersonalInformationCard(context: context),
],
if (widget.adDetails.isMyAd ?? false) ...[
12.height,
buildAdStartEndDates(),

@ -1,5 +1,6 @@
import 'dart:async';
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';
@ -32,7 +33,7 @@ import 'package:easy_localization/easy_localization.dart';
class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
final AdDetailsModel adDetailsModel;
const BuildAdDetailsActionButtonForExploreAds({Key? key, required this.adDetailsModel}) : super(key: key);
const BuildAdDetailsActionButtonForExploreAds({super.key, required this.adDetailsModel});
void reserveAdPriceBreakDownClicked(BuildContext context, AdDetailsModel adDetailsModel) {
showModalBottomSheet(
@ -238,7 +239,13 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
),
if (adDetailsModel.whatsAppNo != null && adDetailsModel.whatsAppNo!.isNotEmpty) ...[
8.width,
Container(height: 55, width: 55, alignment: Alignment.center, decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)), child: MyAssets.whatsAppIcon.buildSvg(height: 33, width: 35)).onPress(() {
Container(
height: 55,
width: 55,
alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)),
child: MyAssets.whatsAppIcon.buildSvg(height: 33, width: 35))
.onPress(() {
Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? "");
}),
],
@ -264,12 +271,12 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
final AdDetailsModel adDetailsModel;
const BuildAdDetailsActionButtonForMyAds({Key? key, required this.adDetailsModel}) : super(key: key);
const BuildAdDetailsActionButtonForMyAds({super.key, required this.adDetailsModel});
void onBookPhotographyServiceClicked(BuildContext context, {required AdDetailsModel adDetailsModel}) async {
AdVM adVM = context.read<AdVM>();
if (adVM.photoOfficeSelectedId.selectedId == -1) {
adVM.getPhotographyServiceScheduleListByOffices(latitude: 46.703430, longitude: 24.625720, isNeedToRebuild: true); // TODO: These Lat Long need to be dynamic
adVM.getPhotographyServiceScheduleListByOffices(latitude: AppState().currentLocation.latitude, longitude: AppState().currentLocation.longitude, isNeedToRebuild: true);
}
return showModalBottomSheet(
@ -582,7 +589,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.markAsSold.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.markAsSold.tr(),
subtitle: LocaleKeys.markAdAsSoldDesc.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
@ -655,15 +662,9 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
shrinkWrap: true,
itemCount: adVM.deActivateAdModelList.length,
separatorBuilder: (BuildContext context, int index) {
if (adVM.deActivateAdModelList[index].index == 3) {
return const SizedBox();
}
return const Divider(thickness: 0.5);
},
itemBuilder: (BuildContext context, int index) {
if (adVM.deActivateAdModelList[index].index == 3) {
return const SizedBox();
}
OfferRequestCommentModel offerRequestCommentModel = adVM.deActivateAdModelList[index];
return CircleCheckBoxWithTitle(
isChecked: offerRequestCommentModel.isSelected ?? false,
@ -948,7 +949,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
child: ShowFillButton(
fontSize: 16,
maxHeight: 55,
title: LocaleKeys.extendAd.tr(),
title: LocaleKeys.reactivateAd.tr(),
onPressed: () {
final AdVM adVM = context.read<AdVM>();
return actionConfirmationBottomSheet(
@ -1054,6 +1055,9 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
case AdPostStatus.sold:
return pendingForReviewAction(pendingText: LocaleKeys.sold.tr());
case AdPostStatus.deActive:
return pendingForReviewAction(pendingText: LocaleKeys.deactivateAd.tr());
case AdPostStatus.expired:
return expiredAdAction(context);
case AdPostStatus.allAds:

@ -17,7 +17,7 @@ import 'package:sizer/sizer.dart';
import 'package:easy_localization/easy_localization.dart';
class BottomSheetAdDamagePartsContent extends StatefulWidget {
const BottomSheetAdDamagePartsContent({Key? key}) : super(key: key);
const BottomSheetAdDamagePartsContent({super.key});
@override
State<BottomSheetAdDamagePartsContent> createState() => _BottomSheetAdDamagePartsContentState();
@ -147,7 +147,7 @@ class _BottomSheetAdDamagePartsContentState extends State<BottomSheetAdDamagePar
}
class BottomSheetAdSpecialServiceContent extends StatelessWidget {
const BottomSheetAdSpecialServiceContent({Key? key}) : super(key: key);
const BottomSheetAdSpecialServiceContent({super.key});
bool isButtonTappable(AdVM adVM) {
bool status = (adVM.selectedVehicleAdsSpecialServicesId.selectedId != -1) && (adVM.selectedVehicleAdsSpecialServicesId.selectedOption != "");

@ -1,8 +1,13 @@
import 'package:carousel_slider/carousel_slider.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/config/routes.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/advertisment_models/ad_details_model.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:sizer/sizer.dart';
@ -17,7 +22,7 @@ class ImagesCorouselWidget extends StatefulWidget {
class _CarouselWithIndicatorState extends State<ImagesCorouselWidget> {
int _current = 0;
CarouselSliderController _controller = CarouselSliderController();
final CarouselSliderController _controller = CarouselSliderController();
@override
Widget build(BuildContext context) {
@ -28,7 +33,7 @@ class _CarouselWithIndicatorState extends State<ImagesCorouselWidget> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
"No Images to Show".toText(
LocaleKeys.noImagesToShow.tr().toText(
fontSize: 12,
color: MyColors.lightTextColor,
isBold: true,
@ -40,17 +45,30 @@ class _CarouselWithIndicatorState extends State<ImagesCorouselWidget> {
}
return SizedBox(
height: 26.h,
child: ListView(shrinkWrap: true, children: [
child: ListView(
shrinkWrap: true,
children: [
CarouselSlider(
items: widget.imagesList
.map((item) =>
Container(
.map(
(item) => Container(
margin: const EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
child: item.imageUrl.buildNetworkImage(),
),
))
).onPress(() {
List<MessageImageModel> images = [];
for (var image in widget.imagesList) {
images.add(MessageImageModel(
id: image.id,
isFromNetwork: true,
imageUrl: image.imageUrl,
));
}
navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: images);
}),
)
.toList(),
carouselController: _controller,
options: CarouselOptions(
@ -61,14 +79,12 @@ class _CarouselWithIndicatorState extends State<ImagesCorouselWidget> {
setState(() {
_current = index;
});
}),
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: widget.imagesList
.asMap()
.entries
.map((entry) {
children: widget.imagesList.asMap().entries.map((entry) {
return GestureDetector(
onTap: () => _controller.animateToPage(entry.key),
child: Container(
@ -83,7 +99,8 @@ class _CarouselWithIndicatorState extends State<ImagesCorouselWidget> {
);
}).toList(),
),
]),
],
),
);
}
}

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
@ -56,7 +58,7 @@ class BookAppointmentSchedulesView extends StatelessWidget {
Row(
children: [
Expanded(
child: ("${LocaleKeys.schedule.tr()} ${scheduleIndex + 1}").toText(fontSize: 20, isBold: true),
child: (scheduleData.scheduleName ?? "").toText(fontSize: 20, isBold: true),
),
],
),
@ -65,7 +67,9 @@ class BookAppointmentSchedulesView extends StatelessWidget {
children: [
(LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
2.width,
(scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.customerLocation.tr() : LocaleKeys.companyLocation.tr()).toText(fontSize: 12, isBold: true).expand(),
(scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.customerLocation.tr() : LocaleKeys.companyLocation.tr())
.toText(fontSize: 12, isBold: true)
.expand(),
],
),
Column(
@ -112,7 +116,9 @@ class BookAppointmentSchedulesView extends StatelessWidget {
SizedBox(
width: double.infinity,
child: BuildTimeSlots(
timeSlots: appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!].availableSlots ?? [],
timeSlots: appointmentsVM.serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList![appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!].availableSlots ??
[],
onPressed: (slotIndex) {
appointmentsVM.updateSelectedAppointmentSlotByDate(scheduleIndex: scheduleIndex, slotIndex: slotIndex);
},

@ -1,4 +1,7 @@
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';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
@ -14,7 +17,7 @@ import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class BookAppointmentServicesView extends StatelessWidget {
const BookAppointmentServicesView({Key? key}) : super(key: key);
const BookAppointmentServicesView({super.key});
@override
Widget build(BuildContext context) {
@ -57,7 +60,7 @@ class BookAppointmentServicesView extends StatelessWidget {
Row(
children: [
Expanded(child: (serviceData.serviceDescription ?? "").toText(fontSize: 15, isBold: true)),
IconButton(onPressed: () => appointmentsVM.removeServiceInCurrentAppointment(serviceIndex), icon: const Icon(Icons.delete_outline, color: MyColors.redColor))
MyAssets.closeWithOrangeBg.buildSvg(height: 30, width: 30).onPress(() => appointmentsVM.removeServiceInCurrentAppointment(serviceIndex))
],
),
if (true) ...[
@ -105,8 +108,7 @@ class BookAppointmentServicesView extends StatelessWidget {
maxHeight: 55,
title: LocaleKeys.cancel.tr(),
onPressed: () {
appointmentsVM.servicesInCurrentAppointment.clear();
appointmentsVM.allSelectedItemsInAppointments.clear();
appointmentsVM.resetAfterBookingAppointment();
Navigator.pop(context);
},
backgroundColor: MyColors.greyButtonColor,

@ -18,7 +18,7 @@ import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class BookAppointmentsItemView extends StatelessWidget {
const BookAppointmentsItemView({Key? key}) : super(key: key);
const BookAppointmentsItemView({super.key});
@override
Widget build(BuildContext context) {

@ -287,7 +287,7 @@ class ReviewAppointment extends StatelessWidget {
]),
Row(children: [
("${LocaleKeys.location.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
(scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12),
(scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.homeLocation.tr() : LocaleKeys.companyLocation.tr()).toText(fontSize: 12),
]),
],
),

@ -167,6 +167,7 @@ class _ChatViewState extends State<ChatView> {
chatMessageModel: chatMessageModel,
requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus,
chatTypeEnum: chatTypeEnum,
requestsTypeEnum: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel!.requestType.toRequestTypeEnum() : RequestsTypeEnum.specialCarRequest,
);
},
).horPaddingMain(),
@ -176,7 +177,7 @@ class _ChatViewState extends State<ChatView> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestType.toRequestTypeStatusEnum() == RequestsTypeEnum.serviceRequest &&
requestVM.currentSelectedRequest!.requestType.toRequestTypeEnum() == RequestsTypeEnum.serviceRequest &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress &&
AppState().currentAppType == AppType.customer) ...[
Expanded(
@ -200,7 +201,7 @@ class _ChatViewState extends State<ChatView> {
// ),
// ).paddingAll(15)
] else if (chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestType.toRequestTypeStatusEnum() == RequestsTypeEnum.specialCarRequest &&
requestVM.currentSelectedRequest!.requestType.toRequestTypeEnum() == RequestsTypeEnum.specialCarRequest &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress &&
requestVM.currentSelectedOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted &&
AppState().currentAppType == AppType.customer) ...[

@ -28,8 +28,9 @@ class ChatMessageCustomWidget extends StatefulWidget {
final ChatMessageModel chatMessageModel;
final RequestStatusEnum? requestStatusEnum;
final ChatTypeEnum chatTypeEnum;
final RequestsTypeEnum requestsTypeEnum;
const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum, required this.chatTypeEnum});
const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum, required this.chatTypeEnum, this.requestsTypeEnum = RequestsTypeEnum.specialCarRequest});
@override
State<ChatMessageCustomWidget> createState() => _ChatMessageCustomWidgetState();
@ -43,8 +44,14 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
String title = "";
if (requestOfferStatusEnum == RequestOfferStatusEnum.cancel) {
title = LocaleKeys.dealNotCompleted.tr();
} else {
title = LocaleKeys.pleaseSpecify.tr();
}
return InfoBottomSheet(
title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
title: title.toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
@ -58,26 +65,30 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
shrinkWrap: true,
itemCount: chatVM.offerRejectModelList.length,
separatorBuilder: (BuildContext context, int index) {
if ((chatVM.offerRejectModelList[index].index == 1 || chatVM.offerRejectModelList[index].index == 2 || chatVM.offerRejectModelList[index].index == 3) && requestOfferStatusEnum == RequestOfferStatusEnum.cancel) {
bool indexContainsInCancel = widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest
? (chatVM.indexesForCancelSpecialCarOffer.indexWhere((i) => i == index) != -1)
: (chatVM.indexesForCancelSparePartOffer.indexWhere((i) => i == index) != -1);
bool indexContainsInReject = chatVM.indexesForRejectOffer.indexWhere((i) => i == index) != -1;
if ((!indexContainsInCancel) && requestOfferStatusEnum == RequestOfferStatusEnum.cancel) {
return const SizedBox();
}
// 0 -> itemNoLongerAvailable
// 1 -> customerNotResponding
if ((chatVM.offerRejectModelList[index].index == 4 || chatVM.offerRejectModelList[index].index == 0) && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) {
if ((!indexContainsInReject) && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) {
return const SizedBox();
}
return const Divider(thickness: 0.5);
},
itemBuilder: (BuildContext context, int index) {
if ((chatVM.offerRejectModelList[index].index == 1 || chatVM.offerRejectModelList[index].index == 2 || chatVM.offerRejectModelList[index].index == 3) && requestOfferStatusEnum == RequestOfferStatusEnum.cancel) {
bool indexContainsInCancel = widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest
? (chatVM.indexesForCancelSpecialCarOffer.indexWhere((i) => i == index) != -1)
: (chatVM.indexesForCancelSparePartOffer.indexWhere((i) => i == index) != -1);
bool indexContainsInReject = chatVM.indexesForRejectOffer.indexWhere((i) => i == index) != -1;
if ((!indexContainsInCancel) && requestOfferStatusEnum == RequestOfferStatusEnum.cancel) {
return const SizedBox();
}
// 0 -> itemNoLongerAvailable
// 1 -> customerNotResponding
// 2 -> veryHighPrice
// 3 -> alreadySold
if ((chatVM.offerRejectModelList[index].index == 4 || chatVM.offerRejectModelList[index].index == 0) && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) {
if ((!indexContainsInReject) && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) {
return const SizedBox();
}
OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index];
@ -136,7 +147,13 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
);
if (status) {
final requestVM = context.read<RequestsVM>();
chatMessageModel.reqOffer!.requestOfferStatusEnum = requestOfferStatusEnum;
int index = chatVM.serviceProviderOffersList.indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId));
if (index != -1) {
chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum;
}
setState(() {});
// Navigator.pop(context);
chatVM.updateRejectOfferDescription('');
@ -184,11 +201,17 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
if (status) {
final requestVM = context.read<RequestsVM>();
ChatVM chatVM = context.read<ChatVM>();
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
requestVM.currentSelectedRequest!.requestStatus = RequestStatusEnum.inProgress;
requestVM.updateAcceptedReqOffer(chatMessageModel.reqOffer!);
requestVM.updateAcceptedRequestOfferProviderName(chatMessageModel.senderName ?? "");
int index = chatVM.serviceProviderOffersList.indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId));
if (index != -1) {
chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum;
}
setState(() {});
// Navigator.pop(context);
Utils.showToast("Offer Accepted");
@ -447,7 +470,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Widget buildFreeTextDetailsInMessage({required ChatMessageTypeEnum chatMessageTypeEnum}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (chatMessageTypeEnum == ChatMessageTypeEnum.offer && (widget.chatMessageModel.isMyMessage == true) && widget.chatMessageModel.isRead == true) ...[
Row(
@ -460,7 +483,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
],
Expanded(
child: Directionality(
textDirection: TextDirection.ltr,
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.ltr : TextDirection.rtl,
child: (widget.chatMessageModel.chatText ?? "").toText(
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontSize: 12,
@ -588,11 +611,24 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
messageTypeWidget = Column(
children: [
buildFreeTextDetailsInMessage(chatMessageTypeEnum: chatMessageTypeEnum),
if (widget.requestsTypeEnum == RequestsTypeEnum.serviceRequest) ...[
2.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"${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,
),
],
),
],
if (widget.chatMessageModel.reqOffer!.reqOfferImages != null && widget.chatMessageModel.reqOffer!.reqOfferImages!.isNotEmpty) ...[
5.height,
buildImagesInOffer(widget.chatMessageModel.reqOffer!.reqOfferImages!, widget.chatMessageModel.isMyMessage ?? false),
],
10.height,
4.height,
buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum!, chatMessageModel: widget.chatMessageModel, context: context),
],
);

@ -104,7 +104,7 @@ class MyRequestsFragment extends StatelessWidget {
FiltersList(
filterList: requestsVM.requestsTypeFilterOptions,
onFilterTapped: (index, selectedFilterId) {
requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: selectedFilterId.toRequestTypeStatusEnum());
requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: selectedFilterId.toRequestTypeEnum());
},
),
8.height,

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
@ -32,7 +34,13 @@ class CreateRequestPage extends StatelessWidget {
pop(context);
},
),
body: Consumer<RequestsVM>(builder: (context, requestsVM, widget) {
body: PopScope(
onPopInvokedWithResult: (bool result, dynamic) {
if (result) {
context.read<RequestsVM>().resetRequestCreationForm();
}
},
child: Consumer<RequestsVM>(builder: (context, requestsVM, widget) {
return Column(
children: [
Expanded(
@ -137,7 +145,8 @@ class CreateRequestPage extends StatelessWidget {
return DropdownField(
(DropValue value) => requestsVM.updateSelectionVehicleConditionId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleOwnerDrop,
dropdownValue: requestsVM.vehicleConditionId.selectedId != -1 ? DropValue(requestsVM.vehicleConditionId.selectedId, requestsVM.vehicleConditionId.selectedOption, "") : null,
dropdownValue:
requestsVM.vehicleConditionId.selectedId != -1 ? DropValue(requestsVM.vehicleConditionId.selectedId, requestsVM.vehicleConditionId.selectedOption, "") : null,
hint: LocaleKeys.condition.tr(),
errorValue: requestsVM.vehicleConditionId.errorValue,
);
@ -152,7 +161,8 @@ class CreateRequestPage extends StatelessWidget {
return DropdownField(
(DropValue value) => requestsVM.updateSelectionVehicleCountryId(SelectionModel(selectedOption: value.value, selectedId: value.id)),
list: vehicleCountriesDrop,
dropdownValue: requestsVM.vehicleCountryId.selectedId != -1 ? DropValue(requestsVM.vehicleCountryId.selectedId, requestsVM.vehicleCountryId.selectedOption, "") : null,
dropdownValue:
requestsVM.vehicleCountryId.selectedId != -1 ? DropValue(requestsVM.vehicleCountryId.selectedId, requestsVM.vehicleCountryId.selectedOption, "") : null,
hint: LocaleKeys.country.tr(),
errorValue: requestsVM.vehicleCountryId.errorValue,
);
@ -269,6 +279,7 @@ class CreateRequestPage extends StatelessWidget {
],
);
}),
),
);
}
}

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
@ -18,23 +19,52 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class OfferListPage extends StatelessWidget {
final OfferListPageArguments offerListPageArguments;
class OfferListPage extends StatefulWidget {
final int requestId;
const OfferListPage({super.key, required this.offerListPageArguments});
const OfferListPage({super.key, required this.requestId});
@override
State<OfferListPage> createState() => _OfferListPageState();
}
class _OfferListPageState extends State<OfferListPage> {
@override
void initState() {
_onRefresh();
super.initState();
}
_onRefresh() async {
scheduleMicrotask(() async {
ChatVM chatVM = context.read<ChatVM>();
await chatVM.getOffersFromProvidersByRequest(requestId: widget.requestId);
});
}
@override
Widget build(BuildContext context) {
final List<ServiceProvidersOffers> serviceProviderOffers = offerListPageArguments.serviceProviderOffers;
return Consumer<ChatVM>(builder: (context, ChatVM chatVM, Widget? child) {
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.offers.tr()),
body: serviceProviderOffers.isEmpty
? Center(child: LocaleKeys.noOffersShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor))
body: RefreshIndicator(
onRefresh: () async {
_onRefresh();
},
child: chatVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: chatVM.serviceProviderOffersList.isEmpty
? Center(
child: LocaleKeys.noOffersShow.tr().toText(
fontSize: 16,
color: MyColors.lightTextColor,
),
)
: ListView.separated(
itemCount: serviceProviderOffers.length,
itemCount: chatVM.serviceProviderOffersList.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
ServiceProvidersOffers offersModel = serviceProviderOffers[index];
ServiceProvidersOffers offersModel = chatVM.serviceProviderOffersList[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -46,7 +76,10 @@ class OfferListPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(offersModel.name ?? "").toText(fontSize: 16, isBold: true),
(offersModel.name ?? "").toText(
fontSize: 16,
isBold: true,
),
Center(
child: "${offersModel.offerCount}".toText(
color: Colors.white,
@ -73,10 +106,6 @@ class OfferListPage extends StatelessWidget {
if (offersModel.createdOn != null && offersModel.createdOn!.isNotEmpty) ...[
" | ${DateTime.parse(offersModel.createdOn!).getTimeAgo()}".toText(color: MyColors.lightTextColor, fontSize: 14),
],
// " | 1 hour ago".toText(
// color: MyColors.lightTextColor,
// fontSize: 12,
// ),
],
),
const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18),
@ -90,9 +119,10 @@ class OfferListPage extends StatelessWidget {
chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: "${offersModel.providerUserId}",
senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestId: offerListPageArguments.requestId,
requestId: widget.requestId,
providerIndex: index,
requestIndex: -1, // This will be only send in case of provider
requestModel: context.read<RequestsVM>().currentSelectedRequest,
requestIndex: -1,
);
ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest);
@ -104,7 +134,7 @@ class OfferListPage extends StatelessWidget {
context: context,
providerId: offersModel.providerId ?? 0,
requestOfferId: 0,
requestId: offerListPageArguments.requestId ?? 0,
requestId: widget.requestId,
providerOfferIndex: index,
)
.whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments));
@ -112,6 +142,8 @@ class OfferListPage extends StatelessWidget {
},
separatorBuilder: (context, index) => 16.height,
),
),
);
});
}
}

@ -48,7 +48,7 @@ Future buildSendOfferBottomSheet({required BuildContext context, required Reques
numbersOnly: true,
onChanged: (v) => requestsVM.updateOfferPrice(v),
),
if (requestDetail.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeStatusEnum()) ...[
if (requestDetail.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeEnum()) ...[
12.height,
TxtField(
value: requestsVM.serviceItem,
@ -87,7 +87,7 @@ Future buildSendOfferBottomSheet({required BuildContext context, required Reques
onChanged: (v) => requestsVM.updateOfferDescription(v),
),
12.height,
if (requestDetail.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeStatusEnum()) ...[
if (requestDetail.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeEnum()) ...[
LocaleKeys.deliveryAvailable.tr().toText(fontSize: 16),
8.height,
Container(

@ -9,6 +9,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/requests_view_model.dart';
import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart';
import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart';
import 'package:mc_common_app/views/requests/request_bottomsheets.dart';
@ -21,9 +22,10 @@ import 'package:easy_localization/easy_localization.dart';
class RequestDetailPage extends StatelessWidget {
final RequestDetailPageArguments requestDetailPageArguments;
const RequestDetailPage({Key? key, required this.requestDetailPageArguments}) : super(key: key);
const RequestDetailPage({super.key, required this.requestDetailPageArguments});
Widget buildRequestDetailActionFooter({required int requestId, required RequestStatusEnum requestStatus, required RequestsTypeEnum requestTypeEnum, required String statusText, required BuildContext context}) {
Widget buildRequestDetailActionFooter(
{required int requestId, required RequestStatusEnum requestStatus, required RequestsTypeEnum requestTypeEnum, required String statusText, required BuildContext context}) {
// final requestsVM = context.read<RequestsVM>();
switch (requestStatus) {
case RequestStatusEnum.submitted:
@ -31,7 +33,7 @@ class RequestDetailPage extends StatelessWidget {
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: LocaleKeys.viewChat.tr(),
title: LocaleKeys.specialRequestChat.tr(),
isBold: false,
onPressed: () => onViewChatTapped(context),
);
@ -115,7 +117,7 @@ class RequestDetailPage extends StatelessWidget {
appBar: CustomAppBar(
title: LocaleKeys.requestDetail.tr(),
actions: [
(requestDetailPageArguments.requestModel.isChatted)
(context.read<RequestsVM>().isOfferSent || requestDetailPageArguments.requestModel.isChatted)
? Padding(
padding: const EdgeInsets.only(top: 8, bottom: 8, right: 21),
child: const Icon(Icons.messenger_outline_rounded, color: Colors.black, size: 18).toContainer(
@ -144,7 +146,7 @@ class RequestDetailPage extends StatelessWidget {
allPading: 12,
margin: const EdgeInsets.only(top: 21, right: 21, left: 21),
),
if (!requestDetailPageArguments.requestModel.isChatted) ...[
if (!context.read<RequestsVM>().isOfferSent && !requestDetailPageArguments.requestModel.isChatted) ...[
ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
@ -160,7 +162,7 @@ class RequestDetailPage extends StatelessWidget {
requestStatus: requestDetailPageArguments.requestModel.requestStatus,
statusText: "Offer ${requestDetailPageArguments.requestModel.requestStatusName}",
context: context,
requestTypeEnum: requestDetailPageArguments.requestModel.requestType.toRequestTypeStatusEnum(),
requestTypeEnum: requestDetailPageArguments.requestModel.requestType.toRequestTypeEnum(),
),
],
],

@ -106,10 +106,7 @@ class RequestItem extends StatelessWidget {
RequestDetailPageArguments requestDetailPageArguments = RequestDetailPageArguments(requestIndex: requestIndex, requestModel: request);
navigateWithName(context, AppRoutes.requestsDetailPage, arguments: requestDetailPageArguments);
} else {
ChatVM chatVM = context.read<ChatVM>();
await chatVM.getOffersFromProvidersByRequest(requestId: request.id, context: context);
OfferListPageArguments offerListPageArguments = OfferListPageArguments(serviceProviderOffers: chatVM.serviceProviderOffersList, requestId: request.id);
navigateWithName(context, AppRoutes.offersListPage, arguments: offerListPageArguments);
navigateWithName(context, AppRoutes.offersListPage, arguments: request.id);
}
});
}

@ -0,0 +1,98 @@
import 'dart:async';
import 'package:flutter/gestures.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/generated/locale_keys.g.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/views/requests/widget/request_item.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class ProviderAcceptedRequestsView extends StatefulWidget {
const ProviderAcceptedRequestsView({super.key});
@override
State<ProviderAcceptedRequestsView> createState() => _ProviderAcceptedRequestsViewState();
}
class _ProviderAcceptedRequestsViewState extends State<ProviderAcceptedRequestsView> {
@override
void initState() {
scheduleMicrotask(() async => context.read<RequestsVM>().applyFilterOnProvidersAcceptedRequests(requestsTypeEnum: RequestsTypeEnum.specialCarRequest));
super.initState();
}
@override
Widget build(BuildContext context) {
return Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) {
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.acceptedRequests.tr()),
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Column(
children: [
16.height,
FiltersList(
filterList: requestsVM.acceptedRequestsTypeFilterOptions,
onFilterTapped: (index, selectedFilterId) {
requestsVM.applyFilterOnProvidersAcceptedRequests(requestsTypeEnum: selectedFilterId.toRequestTypeEnum());
},
),
8.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async => await requestsVM.getProvidersAcceptedRequests(),
child: requestsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: requestsVM.providersAcceptedRequestsList.isEmpty
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.noRequeststoShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor),
if (requestsVM.requestsFiltersCounter > 0) ...[
8.height,
InkWell(
onTap: () async {
requestsVM.clearRequestsFilters();
await requestsVM.getRequestsBasedOnFilters();
},
child: LocaleKeys.clearFilters.tr().toText(
fontSize: 14,
isBold: true,
color: MyColors.darkPrimaryColor,
),
),
],
],
)
: ListView.separated(
itemBuilder: (context, index) {
return RequestItem(request: requestsVM.providersAcceptedRequestsList[index], appType: AppState().currentAppType, requestIndex: index);
},
separatorBuilder: (context, index) {
return 16.height;
},
itemCount: requestsVM.providersAcceptedRequestsList.length,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8),
),
))
],
),
),
);
});
}
}

@ -87,6 +87,19 @@ class _SettingOptionsMoreState extends State<SettingOptionsMore> {
onTap: () => navigateWithName(context, AppRoutes.favoriteListView),
),
] else ...[
CustomSettingOptionsTile(
leadingWidget: SvgPicture.asset(
MyAssets.icRequests,
height: 20,
width: 20,
color: MyColors.darkIconColor,
),
titleText: LocaleKeys.acceptedRequests.tr(),
needBorderBelow: true,
onTap: () {
navigateWithName(context, AppRoutes.providerAcceptedRequestsView);
},
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.local_shipping, size: 20),
titleText: LocaleKeys.shippingManagement.tr(),

@ -1,6 +1,7 @@
import 'dart:async';
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';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
@ -43,7 +44,18 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
super.initState();
}
Future buildUpdateShippingStatusBottomSheet({required int shippingRequestId}) {
List<FilterListModel> getAvailableStatusesList(List<FilterListModel> list, ShippingRequestStatusEnum shippingRequestStatusEnum) {
List<FilterListModel> newList = [];
for (var element in list) {
if (element.id > shippingRequestStatusEnum.getIdFromShippingStatusEnum()) {
newList.add(element);
}
}
return newList;
}
Future buildUpdateShippingStatusBottomSheet({required int shippingRequestId, required ShippingRequestStatusEnum shippingRequestStatusEnum}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
@ -63,7 +75,7 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
12.height,
ListView.separated(
shrinkWrap: true,
itemCount: shippingManagementVM.shippingRequestStatusesList.length,
itemCount: getAvailableStatusesList(shippingManagementVM.shippingRequestStatusesList, shippingRequestStatusEnum).length,
separatorBuilder: (BuildContext context, int index) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 3.0),
@ -71,13 +83,18 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
);
},
itemBuilder: (BuildContext context, int index) {
List<FilterListModel> list = shippingManagementVM.shippingRequestStatusesList;
List<FilterListModel> list = getAvailableStatusesList(shippingManagementVM.shippingRequestStatusesList, shippingRequestStatusEnum);
FilterListModel shippingFilterListModel = list[index];
int id = list[index].id;
return CircleCheckBoxWithTitle(
isChecked: shippingFilterListModel.isSelected,
title: shippingFilterListModel.title,
onSelected: () {
shippingManagementVM.updateSelectionInShippingRequestStatuses(index);
int i = shippingManagementVM.shippingRequestStatusesList.indexWhere((element) => element.id == id);
if (i == -1) {
return;
}
shippingManagementVM.updateSelectionInShippingRequestStatuses(i);
},
selectedColor: MyColors.darkPrimaryColor,
);
@ -103,7 +120,8 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
bool status = await shippingManagementVM.onUpdateShippingStatusTapped(shippingStatusEnum: shippingStatusEnum, shippingRequestId: shippingRequestId, context: context);
if (status) {
pop(context);
await shippingManagementVM.getShippingRequestsListByFilters();
int index = shippingManagementVM.shippingRequestFilterOptions.indexWhere((element) => element.isSelected);
await shippingManagementVM.getShippingRequestsListByFilters(shippingStatusEnum: index.toShippingStatusEnum());
}
},
maxWidth: double.infinity,
@ -166,26 +184,35 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
text: Utils.getNameByShippingRequestStatusEnum(shippingRequest.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated),
chipColor: Utils.getChipColorByShippingRequestStatusEnum(shippingRequest.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated),
),
("Request Name | ${shippingRequest.id.toString()}").toText(fontSize: 16),
8.height,
("${shippingRequest.request!.brand} ${shippingRequest.request!.model} | ${shippingRequest.id.toString()}").toText(fontSize: 16),
(shippingRequest.request!.description ?? "").toText(color: MyColors.lightTextColor, fontSize: 12, fontWeight: MyFonts.Medium),
if (shippingRequest.comment != null && shippingRequest.comment!.isNotEmpty) ...[
("${LocaleKeys.comment.tr()}: ${shippingRequest.comment ?? ""}").toText(color: MyColors.lightTextColor, fontSize: 12, fontWeight: MyFonts.Medium),
],
4.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
"customer name".toText(color: MyColors.lightTextColor, fontSize: 14),
(shippingRequest.customerName ?? "").toText(color: MyColors.lightTextColor, fontSize: 12, fontWeight: MyFonts.Medium),
if (shippingRequest.createdOn != null && shippingRequest.createdOn!.isNotEmpty) ...[
" | ${DateTime.parse(shippingRequest.createdOn!).getTimeAgo()}".toText(color: MyColors.lightTextColor, fontSize: 14),
" | ${DateTime.parse(shippingRequest.createdOn!).getTimeAgo()}".toText(color: MyColors.lightTextColor, fontSize: 12, fontWeight: MyFonts.Medium),
],
],
),
if (shippingRequest.shippingStatusEnum != ShippingRequestStatusEnum.delivered) ...[
const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18),
],
],
),
],
).onPress(() {
buildUpdateShippingStatusBottomSheet(shippingRequestId: shippingRequest.id!);
if (shippingRequest.shippingStatusEnum == ShippingRequestStatusEnum.delivered) {
return;
}
buildUpdateShippingStatusBottomSheet(shippingRequestId: shippingRequest.id!, shippingRequestStatusEnum: shippingRequest.shippingStatusEnum!);
}).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 16.height,

@ -69,7 +69,7 @@ class BranchDetailCard extends StatelessWidget {
if (providerName != null)
Row(
children: [
("${LocaleKeys.providers.tr()}:").toText(color: MyColors.lightTextColor, fontSize: 12),
("${LocaleKeys.provider.tr()}:").toText(color: MyColors.lightTextColor, fontSize: 12),
4.width,
providerName!.toText(fontSize: 12, isBold: true),
],

@ -1,8 +1,5 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/theme/colors.dart';
@ -21,36 +18,49 @@ class MediaViewerScreen extends StatefulWidget {
class MediaViewerScreenState extends State<MediaViewerScreen> {
int selectedIndex = 0; // Track selected image index
PageController pageController = PageController(initialPage: 0);
@override
Widget build(BuildContext context) {
debugPrint("loaded");
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.pictures.tr()),
body: Column(
children: [
// Main Image Display (selected image)
// Main Image Display in PageView
Expanded(
child: Padding(
child: PageView.builder(
itemCount: widget.images.length,
controller: pageController,
onPageChanged: (index) {
setState(() {
selectedIndex = index; // Update the selected index to reflect the current page
});
// Use proper duration and curve
pageController.animateToPage(
index,
duration: const Duration(milliseconds: 500), // Correct duration in milliseconds
curve: Curves.easeInOut, // Smooth easing curve
);
},
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: InteractiveViewer(
panEnabled: true,
// Allow panning
scaleEnabled: true,
// Allow zooming
minScale: 1.0,
// Minimum zoom level
maxScale: 4.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: (widget.images[selectedIndex].isFromNetwork ?? false)
? widget.images[selectedIndex].imageUrl.buildNetworkImage(
child: (widget.images[index].isFromNetwork ?? false)
? widget.images[index].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
)
: widget.images[selectedIndex].imagePath.buildFileImage(
: widget.images[index].imagePath.buildFileImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
@ -58,6 +68,8 @@ class MediaViewerScreenState extends State<MediaViewerScreen> {
),
),
),
);
},
),
),
// Thumbnail List at the bottom
@ -71,8 +83,15 @@ class MediaViewerScreenState extends State<MediaViewerScreen> {
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index; // Change the selected image
selectedIndex = index; // Update the selected index to reflect the current page
});
// Use proper duration and curve
pageController.animateToPage(
index,
duration: const Duration(milliseconds: 100), // Correct duration in milliseconds
curve: Curves.easeInOut, // Smooth easing curve
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
@ -106,7 +125,6 @@ class MediaViewerScreenState extends State<MediaViewerScreen> {
},
),
),
10.height,
],
),
);

Loading…
Cancel
Save