Merge branch 'faiz_development_common' into aamir_dev

# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/classes/consts.dart
#	lib/generated/codegen_loader.g.dart
#	lib/generated/locale_keys.g.dart
#	pubspec.yaml
aamir_dev
Aamir 1 year ago
commit c8e1f6d756

@ -698,7 +698,6 @@
"explore": "استكشاف",
"manageRequests": "إدارة الطلبات",
"serviceNotAvailableAtHomeLocation": "الخدمة المختارة غير متوفرة في الموقع المحدد.",
"noAvailableItems": "لا توجد عناصر متاحة.",
"wantToRescheduleAppointment": "أرغب في إعادة جدولة الموعد.",
"noNeedForService": "لا أحتاج هذه الخدمة بعد الآن.",
"testTheService": "اختبر الخدمة.",
@ -706,9 +705,44 @@
"customerLocation": "موقع العميل",
"deliveryAvailable": "التوصيل متاح",
"viewed": "تم المشاهدة",
"itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا.",
"updateUserDetails": "تحديث تفاصيل المستخدم",
"enterNewFirstName": "أدخل الاسم الأول",
"enterNewLastName": "أدخل الاسم الأخير",
"userDetailsUpdated": "يتم تحديث تفاصيل المستخدم"
"itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا.",
"reactivateAd": "إعادة تنشيط الإعلان",
"dealOutsideApp": "تمت الصفقة خارج التطبيق مع عميل آخر.",
"noAgreementCustomer": "لا يوجد اتفاق من جانب العميل.",
"dealNotCompleted": "لم تكتمل الصفقة",
"changedDesireToSell": "غيرت رغبتي في بيع المركبة.",
"vehicleDealOutsideApp": "تمت صفقة المركبة خارج التطبيق.",
"markAdAsSoldDesc": "سيتم وضع علامة 'تم البيع' على هذا الإعلان، ولن يتمكن المستخدمون الآخرون من التواصل معك.",
"ownerInformation": "معلومات المالك",
"acceptedRequests": "الطلبات المقبولة",
"specialRequestChat": "دردشة الطلب الخاص",
"companyName": "اسم الشركة",
"noAvailableItems": "لا توجد عناصر متاحة.",
"serviceDeliveryType": "نوع تقديم الخدمة",
"noImagesToShow": "لا توجد صور للعرض",
"updateGroupServices": "تحديث خدمات المجموعة",
"addCommentsHere": "أضف التعليقات هنا",
"waitForBranchVerification": "يرجى الانتظار حتى يتم التحقق من الفرع.",
"reportComplaint": "تقديم شكوى",
"descriptionCannotBeEmpty": "وصف لا يمكن أن يكون فارغًا.",
"adMarkedAsSold": "تم وسم الإعلان كـ 'مباع' بنجاح!",
"chargesAndServiceRangeGreaterThanZero": "يجب أن تكون الرسوم ونطاق الخدمة أكبر من الصفر.",
"cannotSelectAllDaysAsOff": "لا يمكنك اختيار جميع الأيام كأيام عطلة.",
"endDateAfterStartDate": "يجب أن تكون تاريخ النهاية بعد تاريخ البدء.",
"pleaseEnterStartDateFirst": "من فضلك أدخل تاريخ البدء أولاً.",
"endTimeAfterStartTime": "يجب أن يكون وقت الانتهاء بعد وقت البدء.",
"pickedLocationNotice": "سيتم استخدام هذا الموقع لجميع الخدمات المنزلية في هذه الموعد",
"serviceNotProvided": "لسوء الحظ، هذه الخدمة غير متوفرة في الفرع الذي اخترته في هذا الموقع المحدد",
"pleaseSelectService": "يرجى اختيار خدمة واحدة على الأقل",
"operationalIssue": "مشكلة تشغيلية",
"materialIssue": "مشكلة في المواد",
"selectReasonBeforeCancel": "يرجى اختيار سبب قبل إلغاء هذا الموعد. لا يمكن التراجع عن هذا الإجراء.",
"blockedByAdmin": "تم حظرها من قبل المسؤول",
"active": "نشط",
"paymentType": "نوع الدفع",
"searchByCreatedDate": "البحث حسب تاريخ الإنشاء"
}

@ -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",
@ -646,7 +646,7 @@
"customerCarePrompt": "Got any question? We are here to service. Click here to talk to our customer care right now.",
"callNow": "Call Now",
"condition": "Condition",
"servicesNotAvailableHomeLocation": "Some services are not available on home location.",
"servicesNotAvailableHomeLocation": "This location will be same for all services",
"additionalChargesNotice": "These charges are additional to the actual service charges. For heavy items the charges may vary.",
"selectServiceLocation": "Select Service Location",
"apptOn": "Appt On",
@ -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.",
@ -704,6 +703,43 @@
"deliveryAvailable": "Delivery Available",
"viewed": "Viewed",
"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",
"addCommentsHere": "Add comments here",
"waitForBranchVerification": "Please wait for the branch verification.",
"reportComplaint": "Report a complaint",
"descriptionCannotBeEmpty": "Description cannot be empty.",
"adMarkedAsSold": "Ad has been marked as sold successfully!",
"chargesAndServiceRangeGreaterThanZero": "Charges and Service range should be greater than zero.",
"cannotSelectAllDaysAsOff": "You cannot select all days as Off days.",
"endDateAfterStartDate": "End Date should be after start date.",
"pleaseEnterStartDateFirst": "Please enter start date first.",
"endTimeAfterStartTime": "End Time should be after start time.",
"pickedLocationNotice": "This picked location will be used for all home-based services in this appointment",
"serviceNotProvided": "Unfortunately, this service is not provided by your selected branch in this picked location.",
"pleaseSelectService": "Please select at least one service.",
"operationalIssue": "Operational Issue",
"materialIssue": "Material Issue",
"selectReasonBeforeCancel": "Please select a reason before cancelling this appointment. This action cannot be undone.",
"blockedByAdmin": "Blocked by admin",
"active": "Active",
"paymentType": "Payment Type",
"searchByCreatedDate": "Search By Created Date"
"itemNoLongerAvailable": "This item is no longer available.",
"updateUserDetails": "Update User Details",
"enterNewFirstName": "Enter First Name",
"enterNewLastName": "Enter Last Name",

@ -14,30 +14,32 @@ 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";
static String Login_V2_OTPVerify = "${baseUrlServices}api/Account/Login_V2_OTPVerify";
static String loginV1 = "${baseUrlServices}api/Account/Login_V1";
static String loginV2OTP = "${baseUrlServices}api/Account/Login_V2_OTP";
static String loginV2OTPVerify = "${baseUrlServices}api/Account/Login_V2_OTPVerify";
static String user = "${baseUrlServices}api/User/";
static String GetAllCountry = "${baseUrlServices}api/Master/Country_Get";
static String GetProviderRoles = "${baseUrlServices}api/Master/RoleServiceProvider_Get";
static String GetAllCities = "${baseUrlServices}api/Master/City_Get";
static String ForgetPasswordOTPRequest = "${baseUrlServices}api/Account/ForgetPasswordOTPRequest";
static String ForgetPasswordOTPCompare = "${baseUrlServices}api/Account/ForgetPasswordOTPCompare";
static String ForgetPassword = "${baseUrlServices}api/Account/ForgetPassword";
static String Login_Email_OTP = "${baseUrlServices}api/Account/EmailVerify";
static String Login_Email_OTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify";
static String ChangePassword = "${baseUrlServices}api/Account/ChangePassword";
static String ChangeMobileNoOTPRequest = "${baseUrlServices}api/Account/ChangeMobileNoOTPRequest";
static String ChangeMobileNo = "${baseUrlServices}api/Account/ChangeMobileNo";
static String ChangeEmailOTPRequest = "${baseUrlServices}api/Account/ChangeEmailOTPRequest";
static String ChangeEmail = "${baseUrlServices}api/Account/ChangeEmail";
static String EmailVerify = "${baseUrlServices}api/Account/EmailVerify";
static String EmailVerifyOTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify";
static String LogoutUser = "${baseUrlServices}api/Account/Logout";
static String UpdateUserImage = "${baseUrlServices}api/User_UpdateProfileImage";
static String GetUserImage = "${baseUrlServices}api/ProfileImage";
static String userUpdate = "${baseUrlServices}api/User_Update";
static String getAllCountry = "${baseUrlServices}api/Master/Country_Get";
static String getProviderRoles = "${baseUrlServices}api/Master/RoleServiceProvider_Get";
static String getAllCities = "${baseUrlServices}api/Master/City_Get";
static String forgetPasswordOTPRequest = "${baseUrlServices}api/Account/ForgetPasswordOTPRequest";
static String forgetPasswordOTPCompare = "${baseUrlServices}api/Account/ForgetPasswordOTPCompare";
static String forgetPassword = "${baseUrlServices}api/Account/ForgetPassword";
static String loginEmailOTP = "${baseUrlServices}api/Account/EmailVerify";
static String loginEmailOTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify";
static String changePassword = "${baseUrlServices}api/Account/ChangePassword";
static String changeMobileNoOTPRequest = "${baseUrlServices}api/Account/ChangeMobileNoOTPRequest";
static String changeMobileNo = "${baseUrlServices}api/Account/ChangeMobileNo";
static String changeEmailOTPRequest = "${baseUrlServices}api/Account/ChangeEmailOTPRequest";
static String changeEmail = "${baseUrlServices}api/Account/ChangeEmail";
static String emailVerify = "${baseUrlServices}api/Account/EmailVerify";
static String emailVerifyOTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify";
static String logoutUser = "${baseUrlServices}api/Account/Logout";
static String updateUserImage = "${baseUrlServices}api/User_UpdateProfileImage";
static String getUserImage = "${baseUrlServices}api/ProfileImage";
static String providerComplaintCreate = "${baseUrlServices}api/ServiceProviders/ProviderComplaint_Create";
//Profile
static String fetProviderDocument = "${baseUrlServices}api/ServiceProviders/ServiceProviderDocument_Get";
@ -97,6 +99,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";
@ -149,7 +152,6 @@ class ApiConsts {
//Branch Users
static String getAllProviderDealers = "${baseUrlServices}api/ServiceProviders/DealershipUserBranchWise_Get";
static String getBranchUser = "${baseUrlServices}api/ServiceProviders/BranchUser_Get";
static String assignDealerToBranch = "${baseUrlServices}api/ServiceProviders/BranchUser_Create";
static String removeDealerFromBranch = "${baseUrlServices}api/ServiceProviders/BranchUser_Update";
@ -228,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';
@ -146,6 +147,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";
@ -211,7 +213,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(),
@ -219,7 +221,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,10 @@ extension AdPostEnum on int {
return AdPostStatus.buyingService;
} else if (this == 11) {
return AdPostStatus.reserveCancel;
} else if (this == 12) {
return AdPostStatus.deActive;
} else if (this == 13) {
return AdPostStatus.pendingForActive;
} else if (this == 0) {
return AdPostStatus.allAds;
} else {
@ -264,15 +268,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 +344,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 +368,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 +427,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 +438,7 @@ extension RequestTypeTypEnum on int {
}
extension RequestTypeStatusToInt on RequestsTypeEnum {
int getIdFromRequestTypeStatusEnum() {
int getIdFromRequestTypeEnum() {
switch (this) {
case RequestsTypeEnum.specialCarRequest:
return 1;
@ -439,6 +457,7 @@ extension AdPostStatusToInt on AdPostStatus {
switch (this) {
case AdPostStatus.pendingForReview:
return 1;
case AdPostStatus.pendingForPayment:
return 2;
@ -468,6 +487,13 @@ extension AdPostStatusToInt on AdPostStatus {
case AdPostStatus.reserveCancel:
return 11;
case AdPostStatus.deActive:
return 12;
case AdPostStatus.pendingForActive:
return 13;
default:
return 0;
}
@ -616,6 +642,33 @@ extension ServiceEnum on int {
}
}
extension BranchServiceEnumStatusToInt on ServiceStatusEnum {
int getIdFromServiceStatusEnum() {
switch (this) {
case ServiceStatusEnum.pending:
return 1;
case ServiceStatusEnum.review:
return 2;
case ServiceStatusEnum.approvedOrActive:
return 3;
case ServiceStatusEnum.rejected:
return 4;
case ServiceStatusEnum.blocked:
return 5;
case ServiceStatusEnum.deactivated:
return 6;
default:
return 1;
}
}
}
extension DateTimeConversions on DateTime {
String getTimeAgo({bool numericDates = true}) {
final date2 = DateTime.now();
@ -910,8 +963,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 +976,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 +993,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": "اختبر الخدمة.",
@ -726,7 +725,42 @@ class CodegenLoader extends AssetLoader{
"updateUserDetails": "تحديث تفاصيل المستخدم",
"enterNewFirstName": "أدخل الاسم الأول",
"enterNewLastName": "أدخل الاسم الأخير",
"userDetailsUpdated": "يتم تحديث تفاصيل المستخدم"
"userDetailsUpdated": "يتم تحديث تفاصيل المستخدم",
"reactivateAd": "إعادة تنشيط الإعلان",
"dealOutsideApp": "تمت الصفقة خارج التطبيق مع عميل آخر.",
"noAgreementCustomer": "لا يوجد اتفاق من جانب العميل.",
"dealNotCompleted": "لم تكتمل الصفقة",
"changedDesireToSell": "غيرت رغبتي في بيع المركبة.",
"vehicleDealOutsideApp": "تمت صفقة المركبة خارج التطبيق.",
"markAdAsSoldDesc": "سيتم وضع علامة 'تم البيع' على هذا الإعلان، ولن يتمكن المستخدمون الآخرون من التواصل معك.",
"ownerInformation": "معلومات المالك",
"acceptedRequests": "الطلبات المقبولة",
"specialRequestChat": "دردشة الطلب الخاص",
"companyName": "اسم الشركة",
"noAvailableItems": "لا توجد عناصر متاحة.",
"serviceDeliveryType": "نوع تقديم الخدمة",
"noImagesToShow": "لا توجد صور للعرض",
"updateGroupServices": "تحديث خدمات المجموعة",
"addCommentsHere": "أضف التعليقات هنا",
"waitForBranchVerification": "يرجى الانتظار حتى يتم التحقق من الفرع.",
"reportComplaint": "تقديم شكوى",
"descriptionCannotBeEmpty": "وصف لا يمكن أن يكون فارغًا.",
"adMarkedAsSold": "تم وسم الإعلان كـ 'مباع' بنجاح!",
"chargesAndServiceRangeGreaterThanZero": "يجب أن تكون الرسوم ونطاق الخدمة أكبر من الصفر.",
"cannotSelectAllDaysAsOff": "لا يمكنك اختيار جميع الأيام كأيام عطلة.",
"endDateAfterStartDate": "يجب أن تكون تاريخ النهاية بعد تاريخ البدء.",
"pleaseEnterStartDateFirst": "من فضلك أدخل تاريخ البدء أولاً.",
"endTimeAfterStartTime": "يجب أن يكون وقت الانتهاء بعد وقت البدء.",
"pickedLocationNotice": "سيتم استخدام هذا الموقع لجميع الخدمات المنزلية في هذه الموعد",
"serviceNotProvided": "لسوء الحظ، هذه الخدمة غير متوفرة في الفرع الذي اخترته في هذا الموقع المحدد",
"pleaseSelectService": "يرجى اختيار خدمة واحدة على الأقل",
"operationalIssue": "مشكلة تشغيلية",
"materialIssue": "مشكلة في المواد",
"selectReasonBeforeCancel": "يرجى اختيار سبب قبل إلغاء هذا الموعد. لا يمكن التراجع عن هذا الإجراء.",
"blockedByAdmin": "تم حظرها من قبل المسؤول",
"active": "نشط",
"paymentType": "نوع الدفع",
"searchByCreatedDate": "البحث حسب تاريخ الإنشاء"
};
static const Map<String,dynamic> en_US = {
"firstTimeLogIn": "First Time Log In",
@ -1425,7 +1459,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.",
@ -1438,6 +1471,42 @@ static const Map<String,dynamic> en_US = {
"enterNewFirstName": "Enter First Name",
"enterNewLastName": "Enter Last Name",
"userDetailsUpdated": "User Details is Updated"
"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",
"addCommentsHere": "Add comments here",
"waitForBranchVerification": "Please wait for the branch verification.",
"reportComplaint": "Report a complaint",
"descriptionCannotBeEmpty": "Description cannot be empty.",
"adMarkedAsSold": "Ad has been marked as sold successfully!",
"chargesAndServiceRangeGreaterThanZero": "Charges and Service range should be greater than zero.",
"cannotSelectAllDaysAsOff": "You cannot select all days as Off days.",
"endDateAfterStartDate": "End Date should be after start date.",
"pleaseEnterStartDateFirst": "Please enter start date first.",
"endTimeAfterStartTime": "End Time should be after start time.",
"pickedLocationNotice": "This picked location will be used for all home-based services in this appointment",
"serviceNotProvided": "Unfortunately, this service is not provided by your selected branch in this picked location.",
"pleaseSelectService": "Please select at least one service.",
"operationalIssue": "Operational Issue",
"materialIssue": "Material Issue",
"selectReasonBeforeCancel": "Please select a reason before cancelling this appointment. This action cannot be undone.",
"blockedByAdmin": "Blocked by admin",
"active": "Active",
"paymentType": "Payment Type",
"searchByCreatedDate": "Search By Created Date"
};
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,6 +685,41 @@ 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';
static const addCommentsHere = 'addCommentsHere';
static const waitForBranchVerification = 'waitForBranchVerification';
static const reportComplaint = 'reportComplaint';
static const descriptionCannotBeEmpty = 'descriptionCannotBeEmpty';
static const adMarkedAsSold = 'adMarkedAsSold';
static const chargesAndServiceRangeGreaterThanZero = 'chargesAndServiceRangeGreaterThanZero';
static const cannotSelectAllDaysAsOff = 'cannotSelectAllDaysAsOff';
static const endDateAfterStartDate = 'endDateAfterStartDate';
static const pleaseEnterStartDateFirst = 'pleaseEnterStartDateFirst';
static const endTimeAfterStartTime = 'endTimeAfterStartTime';
static const pickedLocationNotice = 'pickedLocationNotice';
static const serviceNotProvided = 'serviceNotProvided';
static const pleaseSelectService = 'pleaseSelectService';
static const operationalIssue = 'operationalIssue';
static const materialIssue = 'materialIssue';
static const selectReasonBeforeCancel = 'selectReasonBeforeCancel';
static const blockedByAdmin = 'blockedByAdmin';
static const active = 'active';
static const paymentType = 'paymentType';
static const searchByCreatedDate = 'searchByCreatedDate';
static const updateUserDetails = 'updateUserDetails';
static const enterNewFirstName = 'enterNewFirstName';
static const enterNewLastName = 'enterNewLastName';

@ -12,3 +12,4 @@ bool disableThingsForQA = true;
// todo terminal command to genertate translation keys
// flutter pub run easy_localization:generate --source-dir ./assets/langs -f keys -o locale_keys.g.dart
// command to generate languages data from json

@ -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,
@ -134,11 +138,13 @@ class AdDetailsModel {
priceExcludingDiscount = json['priceExcludingDiscount'];
reservePrice = json['reservePrice'];
isMCHandled = json['isMCHandled'];
modifiedOn = json['modifiedOn'];
modifiedOn = json['modifiedOn'];
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,31 +180,32 @@ class Vehicle {
int? countryID;
String? currency;
Vehicle({this.id,
this.cityID,
this.cityName,
this.demandAmount,
this.isActive,
this.isFinanceAvailable,
this.status,
this.statustext,
this.category,
this.color,
this.condition,
this.mileage,
this.model,
this.modelyear,
this.sellertype,
this.transmission,
this.duration,
this.image,
this.damagereport,
this.vehicleDescription,
this.vehicleTitle,
this.vehicleType,
this.vehicleVIN,
this.countryID,
this.currency});
Vehicle(
{this.id,
this.cityID,
this.cityName,
this.demandAmount,
this.isActive,
this.isFinanceAvailable,
this.status,
this.statusText,
this.category,
this.color,
this.condition,
this.mileage,
this.model,
this.modelyear,
this.sellertype,
this.transmission,
this.duration,
this.image,
this.damagereport,
this.vehicleDescription,
this.vehicleTitle,
this.vehicleType,
this.vehicleVIN,
this.countryID,
this.currency});
Vehicle.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -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;
@ -404,6 +411,7 @@ class DamageReport {
isActive = json['isActive'];
vehicleDamagePartID = json['vehicleDamagePartID'];
partName = json['partName'];
comment = json['comment'];
}
Map<String, dynamic> toJson() {
@ -417,3 +425,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;
}
}

@ -6,9 +6,20 @@ class AdsDurationModel {
int? countryID;
bool? isActive;
String? countryName;
String? currency;
bool? isSelected;
AdsDurationModel({this.id, this.name, this.days, this.price, this.countryID, this.isActive, this.countryName, this.isSelected});
AdsDurationModel({
this.id,
this.name,
this.days,
this.price,
this.countryID,
this.isActive,
this.countryName,
this.currency,
this.isSelected,
});
AdsDurationModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -18,6 +29,7 @@ class AdsDurationModel {
countryID = json['countryID'];
isActive = json['isActive'];
countryName = json['countryName'];
currency = json['currency'];
isSelected = false;
}
@ -30,6 +42,7 @@ class AdsDurationModel {
data['countryID'] = countryID;
data['isActive'] = isActive;
data['countryName'] = countryName;
data['currency'] = currency;
return data;
}
}

@ -81,7 +81,7 @@ class Office {
String? officeAreaName;
String? officeAreaNameN;
int? cityID;
String? city;
CitySSModel? city;
int? specialServiceID;
Office({this.id, this.officeAreaName, this.officeAreaNameN, this.cityID, this.city, this.specialServiceID});
@ -91,11 +91,41 @@ class Office {
officeAreaName = json['officeAreaName'];
officeAreaNameN = json['officeAreaNameN'];
cityID = json['cityID'];
city = json['city'];
if (json['city'] != null) {
city = json['city'] != null ? CitySSModel.fromJson(json['city']) : null;
}
specialServiceID = json['specialServiceID'];
}
}
class CitySSModel {
int? id;
String? cityName;
String? cityNameN;
int? countryID;
String? country;
bool? isActive;
int? createdBy;
String? createdOn;
String? modifiedBy;
String? modifiedOn;
CitySSModel({this.id, this.cityName, this.cityNameN, this.countryID, this.country, this.isActive, this.createdBy, this.createdOn, this.modifiedBy, this.modifiedOn});
CitySSModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
cityName = json['cityName'];
cityNameN = json['cityNameN'];
countryID = json['countryID'];
country = json['country'];
isActive = json['isActive'];
createdBy = json['createdBy'];
createdOn = json['createdOn'];
modifiedBy = json['modifiedBy'];
modifiedOn = json['modifiedOn'];
}
}
class SpecialServiceModelForAds {
int? adsID;
int? specialServiceID;
@ -126,7 +156,6 @@ class SpecialServiceModelForAds {
appointmentStatusEnum = (json['appointmentStatus'] as int).toAppointmentStatusEnum();
appointmentDate = json['appointmentDate'];
appointmentStatusId = json['appointmentStatus'];
}
@override

@ -21,6 +21,10 @@ class AppointmentListModel {
String? providerName;
String? duration;
String? appointmentDate;
String? paymentType;
String? appointmentAddress;
String? appointmentLatitude;
String? appointmentLongitude;
String? branchName;
int? branchId;
int? servicePaymentStatus;
@ -34,7 +38,8 @@ class AppointmentListModel {
List<AppointmentListModel>? customerAppointmentList;
List<MergeAppointmentList>? mergeAppointmentList;
AppointmentListModel({this.id,
AppointmentListModel({
this.id,
this.serviceSlotID,
this.appointmentStatusID,
this.appointmentStatusText,
@ -49,7 +54,10 @@ class AppointmentListModel {
this.appointmentType,
this.appointmentTypeEnum,
this.providerName,
this.duration,
this.paymentType,
this.appointmentAddress,
this.appointmentLatitude,
this.appointmentLongitude,
this.branchName,
this.branchId,
this.servicePaymentStatus,
@ -58,7 +66,9 @@ class AppointmentListModel {
this.isSelected,
this.appointmentDate,
this.appointmentServicesList,
this.customerAppointmentList, this.mergeAppointmentList,});
this.customerAppointmentList,
this.mergeAppointmentList,
});
@override
String toString() {
@ -83,28 +93,25 @@ class AppointmentListModel {
providerName = json['providerName'];
duration = json['duration'];
appointmentDate = json['appointmentDate'];
paymentType = json['paymentType'];
appointmentAddress = json['address'] ?? "";
appointmentLatitude = json['branchLatitude'];
appointmentLongitude = json['branchLongitude'];
branchName = json['branchName'];
branchId = json['branchID'];
servicePaymentStatus = json['servicePaymentStatus'];
totalAmount = json['amountTotal'];
remainingAmount = json['amountRem'];
isSelected = false;
appointmentStatusEnum =
(json['appointmentStatusID'] as int).toAppointmentStatusEnum();
appointmentPaymentStatusEnum =
(json['servicePaymentStatus'] as int).toAppointmentPaymentStatusEnum();
appointmentStatusEnum = (json['appointmentStatusID'] as int).toAppointmentStatusEnum();
appointmentPaymentStatusEnum = (json['servicePaymentStatus'] as int).toAppointmentPaymentStatusEnum();
if (json['serviceList'] != null) {
appointmentServicesList = <ServiceModel>[];
json['serviceList'].forEach((v) {
appointmentServicesList!
.add(ServiceModel.fromJson(v, isForAppointment: true));
appointmentServicesList!.add(ServiceModel.fromJson(v, isForAppointment: true));
});
}
mergeAppointmentList =
json["mergeAppointmentList"] == null ? [] : List<MergeAppointmentList>.from(
json["mergeAppointmentList"]!.map((x) =>
MergeAppointmentList.fromJson(x)));
mergeAppointmentList = json["mergeAppointmentList"] == null ? [] : List<MergeAppointmentList>.from(json["mergeAppointmentList"]!.map((x) => MergeAppointmentList.fromJson(x)));
}
}
@ -114,10 +121,7 @@ class ServiceAppointmentItems {
String? serviceItemName;
String? serviceItemDescription;
ServiceAppointmentItems({this.id,
this.serviceItemID,
this.serviceItemName,
this.serviceItemDescription});
ServiceAppointmentItems({this.id, this.serviceItemID, this.serviceItemName, this.serviceItemDescription});
ServiceAppointmentItems.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -142,12 +146,9 @@ class CustomerData {
final String customerName;
final List<AppointmentListModel> appointmentList;
CustomerData({required this.customerID,
required this.customerName,
required this.appointmentList});
CustomerData({required this.customerID, required this.customerName, required this.appointmentList});
}
class MergeAppointmentList {
int? id;
int? mergeAppointmentId;
@ -159,17 +160,15 @@ class MergeAppointmentList {
this.serviceAppointmentId,
});
factory MergeAppointmentList.fromJson(Map<String, dynamic> json) =>
MergeAppointmentList(
factory MergeAppointmentList.fromJson(Map<String, dynamic> json) => MergeAppointmentList(
id: json["id"],
mergeAppointmentId: json["mergeAppointmentID"],
serviceAppointmentId: json["serviceAppointmentID"],
);
Map<String, dynamic> toJson() =>
{
Map<String, dynamic> toJson() => {
"id": id,
"mergeAppointmentID": mergeAppointmentId,
"serviceAppointmentID": serviceAppointmentId,
};
}
}

@ -11,6 +11,7 @@ class CustomTimeDateSlotModel {
}
class ServiceAppointmentScheduleModel {
String? scheduleName;
List<ServiceSlotList>? serviceSlotList;
List<ServiceModel>? servicesListInAppointment;
int? selectedDateIndex;
@ -20,9 +21,12 @@ class ServiceAppointmentScheduleModel {
double? amountTotal;
double? amountRem;
int? appointmentType;
double? totalLocationCharges;
CurrentLocationInfoModel? locationInfoModel;
AppointmentTypeEnum? appointmentTypeEnum;
ServiceAppointmentScheduleModel({
this.scheduleName,
this.serviceSlotList,
this.servicesListInAppointment,
this.selectedDateIndex,
@ -32,6 +36,8 @@ class ServiceAppointmentScheduleModel {
this.amountTotal,
this.amountRem,
this.appointmentType,
this.totalLocationCharges,
this.locationInfoModel,
this.appointmentTypeEnum,
});
@ -72,7 +78,7 @@ class ServiceAppointmentScheduleModel {
.toList();
List<TimeSlotModel> slotTime = [];
for (var element in slotTimeData) {
slotTime.add(TimeSlotModel(isSelected: false, slotId: element.id!, slot: element.startTime ?? "", date: element.slotDate!.toFormattedDateWithoutTime() ?? ""));
slotTime.add(TimeSlotModel(isSelected: false, slotId: element.id!, slot: element.startTime ?? "", date: element.slotDate!.toFormattedDateWithoutTime()));
}
return slotTime;
@ -90,6 +96,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) {
@ -108,6 +116,8 @@ class ServiceAppointmentScheduleModel {
amountTotal = json['amountTotal'];
amountRem = json['amountRem'];
appointmentType = json['appointmentType'];
totalLocationCharges = 0.0;
locationInfoModel = CurrentLocationInfoModel(address: "", latitude: 0.0, longitude: 0.0, distanceToBranch: 0.0, homeChargesInCurrentService: 0.0);
appointmentTypeEnum = json['appointmentType'] != null ? (json['appointmentType'] as int).toAppointmentTypeEnum() : AppointmentTypeEnum.workshop;
}
}

@ -11,17 +11,19 @@ class SelectionModel {
int selectedId;
String errorValue;
String itemPrice;
String currency;
SelectionModel({
this.selectedOption = "",
this.errorValue = "",
this.selectedId = 0,
this.itemPrice = "",
this.currency = "",
});
@override
String toString() {
return 'SelectionModel{selectedOption: $selectedOption, selectedId: $selectedId, errorValue: $errorValue, itemPrice: $itemPrice}';
return 'SelectionModel{selectedOption: $selectedOption, selectedId: $selectedId, errorValue: $errorValue, itemPrice: $itemPrice, currency: $currency}';
}
}

@ -26,17 +26,17 @@ class Category {
String? message;
factory Category.fromJson(Map<String, dynamic> json) => Category(
totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"],
totalItemsCount: json["totalItemsCount"],
data: json["data"] == null ? null : List<CategoryData>.from(json["data"].map((x) => CategoryData.fromJson(x))),
messageStatus: json["messageStatus"] == null ? null : json["messageStatus"],
message: json["message"] == null ? null : json["message"],
messageStatus: json["messageStatus"],
message: json["message"],
);
Map<String, dynamic> toJson() => {
"totalItemsCount": totalItemsCount == null ? null : totalItemsCount,
"totalItemsCount": totalItemsCount,
"data": data == null ? null : List<dynamic>.from(data!.map((x) => x.toJson())),
"messageStatus": messageStatus == null ? null : messageStatus,
"message": message == null ? null : message,
"messageStatus": messageStatus,
"message": message,
};
}
@ -63,18 +63,18 @@ class CategoryData extends Equatable {
List<ServiceModel>? services;
factory CategoryData.fromJson(Map<String, dynamic> json) => CategoryData(
id: json["id"] == null ? null : json["id"],
categoryName: json["categoryName"] == null ? null : json["categoryName"],
categoryNameN: json["categoryNameN"] == null ? null : json["categoryNameN"],
id: json["id"],
categoryName: json["categoryName"],
categoryNameN: json["categoryNameN"],
serviceCategoryIconUrl: json["serviceCategoryIconUrl"],
serviceCategoryImageUrl: json["serviceCategoryImageUrl"],
services: [],
);
Map<String, dynamic> toJson() => {
"id": id == null ? null : id,
"categoryName": categoryName == null ? null : categoryName,
"categoryNameN": categoryNameN == null ? null : categoryNameN,
"id": id,
"categoryName": categoryName,
"categoryNameN": categoryNameN,
"serviceCategoryIconUrl": serviceCategoryIconUrl,
"serviceCategoryImageUrl": serviceCategoryImageUrl,
};

@ -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,4 +1,6 @@
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_app/utils/enums.dart';
class ServiceModel {
final int? serviceProviderServiceId;
@ -9,19 +11,21 @@ class ServiceModel {
final String? serviceDescription;
final String? serviceDescriptionN;
final int? serviceStatus;
final ServiceStatusEnum? serviceStatusEnum;
final dynamic statusText;
final bool? isAllowAppointment;
final bool? isAllowAppointmentHome;
final int? customerLocationRange;
final String? rangePricePerKm;
String? rangePricePerKm;
final int? itemsCount;
List<ItemData>? serviceItems;
bool isExpandedOrSelected;
int providerServiceId;
String providerServiceName;
bool isHomeSelected;
String homeLocation;
double currentTotalServicePrice;
CurrentLocationInfoModel servicelocationInfo;
bool isActive;
ServiceModel({
this.serviceProviderServiceId,
@ -32,6 +36,7 @@ class ServiceModel {
this.serviceDescription,
this.serviceDescriptionN,
this.serviceStatus,
this.serviceStatusEnum,
this.statusText,
this.isAllowAppointment,
this.isAllowAppointmentHome,
@ -40,11 +45,12 @@ class ServiceModel {
this.itemsCount,
this.serviceItems,
this.isHomeSelected = false,
this.homeLocation = "",
this.currentTotalServicePrice = 0.0,
this.isActive = true,
required this.isExpandedOrSelected,
required this.providerServiceId,
required this.providerServiceName,
required this.servicelocationInfo,
});
factory ServiceModel.fromJson(Map<String, dynamic> json, {bool isForAppointment = false}) => ServiceModel(
@ -56,11 +62,12 @@ class ServiceModel {
serviceDescription: json["serviceDescription"] ?? json["serviceName"],
serviceDescriptionN: json["serviceDescriptionN"] ?? json["serviceNameN"],
serviceStatus: json["serviceStatus"],
serviceStatusEnum: ((json["serviceStatus"] ?? 0) as int).toServiceStatusEnum(),
statusText: json["statusText"],
isAllowAppointment: json["isAllowAppointment"],
isAllowAppointmentHome: json["isAllowAppointmentHome"],
customerLocationRange: json["customerLocationRange"],
rangePricePerKm: json["rangePricePerKm"].toString(),
rangePricePerKm: json["rangePricePerKm"]?.toString(),
itemsCount: json["itemsCount"],
serviceItems: isForAppointment
? json["serviceItemList"] == null
@ -73,7 +80,8 @@ class ServiceModel {
providerServiceId: 0,
providerServiceName: "",
isHomeSelected: false,
homeLocation: "",
isActive: json["isActive"] ?? true,
servicelocationInfo: CurrentLocationInfoModel(address: '', distanceToBranch: 0.0, homeChargesInCurrentService: 0.0, latitude: 0.0, longitude: 0.0),
);
Map<String, dynamic> toJson() => {
@ -99,3 +107,24 @@ class ServiceModel {
return 'ServiceModel{serviceProviderServiceId: $serviceProviderServiceId, providerServiceDescription: $providerServiceDescription, categoryId: $categoryId, categoryName: $categoryName, serviceId: $serviceId, serviceDescription: $serviceDescription, serviceDescriptionN: $serviceDescriptionN, serviceStatus: $serviceStatus, statusText: $statusText, isAllowAppointment: $isAllowAppointment, isAllowAppointmentHome: $isAllowAppointmentHome, customerLocationRange: $customerLocationRange, rangePricePerKm: $rangePricePerKm, itemsCount: $itemsCount, serviceItems: $serviceItems, isExpandedOrSelected: $isExpandedOrSelected}';
}
}
class CurrentLocationInfoModel {
String address;
double latitude;
double longitude;
double distanceToBranch;
double homeChargesInCurrentService;
CurrentLocationInfoModel({
required this.address,
required this.latitude,
required this.longitude,
required this.distanceToBranch,
required this.homeChargesInCurrentService,
});
@override
String toString() {
return 'CurrentLocationInfoModel{address: $address, latitude: $latitude, longitude: $longitude, distanceToBranch: $distanceToBranch, homeChargesInCurrentService: $homeChargesInCurrentService}';
}
}

@ -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;
}
}

@ -34,7 +34,7 @@ class User {
Map<String, dynamic> toJson() => {
"totalItemsCount": totalItemsCount,
"data": data == null ? null : data!.toJson(),
"data": data?.toJson(),
"messageStatus": messageStatus,
"message": message,
};
@ -63,8 +63,8 @@ class UserData {
Map<String, dynamic> toJson() => {
"accessToken": accessToken,
"refreshToken": refreshToken,
"expiryDate": expiryDate == null ? null : expiryDate!.toIso8601String(),
"userInfo": userInfo == null ? null : userInfo!.toJson(),
"expiryDate": expiryDate?.toIso8601String(),
"userInfo": userInfo?.toJson(),
};
}
@ -88,6 +88,8 @@ class UserInfo {
this.isDealershipUser,
this.providerId,
this.customerId,
this.countryId,
this.cityId,
this.dealershipId,
this.userLocalImage});
@ -109,6 +111,8 @@ class UserInfo {
bool? isDealershipUser;
dynamic providerId;
int? customerId;
int? cityId;
int? countryId;
dynamic dealershipId;
File? userLocalImage;
@ -140,6 +144,8 @@ class UserInfo {
isDealershipUser = json["isDealershipUser"];
providerId = json["providerID"];
customerId = json["customerID"];
countryId = json["countryId"] ?? 1;
cityId = json["cityId"] ?? 1;
dealershipId = json["dealershipID"];
userLocalImage = null;
}

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
@ -14,7 +16,7 @@ import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/utils/enums.dart';
abstract class AdsRepo {
Future<List<AdsDurationModel>> getAdsDuration();
Future<List<AdsDurationModel>> getAdsDuration({required int? countryId});
Future<List<SpecialServiceModel>> getSpecialServices({required int specialServiceType});
@ -22,7 +24,14 @@ abstract class AdsRepo {
Future<List<AdDetailsModel>> getAllAds({required bool isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum});
Future<List<AdDetailsModel>> getExploreAdsBasedOnFilters({List<String>? cityIdsList, List<String>? vehicleModelYearIdsList, List<String>? vehicleBrandIdsList, List<String>? createdByRolesIdsList});
Future<List<AdDetailsModel>> getExploreAdsBasedOnFilters({
List<String>? cityIdsList,
List<String>? vehicleModelYearIdsList,
List<String>? vehicleBrandIdsList,
List<String>? createdByRolesIdsList,
List<String>? vehicleAdConditionIdsList,
List<String>? vehicleAdCreatedDateList,
});
Future<List<AdDetailsModel>> getMyReservedAds();
@ -52,10 +61,14 @@ class AdsRepoImp implements AdsRepo {
AppState appState = injector.get<AppState>();
@override
Future<List<AdsDurationModel>> getAdsDuration() async {
Future<List<AdsDurationModel>> getAdsDuration({required int? countryId}) async {
var param = {
"CountryID": countryId.toString() ?? "0",
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),
queryParameters: param,
ApiConsts.vehicleAdsDurationGet,
);
List<AdsDurationModel> vehicleAdsDuration = List.generate(adsGenericModel.data.length, (index) => AdsDurationModel.fromJson(adsGenericModel.data[index]));
@ -67,7 +80,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;
}
@ -75,6 +89,8 @@ class AdsRepoImp implements AdsRepo {
@override
Future<GenericRespModel> createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew}) async {
List vehiclePostingImages = [];
log("adsCreationPayloadModel.ads!.adsDurationID,: ${adsCreationPayloadModel.ads!.adsDurationID}");
adsCreationPayloadModel.vehiclePosting!.vehiclePostingImages?.forEach((element) {
var imageMap = {
"id": element.id ?? 0,
@ -89,7 +105,15 @@ 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};
log("comment: ${element.comment}");
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 +172,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) {
@ -178,6 +204,8 @@ class AdsRepoImp implements AdsRepo {
List<String>? vehicleModelYearIdsList,
List<String>? vehicleBrandIdsList,
List<String>? createdByRolesIdsList,
List<String>? vehicleAdConditionIdsList,
List<String>? vehicleAdCreatedDateList,
}) async {
var parameters = {
"CityIDs": cityIdsList ?? [],
@ -185,8 +213,13 @@ class AdsRepoImp implements AdsRepo {
"VehicleModelYearIDs": vehicleModelYearIdsList ?? [],
"CreatedByRoles": createdByRolesIdsList ?? [],
"AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS
// TODO: This has to be converted into list
"VehicleNew": (vehicleAdConditionIdsList != null && vehicleAdConditionIdsList.isNotEmpty) ? vehicleAdConditionIdsList.first.toString() : "",
"CreatedOn": (vehicleAdCreatedDateList != null && vehicleAdCreatedDateList.isNotEmpty) ? vehicleAdCreatedDateList.first.toString() : "",
"isActive": "true", //only Active ADS
"isExplore": "true"
"isExplore": "true",
"PageSize": "30",
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
@ -222,7 +255,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 +280,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,11 +150,26 @@ 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,
"amountCustomerLocation": schedule.totalLocationCharges.toString(),
"customerLocLat": (schedule.locationInfoModel!.latitude).toString(),
"customerLocLong": (schedule.locationInfoModel!.longitude).toString(),
"customerLocAddress": (schedule.locationInfoModel!.address).toString(),
});
}
log("maplist: ${mapList.toString()}");

@ -191,7 +191,9 @@ class BranchRepoImp implements BranchRepo {
};
String? token = AppState().getUser.data?.accessToken;
debugPrint(token);
return await injector.get<ApiClient>().getJsonForObject((json) => Document.fromJson(json), ApiConsts.fetProviderDocument, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? "");
return await injector
.get<ApiClient>()
.getJsonForObject((json) => Document.fromJson(json), ApiConsts.fetProviderDocument, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? "");
}
@override
@ -438,10 +440,11 @@ class BranchRepoImp implements BranchRepo {
};
if (latitude != null && longitude != null) {
postParams.addAll({"Longitude": "$latitude"});
postParams.addAll({"Latitude": "$longitude"});
postParams.addAll({"Longitude": "$longitude"});
postParams.addAll({"Latitude": "$latitude"});
}
GenericRespModel adsGenericModel = await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams);
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams);
return ProviderProfileModel.fromJson(adsGenericModel.data);
}

@ -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;

@ -54,6 +54,8 @@ abstract class CommonRepo {
Future<VehicleDetailsModel> getVehicleDetails({int? vehicleTypeId, int? vehicleBrandId});
Future<List<EnumsModel>> getEnumTypeValues({int? enumTypeID, String? enumTypeName});
Future<GenericRespModel> createComplainFromProvider({required int customerId, required String comment, required int complainType});
}
class CommonRepoImp implements CommonRepo {
@ -62,7 +64,7 @@ class CommonRepoImp implements CommonRepo {
@override
Future<Country> getAllCountries() async {
return await apiClient.getJsonForObject((json) => Country.fromJson(json), ApiConsts.GetAllCountry);
return await apiClient.getJsonForObject((json) => Country.fromJson(json), ApiConsts.getAllCountry);
}
@override
@ -70,12 +72,12 @@ class CommonRepoImp implements CommonRepo {
var postParams = {
"CountryID": countryId,
};
return await apiClient.getJsonForObject((json) => Cities.fromJson(json), ApiConsts.GetAllCities, queryParameters: postParams);
return await apiClient.getJsonForObject((json) => Cities.fromJson(json), ApiConsts.getAllCities, queryParameters: postParams);
}
@override
Future<Role> getRoles() async {
return await apiClient.getJsonForObject((json) => Role.fromJson(json), ApiConsts.GetProviderRoles);
return await apiClient.getJsonForObject((json) => Role.fromJson(json), ApiConsts.getProviderRoles);
}
@override
@ -166,7 +168,8 @@ class CommonRepoImp implements CommonRepo {
var postParams = {
"VehicleType": vehicleTypeId.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleCategoryGet, queryParameters: postParams);
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleCategoryGet, queryParameters: postParams);
List<VehicleCategoryModel> vehicleCategories = List.generate(adsGenericModel.data.length, (index) => VehicleCategoryModel.fromJson(adsGenericModel.data[index]));
return vehicleCategories;
}
@ -176,7 +179,8 @@ class CommonRepoImp implements CommonRepo {
var postParams = {
"CountryID": countryId.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleCityGet, queryParameters: countryId != -1 ? postParams : null);
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleCityGet, queryParameters: countryId != -1 ? postParams : null);
List<VehicleCityModel> vehicleCities = List.generate(adsGenericModel.data.length, (index) => VehicleCityModel.fromJson(adsGenericModel.data[index]));
return vehicleCities;
}
@ -186,7 +190,8 @@ class CommonRepoImp implements CommonRepo {
var postParams = {
"VehicleType": vehicleTypeId.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleColorGet, queryParameters: postParams);
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleColorGet, queryParameters: postParams);
List<VehicleColorModel> vehicleColors = List.generate(adsGenericModel.data.length, (index) => VehicleColorModel.fromJson(adsGenericModel.data[index]));
return vehicleColors;
}
@ -196,7 +201,8 @@ class CommonRepoImp implements CommonRepo {
var postParams = {
"VehicleType": vehicleTypeId.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleConditionGet, queryParameters: postParams);
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleConditionGet, queryParameters: postParams);
List<VehicleConditionModel> vehicleConditions = List.generate(adsGenericModel.data.length, (index) => VehicleConditionModel.fromJson(adsGenericModel.data[index]));
return vehicleConditions;
}
@ -217,7 +223,8 @@ class CommonRepoImp implements CommonRepo {
var postParams = {
"VehicleType": vehicleTypeId.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleMileageGet, queryParameters: postParams);
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleMileageGet, queryParameters: postParams);
List<VehicleMileageModel> vehicleMileages = List.generate(adsGenericModel.data.length, (index) => VehicleMileageModel.fromJson(adsGenericModel.data[index]));
return vehicleMileages;
}
@ -338,22 +345,21 @@ class CommonRepoImp implements CommonRepo {
return vehicleParts;
}
//
// @override
// Future<List<ProviderCategoryModel>> getProviderServiceCategories() async {
// GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.serviceCategoryGet);
// List<ProviderCategoryModel> providerCategories = List.generate(adsGenericModel.data.length, (index) => ProviderCategoryModel.fromJson(adsGenericModel.data[index]));
// return providerCategories;
// }
// @override
// Future<List<ProviderServiceModel>> getProviderServices({required int categoryId}) async {
// var postParams = {
// "ServiceCategoryID": categoryId.toString(),
// };
// GenericRespModel adsGenericModel =
// await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.serviceCategoryGet, queryParameters: postParams);
// List<ProviderServiceModel> providerServices = List.generate(adsGenericModel.data.length, (index) => ProviderServiceModel.fromJson(adsGenericModel.data[index]));
// return providerServices;
// }
@override
Future<GenericRespModel> createComplainFromProvider({required int customerId, required String comment, required int complainType}) async {
final providerId = appState.getUser.data!.userInfo!.providerId;
final parameters = {
"serviceProviderID": providerId,
"customerID": customerId,
"detail": comment,
"complaintType": complainType,
};
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.providerComplaintCreate,
parameters,
token: appState.getUser.data!.accessToken,
);
return adsGenericModel;
}
}

@ -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,
);

@ -127,7 +127,7 @@ class UserRepoImp implements UserRepo {
"mobileorEmail": phoneNo,
"password": password,
};
return await injector.get<ApiClient>().postJsonForResponse(ApiConsts.Login_V1, postParams);
return await injector.get<ApiClient>().postJsonForResponse(ApiConsts.loginV1, postParams);
//return await injector.get<ApiClient>().postJsonForObject((json) => BasicOtp.fromJson(json), ApiConsts.Login_V1, postParams);
}
@ -138,7 +138,7 @@ class UserRepoImp implements UserRepo {
"loginType": loginType,
};
Response response = await injector.get<ApiClient>().postJsonForResponse(ApiConsts.Login_V2_OTP, postParams);
Response response = await injector.get<ApiClient>().postJsonForResponse(ApiConsts.loginV2OTP, postParams);
LoginPasswordRespModel user = LoginPasswordRespModel.fromJson(jsonDecode(response.body));
return user;
}
@ -151,7 +151,7 @@ class UserRepoImp implements UserRepo {
String deviceToken,
) async {
var postParams = {"userToken": userToken, "userOTP": otp, "deviceType": deviceType, "deviceToken": deviceToken};
return await injector.get<ApiClient>().postJsonForResponse(ApiConsts.Login_V2_OTPVerify, postParams);
return await injector.get<ApiClient>().postJsonForResponse(ApiConsts.loginV2OTPVerify, postParams);
}
@override
@ -163,7 +163,7 @@ class UserRepoImp implements UserRepo {
@override
Future<Country> getAllCountries() async {
return await injector.get<ApiClient>().getJsonForObject((json) => Country.fromJson(json), ApiConsts.GetAllCountry);
return await injector.get<ApiClient>().getJsonForObject((json) => Country.fromJson(json), ApiConsts.getAllCountry);
}
@override
@ -171,12 +171,12 @@ class UserRepoImp implements UserRepo {
var postParams = {
"CountryID": countryId,
};
return await injector.get<ApiClient>().getJsonForObject((json) => Cities.fromJson(json), ApiConsts.GetAllCities, queryParameters: postParams);
return await injector.get<ApiClient>().getJsonForObject((json) => Cities.fromJson(json), ApiConsts.getAllCities, queryParameters: postParams);
}
@override
Future<Role> getRoles() async {
return await injector.get<ApiClient>().getJsonForObject((json) => Role.fromJson(json), ApiConsts.GetProviderRoles);
return await injector.get<ApiClient>().getJsonForObject((json) => Role.fromJson(json), ApiConsts.getProviderRoles);
}
@override
@ -185,14 +185,14 @@ class UserRepoImp implements UserRepo {
"userName": userName,
"otpType": 1,
};
return await injector.get<ApiClient>().postJsonForResponse(ApiConsts.ForgetPasswordOTPRequest, postParams);
return await injector.get<ApiClient>().postJsonForResponse(ApiConsts.forgetPasswordOTPRequest, postParams);
//return await injector.get<ApiClient>().postJsonForObject((json) => PasswordOTPRequest.fromJson(json), ApiConsts.ForgetPasswordOTPRequest, postParams);
}
@override
Future<PasswordOTPCompareResModel> forgetPasswordOTPCompare(String userToken, String userOTP) async {
var postParams = {"userToken": userToken, "userOTP": userOTP};
Response response = await injector.get<ApiClient>().postJsonForResponse(ApiConsts.ForgetPasswordOTPCompare, postParams);
Response response = await injector.get<ApiClient>().postJsonForResponse(ApiConsts.forgetPasswordOTPCompare, postParams);
PasswordOTPCompareResModel otpCompare = PasswordOTPCompareResModel.fromJson(jsonDecode(response.body));
return otpCompare;
@ -205,7 +205,7 @@ class UserRepoImp implements UserRepo {
"newPassword": newPassword,
};
Response response = await injector.get<ApiClient>().postJsonForResponse(ApiConsts.ForgetPassword, postParams);
Response response = await injector.get<ApiClient>().postJsonForResponse(ApiConsts.forgetPassword, postParams);
ConfirmPasswordRespModel data = ConfirmPasswordRespModel.fromJson(jsonDecode(response.body));
return data;
@ -222,14 +222,14 @@ class UserRepoImp implements UserRepo {
String t = AppState().getUser.data!.accessToken ?? "";
debugPrint("token $t");
return await injector.get<ApiClient>().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.ChangePassword, postParams, token: t);
return await injector.get<ApiClient>().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.changePassword, postParams, token: t);
}
@override
Future<ChangeMobileRespModel> changeMobileNoOTPRequest(countryID, String mobileNo, String password) async {
var postParams = {"countryID": 1, "mobileNo": mobileNo, "password": password};
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => ChangeMobileRespModel.fromJson(json), ApiConsts.ChangeMobileNoOTPRequest, postParams, token: t);
return await injector.get<ApiClient>().postJsonForObject((json) => ChangeMobileRespModel.fromJson(json), ApiConsts.changeMobileNoOTPRequest, postParams, token: t);
}
@override
@ -239,21 +239,21 @@ class UserRepoImp implements UserRepo {
"userOTP": userOTP,
};
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => ConfirmMobileRespModel.fromJson(json), ApiConsts.ChangeMobileNo, postParams, token: t);
return await injector.get<ApiClient>().postJsonForObject((json) => ConfirmMobileRespModel.fromJson(json), ApiConsts.changeMobileNo, postParams, token: t);
}
@override
Future<ChanEmailRespModel> changeEmailOTPRequest(String email, String password) async {
var postParams = {"email": email, "password": password};
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => ChanEmailRespModel.fromJson(json), ApiConsts.ChangeEmailOTPRequest, postParams, token: t);
return await injector.get<ApiClient>().postJsonForObject((json) => ChanEmailRespModel.fromJson(json), ApiConsts.changeEmailOTPRequest, postParams, token: t);
}
@override
Future<ConfirmEmailRespModel> changeEmail(String userToken, String userOTP) async {
var postParams = {"userToken": userToken, "userOTP": userOTP};
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => ConfirmEmailRespModel.fromJson(json), ApiConsts.ChangeEmail, postParams, token: t);
return await injector.get<ApiClient>().postJsonForObject((json) => ConfirmEmailRespModel.fromJson(json), ApiConsts.changeEmail, postParams, token: t);
}
@override
@ -273,7 +273,7 @@ class UserRepoImp implements UserRepo {
};
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => VerifyEmailRespModel.fromJson(json), ApiConsts.EmailVerify, postParams, token: t);
return await injector.get<ApiClient>().postJsonForObject((json) => VerifyEmailRespModel.fromJson(json), ApiConsts.emailVerify, postParams, token: t);
}
@override
@ -281,7 +281,7 @@ class UserRepoImp implements UserRepo {
var postParams = {"userToken": userToken, "userOTP": userOTP};
String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.EmailVerifyOTPVerify, postParams, token: t);
return await injector.get<ApiClient>().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.emailVerifyOTPVerify, postParams, token: t);
}
@override
@ -293,7 +293,7 @@ class UserRepoImp implements UserRepo {
debugPrint("token $t");
return await injector.get<ApiClient>().postJsonForObject(
(json) => ImageResponse.fromJson(json),
ApiConsts.UpdateUserImage,
ApiConsts.updateUserImage,
postParams,
token: t,
);
@ -306,7 +306,7 @@ class UserRepoImp implements UserRepo {
String t = AppState().getUser.data!.accessToken ?? "";
debugPrint("token $t");
return await injector.get<ApiClient>().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.UpdateUserImage, postParams, token: t);
return await injector.get<ApiClient>().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.updateUserImage, postParams, token: t);
}
@override
@ -328,6 +328,6 @@ class UserRepoImp implements UserRepo {
Future<Response> logoutUser() async {
String t = AppState().getUser.data!.accessToken ?? "";
var postParams = {"token": AppState().getUser.data!.accessToken ?? ""};
return await injector.get<ApiClient>().postJsonForResponse(ApiConsts.LogoutUser, postParams, token: t);
return await injector.get<ApiClient>().postJsonForResponse(ApiConsts.logoutUser, postParams, token: t);
}
}

@ -104,7 +104,7 @@ class CommonServicesImp implements CommonAppServices {
destLongitude,
);
return distance ?? 0.0;
return (distance / 1000) ?? 0.0;
} catch (e) {
logger.e(e.toString());
return 0.0;

@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class DateHelper {
@ -50,3 +51,44 @@ class DateHelper {
return formatter.format(date);
}
}
extension TimeOfDayUtils on TimeOfDay {
bool isAfter(TimeOfDay other) {
final thisTime = DateTime(0, 1, 1, hour, minute);
final otherTime = DateTime(0, 1, 1, other.hour, other.minute);
return thisTime.isAfter(otherTime);
}
bool isBefore(TimeOfDay other) {
final thisTime = DateTime(0, 1, 1, hour, minute);
final otherTime = DateTime(0, 1, 1, other.hour, other.minute);
return thisTime.isBefore(otherTime);
}
static TimeOfDay convertStringToTimeOfDay(String timeString) {
bool is12HourFormat = timeString.contains(RegExp(r'AM|PM', caseSensitive: false));
int hour;
int minute;
if (is12HourFormat) {
List<String> parts = timeString.split(' '); // Split time and AM/PM part
List<String> timeParts = parts[0].split(':'); // Split hours and minutes
hour = int.parse(timeParts[0]);
minute = int.parse(timeParts[1]);
if (parts[1].toUpperCase() == 'PM' && hour != 12) {
hour += 12;
} else if (parts[1].toUpperCase() == 'AM' && hour == 12) {
hour = 0;
}
} else {
List<String> timeParts = timeString.split(':');
hour = int.parse(timeParts[0]);
minute = int.parse(timeParts[1]);
}
return TimeOfDay(hour: hour, minute: minute);
}
}

@ -11,28 +11,21 @@
class AppEnums {
static const int requestFilterEnumId = 16; // Requests Filter Enums
static const int branchServicesEnumId = 6; // Branch Services Filter Enums
static const int myAdsFilterEnumId = 18; // My Ads Filter Enums
static const int exploreAdsFilterEnumId = 23; // Explore Ads Filter Enums
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 +63,8 @@ enum AdPostStatus {
reserved,
buyingService,
reserveCancel,
deActive,
pendingForActive,
allAds,
}
@ -142,6 +137,7 @@ enum ServiceStatusEnum {
rejected, //4
blocked, //5
deactivated, //6
allServices, //0
}
enum DocumentStatusEnum {
@ -173,9 +169,11 @@ enum AppointmentStatusEnum {
cancelled,
rescheduled,
allAppointments,
upcoming,
workStarted,
visitCompleted,
today,
past,
upcoming,
}
enum AppointmentPaymentStatusEnum {
@ -228,6 +226,7 @@ enum SubscriptionActionTypeEnum {
enum ShippingRequestStatusEnum {
allRequests,
pending,
initiated,
inTransit,
outForDelivery,

@ -29,7 +29,8 @@ 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 {
@ -62,10 +63,10 @@ class Utils {
await launchUrlString(url, mode: LaunchMode.externalApplication);
}
static Future<String> pickDateFromDatePicker(BuildContext context, {DateTime? firstDate, DateTime? lastDate}) async {
static Future<String> pickDateFromDatePicker(BuildContext context, {DateTime? initial, DateTime? firstDate, DateTime? lastDate}) async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(), //get today's date
initialDate: initial ?? DateTime.now(), //get today's date
firstDate: firstDate ?? DateTime.now(), //DateTime.now() - not to allow to choose before today.
lastDate: lastDate ?? DateTime(2101));
@ -78,7 +79,10 @@ class Utils {
}
static Future<String> pickTime(BuildContext context, {TimeOfDay? initialTime}) async {
TimeOfDay? timeOfDay = await showTimePicker(context: context, initialTime: initialTime ?? TimeOfDay.now());
TimeOfDay? timeOfDay = await showTimePicker(
context: context,
initialTime: initialTime ?? TimeOfDay.now(),
);
if (timeOfDay == null) {
return "";
@ -220,6 +224,7 @@ class Utils {
return MyColors.adCancelledStatusColor;
case AdPostStatus.pendingForPost:
case AdPostStatus.pendingForActive:
return MyColors.inProgressColor;
case AdPostStatus.active:
@ -234,6 +239,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 +321,9 @@ class Utils {
case ShippingRequestStatusEnum.delivered:
return "Delivered";
case ShippingRequestStatusEnum.pending:
return "Pending";
}
}
@ -332,6 +343,9 @@ class Utils {
case ShippingRequestStatusEnum.delivered:
return MyColors.greenColor;
case ShippingRequestStatusEnum.pending:
return MyColors.pendingColor;
}
}
@ -350,7 +364,7 @@ class Utils {
return MyColors.adCancelledStatusColor;
case BranchStatusEnum.blocked:
return MyColors.adPendingStatusColor;
return MyColors.expiredColor;
case BranchStatusEnum.approvedOrActive:
return MyColors.greenColor;
@ -379,7 +393,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,

@ -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;
@ -284,11 +284,17 @@ class AdVM extends BaseVM {
}
Future<List<AdDetailsModel>> getMyReservedAds() async {
setState(ViewState.busy);
myReservedAds = await adsRepo.getMyReservedAds();
isFetchingLists = false;
setState(ViewState.idle);
return myReservedAds;
try {
setState(ViewState.busy);
myReservedAds = await adsRepo.getMyReservedAds();
isFetchingLists = false;
setState(ViewState.idle);
return myReservedAds;
} catch (e) {
logger.e(e.toString());
setState(ViewState.idle);
return [];
}
}
Future<void> getVehicleTypes() async {
@ -297,7 +303,8 @@ class AdVM extends BaseVM {
}
Future<void> getVehicleAdsDuration() async {
vehicleAdsDurations = await adsRepo.getAdsDuration();
final countryId = AppState().getUser.data!.userInfo!.countryId;
vehicleAdsDurations = await adsRepo.getAdsDuration(countryId: countryId);
notifyListeners();
}
@ -329,7 +336,7 @@ class AdVM extends BaseVM {
return;
}
Utils.hideLoading(context);
Utils.showToast(respModel.message ?? "Ad has been marked as sold successfully!");
Utils.showToast(respModel.message ?? LocaleKeys.adMarkedAsSold.tr());
updateIsExploreAds(false);
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.sold); //pending for review
navigateReplaceWithName(context, AppRoutes.dashboard);
@ -540,8 +547,17 @@ class AdVM extends BaseVM {
SelectionModel vehicleConditionId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateSelectionVehicleConditionId(SelectionModel id) {
vehicleConditionId = id;
void updateSelectionVehicleConditionId(SelectionModel id, {bool showSelectionInDropdown = true, bool isForSearch = false}) {
if (isForSearch) {
EnumsModel owner = vehicleConditionsEnum.firstWhere((element) => element.enumValue == id.selectedId);
DropValue ownerValue = DropValue(owner.enumValue, owner.enumValueStr, "");
if (!ifAlreadyExist(list: vehicleAdConditionSearchHistory, value: ownerValue)) {
addToVehicleAdConditionSearchHistory(value: ownerValue);
}
}
if (showSelectionInDropdown) {
vehicleConditionId = id;
}
notifyListeners();
}
@ -591,26 +607,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 +632,7 @@ class AdVM extends BaseVM {
value.isSelected = false;
}
selectedDeActivateAdCommentModel = deActivateAdModelList[index];
log("selectedDeActivateAdCommentModel: ${selectedDeActivateAdCommentModel.index}");
deActivateAdModelList[index].isSelected = true;
notifyListeners();
}
@ -684,9 +691,11 @@ class AdVM extends BaseVM {
SelectionModel vehicleSellerTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateSelectionVehicleSellerTypeId(SelectionModel id) {
void updateSelectionVehicleSellerTypeId(SelectionModel id, {bool needRefresh = true}) {
vehicleSellerTypeId = id;
notifyListeners();
if (needRefresh) {
notifyListeners();
}
}
// SelectionModel vehicleDamagePartId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
@ -925,12 +934,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();
@ -1033,7 +1042,7 @@ class AdVM extends BaseVM {
bool isAdDurationValidated() {
bool isValidated = true;
if (vehicleAdDurationId.selectedId == -1) {
if (AppState().userType != UserType.providerDealer && vehicleAdDurationId.selectedId == -1) {
vehicleAdDurationId.errorValue = LocaleKeys.vehicle_vehiclePart.tr();
isValidated = false;
} else {
@ -1259,6 +1268,11 @@ class AdVM extends BaseVM {
notifyListeners();
}
void updateDamagePartDescription(int index, String value) {
vehicleDamageCards[index].damagePartDescription = value;
notifyListeners();
}
void removeDamageImageFromCard(int imageIndex, String filePath, int cardIndex) {
VehicleDamageCard card = vehicleDamageCards.elementAt(cardIndex);
if (card.partImages == null) return;
@ -1427,14 +1441,14 @@ class AdVM extends BaseVM {
Utils.hideLoading(context);
if (genericRespModel.messageStatus != 1) {
Utils.showToast(LocaleKeys.error.tr() + ": ${genericRespModel.message}");
Utils.showToast("${LocaleKeys.error.tr()}: ${genericRespModel.message}");
} else {
resetSpecialServiceBottomSheet();
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.ads);
}
} catch (e) {
Utils.hideLoading(context);
Utils.showToast(LocaleKeys.error.tr() + ": ${e.toString()}");
Utils.showToast("${LocaleKeys.error.tr()}: ${e.toString()}");
}
}
@ -1447,7 +1461,7 @@ class AdVM extends BaseVM {
}
Ads ads = Ads(
id: isAdEditEnabled ? previousAdDetails!.id : null,
adsDurationID: vehicleAdDurationId.selectedId,
adsDurationID: vehicleAdDurationId.selectedId == -1 ? 0 : vehicleAdDurationId.selectedId,
startDate: selectionDurationStartDate,
countryId: vehicleCountryId.selectedId,
specialServiceIDs: adsSelectedServices,
@ -1466,6 +1480,7 @@ class AdVM extends BaseVM {
VehiclePostingDamageParts stringImage = await convertFileToVehiclePostingDamageParts(
imageModel: image,
damagePartId: card.partSelectedId!.selectedId,
commentParam: card.damagePartDescription ?? "",
);
vehicleDamageImages.add(stringImage);
}
@ -1483,7 +1498,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,
@ -1530,7 +1545,7 @@ class AdVM extends BaseVM {
return vehiclePostingImages;
}
Future<VehiclePostingDamageParts> convertFileToVehiclePostingDamageParts({required ImageModel imageModel, required int damagePartId}) async {
Future<VehiclePostingDamageParts> convertFileToVehiclePostingDamageParts({required ImageModel imageModel, required int damagePartId, required String commentParam}) async {
VehiclePostingDamageParts vehiclePostingDamageParts;
if (imageModel.isFromNetwork ?? false) {
@ -1538,17 +1553,14 @@ class AdVM extends BaseVM {
id: imageModel.id,
vehicleDamagePartID: damagePartId,
imageUrl: imageModel.filePath,
comment: commentParam,
vehiclePostingID: isAdEditEnabled ? previousAdDetails!.vehiclePostingID : null,
);
} else {
File file = File(imageModel.filePath!);
List<int> imageBytes = await file.readAsBytes();
String image = base64Encode(imageBytes);
vehiclePostingDamageParts = VehiclePostingDamageParts(
vehicleImageBase64: image,
vehicleDamagePartID: damagePartId,
imageUrl: file.path,
);
vehiclePostingDamageParts = VehiclePostingDamageParts(vehicleImageBase64: image, vehicleDamagePartID: damagePartId, imageUrl: file.path, comment: commentParam);
}
return vehiclePostingDamageParts;
@ -1556,8 +1568,15 @@ class AdVM extends BaseVM {
// ************ ADS SEARCH VIEW ****************
List<EnumsModel> vehicleConditionsEnum = [];
Future<void> populateDataForAdFilter() async {
setState(ViewState.busy);
if (vehicleConditionsEnum.isEmpty) {
vehicleConditionsEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.conditionEnumId);
}
if (vehicleBrands.isEmpty) {
vehicleBrands = await commonRepo.getVehicleBrands(vehicleTypeId: -1);
}
@ -1693,6 +1712,55 @@ class AdVM extends BaseVM {
notifyListeners();
}
// Created Date
List<DropValue> vehicleAdCreatedDateSearchHistory = [];
void removeVehicleAdCreatedDateSearchHistory({bool isClear = false, required int index}) {
if (isClear) {
vehicleAdCreatedDateSearchHistory.clear();
notifyListeners();
return;
}
vehicleAdCreatedDateSearchHistory.removeAt(index);
if (vehicleAdCreatedDateSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter - 1);
}
notifyListeners();
}
void addToVehicleAdCreatedDateSearchHistory({required DropValue value}) {
if (vehicleAdCreatedDateSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter + 1);
}
vehicleAdCreatedDateSearchHistory.add(value);
notifyListeners();
}
// Ad Condition
List<DropValue> vehicleAdConditionSearchHistory = [];
void removeVehicleAdConditionSearchHistory({bool isClear = false, required int index}) {
if (isClear) {
vehicleAdConditionSearchHistory.clear();
notifyListeners();
return;
}
vehicleAdConditionSearchHistory.removeAt(index);
if (vehicleAdConditionSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter - 1);
}
notifyListeners();
}
void addToVehicleAdConditionSearchHistory({required DropValue value}) {
if (vehicleAdConditionSearchHistory.isEmpty) {
updateAdsFiltersCounter(adsFiltersCounter + 1);
}
vehicleAdConditionSearchHistory.add(value);
notifyListeners();
}
int adsFiltersCounter = 0;
updateAdsFiltersCounter(int value) {
@ -1705,6 +1773,8 @@ class AdVM extends BaseVM {
vehicleAdOwnerSearchHistory.clear();
vehicleLocationAdSearchHistory.clear();
vehicleYearAdSearchHistory.clear();
vehicleAdCreatedDateSearchHistory.clear();
vehicleAdConditionSearchHistory.clear();
adsFiltersCounter = 0;
getExploreAds();
notifyListeners();
@ -1747,11 +1817,27 @@ class AdVM extends BaseVM {
}
}
List<String> conditionsIdsList = [];
if (vehicleAdConditionSearchHistory.isNotEmpty) {
for (var element in vehicleAdConditionSearchHistory) {
conditionsIdsList.add(element.id.toString());
}
}
List<String> createdDatesList = [];
if (vehicleAdCreatedDateSearchHistory.isNotEmpty) {
for (var element in vehicleAdCreatedDateSearchHistory) {
createdDatesList.add(element.value.toString());
}
}
exploreAdsFilteredList = await adsRepo.getExploreAdsBasedOnFilters(
cityIdsList: cityIdsList,
createdByRolesIdsList: adOwnerIdsList,
vehicleBrandIdsList: brandsIdsList,
vehicleModelYearIdsList: vehicleYearIdsList,
vehicleAdConditionIdsList: conditionsIdsList,
vehicleAdCreatedDateList: createdDatesList,
);
setState(ViewState.idle);
}
@ -1782,7 +1868,6 @@ class AdVM extends BaseVM {
Utils.showToast(e.toString());
return false;
}
return false;
}
void autoFillSelectedVehicleType() async {
@ -1813,7 +1898,8 @@ class AdVM extends BaseVM {
SelectionModel(
selectedId: vehicleAdsDurations[index].id ?? 0,
selectedOption: "${vehicleAdsDurations[index].days} Days",
itemPrice: vehicleAdsDurations[index].price!.toInt().toString(),
itemPrice: vehicleAdsDurations[index].price!.toString(),
currency: vehicleAdsDurations[index].currency!.toString(),
),
);
}
@ -1843,7 +1929,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);
@ -1882,6 +1969,7 @@ class AdVM extends BaseVM {
selectedId: element.vehicleDamagePartID!,
selectedOption: element.partName ?? "",
),
damagePartDescription: "",
partImages: [imageModel],
);
addNewDamagePartCard(damageCard: vehicleDamageCard);
@ -1970,17 +2058,19 @@ class AdVM extends BaseVM {
class VehicleDamageCard {
List<ImageModel>? partImages;
SelectionModel? partSelectedId;
String? damagePartDescription;
String partImageErrorValue;
VehicleDamageCard({
this.partImages,
this.partSelectedId,
this.damagePartDescription,
this.partImageErrorValue = "",
});
@override
String toString() {
return 'VehicleDamageCard{partImages: $partImages, partSelectedId: $partSelectedId, partImageErrorValue: $partImageErrorValue}';
return 'VehicleDamageCard{partImages: $partImages, partSelectedId: $partSelectedId,damagePartDescription: $damagePartDescription, partImageErrorValue: $partImageErrorValue}';
}
}

@ -38,9 +38,11 @@ import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/payment_view_model.dart';
import 'package:mc_common_app/views/appointments/book_appointment_schedules_view.dart';
import 'package:mc_common_app/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
@ -81,6 +83,7 @@ class AppointmentsVM extends BaseVM {
bool ifItemAlreadySelected(int id) {
int indexFound = allSelectedItemsInAppointments.indexWhere((element) => element.id == id);
log("already selected at: $indexFound");
if (indexFound != -1) {
return true;
}
@ -101,6 +104,7 @@ class AppointmentsVM extends BaseVM {
}
resetCategorySelectionBottomSheet();
notifyListeners();
}
}
@ -134,7 +138,6 @@ class AppointmentsVM extends BaseVM {
} else {
isSuccess = false;
Utils.showToast(element['message']);
return;
}
});
@ -226,7 +229,7 @@ class AppointmentsVM extends BaseVM {
Utils.showToast(genericRespModel.message.toString());
return;
}
if (genericRespModel.messageStatus == 1) {
if (genericRespModel.messageStatus == 1 && AppState().currentAppType == AppType.customer) {
context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast(genericRespModel.message.toString());
@ -236,6 +239,7 @@ class AppointmentsVM extends BaseVM {
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
} catch (e) {
logger.e(e.toString());
Utils.showToast(e.toString());
}
}
@ -248,16 +252,16 @@ class AppointmentsVM extends BaseVM {
notifyListeners();
}
String pickedHomeLocation = "";
void updatePickedHomeLocation(String value) {
pickedHomeLocation = value;
pickHomeLocationError = "";
if (currentServiceSelection != null) {
currentServiceSelection!.homeLocation = value;
}
notifyListeners();
}
// String pickedHomeLocation = "";
//
// void updatePickedHomeLocation(String value) {
// pickedHomeLocation = value;
// pickHomeLocationError = "";
// if (currentServiceSelection != null) {
// currentServiceSelection!.homeLocation = value;
// }
// notifyListeners();
// }
SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
@ -272,12 +276,64 @@ class AppointmentsVM extends BaseVM {
List<ServiceModel> servicesInCurrentAppointment = [];
ServiceModel? currentServiceSelection;
CurrentLocationInfoModel? currentLocationInfoModel;
void updateCurrentLocationInfoModel(CurrentLocationInfoModel? value) {
currentLocationInfoModel = value;
pickHomeLocationError = "";
if (currentServiceSelection != null && value != null) {
currentServiceSelection!.servicelocationInfo = value;
}
if (value != null) {
for (var service in servicesInCurrentAppointment) {
service.servicelocationInfo.address = value.address;
service.servicelocationInfo.latitude = value.latitude;
service.servicelocationInfo.longitude = value.longitude;
service.servicelocationInfo.distanceToBranch = value.distanceToBranch;
service.servicelocationInfo.homeChargesInCurrentService = double.parse(service.rangePricePerKm ?? "0.0");
}
}
notifyListeners();
}
void updateBranchServiceId(SelectionModel id) async {
branchSelectedServiceId = id;
currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId);
if (currentServiceSelection == null) {
return;
}
servicesInCurrentAppointment.forEach((i) {
log("hlo1: ${i.serviceDescription} ${i.servicelocationInfo.homeChargesInCurrentService}");
});
if (!(currentServiceSelection!.isAllowAppointmentHome ?? false)) {
updateIsHomeTapped(false);
return;
}
if (currentLocationInfoModel != null) {
currentLocationInfoModel!.homeChargesInCurrentService = double.parse(currentServiceSelection!.rangePricePerKm ?? "0.0");
currentServiceSelection!.servicelocationInfo = CurrentLocationInfoModel(
address: currentLocationInfoModel!.address,
latitude: currentLocationInfoModel!.latitude,
longitude: currentLocationInfoModel!.longitude,
distanceToBranch: currentLocationInfoModel!.distanceToBranch,
homeChargesInCurrentService: double.parse(currentServiceSelection!.rangePricePerKm ?? "0.0"),
);
// for (var service in servicesInCurrentAppointment) {
// service.servicelocationInfo.address = currentLocationInfoModel!.address;
// service.servicelocationInfo.latitude = currentLocationInfoModel!.latitude;
// service.servicelocationInfo.longitude = currentLocationInfoModel!.longitude;
// service.servicelocationInfo.distanceToBranch = currentLocationInfoModel!.distanceToBranch;
// }
}
servicesInCurrentAppointment.forEach((i) {
log("hlo2: ${i.serviceDescription} ${i.servicelocationInfo.homeChargesInCurrentService}");
});
log("currentServiceSelection isAllowAppointmentHome: ${currentServiceSelection!.isAllowAppointmentHome}");
log("currentServiceSelection serviceDescription: ${currentServiceSelection!.serviceDescription}");
notifyListeners();
}
@ -298,9 +354,18 @@ 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 = [];
currentLocationInfoModel = null;
log("here servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}");
}
List<EnumsModel> myAppointmentsEnum = [];
@ -315,13 +380,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 +468,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 +498,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 +557,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,38 +614,35 @@ 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}");
serviceItemsFromApi[index].isUpdateOrSelected = selected;
serviceItemsFromApi[index].isHomeSelected = isHomeTapped;
log(" serviceItemsFromApi[index].isHomeSelected: ${serviceItemsFromApi[index].isHomeSelected}");
log(" serviceIndex: $serviceIndex");
if (selected) {
selectedSubServicesCounter = selectedSubServicesCounter + 1;
selectSubServicesError = "";
currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]);
allSelectedItemsInAppointments.add(serviceItemsFromApi[index]);
for (var element in allSelectedItemsInAppointments) {
if (!ifItemAlreadySelected(element.id!)) {
if (serviceIndex != -1) {
servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
}
if (!ifItemAlreadySelected(serviceItemsFromApi[index].id!)) {
currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]);
allSelectedItemsInAppointments.add(serviceItemsFromApi[index]);
log("Here with: ${serviceItemsFromApi[index].description}");
if (serviceIndex != -1) {
// servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
}
}
} else {
log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}");
selectedSubServicesCounter = selectedSubServicesCounter - 1;
currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId);
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].serviceItems!.removeWhere((element) => element.id == itemId);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0");
if (servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice < 0.0) {
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice * -1;
}
// servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId);
}
}
@ -593,9 +664,11 @@ class AppointmentsVM extends BaseVM {
setState(ViewState.busy);
serviceItemsFromApi.clear();
serviceItemsFromApi = await branchRepo.getServiceItems(serviceId);
selectedSubServicesCounter = 0;
for (var item in serviceItemsFromApi) {
if (ifItemAlreadySelected(item.id!)) {
item.isUpdateOrSelected = true;
selectedSubServicesCounter++;
}
}
setState(ViewState.idle);
@ -618,7 +691,7 @@ class AppointmentsVM extends BaseVM {
}
if (isHomeTapped) {
if (pickedHomeLocation == "") {
if (currentLocationInfoModel == null || currentLocationInfoModel!.address == "") {
updatePickHomeLocationError(GlobalConsts.homeLocationEmptyError);
return false;
}
@ -662,8 +735,8 @@ class AppointmentsVM extends BaseVM {
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
// TODO: It should be calculated from API
double totalKms = 15.3;
double totalKms = double.parse(selectedService.servicelocationInfo.distanceToBranch.toStringAsFixed(2));
double pricePerKm = double.parse(selectedService.servicelocationInfo.homeChargesInCurrentService.toStringAsFixed(2));
return InfoBottomSheet(
title: LocaleKeys.chargesBreakdown.tr().toText(fontSize: 24, isBold: true),
description: Column(
@ -695,14 +768,14 @@ class AppointmentsVM extends BaseVM {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${totalKms}km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"${selectedService.rangePricePerKm} x $totalKms".toText(fontSize: 12, isBold: true),
"$pricePerKm x $totalKms".toText(fontSize: 12, isBold: true),
],
),
8.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"${(double.parse(selectedService.rangePricePerKm!)) * totalKms} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true),
"${((pricePerKm) * totalKms).toStringAsFixed(2)} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true),
],
),
],
@ -714,7 +787,7 @@ 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) + (pricePerKm * totalKms)).toStringAsFixed(2) : "${selectedService.currentTotalServicePrice}")
.toText(fontSize: 29, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
@ -762,7 +835,7 @@ class AppointmentsVM extends BaseVM {
void onServicesNextPressed(BuildContext context) async {
if (allSelectedItemsInAppointments.isEmpty) {
Utils.showToast("Please select at least one service.");
Utils.showToast(LocaleKeys.pleaseSelectService.tr());
return;
}
Utils.showLoading(context);
@ -770,8 +843,6 @@ class AppointmentsVM extends BaseVM {
List<String> serviceItemIdsForHome = [];
List<String> serviceItemIdsForWorkshop = [];
for (var serviceItem in allSelectedItemsInAppointments) {
log("homeselcted for : ${serviceItem.isHomeSelected}");
if (serviceItem.isHomeSelected!) {
serviceItemIdsForHome.add(serviceItem.id!.toString());
} else {
@ -794,6 +865,19 @@ class AppointmentsVM extends BaseVM {
for (var schedule in serviceAppointmentScheduleList) {
amountToPayForAppointment = amountToPayForAppointment + (schedule.amountToPay ?? 0.0);
totalAmount = totalAmount + (schedule.amountTotal ?? 0.0);
for (var service in schedule.servicesListInAppointment!) {
int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceProviderServiceId == service.serviceProviderServiceId);
if (index != -1) {
if (servicesInCurrentAppointment[index].isHomeSelected) {
service.servicelocationInfo = servicesInCurrentAppointment[index].servicelocationInfo;
schedule.locationInfoModel = servicesInCurrentAppointment[index].servicelocationInfo;
totalAmount =
totalAmount + ((servicesInCurrentAppointment[index].servicelocationInfo.distanceToBranch) * servicesInCurrentAppointment[index].servicelocationInfo.homeChargesInCurrentService);
schedule.totalLocationCharges = schedule.totalLocationCharges! +
(servicesInCurrentAppointment[index].servicelocationInfo.distanceToBranch * servicesInCurrentAppointment[index].servicelocationInfo.homeChargesInCurrentService);
}
}
}
}
Utils.hideLoading(context);
@ -867,7 +951,90 @@ class AppointmentsVM extends BaseVM {
}
}
// BRANCHES RELATED
// REPORT A COMPLAINT
String complaintDescription = "";
String complaintDescriptionError = "";
void updateComplaintDescription(String value) {
complaintDescription = value;
if (value.isNotEmpty) {
complaintDescriptionError = "";
}
}
Future buildComplaintBottomSheet({required int customerID, required int complainType, required String customerName, required BuildContext context}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return InfoBottomSheet(
title: LocaleKeys.reportComplaint.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (customerName.isNotEmpty) ...[
("${LocaleKeys.customerName.tr()}: $customerName").toText(fontSize: 15, color: MyColors.lightTextColor),
],
10.height,
TxtField(
maxLines: 5,
value: complaintDescription,
errorValue: complaintDescriptionError,
keyboardType: TextInputType.text,
hint: LocaleKeys.description.tr(),
onChanged: (v) => updateComplaintDescription(v),
),
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
onPressed: () async {
if (complaintDescription.isEmpty) {
Utils.showToast(LocaleKeys.descriptionCannotBeEmpty.tr());
return;
}
final status = await reportComplaintAgainstCustomer(
customerID: customerID,
comment: complaintDescription,
complaintType: complainType, // 1 for Appointment and 2 for Requests
context: context,
);
if (status) {
updateComplaintDescription("");
pop(context);
}
},
maxWidth: double.infinity,
),
19.height,
],
),
),
);
},
);
}
Future<bool> reportComplaintAgainstCustomer({required int customerID, required String comment, required int complaintType, required BuildContext context}) async {
Utils.showLoading(context);
try {
GenericRespModel genericRespModel = await commonRepo.createComplainFromProvider(customerId: customerID, comment: comment, complainType: complaintType);
Utils.showToast(genericRespModel.message.toString());
Utils.hideLoading(context);
return genericRespModel.messageStatus == 1;
} catch (e, s) {
logger.e(s);
Utils.hideLoading(context);
Utils.showToast(e.toString());
return false;
}
}
// BRANCHES RELATED
List<BranchDetailModel> nearbyBranches = [];
List<BranchDetailModel> myRecentBranches = [];
@ -937,7 +1104,6 @@ class AppointmentsVM extends BaseVM {
Future<void> getBranchServices({required int categoryId}) async {
branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
isHomeTapped = false;
pickedHomeLocation = "";
pickHomeLocationError = "";
if (categoryId != -1) {
isFetchingServices = true;

@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/main.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/utils/enums.dart';
class BaseVM extends ChangeNotifier {

@ -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 = [];

@ -82,6 +82,7 @@ class DashboardVMProvider extends BaseVM {
appointmentVM.populateAppointmentsFilterList();
shippingManagementVM.populateShippingRequestFilterList();
await serviceVM.getBranchAndServices();
await serviceVM.populateBranchServiceFilters();
await appointmentVM.getMyAppointmentsForProvider({"ServiceProviderID": injector.get<AppState>().getUser.data?.userInfo?.providerId.toString() ?? "0"});
adVM.populateAdsFilterList();
await subscriptionsVM.getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);

@ -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) {
// Utils.showToast(LocaleKeys.paymentFailed.tr());
// pop(context);
// return;
// }
Utils.showToast(LocaleKeys.paymentSuccessful.tr());
if (!isPaid) {
// Utils.showToast(LocaleKeys.paymentFailed.tr());
// pop(context);
return;
} else {
Utils.showToast(LocaleKeys.paymentSuccessful.tr());
}
onVerified();
} catch (e) {
Utils.showToast(e.toString());

@ -46,6 +46,7 @@ class RequestsVM extends BaseVM {
List<EnumsModel> myRequestsTypeEnum = [];
List<EnumsModel> vehicleConditionsEnum = [];
List<EnumsModel> requestStatusesEnum = [];
RequestModel? currentSelectedRequest;
@ -86,6 +87,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,25 +113,63 @@ class RequestsVM extends BaseVM {
requestStatusIdApi = requestStatusId.selectedId;
}
// try {
myFilteredRequests = await requestRepo.getRequestsBasedOnFilters(
requestTypeId: requestTypeIdApi,
cityId: requestCityIdApi,
conditionId: requestConditionIdApi,
vehicleBrand: brandForSearch,
vehicleModel: modelForSearch,
vehicleYearId: requestVehicleYearIdApi,
requestStatusId: requestStatusIdApi,
requestedDate: requestedDateFilter,
);
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;
// }
try {
myFilteredRequests = await requestRepo.getRequestsBasedOnFilters(
requestTypeId: requestTypeIdApi,
cityId: requestCityIdApi,
conditionId: requestConditionIdApi,
vehicleBrand: brandForSearch,
vehicleModel: modelForSearch,
vehicleYearId: requestVehicleYearIdApi,
requestStatusId: requestStatusIdApi,
requestedDate: requestedDateFilter,
);
setState(ViewState.idle);
return true;
} 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 +206,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 +261,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 +763,8 @@ class RequestsVM extends BaseVM {
}
} catch (e, s) {
Utils.hideLoading(context);
log(s.toString());
log(e.toString());
Utils.showToast(e.toString());
}
}
}
@ -904,6 +947,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 +996,8 @@ class RequestsVM extends BaseVM {
return;
}
updateIsOfferSent(true);
final senderName = AppState().getUser.data!.userInfo!.firstName;
final senderId = AppState().getUser.data!.userInfo!.userId;
resetSendOfferBottomSheet();

@ -6,8 +6,11 @@ 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/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/enums_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/categroy.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/document.dart';
@ -67,6 +70,8 @@ class ServiceVM extends BaseVM {
List<BranchDetailModel> homePageBranches = [];
List<CategoryData> categories = [];
bool isNeedRefreshServicesList = false;
void updateSelectionOpenTime(String date) {
openTime = date;
notifyListeners();
@ -174,6 +179,31 @@ class ServiceVM extends BaseVM {
notifyListeners();
}
List<EnumsModel> branchServiceEnums = [];
populateBranchServiceFilters() async {
if (branchServiceEnums.isEmpty) {
branchServiceEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.branchServicesEnumId);
}
branchServicesFilterOptions.clear();
for (int i = 0; i < branchServiceEnums.length; i++) {
branchServicesFilterOptions.add(FilterListModel(title: branchServiceEnums[i].enumValueStr, isSelected: false, id: branchServiceEnums[i].enumValue));
}
}
List<FilterListModel> branchServicesFilterOptions = [];
applyFilterOnBranchServices({required ServiceStatusEnum serviceStatusEnum}) async {
if (branchServicesFilterOptions.isEmpty) return;
for (var value in branchServicesFilterOptions) {
value.isSelected = false;
}
branchServicesFilterOptions[serviceStatusEnum.getIdFromServiceStatusEnum() - 1].isSelected = true; // -1 to match with the index
}
// Future<String?> selectFile(BuildContext context, int index) async {
// final status = await AppPermissions.checkStoragePermissions(context);
// if (status) {
@ -234,7 +264,11 @@ class ServiceVM extends BaseVM {
context,
allowMultiple: false,
);
if (files != null && files.any((element) => element.path.split('.').last.toLowerCase() != 'pdf')) {
if (files != null && files.any((element) =>
element.path
.split('.')
.last
.toLowerCase() != 'pdf')) {
Utils.showToast("Only PDF Files are allowed");
return;
}
@ -248,8 +282,8 @@ class ServiceVM extends BaseVM {
documentID == 1
? commerceCertificates.addAll(imageModels)
: documentID == 2
? commercialCertificates.addAll(imageModels)
: vatCertificates.addAll(imageModels);
? commercialCertificates.addAll(imageModels)
: vatCertificates.addAll(imageModels);
document!.data![index].document = Utils.convertFileToBase64(files.first);
document!.data![index].fileExt = Utils.checkFileExt(files.first.path);
document!.data![index].documentUrl = files.first.path;
@ -304,6 +338,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();
}
@ -423,10 +459,10 @@ class ServiceVM extends BaseVM {
DropValue(
element.id ?? 0,
((element.categoryName!.isEmpty
? "N/A"
: countryCode == "SA"
? element.categoryNameN
: element.categoryName) ??
? "N/A"
: countryCode == "SA"
? element.categoryNameN
: element.categoryName) ??
"N/A"),
"",
),
@ -621,7 +657,9 @@ class ServiceVM extends BaseVM {
File file = File(imageModel.filePath!);
List<int> imageBytes = await file.readAsBytes();
String image = base64Encode(imageBytes);
String fileName = file.path.split('/').last;
String fileName = file.path
.split('/')
.last;
branchPostingImages = BranchPostingImages(
imageName: fileName,
imageStr: image,

@ -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());

@ -37,6 +37,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';
@ -693,8 +694,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;

@ -1,11 +1,13 @@
import 'package:country_code_picker/country_code_picker.dart';
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/theme/colors.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/views/advertisement/bottom_sheets/ad_special_service_sheet.dart';
@ -17,7 +19,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(
@ -60,19 +62,22 @@ class AdDuration extends StatelessWidget {
children: [
LocaleKeys.adDuration.tr().toText(fontSize: 18, isBold: true),
8.height,
TxtField(
isBackgroundEnabled: true,
isButtonEnable: false,
isNeedBorder: false,
hint: LocaleKeys.adDuration.tr(),
value: adVM.vehicleAdDurationId.selectedOption,
isNeedClickAll: true,
postFixDataColor: MyColors.darkTextColor,
onTap: () async {
final formattedDate = await Utils.pickDateFromDatePicker(context);
adVM.updateSelectionDurationStartDate(formattedDate);
},
),
if (AppState().userType != UserType.providerDealer) ...[
TxtField(
isBackgroundEnabled: true,
isButtonEnable: false,
isNeedBorder: false,
hint: LocaleKeys.adDuration.tr(),
value: adVM.vehicleAdDurationId.selectedOption,
isNeedClickAll: true,
postFixDataColor: MyColors.darkTextColor,
onTap: () async {
final formattedDate = await Utils.pickDateFromDatePicker(context);
adVM.updateSelectionDurationStartDate(formattedDate);
},
),
],
// Builder(builder: (context) {
// List<DropValue> vehicleAdsDurationsDrop = [];
// for (var element in adVM.vehicleAdsDurations) {

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.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/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/components/picked_images_container_widget.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
@ -10,7 +12,7 @@ import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class ReviewAd extends StatelessWidget {
const ReviewAd({Key? key}) : super(key: key);
const ReviewAd({super.key});
@override
Widget build(BuildContext context) {
@ -30,7 +32,7 @@ class SingleDetailWidget extends StatelessWidget {
final String text;
final String type;
const SingleDetailWidget({Key? key, required this.text, required this.type}) : super(key: key);
const SingleDetailWidget({super.key, required this.text, required this.type});
@override
Widget build(BuildContext context) {
@ -45,7 +47,7 @@ class SingleDetailWidget extends StatelessWidget {
}
class VehicleDetailsReview extends StatelessWidget {
const VehicleDetailsReview({Key? key}) : super(key: key);
const VehicleDetailsReview({super.key});
@override
Widget build(BuildContext context) {
@ -127,7 +129,7 @@ class VehicleDetailsReview extends StatelessWidget {
}
class DamagePartsReview extends StatelessWidget {
const DamagePartsReview({Key? key}) : super(key: key);
const DamagePartsReview({super.key});
Widget buildDamagePartList(AdVM adVM) {
List<Widget> list = List.generate(
@ -138,11 +140,26 @@ class DamagePartsReview extends StatelessWidget {
8.height,
Row(
children: [
SingleDetailWidget(type: LocaleKeys.vehiclePart.tr(), text: adVM.vehicleDamageCards[index].partSelectedId!.selectedOption),
("${LocaleKeys.vehiclePart.tr()}:").toText(fontSize: 12, color: MyColors.lightTextColor),
4.width,
(adVM.vehicleDamageCards[index].partSelectedId!.selectedOption ?? "").toText(fontSize: 14, color: MyColors.darkTextColor),
],
),
if (adVM.vehicleDamageCards[index].damagePartDescription != null && adVM.vehicleDamageCards[index].damagePartDescription!.isNotEmpty) ...[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
("${LocaleKeys.comment.tr()}:").toText(fontSize: 12, color: MyColors.lightTextColor),
4.width,
Flexible(
child: (adVM.vehicleDamageCards[index].damagePartDescription ?? "").toText(fontSize: 12, color: MyColors.darkTextColor),
),
],
),
5.height,
],
if (adVM.vehicleDamageCards[index].partImages != null && adVM.vehicleDamageCards[index].partImages!.isNotEmpty) ...[
("${LocaleKeys.damagePartPictures.tr()}:").toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
("${LocaleKeys.damagePartPictures.tr()}:").toText(fontSize: 12, color: MyColors.lightTextColor),
PickedFilesContainer(
pickedFiles: adVM.vehicleDamageCards[index].partImages!,
onCrossPressedPrimary: adVM.removeImageFromList,
@ -176,7 +193,7 @@ class DamagePartsReview extends StatelessWidget {
}
class AdDurationReview extends StatelessWidget {
const AdDurationReview({Key? key}) : super(key: key);
const AdDurationReview({super.key});
@override
Widget build(BuildContext context) {
@ -189,10 +206,12 @@ class AdDurationReview extends StatelessWidget {
8.height,
Row(
children: [
Expanded(
flex: 7,
child: SingleDetailWidget(type: LocaleKeys.duration.tr(), text: adVM.vehicleAdDurationId.selectedOption),
),
if (AppState().userType != UserType.providerDealer) ...[
Expanded(
flex: 7,
child: SingleDetailWidget(type: LocaleKeys.duration.tr(), text: adVM.vehicleAdDurationId.selectedOption),
),
],
Expanded(
flex: 5,
child: SingleDetailWidget(type: LocaleKeys.startDate.tr(), text: adVM.selectionDurationStartDate),

@ -9,11 +9,12 @@ import 'package:mc_common_app/views/advertisement/bottom_sheets/ad_damage_parts_
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/widgets/common_widgets/custom_add_button_widget.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class DamageParts extends StatelessWidget {
const DamageParts({Key? key}) : super(key: key);
const DamageParts({super.key});
bool getEnabledStatus(bool? isSelected) {
if (isSelected == null) {
@ -79,7 +80,14 @@ class DamageParts extends StatelessWidget {
).onPress(() => adVM.removeDamagePartCard(index))
],
),
10.height,
3.height,
TxtField(
isNeedBorder: false,
value: vehicleDamageCard.damagePartDescription,
hint: LocaleKeys.addCommentsHere.tr(),
onChanged: (v) => adVM.updateDamagePartDescription(index, v),
),
3.height,
// Builder(builder: (context) {
// List<DropValue> vehicleDamagePartsDrop = [];

@ -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,19 +31,21 @@ class VehicleDetails extends StatelessWidget {
children: [
LocaleKeys.vehicleDetail.tr().toText(fontSize: 18, isBold: true),
8.height,
Builder(builder: (context) {
List<DropValue> vehicleBrandsDrop = [];
for (var element in adVM.vehicleBrands) {
vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleBrandDescription ?? "", ""));
}
return DropdownField(
(DropValue value) => adVM.updateSelectionVehicleBrandId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleBrandsDrop,
dropdownValue: adVM.vehicleBrandId.selectedId != -1 ? DropValue(adVM.vehicleBrandId.selectedId, adVM.vehicleBrandId.selectedOption, "") : null,
hint: LocaleKeys.vehicleBrand.tr(),
errorValue: adVM.vehicleBrandId.errorValue,
);
}),
Builder(
builder: (context) {
List<DropValue> vehicleBrandsDrop = [];
for (var element in adVM.vehicleBrands) {
vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleBrandDescription ?? "", ""));
}
return DropdownField(
(DropValue value) => adVM.updateSelectionVehicleBrandId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleBrandsDrop,
dropdownValue: adVM.vehicleBrandId.selectedId != -1 ? DropValue(adVM.vehicleBrandId.selectedId, adVM.vehicleBrandId.selectedOption, "") : null,
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) {
@ -203,6 +223,7 @@ class VehicleDetails extends StatelessWidget {
value: adVM.vehicleDemandAmount,
errorValue: adVM.demandAmountError,
keyboardType: TextInputType.number,
numbersOnly: true,
hint: LocaleKeys.demandAmount.tr(),
onChanged: (v) => adVM.updateVehicleDemandAmount(v),
),

@ -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),
@ -140,7 +147,7 @@ class _AdsDetailViewState extends State<AdsDetailView> {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, height: 1.2, isBold: true),
LocaleKeys.sar.tr().toText(fontSize: 15, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
(widget.adDetails.vehicle!.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 15, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
if (widget.adDetails.adPostStatus == AdPostStatus.expired) ...[
@ -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(
children: [
("${LocaleKeys.startDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
startDate.isNotEmpty ? startDate.toText(fontSize: 10) : const SizedBox(),
],
),
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(),
],
),
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(),
@ -397,7 +446,7 @@ class _AdsDetailViewState extends State<AdsDetailView> {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true),
LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
(widget.adDetails.vehicle!.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
14.height,

@ -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(
@ -1046,6 +1047,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
case AdPostStatus.cancelled:
return pendingForReviewAction(pendingText: LocaleKeys.cancelledByOwner.tr());
case AdPostStatus.pendingForPost:
case AdPostStatus.pendingForActive:
return pendingForReviewAction(pendingText: LocaleKeys.waitingAdminPost.tr());
case AdPostStatus.pendingForReview:
@ -1054,6 +1056,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:

@ -9,12 +9,14 @@ import 'package:mc_common_app/generated/locale_keys.g.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/utils/utils.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/search_entity_widget.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
@ -134,22 +136,45 @@ class _AdsFilterViewState extends State<AdsFilterView> {
),
const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7),
SearchEntityWidget(
title: LocaleKeys.searchByAdOwner.tr(),
actionWidget: Builder(builder: (context) {
List<DropValue> vehicleOwnerDrop = [];
for (var element in adVM.exploreAdsEnums) {
vehicleOwnerDrop.add(DropValue(element.enumValue.toInt(), "${element.enumValueStr} Ads", ""));
}
return DropdownField(
(DropValue value) => adVM.updateSelectionVehicleAdOwnerId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: vehicleOwnerDrop,
dropdownValue: adVM.vehicleOwnerId.selectedId != -1 ? DropValue(adVM.vehicleOwnerId.selectedId, adVM.vehicleOwnerId.selectedOption, "") : null,
hint: LocaleKeys.selectOwner.tr(),
errorValue: adVM.vehicleOwnerId.errorValue,
);
}),
historyContent: adVM.vehicleAdOwnerSearchHistory,
onHistoryItemDeleted: (index) => adVM.removeVehicleAdOwnerSearchHistory(index: index),
title: LocaleKeys.searchByCondition.tr(),
actionWidget: Builder(
builder: (context) {
List<DropValue> vehicleOwnerDrop = [];
for (var element in adVM.vehicleConditionsEnum) {
vehicleOwnerDrop.add(DropValue(element.id.toInt(), element.enumValueStr, ""));
}
return DropdownField(
(DropValue value) =>
adVM.updateSelectionVehicleConditionId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true, showSelectionInDropdown: false),
list: vehicleOwnerDrop,
dropdownValue: adVM.vehicleConditionId.selectedId != -1 ? DropValue(adVM.vehicleConditionId.selectedId, adVM.vehicleConditionId.selectedOption, "") : null,
hint: LocaleKeys.selectCondition.tr(),
errorValue: adVM.vehicleConditionId.errorValue,
);
},
),
historyContent: adVM.vehicleAdConditionSearchHistory,
onHistoryItemDeleted: (index) => adVM.removeVehicleAdConditionSearchHistory(index: index),
onHistoryItemTapped: (DropValue value) => null,
),
const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7),
SearchEntityWidget(
title: LocaleKeys.searchByCreatedDate.tr(),
actionWidget: TxtField(
isNeedBorder: true,
hint: LocaleKeys.createdOn.tr(),
value: adVM.vehicleAdDurationId.selectedOption,
isNeedClickAll: true,
postFixDataColor: MyColors.darkTextColor,
onTap: () async {
final formattedDate = await Utils.pickDateFromDatePicker(context);
adVM.updateSelectionDurationStartDate(formattedDate);
DropValue value = DropValue(0, formattedDate, "");
adVM.addToVehicleAdCreatedDateSearchHistory(value: value);
},
),
historyContent: adVM.vehicleAdCreatedDateSearchHistory,
onHistoryItemDeleted: (index) => adVM.removeVehicleAdCreatedDateSearchHistory(index: index),
onHistoryItemTapped: (DropValue value) => null,
)
],

@ -125,6 +125,7 @@ class _AdDamagePartsSelectionSheetState extends State<AdDamagePartsSelectionShee
damageCard: VehicleDamageCard(
partImageErrorValue: "",
partImages: null,
damagePartDescription: "",
partSelectedId: SelectionModel(selectedOption: value.partName!, selectedId: value.id!, errorValue: ""),
),
);

@ -58,7 +58,7 @@ class AdDurationSelectionSheet extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
("${adDuration.days}${LocaleKeys.daysVar.tr()}").toString().toText(fontSize: 16, isBold: true),
("${adDuration.days} ${LocaleKeys.daysVar.tr()}").toString().toText(fontSize: 16, isBold: true),
4.height,
"Your Ad will be active for ${adDuration.days} Days and then you will need to extend it to keep it active. ".toText(
fontSize: 14,
@ -73,7 +73,7 @@ class AdDurationSelectionSheet extends StatelessWidget {
2.width,
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: LocaleKeys.sar.tr().toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
child: (adDuration.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
),
],
),
@ -101,7 +101,8 @@ class AdDurationSelectionSheet extends StatelessWidget {
SelectionModel(
selectedId: adDuration.id ?? 0,
selectedOption: ("${adDuration.days} ${LocaleKeys.daysVar.tr()}"),
itemPrice: adDuration.price!.toInt().toString(),
itemPrice: adDuration.price!.toString(),
currency: adDuration.currency!.toString(),
),
);
});

@ -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();
@ -129,6 +129,7 @@ class _BottomSheetAdDamagePartsContentState extends State<BottomSheetAdDamagePar
damageCard: VehicleDamageCard(
partImageErrorValue: "",
partImages: null,
damagePartDescription: "",
partSelectedId: SelectionModel(selectedOption: value.partName!, selectedId: value.id!, errorValue: ""),
),
);
@ -147,7 +148,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,7 +1,9 @@
import 'dart:async';
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:mc_common_app/config/routes.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/models/general_models/widgets_models.dart';
@ -41,6 +43,7 @@ class _AdDamagePartPicturesSheetState extends State<AdDamagePartPicturesSheet> {
selectedId: element.vehicleDamagePartID!,
selectedOption: element.partName ?? "",
),
damagePartDescription: element.comment ?? "",
partImages: [imageModel],
);
vehicleDamageCards.add(vehicleDamageCard);
@ -62,6 +65,9 @@ class _AdDamagePartPicturesSheetState extends State<AdDamagePartPicturesSheet> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
vehicleDamageCard.partSelectedId!.selectedOption.toText(fontSize: 18, color: MyColors.darkTextColor),
if (vehicleDamageCard.damagePartDescription!.isNotEmpty) ...[
vehicleDamageCard.damagePartDescription!.toText(fontSize: 14, color: MyColors.lightTextColor),
],
PickedFilesContainer(
pickedFiles: vehicleDamageCard.partImages ?? [],
isReview: true,
@ -78,7 +84,6 @@ class _AdDamagePartPicturesSheetState extends State<AdDamagePartPicturesSheet> {
id: image.id,
isFromNetwork: true,
imageUrl: image.filePath,
));
}
navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: images);

@ -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,11 +33,11 @@ class _CarouselWithIndicatorState extends State<ImagesCorouselWidget> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
"No Images to Show".toText(
fontSize: 12,
color: MyColors.lightTextColor,
isBold: true,
),
LocaleKeys.noImagesToShow.tr().toText(
fontSize: 12,
color: MyColors.lightTextColor,
isBold: true,
),
],
),
),
@ -40,20 +45,33 @@ class _CarouselWithIndicatorState extends State<ImagesCorouselWidget> {
}
return SizedBox(
height: 26.h,
child: ListView(shrinkWrap: true, children: [
CarouselSlider(
items: widget.imagesList
.map((item) =>
Container(
margin: const EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
child: item.imageUrl.buildNetworkImage(),
),
))
.toList(),
carouselController: _controller,
options: CarouselOptions(
child: ListView(
shrinkWrap: true,
children: [
CarouselSlider(
items: widget.imagesList
.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(
autoPlay: false,
enlargeCenterPage: false,
aspectRatio: 1.8,
@ -61,29 +79,28 @@ class _CarouselWithIndicatorState extends State<ImagesCorouselWidget> {
setState(() {
_current = index;
});
}),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: widget.imagesList
.asMap()
.entries
.map((entry) {
return GestureDetector(
onTap: () => _controller.animateToPage(entry.key),
child: Container(
width: 12.0,
height: 12.0,
margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _current == entry.key ? MyColors.darkPrimaryColor : MyColors.lightTextColor.withOpacity(0.5),
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: widget.imagesList.asMap().entries.map((entry) {
return GestureDetector(
onTap: () => _controller.animateToPage(entry.key),
child: Container(
width: 12.0,
height: 12.0,
margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _current == entry.key ? MyColors.darkPrimaryColor : MyColors.lightTextColor.withOpacity(0.5),
),
),
),
);
}).toList(),
),
]),
);
}).toList(),
),
],
),
);
}
}

@ -17,7 +17,7 @@ import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class CreateAdView extends StatefulWidget {
const CreateAdView({Key? key}) : super(key: key);
const CreateAdView({super.key});
@override
State<CreateAdView> createState() => _CreateAdViewState();
@ -68,7 +68,7 @@ class _CreateAdViewState extends State<CreateAdView> {
}
class BuildFooterButton extends StatelessWidget {
const BuildFooterButton({Key? key}) : super(key: key);
const BuildFooterButton({super.key});
@override
Widget build(BuildContext context) {
@ -187,13 +187,13 @@ class BuildAdStepContainer extends StatelessWidget {
final AdCreationSteps adCreationStepsEnum;
const BuildAdStepContainer({
Key? key,
super.key,
required this.onVehicleDetails,
required this.onDamageParts,
required this.onAdDuration,
required this.onReviewAd,
required this.adCreationStepsEnum,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

@ -14,6 +14,7 @@ import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/views/advertisement/ads_buyer_chats_view.dart';
import 'package:mc_common_app/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart';
import 'package:mc_common_app/widgets/bottom_sheet.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
@ -24,7 +25,7 @@ import 'package:easy_localization/easy_localization.dart';
class SelectAdTypeView extends StatelessWidget {
final List<dynamic> arguments; // [0] isProviderOrCustomer [1] isFromExtendAd
const SelectAdTypeView({Key? key, required this.arguments}) : super(key: key);
const SelectAdTypeView({super.key, required this.arguments});
Widget getVehicleAdTypeIcon(VehicleType vehicleTypeEnum) {
switch (vehicleTypeEnum) {
@ -74,12 +75,24 @@ class SelectAdTypeView extends StatelessWidget {
itemBuilder: (BuildContext context, int index) {
VehicleTypeModel vehicleTypeModel = adVM.vehicleTypes[index];
return InkWell(
onTap: () {
onTap: () async {
adVM.updateSelectionVehicleTypeId(SelectionModel(selectedId: vehicleTypeModel.id!, selectedOption: vehicleTypeModel.vehicleTypeName ?? "", errorValue: ""));
if (adVM.isAdEditEnabled) {
adVM.autoFillSelectedVehicleAdsDetails();
}
showMyBottomSheet(context, child: AdDurationSelectionSheet(isUpdateAdSelected: adVM.isAdEditEnabled, isFromExtendAd: isFromExtendAd, adsID: adsId));
if (AppState().currentAppType == AppType.provider && AppState().userType == UserType.providerDealer) {
if (isFromExtendAd && !adVM.isAdEditEnabled) {
bool status = await adVM.createAdExtensionOrder(context, adId: adsId, adsDurationId: adVM.vehicleAdDurationId.selectedId);
log("hereStatus: $status");
if (status) {
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.extendAds);
}
} else {
navigateReplaceWithName(context, AppRoutes.createAdView);
}
} else {
showMyBottomSheet(context, child: AdDurationSelectionSheet(isUpdateAdSelected: adVM.isAdEditEnabled, isFromExtendAd: isFromExtendAd, adsID: adsId));
}
},
child: SizedBox(
width: double.infinity,
@ -95,21 +108,23 @@ class SelectAdTypeView extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
vehicleTypeModel.vehicleTypeName.toString().toText(fontSize: 18, isBold: true),
vehicleTypeModel.vehicleTypeName.toString().toText(fontSize: 18),
getVehicleAdTypeIcon(vehicleTypeModel.vehicleTypeEnum!),
],
),
if (isProvider) ...[
Row(
children: [
LocaleKeys.duration.tr().toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
LocaleKeys.validUntilSubscriptionExpiration.tr().toText(fontSize: 13, isBold: true),
"${LocaleKeys.duration.tr()}: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
2.width,
LocaleKeys.validUntilSubscriptionExpiration.tr().toText(fontSize: 13),
],
).paddingOnly(top: 5, bottom: 5),
] else ...[
Row(
children: [
LocaleKeys.duration.tr().toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
4.width,
adVM.vehicleAdDurationId.selectedOption.toText(fontSize: 13, isBold: true),
const Icon(Icons.keyboard_arrow_down_sharp, color: MyColors.darkPrimaryColor, size: 20),
],
@ -123,7 +138,7 @@ class SelectAdTypeView extends StatelessWidget {
2.width,
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: LocaleKeys.sar.tr().toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
child: adVM.vehicleAdDurationId.currency.toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
),
],
),
@ -150,7 +165,7 @@ class SelectAdTypeView extends StatelessWidget {
Row(
children: [
"$remainingAds ".toText(fontSize: 29, isBold: true, letterSpacing: 0, height: 1),
LocaleKeys.adsRemaining.tr().toText(fontSize: 17, color: MyColors.lightTextColor, isBold: true),
LocaleKeys.adsRemaining.tr().toText(fontSize: 17, color: MyColors.lightTextColor),
],
),
Text.rich(

@ -26,7 +26,7 @@ import 'package:sizer/sizer.dart';
class AppointmentDetailView extends StatelessWidget {
final AppointmentListModel appointmentListModel;
const AppointmentDetailView({Key? key, required this.appointmentListModel}) : super(key: key);
const AppointmentDetailView({super.key, required this.appointmentListModel});
Widget getBaseActionButtonWidget({required Color color, required String text, Color textColor = MyColors.white, required Function() onPressed}) {
return Expanded(

@ -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,10 +1,16 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
@ -14,7 +20,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 +63,23 @@ 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))
Row(
children: [
MyAssets.icEdit.buildSvg(height: 17, width: 17).onPress(() {
appointmentsVM.updateBranchServiceId(
SelectionModel(
selectedId: serviceData.serviceProviderServiceId!,
selectedOption: serviceData.serviceDescription ?? "",
itemPrice: "",
),
);
appointmentsVM.getServiceItems(serviceData.serviceProviderServiceId!);
navigateWithName(context, AppRoutes.bookAppointmentsItemView);
}),
10.width,
MyAssets.closeWithOrangeBg.buildSvg(height: 30, width: 30).onPress(() => appointmentsVM.removeServiceInCurrentAppointment(serviceIndex)),
],
),
],
),
if (true) ...[
@ -79,14 +101,19 @@ class BookAppointmentServicesView extends StatelessWidget {
children: [
(LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor),
4.width,
(serviceData.isHomeSelected ? serviceData.homeLocation : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(),
(serviceData.isHomeSelected ? serviceData.servicelocationInfo.address : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(),
],
),
5.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
((serviceData.currentTotalServicePrice).toString()).toText(fontSize: 32, isBold: true),
if (serviceData.isHomeSelected) ...[
((serviceData.currentTotalServicePrice + (serviceData.servicelocationInfo.distanceToBranch * double.parse(serviceData.rangePricePerKm!))).toStringAsFixed(2))
.toText(fontSize: 32, isBold: true),
] else ...[
((serviceData.currentTotalServicePrice).toString()).toText(fontSize: 32, isBold: true),
],
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
const Icon(Icons.arrow_drop_down, size: 30)
@ -105,8 +132,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) {
@ -65,9 +65,9 @@ class BookAppointmentsItemView extends StatelessWidget {
],
).horPaddingMain(),
appointmentsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
? const Expanded(child: Center(child: CircularProgressIndicator()))
: appointmentsVM.serviceItemsFromApi.isEmpty
? Expanded(child: Center(child: LocaleKeys.noAvailableItems.toText(fontSize: 16, color: MyColors.lightTextColor)))
? Expanded(child: Center(child: LocaleKeys.noAvailableItems.tr().toText(fontSize: 16, color: MyColors.lightTextColor)))
: ListView.separated(
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemCount: appointmentsVM.serviceItemsFromApi.length,

@ -206,6 +206,7 @@ class ReviewAppointment extends StatelessWidget {
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: appointmentsVM.serviceAppointmentScheduleList.length,
itemBuilder: (BuildContext context, int scheduleIndex) {
ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex];
@ -223,7 +224,7 @@ class ReviewAppointment extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
// physics: const NeverScrollableScrollPhysics(),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: scheduleData.servicesListInAppointment!.length,
itemBuilder: (BuildContext context, int serviceIndex) {
@ -238,11 +239,18 @@ class ReviewAppointment extends StatelessWidget {
children: List.generate(scheduleData.servicesListInAppointment!.length, (serviceIndex) {
ServiceModel serviceData = scheduleData.servicesListInAppointment![serviceIndex];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${serviceData.providerServiceDescription}".toText(fontSize: 16, isBold: true),
if (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home) ...[
"${LocaleKeys.chargesPerKM.tr()}: ${serviceData.servicelocationInfo.homeChargesInCurrentService.toStringAsFixed(2)} ${LocaleKeys.sar.tr()}/km".toText(
fontSize: 12,
color: MyColors.lightTextColor,
),
],
],
),
Column(
@ -250,6 +258,7 @@ class ReviewAppointment extends StatelessWidget {
ItemData itemData = serviceData.serviceItems![itemIndex];
return Column(
children: [
// Item Name and Price
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
@ -257,13 +266,15 @@ class ReviewAppointment extends StatelessWidget {
("${itemData.name}: ${itemData.price} ${LocaleKeys.sar.tr()}").toText(fontSize: 13, color: MyColors.lightTextColor),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
("${itemData.description}").toText(fontSize: 11, color: MyColors.lightTextColor),
],
),
//Description
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// ("${itemData.description}").toText(fontSize: 11, color: MyColors.lightTextColor),
// ],
// ),
],
);
}),
@ -285,22 +296,32 @@ class ReviewAppointment extends StatelessWidget {
"${LocaleKeys.dateAndTime.tr()}: ".toText(fontSize: 12, color: MyColors.lightTextColor),
"${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at $selectedTimeSlot".toText(fontSize: 12),
]),
Row(children: [
("${LocaleKeys.location.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
(scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12),
]),
Row(
children: [
("${LocaleKeys.location.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
(scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.homeLocation.tr() : LocaleKeys.companyLocation.tr()).toText(fontSize: 12),
if (appointmentsVM.currentLocationInfoModel != null && scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home) ...[
" , ${appointmentsVM.currentLocationInfoModel!.distanceToBranch.toStringAsFixed(2)} KM".toText(fontSize: 12),
],
],
),
if (appointmentsVM.currentLocationInfoModel != null && scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home) ...[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
("${LocaleKeys.address.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
Flexible(child: appointmentsVM.currentLocationInfoModel!.address.toText(fontSize: 12)),
],
),
],
],
),
),
10.height,
const Divider(thickness: 0.7),
Builder(builder: (BuildContext context) {
//TODO: THIS SHOULD BE FINALIZED FROM API
double totalServicePrice = 0.0;
double totalKms = 15.3;
double rangePricePerKm = 5;
totalServicePrice = rangePricePerKm * totalKms;
double totalServicePrice = scheduleData.totalLocationCharges ?? 0.0;
double totalKms = scheduleData.locationInfoModel!.distanceToBranch;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -316,8 +337,8 @@ class ReviewAppointment extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
("${LocaleKeys.locationCharges.tr()} ($rangePricePerKm x $totalKms )").toText(fontSize: 14, color: MyColors.lightTextColor),
("${totalServicePrice.toString()} ${LocaleKeys.sar.tr()}").toText(fontSize: 16, isBold: true),
Flexible(child: ("${LocaleKeys.locationCharges.tr()} (${totalKms.toStringAsFixed(2)}km away)").toText(fontSize: 14, color: MyColors.lightTextColor)),
("${totalServicePrice.toStringAsFixed(2)} ${LocaleKeys.sar.tr()}").toText(fontSize: 16, isBold: true),
],
),
],
@ -345,13 +366,13 @@ class ReviewAppointment extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Divider(thickness: 0.7, height: 3),
const Divider(thickness: 0.8, height: 2),
8.height,
if (appointmentsVM.amountToPayForAppointment > 0) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.payableNow.tr().toText(fontSize: 14, isBold: true),
LocaleKeys.payableNow.tr().toText(fontSize: 14),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
@ -365,11 +386,11 @@ class ReviewAppointment extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.remainingAmount.tr().toText(fontSize: 14, isBold: true),
LocaleKeys.remainingAmount.tr().toText(fontSize: 14),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
(appointmentsVM.totalAmount - appointmentsVM.amountToPayForAppointment).toString().toText(fontSize: 16, isBold: true),
(appointmentsVM.totalAmount - appointmentsVM.amountToPayForAppointment).toStringAsFixed(2).toText(fontSize: 16, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2),
],
@ -384,7 +405,7 @@ class ReviewAppointment extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
appointmentsVM.totalAmount.toString().toText(fontSize: 29, isBold: true),
appointmentsVM.totalAmount.toStringAsFixed(2).toText(fontSize: 29, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
],
@ -436,6 +457,7 @@ class ReviewAppointment extends StatelessWidget {
buildServicesInfoCard(context: context),
10.height,
buildPersonalInformationCard(context: context),
20.height,
],
).expand(),
buildNextButtonFooter(context: context),

@ -1,10 +1,15 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/services/common_services.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
@ -18,7 +23,7 @@ import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
class AppointmentServicePickBottomSheet extends StatelessWidget {
const AppointmentServicePickBottomSheet({Key? key}) : super(key: key);
const AppointmentServicePickBottomSheet({super.key});
@override
Widget build(BuildContext context) {
@ -50,7 +55,9 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
(DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: appointmentsVM.branchCategories,
hint: LocaleKeys.selectCategory.tr(),
dropdownValue: appointmentsVM.branchSelectedCategoryId.selectedId != -1 ? DropValue(appointmentsVM.branchSelectedCategoryId.selectedId, appointmentsVM.branchSelectedCategoryId.selectedOption, "") : null,
dropdownValue: appointmentsVM.branchSelectedCategoryId.selectedId != -1
? DropValue(appointmentsVM.branchSelectedCategoryId.selectedId, appointmentsVM.branchSelectedCategoryId.selectedOption, "")
: null,
);
},
),
@ -78,7 +85,9 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
(DropValue value) => appointmentsVM.updateBranchServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: serviceCategories,
hint: LocaleKeys.selectService.tr(),
dropdownValue: appointmentsVM.branchSelectedServiceId.selectedId != -1 ? DropValue(appointmentsVM.branchSelectedServiceId.selectedId, appointmentsVM.branchSelectedServiceId.selectedOption, "") : null,
dropdownValue: appointmentsVM.branchSelectedServiceId.selectedId != -1
? DropValue(appointmentsVM.branchSelectedServiceId.selectedId, appointmentsVM.branchSelectedServiceId.selectedOption, "")
: null,
);
},
),
@ -91,6 +100,10 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
children: [
Expanded(
child: ShowFillButton(
borderColor: !(appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false))
? Colors.transparent
: MyColors.darkPrimaryColor,
isDisabled: !(appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)),
isFilled: appointmentsVM.isHomeTapped,
maxHeight: 48,
title: LocaleKeys.home.tr(),
@ -117,11 +130,11 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
],
),
if (appointmentsVM.isHomeTapped) ...[
8.height,
12.height,
TxtField(
hint: LocaleKeys.pickLocation.tr(),
errorValue: appointmentsVM.pickHomeLocationError,
value: appointmentsVM.pickedHomeLocation,
value: appointmentsVM.currentLocationInfoModel == null ? "" : appointmentsVM.currentLocationInfoModel!.address,
isNeedClickAll: true,
postfixData: Icons.add_location_outlined,
postFixDataColor: MyColors.darkPrimaryColor,
@ -129,9 +142,41 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
navigateTo(
context,
PickLocationPage(
onPickAddress: (double latitude, double longitude, String address) {
appointmentsVM.updatePickedHomeLocation(address);
onPickAddress: (double latitude, double longitude, String address) async {
appointmentsVM.setState(ViewState.idle);
double distance = injector.get<CommonAppServices>().getDistanceBetween(
curLatitude: double.parse(appointmentsVM.selectedBranchModel!.latitude!),
curLongitude: double.parse(appointmentsVM.selectedBranchModel!.longitude!),
destLatitude: latitude,
destLongitude: longitude,
);
log("distance: $distance");
log("customerLocationRange : ${appointmentsVM.currentServiceSelection!.customerLocationRange}");
if (distance > (appointmentsVM.currentServiceSelection!.customerLocationRange ?? 0)) {
Utils.showToast(LocaleKeys.serviceNotProvided.tr());
appointmentsVM.currentServiceSelection!.servicelocationInfo = CurrentLocationInfoModel(
address: '',
latitude: 0.0,
longitude: 0.0,
distanceToBranch: 0.0,
homeChargesInCurrentService: 0.0,
);
appointmentsVM.updateCurrentLocationInfoModel(null);
return;
}
CurrentLocationInfoModel temp = CurrentLocationInfoModel(
address: address,
latitude: latitude,
longitude: longitude,
distanceToBranch: distance,
homeChargesInCurrentService: double.parse(appointmentsVM.currentServiceSelection!.rangePricePerKm ?? "0.0"),
);
log("appointmentsVM.currentServiceSelection!.rangePricePerKm : ${temp.toString()}");
appointmentsVM.updateCurrentLocationInfoModel(temp);
},
),
);
@ -139,11 +184,11 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
),
14.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.warning, color: MyColors.adPendingStatusColor, size: 19).paddingOnly(bottom: 2),
3.width,
LocaleKeys.servicesNotAvailableHomeLocation.tr().toText(color: MyColors.adPendingStatusColor, fontSize: 12, isItalic: true),
Flexible(child: LocaleKeys.pickedLocationNotice.tr().toText(color: MyColors.adPendingStatusColor, fontSize: 12, isItalic: true)),
],
),
]
@ -155,14 +200,30 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
width: double.infinity,
child: Column(
children: [
if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices && appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) ...[
if (appointmentsVM.isHomeTapped &&
!appointmentsVM.isFetchingServices &&
appointmentsVM.currentServiceSelection != null &&
(appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) ...[
const Divider(thickness: 1, height: 1),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// TODO: This Price will be decided according to the service selected, We will calculate the KMs and multiple it with price per KMs
appointmentsVM.currentServiceSelection!.rangePricePerKm.toString().toText(fontSize: 30, isBold: true),
"${LocaleKeys.sar.tr()}/km".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
if (appointmentsVM.currentLocationInfoModel == null) ...[
appointmentsVM.currentServiceSelection!.rangePricePerKm.toString().toText(fontSize: 30, isBold: true),
"${LocaleKeys.sar.tr()}/km".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
] else ...[
(appointmentsVM.currentServiceSelection!.servicelocationInfo.homeChargesInCurrentService * appointmentsVM.currentServiceSelection!.servicelocationInfo.distanceToBranch)
.toStringAsFixed(2)
.toText(
fontSize: 30,
isBold: true,
),
LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
10.width,
("(${(appointmentsVM.currentServiceSelection!.servicelocationInfo.homeChargesInCurrentService).toStringAsFixed(2)} ${LocaleKeys.sar.tr()} x ${appointmentsVM.currentServiceSelection!.servicelocationInfo.distanceToBranch.toStringAsFixed(2)} KMs )")
.toText(fontSize: 13, color: MyColors.lightTextColor)
.paddingOnly(bottom: 5),
],
],
),
LocaleKeys.additionalChargesNotice.tr().toText(fontSize: 12, color: MyColors.lightTextColor),

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

@ -7,6 +7,7 @@ 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/ad_details_model.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/utils/navigator.dart';
@ -77,7 +78,6 @@ class AdsFragment extends StatelessWidget {
txtColor: adVM.isExploreAdsTapped ? MyColors.white : MyColors.darkTextColor,
onPressed: () {
print("accessToken: ${AppState().getUser.data!.accessToken}");
adVM.populateAdsFilterList();
if (adVM.myAds.isEmpty) {
adVM.getMyAds();
}
@ -134,9 +134,7 @@ class AdsFragment extends StatelessWidget {
}
},
child: adVM.state == ViewState.busy
? const Center(
child: CircularProgressIndicator(),
)
? const Center(child: CircularProgressIndicator())
: AdsListWidget(isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM)),
),
)
@ -145,6 +143,18 @@ class AdsFragment extends StatelessWidget {
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
if (AppState().userType != UserType.providerDealer) {
if (adVM.vehicleAdDurationId.selectedId == -1 && adVM.vehicleAdsDurations.isNotEmpty) {
SelectionModel selection = SelectionModel(
selectedId: adVM.vehicleAdsDurations.first.id ?? 0,
selectedOption: ("${adVM.vehicleAdsDurations.first.days} ${LocaleKeys.daysVar.tr()}"),
itemPrice: adVM.vehicleAdsDurations.first.price!.toString(),
currency: adVM.vehicleAdsDurations.first.currency!.toString(),
);
adVM.updateVehicleAdDurationId(selection);
}
}
navigateWithName(context, AppRoutes.selectAdTypeView, arguments: [injector.get<AppState>().currentAppType == AppType.provider, false, -1]);
},
backgroundColor: MyColors.darkPrimaryColor,

@ -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,
@ -155,7 +155,12 @@ class MyRequestsFragment extends StatelessWidget {
cancelRequestBottomSheet(context, requestsVM.myFilteredRequests[index].id);
return;
},
child: RequestItem(request: requestsVM.myFilteredRequests[index], appType: AppState().currentAppType, requestIndex: index),
child: RequestItem(
request: requestsVM.myFilteredRequests[index],
appType: AppState().currentAppType,
requestIndex: index,
shouldShowStatuses: AppState().currentAppType == AppType.customer,
),
);
},
separatorBuilder: (context, index) {

@ -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,243 +34,252 @@ class CreateRequestPage extends StatelessWidget {
pop(context);
},
),
body: Consumer<RequestsVM>(builder: (context, requestsVM, widget) {
return Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.requestType.tr().toText(fontSize: 18, isBold: true),
8.height,
if (requestsVM.isFetchingRequestType) ...[
const Center(
child: CircularProgressIndicator(),
),
] else ...[
if (AppState().getUser.data!.userInfo != null) ...[
TxtField(
value: "${AppState().getUser.data!.userInfo!.firstName ?? ""} ${AppState().getUser.data!.userInfo!.lastName ?? ""}",
isBackgroundEnabled: true,
isNeedClickAll: true,
hint: '',
onTap: () {},
),
],
body: PopScope(
onPopInvokedWithResult: (bool result, dynamic) {
if (result) {
context.read<RequestsVM>().resetRequestCreationForm();
}
},
child: Consumer<RequestsVM>(builder: (context, requestsVM, widget) {
return Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.requestType.tr().toText(fontSize: 18, isBold: true),
8.height,
Builder(builder: (context) {
List<DropValue> requestTypeDrop = [];
for (var element in requestsVM.myRequestsTypeEnum) {
requestTypeDrop.add(DropValue(element.enumValue.toInt() ?? 0, element.enumValueStr ?? "", ""));
}
return DropdownField(
(DropValue value) => requestsVM.updateSelectionRequestTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: requestTypeDrop,
dropdownValue: requestsVM.requestTypeId.selectedId != -1 ? DropValue(requestsVM.requestTypeId.selectedId, requestsVM.requestTypeId.selectedOption, "") : null,
hint: LocaleKeys.requestType.tr(),
errorValue: requestsVM.requestTypeId.errorValue,
);
}),
],
8.height,
if (requestsVM.requestTypeId.selectedId != -1)
if (requestsVM.isFetchingVehicleType) ...[
if (requestsVM.isFetchingRequestType) ...[
const Center(
child: CircularProgressIndicator(),
),
] else ...[
if (AppState().getUser.data!.userInfo != null) ...[
TxtField(
value: "${AppState().getUser.data!.userInfo!.firstName ?? ""} ${AppState().getUser.data!.userInfo!.lastName ?? ""}",
isBackgroundEnabled: true,
isNeedClickAll: true,
hint: '',
onTap: () {},
),
],
8.height,
Builder(builder: (context) {
List<DropValue> vehicleTypeDrop = [];
for (var element in requestsVM.vehicleTypes) {
vehicleTypeDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleTypeName ?? "", ""));
List<DropValue> requestTypeDrop = [];
for (var element in requestsVM.myRequestsTypeEnum) {
requestTypeDrop.add(DropValue(element.enumValue.toInt() ?? 0, element.enumValueStr ?? "", ""));
}
return DropdownField(
(DropValue value) => requestsVM.updateSelectionVehicleTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleTypeDrop,
dropdownValue: requestsVM.vehicleTypeId.selectedId != -1 ? DropValue(requestsVM.vehicleTypeId.selectedId, requestsVM.vehicleTypeId.selectedOption, "") : null,
hint: LocaleKeys.vehicleType.tr(),
errorValue: requestsVM.vehicleTypeId.errorValue,
(DropValue value) => requestsVM.updateSelectionRequestTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: requestTypeDrop,
dropdownValue: requestsVM.requestTypeId.selectedId != -1 ? DropValue(requestsVM.requestTypeId.selectedId, requestsVM.requestTypeId.selectedOption, "") : null,
hint: LocaleKeys.requestType.tr(),
errorValue: requestsVM.requestTypeId.errorValue,
);
}),
],
8.height,
if (requestsVM.vehicleTypeId.selectedId != -1)
if (requestsVM.isFetchingVehicleDetail) ...[
const Center(
child: CircularProgressIndicator(),
),
] else ...[
Column(
children: [
TxtField(
hint: LocaleKeys.brand.tr(),
value: requestsVM.brand,
onChanged: (e) => requestsVM.updateBrand(e),
),
8.height,
TxtField(
hint: LocaleKeys.model.tr(),
value: requestsVM.model,
onChanged: (e) => requestsVM.updateModel(e),
),
8.height,
Builder(builder: (context) {
List<DropValue> vehicleYearModelsDrop = [];
for (var element in requestsVM.vehicleYears) {
vehicleYearModelsDrop.add(DropValue(element.id?.toInt() ?? 0, element.modelYear ?? "", ""));
}
return DropdownField(
(DropValue value) => requestsVM.updateSelectionVehicleYearId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleYearModelsDrop,
dropdownValue: requestsVM.vehicleYearId.selectedId != -1 ? DropValue(requestsVM.vehicleYearId.selectedId, requestsVM.vehicleYearId.selectedOption, "") : null,
hint: LocaleKeys.year.tr(),
errorValue: requestsVM.vehicleYearId.errorValue,
);
}),
8.height,
Builder(
builder: (context) {
List<DropValue> vehicleOwnerDrop = [];
for (var element in requestsVM.vehicleConditionsEnum) {
vehicleOwnerDrop.add(DropValue(element.id.toInt(), element.enumValueStr, ""));
8.height,
if (requestsVM.requestTypeId.selectedId != -1)
if (requestsVM.isFetchingVehicleType) ...[
const Center(
child: CircularProgressIndicator(),
),
] else ...[
Builder(builder: (context) {
List<DropValue> vehicleTypeDrop = [];
for (var element in requestsVM.vehicleTypes) {
vehicleTypeDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleTypeName ?? "", ""));
}
return DropdownField(
(DropValue value) => requestsVM.updateSelectionVehicleTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleTypeDrop,
dropdownValue: requestsVM.vehicleTypeId.selectedId != -1 ? DropValue(requestsVM.vehicleTypeId.selectedId, requestsVM.vehicleTypeId.selectedOption, "") : null,
hint: LocaleKeys.vehicleType.tr(),
errorValue: requestsVM.vehicleTypeId.errorValue,
);
}),
],
8.height,
if (requestsVM.vehicleTypeId.selectedId != -1)
if (requestsVM.isFetchingVehicleDetail) ...[
const Center(
child: CircularProgressIndicator(),
),
] else ...[
Column(
children: [
TxtField(
hint: LocaleKeys.brand.tr(),
value: requestsVM.brand,
onChanged: (e) => requestsVM.updateBrand(e),
),
8.height,
TxtField(
hint: LocaleKeys.model.tr(),
value: requestsVM.model,
onChanged: (e) => requestsVM.updateModel(e),
),
8.height,
Builder(builder: (context) {
List<DropValue> vehicleYearModelsDrop = [];
for (var element in requestsVM.vehicleYears) {
vehicleYearModelsDrop.add(DropValue(element.id?.toInt() ?? 0, element.modelYear ?? "", ""));
}
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,
hint: LocaleKeys.condition.tr(),
errorValue: requestsVM.vehicleConditionId.errorValue,
(DropValue value) => requestsVM.updateSelectionVehicleYearId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleYearModelsDrop,
dropdownValue: requestsVM.vehicleYearId.selectedId != -1 ? DropValue(requestsVM.vehicleYearId.selectedId, requestsVM.vehicleYearId.selectedOption, "") : null,
hint: LocaleKeys.year.tr(),
errorValue: requestsVM.vehicleYearId.errorValue,
);
},
),
8.height,
Builder(builder: (context) {
List<DropValue> vehicleCountriesDrop = [];
for (var element in requestsVM.vehicleCountries) {
vehicleCountriesDrop.add(DropValue(element.id?.toInt() ?? 0, element.countryName ?? "", ""));
}
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,
hint: LocaleKeys.country.tr(),
errorValue: requestsVM.vehicleCountryId.errorValue,
);
}),
if (requestsVM.vehicleCountryId.selectedId != -1) ...[
if (requestsVM.isCountryFetching) ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [const CircularProgressIndicator().paddingAll(10)],
),
] else ...[
8.height,
Builder(builder: (context) {
List<DropValue> vehicleCitiesDrop = [];
for (var element in requestsVM.vehicleCities) {
vehicleCitiesDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", ""));
}),
8.height,
Builder(
builder: (context) {
List<DropValue> vehicleOwnerDrop = [];
for (var element in requestsVM.vehicleConditionsEnum) {
vehicleOwnerDrop.add(DropValue(element.id.toInt(), element.enumValueStr, ""));
}
return DropdownField(
(DropValue value) => requestsVM.updateSelectionVehicleCityId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleCitiesDrop,
dropdownValue: requestsVM.vehicleCityId.selectedId != -1 ? DropValue(requestsVM.vehicleCityId.selectedId, requestsVM.vehicleCityId.selectedOption, "") : null,
hint: LocaleKeys.city.tr(),
errorValue: requestsVM.vehicleCityId.errorValue,
(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,
hint: LocaleKeys.condition.tr(),
errorValue: requestsVM.vehicleConditionId.errorValue,
);
}),
],
],
// 8.height,
// TxtField(
// hint: LocaleKeys.price.tr(),
// value: requestsVM.price,
// keyboardType: TextInputType.number,
// onChanged: (e) => requestsVM.updatePrice(e),
// ),
8.height,
TxtField(
hint: LocaleKeys.description.tr(),
maxLines: 3,
value: requestsVM.description,
onChanged: (e) => requestsVM.updateDescription(e),
),
8.height,
// 1 -> SpecialCarRequest
// 2 -> ServiceRequest
if (requestsVM.requestTypeId.selectedId == 2) ...[
TxtField(
hint: LocaleKeys.address.tr(),
isNeedClickAll: false,
value: requestsVM.address,
postfixWidget: IconButton(
icon: const Icon(
size: 28,
Icons.add_location_outlined,
),
onPressed: () {
navigateTo(
context,
PickLocationPage(
onPickAddress: (double latitude, double longitude, String address) {
requestsVM.address = address;
requestsVM.setState(ViewState.idle);
},
),
);
}),
onChanged: (e) => requestsVM.updateAddress(e),
},
),
8.height,
],
if (requestsVM.pickedVehicleImages.isEmpty) ...[
DottedRectContainer(
onTap: () => context.read<RequestsVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
),
],
if (requestsVM.vehicleImageError != "") ...[
10.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
requestsVM.vehicleImageError.toText(fontSize: 14, color: Colors.red),
Builder(builder: (context) {
List<DropValue> vehicleCountriesDrop = [];
for (var element in requestsVM.vehicleCountries) {
vehicleCountriesDrop.add(DropValue(element.id?.toInt() ?? 0, element.countryName ?? "", ""));
}
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,
hint: LocaleKeys.country.tr(),
errorValue: requestsVM.vehicleCountryId.errorValue,
);
}),
if (requestsVM.vehicleCountryId.selectedId != -1) ...[
if (requestsVM.isCountryFetching) ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [const CircularProgressIndicator().paddingAll(10)],
),
] else ...[
8.height,
Builder(builder: (context) {
List<DropValue> vehicleCitiesDrop = [];
for (var element in requestsVM.vehicleCities) {
vehicleCitiesDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", ""));
}
return DropdownField(
(DropValue value) => requestsVM.updateSelectionVehicleCityId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleCitiesDrop,
dropdownValue: requestsVM.vehicleCityId.selectedId != -1 ? DropValue(requestsVM.vehicleCityId.selectedId, requestsVM.vehicleCityId.selectedOption, "") : null,
hint: LocaleKeys.city.tr(),
errorValue: requestsVM.vehicleCityId.errorValue,
);
}),
],
).paddingOnly(right: 10)
],
if (requestsVM.pickedVehicleImages.isNotEmpty) ...[
16.height,
PickedFilesContainer(
pickedFiles: requestsVM.pickedVehicleImages,
onCrossPressedPrimary: requestsVM.removeImageFromList,
onAddFilePressed: () {
context.read<RequestsVM>().pickMultipleImages();
},
],
// 8.height,
// TxtField(
// hint: LocaleKeys.price.tr(),
// value: requestsVM.price,
// keyboardType: TextInputType.number,
// onChanged: (e) => requestsVM.updatePrice(e),
// ),
8.height,
TxtField(
hint: LocaleKeys.description.tr(),
maxLines: 3,
value: requestsVM.description,
onChanged: (e) => requestsVM.updateDescription(e),
),
8.height,
// 1 -> SpecialCarRequest
// 2 -> ServiceRequest
if (requestsVM.requestTypeId.selectedId == 2) ...[
TxtField(
hint: LocaleKeys.address.tr(),
isNeedClickAll: false,
value: requestsVM.address,
postfixWidget: IconButton(
icon: const Icon(
size: 28,
Icons.add_location_outlined,
),
onPressed: () {
navigateTo(
context,
PickLocationPage(
onPickAddress: (double latitude, double longitude, String address) {
requestsVM.address = address;
requestsVM.setState(ViewState.idle);
},
),
);
}),
onChanged: (e) => requestsVM.updateAddress(e),
),
8.height,
],
if (requestsVM.pickedVehicleImages.isEmpty) ...[
DottedRectContainer(
onTap: () => context.read<RequestsVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
),
],
if (requestsVM.vehicleImageError != "") ...[
10.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
requestsVM.vehicleImageError.toText(fontSize: 14, color: Colors.red),
],
).paddingOnly(right: 10)
],
if (requestsVM.pickedVehicleImages.isNotEmpty) ...[
16.height,
PickedFilesContainer(
pickedFiles: requestsVM.pickedVehicleImages,
onCrossPressedPrimary: requestsVM.removeImageFromList,
onAddFilePressed: () {
context.read<RequestsVM>().pickMultipleImages();
},
),
],
],
],
),
]
],
).toContainer(isShadowEnabled: true, marginAll: 16, paddingAll: 12),
),
]
],
).toContainer(isShadowEnabled: true, marginAll: 16, paddingAll: 12),
),
),
),
ShowFillButton(
title: LocaleKeys.createRequest.tr(),
maxWidth: double.infinity,
maxHeight: 55,
margin: const EdgeInsets.all(16),
onPressed: () async {
await context.read<RequestsVM>().onCreateRequestTapped(context);
},
)
],
);
}),
ShowFillButton(
title: LocaleKeys.createRequest.tr(),
maxWidth: double.infinity,
maxHeight: 55,
margin: const EdgeInsets.all(16),
onPressed: () async {
await context.read<RequestsVM>().onCreateRequestTapped(context);
},
)
],
);
}),
),
);
}
}

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
@ -18,100 +19,131 @@ 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 Scaffold(
appBar: CustomAppBar(title: LocaleKeys.offers.tr()),
body: serviceProviderOffers.isEmpty
? Center(child: LocaleKeys.noOffersShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor))
: ListView.separated(
itemCount: serviceProviderOffers.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
ServiceProvidersOffers offersModel = serviceProviderOffers[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.statusContainerChip(
text: Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!),
chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(offersModel.name ?? "").toText(fontSize: 16, isBold: true),
Center(
child: "${offersModel.offerCount}".toText(
color: Colors.white,
isBold: true,
fontSize: 10,
return Consumer<ChatVM>(builder: (context, ChatVM chatVM, Widget? child) {
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.offers.tr()),
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,
),
).toContainer(
backgroundColor: MyColors.cancelledColor,
borderRadius: 100,
paddingAll: 1,
width: 22,
height: 22,
),
],
),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
)
: ListView.separated(
itemCount: chatVM.serviceProviderOffersList.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
ServiceProvidersOffers offersModel = chatVM.serviceProviderOffersList[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${offersModel.companyName}".toText(color: MyColors.lightTextColor, fontSize: 14),
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,
// ),
Utils.statusContainerChip(
text: Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!),
chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(offersModel.name ?? "").toText(
fontSize: 16,
isBold: true,
),
Center(
child: "${offersModel.offerCount}".toText(
color: Colors.white,
isBold: true,
fontSize: 10,
),
).toContainer(
backgroundColor: MyColors.cancelledColor,
borderRadius: 100,
paddingAll: 1,
width: 22,
height: 22,
),
],
),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
"${offersModel.companyName}".toText(color: MyColors.lightTextColor, fontSize: 14),
if (offersModel.createdOn != null && offersModel.createdOn!.isNotEmpty) ...[
" | ${DateTime.parse(offersModel.createdOn!).getTimeAgo()}".toText(color: MyColors.lightTextColor, fontSize: 14),
],
],
),
const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18),
],
),
],
),
const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18),
],
),
],
).onPress(() async {
context.read<RequestsVM>().currentSelectedOffer = offersModel;
).onPress(() async {
context.read<RequestsVM>().currentSelectedOffer = offersModel;
ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest(
chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: "${offersModel.providerUserId}",
senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestId: offerListPageArguments.requestId,
providerIndex: index,
requestIndex: -1, // This will be only send in case of provider
);
ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest(
chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: "${offersModel.providerUserId}",
senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestId: widget.requestId,
providerIndex: index,
requestModel: context.read<RequestsVM>().currentSelectedRequest,
requestIndex: -1,
);
ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest);
ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest);
final chatVM = context.read<ChatVM>();
final chatVM = context.read<ChatVM>();
await chatVM
.getRequestsChatMessagesForCustomer(
context: context,
providerId: offersModel.providerId ?? 0,
requestOfferId: 0,
requestId: offerListPageArguments.requestId ?? 0,
providerOfferIndex: index,
)
.whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments));
}).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 16.height,
),
);
await chatVM
.getRequestsChatMessagesForCustomer(
context: context,
providerId: offersModel.providerId ?? 0,
requestOfferId: 0,
requestId: widget.requestId,
providerOfferIndex: index,
)
.whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments));
}).toContainer(isShadowEnabled: true);
},
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(

@ -8,7 +8,9 @@ import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/appointments_view_model.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,17 +23,22 @@ 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}) {
// final requestsVM = context.read<RequestsVM>();
Widget buildRequestDetailActionFooter({
required int requestId,
required RequestStatusEnum requestStatus,
required RequestsTypeEnum requestTypeEnum,
required String statusText,
required BuildContext context,
}) {
switch (requestStatus) {
case RequestStatusEnum.submitted:
return ShowFillButton(
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 +122,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 +151,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 +167,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(),
),
],
],
@ -211,6 +218,30 @@ class RequestDetailPage extends StatelessWidget {
],
showItem("${LocaleKeys.description.tr()}: ", requestDetail.description),
16.height,
if (AppState().currentAppType == AppType.provider &&
(requestDetailPageArguments.requestModel.requestStatus == RequestStatusEnum.inProgress || requestDetailPageArguments.requestModel.requestStatus == RequestStatusEnum.submitted)) ...[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
("${LocaleKeys.reportComplaint.tr()}?").toText(fontSize: 14, color: MyColors.darkTextColor),
],
).onPress(
() {
int customerID = requestDetailPageArguments.requestModel.customerId;
int complainType = 2;
String customerName = requestDetailPageArguments.requestModel.customerName;
context.read<AppointmentsVM>().buildComplaintBottomSheet(
customerID: customerID,
customerName: customerName,
complainType: complainType,
context: context,
);
},
),
],
// showItem("${LocaleKeys.priceRange.tr()}:", ""),
// Row(
// children: [

@ -20,28 +20,44 @@ class RequestItem extends StatelessWidget {
final RequestModel request;
final AppType appType;
final int requestIndex;
final bool shouldShowStatuses;
const RequestItem({super.key, required this.request, required this.appType, required this.requestIndex});
const RequestItem({super.key, required this.request, required this.appType, required this.requestIndex, this.shouldShowStatuses = true});
@override
Widget build(BuildContext context) {
String imageUrl = "";
if (request.requestImages != null && request.requestImages!.isNotEmpty) {
imageUrl = request.requestImages!.first.imageUrl ?? "";
}
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
imageUrl.buildNetworkImage(
width: 80,
height: 80,
fit: BoxFit.cover,
),
12.width,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.statusContainerChip(text: request.requestStatusName.toLowerCase() == "InProgress".toLowerCase() ? "In Progress" : request.requestStatusName, chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)),
if (shouldShowStatuses) ...[
Utils.statusContainerChip(
text: request.requestStatusName.toLowerCase() == "InProgress".toLowerCase() ? "In Progress" : request.requestStatusName,
chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)),
],
6.height,
"${request.brand} ${request.model} | ${request.id}".toText(fontSize: 16, letterSpacing: -0.64),
showItem("${LocaleKeys.year.tr()}:", "${request.year}"),
if (request.customerName.isNotEmpty) ...[
showItem("${LocaleKeys.customerName.tr()}:", request.customerName),
],
showItem("${LocaleKeys.description.tr()}:", request.description),
],
),
),
@ -72,7 +88,6 @@ class RequestItem extends StatelessWidget {
)
],
),
showItem("${LocaleKeys.description.tr()}:", request.description),
// showItem("${LocaleKeys.priceRange.tr()}: ", ""),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -106,10 +121,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),
),
))
],
),
),
);
});
}
}

@ -90,13 +90,15 @@ class _SettingOptionsContactUsState extends State<SettingOptionsContactUs> {
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
ContactInfoModel contactInfoModel = settingsOptionsVM.contactInfosList[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
contactInfoModel.companyName.toString().toText(fontSize: 16),
5.height,
if (contactInfoModel.latitude != null && contactInfoModel.latitude!.isNotEmpty && contactInfoModel.longitude != null && contactInfoModel.longitude!.isNotEmpty) ...[
if (contactInfoModel.latitude != null &&
contactInfoModel.latitude!.isNotEmpty &&
contactInfoModel.longitude != null &&
contactInfoModel.longitude!.isNotEmpty) ...[
LocaleKeys.openMapLocation.tr().toText(isUnderLine: true, color: MyColors.darkPrimaryColor, fontSize: 10).onPress(() async {
double latitude, longitude = 0.0;
latitude = double.parse(contactInfoModel.latitude!);

@ -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),
],
],
),
const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18),
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,

@ -8,8 +8,16 @@ class CheckBoxWithTitleDescription extends StatelessWidget {
final bool isSelected;
final String title, description;
final Function(bool) onSelection;
final bool isDisabled;
const CheckBoxWithTitleDescription({required this.isSelected, required this.title, required this.description, required this.onSelection, Key? key}) : super(key: key);
const CheckBoxWithTitleDescription({
required this.isSelected,
required this.title,
required this.description,
required this.onSelection,
this.isDisabled = false,
super.key,
});
@override
Widget build(BuildContext context) {
@ -21,7 +29,9 @@ class CheckBoxWithTitleDescription extends StatelessWidget {
children: [
Checkbox(
value: isSelected,
activeColor: isDisabled ? MyColors.lightIconColor : MyColors.darkPrimaryColor,
onChanged: (bool? v) {
if (isDisabled) return;
onSelection(v ?? false);
},
),

@ -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,43 +18,58 @@ 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(
padding: const EdgeInsets.all(16.0),
child: Center(
child: InteractiveViewer(
child: PageView.builder(
itemCount: widget.images.length,
controller: pageController,
onPageChanged: (index) {
setState(() {
selectedIndex = index; // Update the selected index to reflect the current page
});
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(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
)
: widget.images[selectedIndex].imagePath.buildFileImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
// 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,
scaleEnabled: true,
minScale: 1.0,
maxScale: 4.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: (widget.images[index].isFromNetwork ?? false)
? widget.images[index].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
)
: widget.images[index].imagePath.buildFileImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
),
),
),
),
),
);
},
),
),
// 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,
],
),
);

@ -7,11 +7,11 @@ import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
class RoleTypeTab extends StatefulWidget {
int selectedIndex;
List<DropValue> dropList;
Function(DropValue value) onSelect;
double? width;
final List<DropValue> dropList;
final Function(DropValue value) onSelect;
final double? width;
RoleTypeTab(this.selectedIndex, this.dropList, {required this.onSelect, this.width});
RoleTypeTab(this.selectedIndex, this.dropList, {super.key, required this.onSelect, this.width});
@override
State<RoleTypeTab> createState() => _RoleTypeTabState();

@ -16,9 +16,9 @@ dependencies:
injector: ^4.0.0
provider: ^6.0.0
easy_localization: ^3.0.3
http: ^0.13.6
http: ^1.2.2
permission_handler: ^11.3.1
flutter_svg: ^2.0.9
flutter_svg: ^2.0.14
sizer: ^3.0.4
fluttertoast: ^8.0.8
shared_preferences: ^2.0.6
@ -61,9 +61,9 @@ dependencies:
# Auth
local_auth: ^2.2.0
#huawei_fido: ^6.3.0+305
# huawei_fido: ^6.3.0+305
device_info_plus: ^11.0.0
flutter_lints: any
dev_dependencies:
flutter_test:

Loading…
Cancel
Save