diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 5a53e2e..497c578 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -698,7 +698,6 @@ "explore": "استكشاف", "manageRequests": "إدارة الطلبات", "serviceNotAvailableAtHomeLocation": "الخدمة المختارة غير متوفرة في الموقع المحدد.", - "noAvailableItems": "لا توجد عناصر متاحة.", "wantToRescheduleAppointment": "أرغب في إعادة جدولة الموعد.", "noNeedForService": "لا أحتاج هذه الخدمة بعد الآن.", "testTheService": "اختبر الخدمة.", @@ -706,5 +705,20 @@ "customerLocation": "موقع العميل", "deliveryAvailable": "التوصيل متاح", "viewed": "تم المشاهدة", - "itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا." + "itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا.", + "reactivateAd": "إعادة تنشيط الإعلان", + "dealOutsideApp": "تمت الصفقة خارج التطبيق مع عميل آخر.", + "noAgreementCustomer": "لا يوجد اتفاق من جانب العميل.", + "dealNotCompleted": "لم تكتمل الصفقة", + "changedDesireToSell": "غيرت رغبتي في بيع المركبة.", + "vehicleDealOutsideApp": "تمت صفقة المركبة خارج التطبيق.", + "markAdAsSoldDesc": "سيتم وضع علامة 'تم البيع' على هذا الإعلان، ولن يتمكن المستخدمون الآخرون من التواصل معك.", + "ownerInformation": "معلومات المالك", + "acceptedRequests": "الطلبات المقبولة", + "specialRequestChat": "دردشة الطلب الخاص", + "companyName": "اسم الشركة", + "noAvailableItems": "لا توجد عناصر متاحة.", + "serviceDeliveryType": "نوع تقديم الخدمة", + "noImagesToShow": "لا توجد صور للعرض", + "updateGroupServices": "تحديث خدمات المجموعة" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 3bf1e99..c29fde3 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -626,7 +626,7 @@ "priceRange": "Price Range", "cancelOffer": "Cancel Offer", "pleaseSpecify": "Please Specify", - "customerNotResponding": "Customer Not Responding", + "customerNotResponding": "The customer is not responding.", "cancelRequestPrompt": "Do you want to cancel this request?", "requestPermanentlyCancelled": "Your request will be permanently cancelled. You cannot undo this action.", "awaitingResponseFromCustomer": "Awaiting Response From Customer", @@ -695,7 +695,6 @@ "explore": "Explore", "manageRequests": "Manage Requests", "serviceNotAvailableAtHomeLocation": "The Selected Service is not available at Home Location.", - "noAvailableItems": "There are no available items.", "wantToRescheduleAppointment": "I want to reschedule the appointment.", "noNeedForService": "I do not need this service anymore.", "testTheService": "Test the service.", @@ -703,6 +702,21 @@ "customerLocation": "Customer Location", "deliveryAvailable": "Delivery Available", "viewed": "Viewed", - "itemNoLongerAvailable": "This item is no longer available." - + "itemNoLongerAvailable": "This item is no longer available.", + "reactivateAd": "Reactivate Ad", + "dealOutsideApp": "The deal was done outside the app with another customer.", + "noAgreementCustomer": "There is no agreement from the customer side.", + "dealNotCompleted": "Deal not completed", + "changedDesireToSell": "I changed my desire to sell the vehicle.", + "vehicleDealOutsideApp": "The vehicle deal was done outside the app.", + "markAdAsSoldDesc": "This ad will be marked as sold, and other users will no longer be able to contact you.", + "ownerInformation": "Owner Information", + "noItemsToShow": "There are no Items no show.", + "acceptedRequests": "Accepted Requests", + "specialRequestChat": "Special Request Chat", + "companyName": "Company Name", + "noAvailableItems": "There are no available items.", + "serviceDeliveryType": "Service Delivery Type", + "noImagesToShow": "No Images to Show", + "updateGroupServices": "Update Group Services" } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index da876fe..52d3c6d 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -14,6 +14,8 @@ class ApiConsts { static String basicComplete = "${baseUrlServices}api/Register/BasicComplete"; static String refreshToken = "${baseUrlServices}api/Account/RefreshToken"; + static String signalRUrl = "$baseUrl/McHub"; + //User static String Login_V1 = "${baseUrlServices}api/Account/Login_V1"; static String Login_V2_OTP = "${baseUrlServices}api/Account/Login_V2_OTP"; @@ -96,6 +98,7 @@ class ApiConsts { static String updateSchedule = "${baseUrlServices}api/ServiceProviders/BranchAppointmentSchedule_Update"; static String createGroup = "${baseUrlServices}api/ServiceProviders/BranchScheduleGroupService_Create"; static String updateGroup = "${baseUrlServices}api/ServiceProviders/BranchScheduleGroupService_Update"; + static String checkGroupServiceInBranchSchedule = "${baseUrlServices}api/ServiceProviders/BranchScheduleGroupService_Check"; //Advertisement APIs static String vehicleTypeGet = "${baseUrlServices}api/ServiceProviders/VehicleType_Get"; @@ -227,7 +230,8 @@ class GlobalConsts { static String homeLocationEmptyError = "Home location cannot be empty"; static String fillAllFields = "Please fill out all the fields."; static String requestTypeCannotBeEmpty = "Request type cannot be empty."; - static String reserveAdPriceInfo = "Some dummy description to explain the following concept. This price will be for 24 hours and if a user cancels the reservations before 24 hours then the amount will be automatically refunded to the buyer."; + static String reserveAdPriceInfo = + "Some dummy description to explain the following concept. This price will be for 24 hours and if a user cancels the reservations before 24 hours then the amount will be automatically refunded to the buyer."; static String appInvitationMessageEn = "🚗 Hey , check out MOWATER to book car services, buy/sell vehicles, and more—join here! [Invite Link]"; static String getAppInvitationLink() { diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 869d16b..525727e 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -18,6 +18,7 @@ import 'package:mc_common_app/views/requests/offer_list_page.dart'; import 'package:mc_common_app/views/requests/request_detail_page.dart'; import 'package:mc_common_app/views/requests/requests_filter_view.dart'; import 'package:mc_common_app/views/requests/review_request_offer.dart'; +import 'package:mc_common_app/views/setting_options/provider_accepted_requests_view.dart'; import 'package:mc_common_app/views/setting_options/provider_license_page.dart'; import 'package:mc_common_app/views/setting_options/setting_option_help.dart'; import 'package:mc_common_app/views/setting_options/setting_options_app_info.dart'; @@ -145,6 +146,7 @@ class AppRoutes { static const String createRequestPage = "/createRequestPage"; static const String offersListPage = "/offersListPage"; static const String reviewRequestOffer = "/reviewRequestOffer"; + static const String providerAcceptedRequestsView = "/providerAcceptedRequestsView"; //Setting Options static const String settingOptionsFaqs = "/settingOptionsFaqs"; @@ -208,7 +210,7 @@ class AppRoutes { //Requests AppRoutes.requestsDetailPage: (context) => RequestDetailPage(requestDetailPageArguments: ModalRoute.of(context)!.settings.arguments as RequestDetailPageArguments), AppRoutes.createRequestPage: (context) => const CreateRequestPage(), - AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments), + AppRoutes.offersListPage: (context) => OfferListPage(requestId: ModalRoute.of(context)!.settings.arguments as int), AppRoutes.reviewRequestOffer: (context) => const ReviewRequestOffer(), AppRoutes.requestsFilterView: (context) => const RequestsFilterView(), @@ -216,7 +218,7 @@ class AppRoutes { AppRoutes.mediaViewerScreen: (context) => MediaViewerScreen(images: ModalRoute.of(context)!.settings.arguments as List), // 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(), diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 15572cb..c9f94d8 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -238,6 +238,8 @@ extension AdPostEnum on int { return AdPostStatus.buyingService; } else if (this == 11) { return AdPostStatus.reserveCancel; + } else if (this == 12) { + return AdPostStatus.deActive; } else if (this == 0) { return AdPostStatus.allAds; } else { @@ -264,15 +266,21 @@ extension AppointmentStatusToInt on AppointmentStatusEnum { case AppointmentStatusEnum.rescheduled: return 5; - case AppointmentStatusEnum.upcoming: - return 6; - case AppointmentStatusEnum.workStarted: return 7; case AppointmentStatusEnum.visitCompleted: return 8; + case AppointmentStatusEnum.today: + return 9; + + case AppointmentStatusEnum.past: + return 10; + + case AppointmentStatusEnum.upcoming: + return 11; + case AppointmentStatusEnum.allAppointments: return 0; @@ -334,14 +342,18 @@ extension AppointmentStatusToString on AppointmentStatusEnum { case AppointmentStatusEnum.visitCompleted: return "Visit Completed"; + case AppointmentStatusEnum.past: + return "Past"; + + case AppointmentStatusEnum.today: + return "Today"; + default: return "Booked"; } } } -//TODO: Need to verify Enum on upcoming and inprogress with the database -//TODO: 6 is service Deactivated extension AppointmentEnum on int { AppointmentStatusEnum toAppointmentStatusEnum() { if (this == 1) { @@ -354,12 +366,16 @@ extension AppointmentEnum on int { return AppointmentStatusEnum.cancelled; } else if (this == 5) { return AppointmentStatusEnum.rescheduled; - } else if (this == 6) { - return AppointmentStatusEnum.upcoming; } else if (this == 7) { return AppointmentStatusEnum.workStarted; } else if (this == 8) { return AppointmentStatusEnum.visitCompleted; + } else if (this == 9) { + return AppointmentStatusEnum.today; + } else if (this == 10) { + return AppointmentStatusEnum.past; + } else if (this == 11) { + return AppointmentStatusEnum.upcoming; } else { return AppointmentStatusEnum.allAppointments; } @@ -409,7 +425,7 @@ extension AppointmentPaymentEnum on int { } extension RequestTypeTypEnum on int { - RequestsTypeEnum toRequestTypeStatusEnum() { + RequestsTypeEnum toRequestTypeEnum() { if (this == 1) { return RequestsTypeEnum.specialCarRequest; } else if (this == 2) { @@ -420,7 +436,7 @@ extension RequestTypeTypEnum on int { } extension RequestTypeStatusToInt on RequestsTypeEnum { - int getIdFromRequestTypeStatusEnum() { + int getIdFromRequestTypeEnum() { switch (this) { case RequestsTypeEnum.specialCarRequest: return 1; @@ -439,6 +455,7 @@ extension AdPostStatusToInt on AdPostStatus { switch (this) { case AdPostStatus.pendingForReview: return 1; + case AdPostStatus.pendingForPayment: return 2; @@ -468,6 +485,10 @@ extension AdPostStatusToInt on AdPostStatus { case AdPostStatus.reserveCancel: return 11; + + case AdPostStatus.deActive: + return 12; + default: return 0; } @@ -910,8 +931,10 @@ extension SubscriptionTypeEnumToString on SubscriptionTypeEnum { extension ShippingStatusEnumExt on int { ShippingRequestStatusEnum toShippingStatusEnum() { - if (this == 0) { + if (this == -1) { return ShippingRequestStatusEnum.allRequests; + } else if (this == 0) { + return ShippingRequestStatusEnum.pending; } else if (this == 1) { return ShippingRequestStatusEnum.initiated; } else if (this == 2) { @@ -921,13 +944,15 @@ extension ShippingStatusEnumExt on int { } else if (this == 4) { return ShippingRequestStatusEnum.delivered; } - return ShippingRequestStatusEnum.initiated; + return ShippingRequestStatusEnum.pending; } } extension ShippingStatusEnumToInt on ShippingRequestStatusEnum { int getIdFromShippingStatusEnum() { switch (this) { + case ShippingRequestStatusEnum.pending: + return 0; case ShippingRequestStatusEnum.initiated: return 1; case ShippingRequestStatusEnum.inTransit: @@ -936,7 +961,6 @@ extension ShippingStatusEnumToInt on ShippingRequestStatusEnum { return 3; case ShippingRequestStatusEnum.delivered: return 4; - default: return 0; } diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index cd3bee7..066bb54 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -714,7 +714,6 @@ class CodegenLoader extends AssetLoader{ "explore": "استكشاف", "manageRequests": "إدارة الطلبات", "serviceNotAvailableAtHomeLocation": "الخدمة المختارة غير متوفرة في الموقع المحدد.", - "noAvailableItems": "لا توجد عناصر متاحة.", "wantToRescheduleAppointment": "أرغب في إعادة جدولة الموعد.", "noNeedForService": "لا أحتاج هذه الخدمة بعد الآن.", "testTheService": "اختبر الخدمة.", @@ -722,7 +721,22 @@ class CodegenLoader extends AssetLoader{ "customerLocation": "موقع العميل", "deliveryAvailable": "التوصيل متاح", "viewed": "تم المشاهدة", - "itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا." + "itemNoLongerAvailable": "لم يعد هذا العنصر متاحًا.", + "reactivateAd": "إعادة تنشيط الإعلان", + "dealOutsideApp": "تمت الصفقة خارج التطبيق مع عميل آخر.", + "noAgreementCustomer": "لا يوجد اتفاق من جانب العميل.", + "dealNotCompleted": "لم تكتمل الصفقة", + "changedDesireToSell": "غيرت رغبتي في بيع المركبة.", + "vehicleDealOutsideApp": "تمت صفقة المركبة خارج التطبيق.", + "markAdAsSoldDesc": "سيتم وضع علامة 'تم البيع' على هذا الإعلان، ولن يتمكن المستخدمون الآخرون من التواصل معك.", + "ownerInformation": "معلومات المالك", + "acceptedRequests": "الطلبات المقبولة", + "specialRequestChat": "دردشة الطلب الخاص", + "companyName": "اسم الشركة", + "noAvailableItems": "لا توجد عناصر متاحة.", + "serviceDeliveryType": "نوع تقديم الخدمة", + "noImagesToShow": "لا توجد صور للعرض", + "updateGroupServices": "تحديث خدمات المجموعة" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1352,7 +1366,7 @@ static const Map en_US = { "priceRange": "Price Range", "cancelOffer": "Cancel Offer", "pleaseSpecify": "Please Specify", - "customerNotResponding": "Customer Not Responding", + "customerNotResponding": "The customer is not responding.", "cancelRequestPrompt": "Do you want to cancel this request?", "requestPermanentlyCancelled": "Your request will be permanently cancelled. You cannot undo this action.", "awaitingResponseFromCustomer": "Awaiting Response From Customer", @@ -1421,7 +1435,6 @@ static const Map en_US = { "explore": "Explore", "manageRequests": "Manage Requests", "serviceNotAvailableAtHomeLocation": "The Selected Service is not available at Home Location.", - "noAvailableItems": "There are no available items.", "wantToRescheduleAppointment": "I want to reschedule the appointment.", "noNeedForService": "I do not need this service anymore.", "testTheService": "Test the service.", @@ -1429,7 +1442,23 @@ static const Map en_US = { "customerLocation": "Customer Location", "deliveryAvailable": "Delivery Available", "viewed": "Viewed", - "itemNoLongerAvailable": "This item is no longer available." + "itemNoLongerAvailable": "This item is no longer available.", + "reactivateAd": "Reactivate Ad", + "dealOutsideApp": "The deal was done outside the app with another customer.", + "noAgreementCustomer": "There is no agreement from the customer side.", + "dealNotCompleted": "Deal not completed", + "changedDesireToSell": "I changed my desire to sell the vehicle.", + "vehicleDealOutsideApp": "The vehicle deal was done outside the app.", + "markAdAsSoldDesc": "This ad will be marked as sold, and other users will no longer be able to contact you.", + "ownerInformation": "Owner Information", + "noItemsToShow": "There are no Items no show.", + "acceptedRequests": "Accepted Requests", + "specialRequestChat": "Special Request Chat", + "companyName": "Company Name", + "noAvailableItems": "There are no available items.", + "serviceDeliveryType": "Service Delivery Type", + "noImagesToShow": "No Images to Show", + "updateGroupServices": "Update Group Services" }; static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index c74ae1a..de86937 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1,6 +1,6 @@ // DO NOT EDIT. This is code generated via package:easy_localization/generate.dart -abstract class LocaleKeys { +abstract class LocaleKeys { static const firstTimeLogIn = 'firstTimeLogIn'; static const signUp = 'signUp'; static const changeMobile = 'changeMobile'; @@ -47,7 +47,7 @@ abstract class LocaleKeys { static const emailChangedSuccessfully = 'emailChangedSuccessfully'; static const passwordIsUpdated = 'passwordIsUpdated'; static const passwordShouldContains = 'passwordShouldContains'; - static const successfullyRegistered = ' successfullyRegistered'; + static const successfullyRegistered = ' successfullyRegistered'; static const pleaseEnterSamePassword = 'pleaseEnterSamePassword'; static const firstNameMandatory = 'firstNameMandatory'; static const surnameNameMandatory = 'surnameNameMandatory'; @@ -677,7 +677,6 @@ abstract class LocaleKeys { static const explore = 'explore'; static const manageRequests = 'manageRequests'; static const serviceNotAvailableAtHomeLocation = 'serviceNotAvailableAtHomeLocation'; - static const noAvailableItems = 'noAvailableItems'; static const wantToRescheduleAppointment = 'wantToRescheduleAppointment'; static const noNeedForService = 'noNeedForService'; static const testTheService = 'testTheService'; @@ -686,5 +685,19 @@ abstract class LocaleKeys { static const deliveryAvailable = 'deliveryAvailable'; static const viewed = 'viewed'; static const itemNoLongerAvailable = 'itemNoLongerAvailable'; - + static const reactivateAd = 'reactivateAd'; + static const dealOutsideApp = 'dealOutsideApp'; + static const noAgreementCustomer = 'noAgreementCustomer'; + static const dealNotCompleted = 'dealNotCompleted'; + static const changedDesireToSell = 'changedDesireToSell'; + static const vehicleDealOutsideApp = 'vehicleDealOutsideApp'; + static const markAdAsSoldDesc = 'markAdAsSoldDesc'; + static const ownerInformation = 'ownerInformation'; + static const acceptedRequests = 'acceptedRequests'; + static const specialRequestChat = 'specialRequestChat'; + static const companyName = 'companyName'; + static const noAvailableItems = 'noAvailableItems'; + static const serviceDeliveryType = 'serviceDeliveryType'; + static const noImagesToShow = 'noImagesToShow'; + static const updateGroupServices = 'updateGroupServices'; } diff --git a/lib/models/advertisment_models/ad_details_model.dart b/lib/models/advertisment_models/ad_details_model.dart index f239903..3a4af29 100644 --- a/lib/models/advertisment_models/ad_details_model.dart +++ b/lib/models/advertisment_models/ad_details_model.dart @@ -45,6 +45,8 @@ class AdDetailsModel { String? phoneNo; String? whatsAppNo; String? adOwnerName; + String? adOwnerEmail; + AdOwnerDetails? adOwnerDetails; String? warrantyYears; CreatedByRoleEnum? createdByRoleEnum; List? 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 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; @@ -417,3 +424,28 @@ class DamageReport { return data; } } + +class AdOwnerDetails { + String? name; + String? email; + String? mobileNo; + String? profilePic; + + AdOwnerDetails({this.name, this.email, this.mobileNo, this.profilePic}); + + AdOwnerDetails.fromJson(Map json) { + name = json['name']; + email = json['email']; + mobileNo = json['mobileNo']; + profilePic = json['profilePic']; + } + + Map toJson() { + final Map data = {}; + data['name'] = name; + data['email'] = email; + data['mobileNo'] = mobileNo; + data['profilePic'] = profilePic; + return data; + } +} diff --git a/lib/models/appointments_models/service_schedule_model.dart b/lib/models/appointments_models/service_schedule_model.dart index a515097..3f07f73 100644 --- a/lib/models/appointments_models/service_schedule_model.dart +++ b/lib/models/appointments_models/service_schedule_model.dart @@ -11,6 +11,7 @@ class CustomTimeDateSlotModel { } class ServiceAppointmentScheduleModel { + String? scheduleName; List? serviceSlotList; List? servicesListInAppointment; int? selectedDateIndex; @@ -23,6 +24,7 @@ class ServiceAppointmentScheduleModel { AppointmentTypeEnum? appointmentTypeEnum; ServiceAppointmentScheduleModel({ + this.scheduleName, this.serviceSlotList, this.servicesListInAppointment, this.selectedDateIndex, @@ -90,6 +92,8 @@ class ServiceAppointmentScheduleModel { } ServiceAppointmentScheduleModel.fromJson(Map json, {bool isForAppointment = false}) { + scheduleName = json['scheduleName']; + if (json['serviceSlotList'] != null) { serviceSlotList = []; json['serviceSlotList'].forEach((v) { diff --git a/lib/models/requests_models/provider_offers_model.dart b/lib/models/requests_models/provider_offers_model.dart index c829a8c..a2f6fe2 100644 --- a/lib/models/requests_models/provider_offers_model.dart +++ b/lib/models/requests_models/provider_offers_model.dart @@ -34,7 +34,7 @@ class ProviderOffersModel { this.serviceProviders, }); - ProviderOffersModel.fromJson(Map json) { + ProviderOffersModel.fromJson(Map json, int? reqId) { id = json['id']; customerID = json['customerID']; requestType = json['requestType']; @@ -49,7 +49,7 @@ class ProviderOffersModel { if (json['serviceProviders'] != null) { serviceProviders = []; 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? 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 json) { + ServiceProvidersOffers.fromJson(Map 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 = []; diff --git a/lib/models/shipping_models/shipping_status_model.dart b/lib/models/shipping_models/shipping_status_model.dart index c68f73c..d8979ef 100644 --- a/lib/models/shipping_models/shipping_status_model.dart +++ b/lib/models/shipping_models/shipping_status_model.dart @@ -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 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 toJson() { + final Map data = {}; + 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 json) { + requestType = json['requestType']; + brand = json['brand']; + model = json['model']; + year = json['year']; + isNew = json['isNew']; + description = json['description']; + price = json['price']; + } + + Map toJson() { + final Map data = {}; + data['requestType'] = requestType; + data['brand'] = brand; + data['model'] = model; + data['year'] = year; + data['isNew'] = isNew; + data['description'] = description; + data['price'] = price; + return data; } } diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index 5a3fa6f..1bdc877 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -67,7 +67,8 @@ class AdsRepoImp implements AdsRepo { var params = { "SpecialServiceType": specialServiceType.toString(), }; - GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsSpecialServicesGet, queryParameters: params); + GenericRespModel adsGenericModel = + await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsSpecialServicesGet, queryParameters: params); List vehicleAdsDuration = List.generate(adsGenericModel.data.length, (index) => SpecialServiceModel.fromJson(adsGenericModel.data[index])); return vehicleAdsDuration; } @@ -89,7 +90,14 @@ class AdsRepoImp implements AdsRepo { List vehiclePostingDamageParts = []; adsCreationPayloadModel.vehiclePosting!.vehiclePostingDamageParts?.forEach((element) { - var imageMap = {"id": element.id ?? 0, "comment": element.comment, "vehicleImageBase64": element.vehicleImageBase64, "vehicleDamagePartID": element.vehicleDamagePartID, "vehiclePostingID": element.vehiclePostingID ?? 0, "isActive": true}; + var imageMap = { + "id": element.id ?? 0, + "comment": element.comment, + "vehicleImageBase64": element.vehicleImageBase64, + "vehicleDamagePartID": element.vehicleDamagePartID, + "vehiclePostingID": element.vehiclePostingID ?? 0, + "isActive": true + }; vehiclePostingDamageParts.add(imageMap); }); var postParams = { @@ -148,13 +156,15 @@ class AdsRepoImp implements AdsRepo { }; if (isMyAds && adPostStatus != null) { onlyMyAdsParams.addAll({ - "AdsStatuses": ["${adPostStatus.getIdFromAdPostStatusEnum()}"] + "AdsStatuses": ["${adPostStatus.getIdFromAdPostStatusEnum()}"], + "PageSize": "30", }); } var allAdsParams = { "AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS "isActive": "true", //only Active ADS - "isExplore": "true" + "isExplore": "true", + "PageSize": "30", }; if (!isMyAds && createdByRoleEnum != null) { @@ -186,7 +196,8 @@ class AdsRepoImp implements AdsRepo { "CreatedByRoles": createdByRolesIdsList ?? [], "AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS "isActive": "true", //only Active ADS - "isExplore": "true" + "isExplore": "true", + "PageSize": "30", }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( @@ -222,7 +233,11 @@ class AdsRepoImp implements AdsRepo { } Future> getAdsPerSpecificIds({required List ids, required List reservedAds}) async { - var params = {"AdsIDs": ids, "isActive": "true"}; + var params = { + "AdsIDs": ids, + "isActive": "true", + "PageSize": "30", + }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), @@ -243,6 +258,7 @@ class AdsRepoImp implements AdsRepo { Future> getMyAds() async { var params = { "userID": appState.getUser.data!.userInfo!.userId ?? "", + "PageSize": "30", }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, diff --git a/lib/repositories/appointment_repo.dart b/lib/repositories/appointment_repo.dart index c2755d4..b5037ec 100644 --- a/lib/repositories/appointment_repo.dart +++ b/lib/repositories/appointment_repo.dart @@ -132,7 +132,8 @@ class AppointmentRepoImp implements AppointmentRepo { if (adsGenericModel.data == null) { return []; } - List serviceAppointmentScheduleModel = List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index], isForAppointment: true)); + List serviceAppointmentScheduleModel = + List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index], isForAppointment: true)); return serviceAppointmentScheduleModel; } @@ -149,8 +150,19 @@ class AppointmentRepoImp implements AppointmentRepo { serviceItemIds.add(item.id!); } } + + int slotId = 0; + + int index = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.indexWhere((element) => element.isSelected); + + if (index == -1) { + index = 0; + } + + slotId = schedule.selectedCustomTimeDateSlotModel!.availableSlots![index].slotId; + mapList.add({ - "serviceSlotID": schedule.selectedCustomTimeDateSlotModel!.date!.slotId, + "serviceSlotID": slotId, "serviceProviderID": serviceProviderID, "customerID": customerId, "serviceItemID": serviceItemIds, diff --git a/lib/repositories/chat_repo.dart b/lib/repositories/chat_repo.dart index f099937..7572124 100644 --- a/lib/repositories/chat_repo.dart +++ b/lib/repositories/chat_repo.dart @@ -51,7 +51,7 @@ class ChatRepoImp implements ChatRepo { @override Future 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; diff --git a/lib/repositories/request_repo.dart b/lib/repositories/request_repo.dart index c3547c2..514299d 100644 --- a/lib/repositories/request_repo.dart +++ b/lib/repositories/request_repo.dart @@ -38,6 +38,7 @@ abstract class RequestRepo { Future> 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> 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; } diff --git a/lib/repositories/schedule_repo.dart b/lib/repositories/schedule_repo.dart index 9bd78e9..233de4d 100644 --- a/lib/repositories/schedule_repo.dart +++ b/lib/repositories/schedule_repo.dart @@ -11,6 +11,8 @@ abstract class ScheduleRepo { Future createSchedule(Map map); + Future checkServiceGroupInBranchSchedule(Map map); + Future addServicesInSchedule(Map map); Future updateSchedule(Map map); @@ -36,6 +38,12 @@ class ScheduleRepoImp implements ScheduleRepo { return await injector.get().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.createSchedule, map, token: t); } + @override + Future checkServiceGroupInBranchSchedule(Map map) async { + String t = AppState().getUser.data!.accessToken ?? ""; + return await injector.get().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.checkGroupServiceInBranchSchedule, map, token: t); + } + @override Future addServicesInSchedule(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; diff --git a/lib/repositories/setting_options_repo.dart b/lib/repositories/setting_options_repo.dart index c26aa28..6213a94 100644 --- a/lib/repositories/setting_options_repo.dart +++ b/lib/repositories/setting_options_repo.dart @@ -61,11 +61,21 @@ class SettingOptionsRepoImp extends SettingOptionsRepo { @override Future> 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().getJsonForObject( (json) => GenericRespModel.fromJson(json), ApiConsts.getContactInfo, + queryParameters: params, token: token, ); diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index be3aa1c..3d558e1 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -16,23 +16,15 @@ class AppEnums { static const int appointmentsFilterEnumId = 13; // Appointments Filter Enums static const int requestStatusesFilterEnumId = 15; // Appointments Filter Enums static const int conditionEnumId = -1; // to get the Condition Filter Enums - static const int shippingStatusEnumId = -2; // to get the Shipping Filter Enums + static const int shippingStatusEnumId = 30; // to get the Shipping Filter Enums } enum DashboardRouteEnum { fromAdsPayment, fromAdsSubmit, - - - - - - none, } - - enum VehicleType { car, motorCycle, @@ -70,6 +62,7 @@ enum AdPostStatus { reserved, buyingService, reserveCancel, + deActive, allAds, } @@ -173,9 +166,11 @@ enum AppointmentStatusEnum { cancelled, rescheduled, allAppointments, - upcoming, workStarted, visitCompleted, + today, + past, + upcoming, } enum AppointmentPaymentStatusEnum { @@ -228,6 +223,7 @@ enum SubscriptionActionTypeEnum { enum ShippingRequestStatusEnum { allRequests, + pending, initiated, inTransit, outForDelivery, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index e709ddd..e39d9fe 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -29,7 +29,14 @@ class Utils { static bool get isLoading => _isLoadingVisible; static void showToast(String message) { - Fluttertoast.showToast(msg: message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.black54, textColor: Colors.white, fontSize: 16.0); + Fluttertoast.showToast( + msg: message, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + timeInSecForIosWeb: 2, + backgroundColor: Colors.black54, + textColor: Colors.white, + fontSize: 16.0); } static Future openNumberViaCaller({required String phoneNumber}) async { @@ -84,7 +91,11 @@ class Utils { return ""; } - return ("${timeOfDay.hour.toString().length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute.toString().length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); + return ("${timeOfDay.hour + .toString() + .length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute + .toString() + .length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); } static dynamic getNotNullValue(List list, int index) { @@ -234,6 +245,9 @@ class Utils { case AdPostStatus.reserved: return MyColors.primaryColor; + case AdPostStatus.deActive: + return MyColors.textColor; + case AdPostStatus.buyingService: case AdPostStatus.reserveCancel: case AdPostStatus.allAds: @@ -313,6 +327,9 @@ class Utils { case ShippingRequestStatusEnum.delivered: return "Delivered"; + + case ShippingRequestStatusEnum.pending: + return "Pending"; } } @@ -332,6 +349,9 @@ class Utils { case ShippingRequestStatusEnum.delivered: return MyColors.greenColor; + + case ShippingRequestStatusEnum.pending: + return MyColors.pendingColor; } } @@ -379,7 +399,12 @@ class Utils { } } - static statusContainerChip({required String text, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), Color chipColor = MyColors.greenColor, Color textColor = MyColors.white}) { + static statusContainerChip({ + required String text, + EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), + Color chipColor = MyColors.greenColor, + Color textColor = MyColors.white, + }) { return Container( decoration: BoxDecoration( color: chipColor, @@ -521,7 +546,7 @@ class Utils { border: Border.all( width: w, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -544,7 +569,7 @@ class Utils { border: Border.all( width: 1, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -562,7 +587,8 @@ class Utils { } static String getAdsPaymentBrowserForm({required int paymentId, required int adId}) { - return '
'; + return '
'; } // BOTTOM SHEETS diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 152d9be..81a9558 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -190,11 +190,12 @@ class AdVM extends BaseVM { } myAdsFilterOptions.insert(0, FilterListModel(title: "All Ads", isSelected: true, id: 0)); + log("myAdsFilterOptions: ${myAdsFilterOptions.last.id}"); + for (int i = 0; i < exploreAdsEnums.length; i++) { exploreAdsFilterOptions.add(FilterListModel(title: "${exploreAdsEnums[i].enumValueStr} Ads", isSelected: false, id: exploreAdsEnums[i].enumValue)); } exploreAdsFilterOptions.insert(0, FilterListModel(title: "All Ads", isSelected: true, id: 0)); - notifyListeners(); } @@ -207,8 +208,7 @@ class AdVM extends BaseVM { } exploreAdsFilterOptions[index].isSelected = true; if (createdByRoleFilter == CreatedByRoleEnum.allAds) { - exploreAdsFilteredList = exploreAds; - notifyListeners(); + getExploreAds(); return; } setState(ViewState.busy); @@ -232,12 +232,12 @@ class AdVM extends BaseVM { myAdsFilterOptions[index].isSelected = true; if (adPostStatusEnum.getIdFromAdPostStatusEnum() == 0) { - myAdsFilteredList = myAds; - notifyListeners(); + getMyAds(); return; } // this means if the filter is reserved ads if (adPostStatusEnum == AdPostStatus.reserved) { + log("reserved: ${myReservedAds.length}"); myAdsFilteredList = myReservedAds; for (var ad in myAdsFilteredList) { ad.isReservedByMe = true; @@ -591,26 +591,16 @@ class AdVM extends BaseVM { OfferRequestCommentModel( index: 0, isSelected: true, - title: LocaleKeys.changedMind.tr(), + title: LocaleKeys.vehicleDealOutsideApp.tr(), ), OfferRequestCommentModel( index: 1, isSelected: false, - title: LocaleKeys.veryHighPrice.tr(), + title: LocaleKeys.changedDesireToSell.tr(), ), OfferRequestCommentModel( index: 2, isSelected: false, - title: LocaleKeys.alreadySold.tr(), - ), - OfferRequestCommentModel( - index: 3, - isSelected: false, - title: LocaleKeys.customerNotResponding.tr(), - ), - OfferRequestCommentModel( - index: 4, - isSelected: false, title: LocaleKeys.otherVar.tr(), ), ]; @@ -626,6 +616,7 @@ class AdVM extends BaseVM { value.isSelected = false; } selectedDeActivateAdCommentModel = deActivateAdModelList[index]; + log("selectedDeActivateAdCommentModel: ${selectedDeActivateAdCommentModel.index}"); deActivateAdModelList[index].isSelected = true; notifyListeners(); } @@ -684,9 +675,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 +918,12 @@ class AdVM extends BaseVM { vehicleTransmissionId.errorValue = ""; } - if (vehicleSellerTypeId.selectedId == -1) { - vehicleSellerTypeId.errorValue = LocaleKeys.vehicle_vehicleSellerType.tr(); - isValidated = false; - } else { - vehicleSellerTypeId.errorValue = ""; - } + // if (vehicleSellerTypeId.selectedId == -1) { + // vehicleSellerTypeId.errorValue = LocaleKeys.vehicle_vehicleSellerType.tr(); + // isValidated = false; + // } else { + // vehicleSellerTypeId.errorValue = ""; + // } if (vehicleCountryId.selectedId == -1) { vehicleCountryId.errorValue = LocaleKeys.vehicle_selectAny.tr(); @@ -1483,7 +1476,7 @@ class AdVM extends BaseVM { vehicleConditionID: vehicleConditionId.selectedId, vehicleMileageID: vehicleMileageId.selectedId, vehicleTransmissionID: vehicleTransmissionId.selectedId, - vehicleSellerTypeID: vehicleSellerTypeId.selectedId, + vehicleSellerTypeID: vehicleSellerTypeId.selectedId == -1 ? 1 : vehicleSellerTypeId.selectedId, cityID: vehicleCityId.selectedId, price: int.parse(vehicleDemandAmount), vehicleVIN: vehicleVin, @@ -1843,7 +1836,8 @@ class AdVM extends BaseVM { updateSelectionVehicleColorId(SelectionModel(selectedId: previousAdDetails!.vehicle!.color!.id!, selectedOption: previousAdDetails!.vehicle!.color!.label ?? "")); updateSelectionVehicleConditionId(SelectionModel(selectedId: previousAdDetails!.vehicle!.condition!.id!, selectedOption: previousAdDetails!.vehicle!.condition!.label ?? "")); updateSelectionVehicleCategoryId(SelectionModel(selectedId: previousAdDetails!.vehicle!.category!.id!, selectedOption: previousAdDetails!.vehicle!.category!.label ?? "")); - updateSelectionVehicleMileageId(SelectionModel(selectedId: previousAdDetails!.vehicle!.mileage!.id!, selectedOption: "${previousAdDetails!.vehicle!.mileage!.mileageStart} - ${previousAdDetails!.vehicle!.mileage!.mileageEnd}")); + updateSelectionVehicleMileageId(SelectionModel( + selectedId: previousAdDetails!.vehicle!.mileage!.id!, selectedOption: "${previousAdDetails!.vehicle!.mileage!.mileageStart} - ${previousAdDetails!.vehicle!.mileage!.mileageEnd}")); updateSelectionVehicleTransmissionId(SelectionModel(selectedId: previousAdDetails!.vehicle!.transmission!.id!, selectedOption: previousAdDetails!.vehicle!.transmission!.label ?? "")); updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: previousAdDetails!.vehicle!.sellertype!.id!, selectedOption: previousAdDetails!.vehicle!.sellertype!.label ?? "")); int indexCountry = vehicleCountries.indexWhere((element) => element.id == previousAdDetails!.vehicle!.countryID); diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 96db74f..4906905 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -298,9 +298,17 @@ class AppointmentsVM extends BaseVM { } resetAfterBookingAppointment() { - // allSelectedItemsInAppointments.clear(); + allSelectedItemsInAppointments.clear(); + currentServiceSelection = null; + for (var element in servicesInCurrentAppointment) { + if (element.serviceItems != null) { + element.serviceItems!.clear(); + element.serviceItems = []; + } + } servicesInCurrentAppointment.clear(); - // serviceAppointmentScheduleList.clear(); + servicesInCurrentAppointment = []; + log("here servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); } List myAppointmentsEnum = []; @@ -315,13 +323,11 @@ class AppointmentsVM extends BaseVM { } appointmentsFilterOptions.insert(0, FilterListModel(title: "All Appointments", isSelected: true, id: 0)); - // TODO: THIS SHOULD REMOVED AND ADDED IN THE ENUMS API - appointmentsFilterOptions.add(FilterListModel(title: "Work In Progress", isSelected: false, id: 7)); - appointmentsFilterOptions.add(FilterListModel(title: "Visit Completed", isSelected: false, id: 8)); notifyListeners(); } applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) { + log("appointmentStatusEnum: ${appointmentStatusEnum}"); // isNeedCustomerFilter IS ONLY FOR THE PROVIDER APP if (appointmentsFilterOptions.isEmpty) return; for (var value in appointmentsFilterOptions) { @@ -405,7 +411,9 @@ class AppointmentsVM extends BaseVM { myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters(); // myFilteredAppointments = myAppointments; myUpComingAppointments = myAppointments - .where((element) => (element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) && (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now()))) + .where((element) => + (element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) && + (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now()))) .toList(); setState(ViewState.idle); applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); @@ -433,39 +441,47 @@ class AppointmentsVM extends BaseVM { myAppointments = await appointmentRepo.getMyAppointmentsForProvider(map); myFilteredAppointments = myAppointments; myUpComingAppointments = myAppointments - .where((element) => (element.appointmentStatusEnum == AppointmentStatusEnum.confirmed || element.appointmentStatusEnum == AppointmentStatusEnum.booked) && (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now()))) + .where((element) => + (element.appointmentStatusEnum == AppointmentStatusEnum.confirmed || element.appointmentStatusEnum == AppointmentStatusEnum.booked) && + (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now()))) .toList(); applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true); setState(ViewState.idle); } - updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum, bool isNeedToRebuild = false}) async { + Future 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 map, {bool isNeedToRebuild = false}) async { + Future updateAppointmentPaymentStatus(Map map, {bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); try { GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentPaymentStatus(map); if (genericRespModel.messageStatus == 1) { Utils.showToast(LocaleKeys.paymentStatusUpdated.tr()); + return true; } else { Utils.showToast(genericRespModel.message.toString()); + return false; } } catch (e) { Utils.showToast(e.toString()); + return false; } } @@ -484,7 +500,8 @@ class AppointmentsVM extends BaseVM { bool inNeedToEnableMergeButton = false; void updateCheckBoxInMergeRequest(int currentIndex) { - myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected = !(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false); + myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected = + !(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false); int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList ?? []); if (count > 1) { @@ -540,10 +557,12 @@ class AppointmentsVM extends BaseVM { void onItemUpdateOrSelected(int index, bool selected, int itemId) { int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!); - log("index: $index"); - log("selected: $selected"); - log("itemId: $itemId"); - log("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); + // log("currentServiceSelection!.allSelectedItemsInAppointments: ${allSelectedItemsInAppointments.length}"); + // log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}"); + // log("index: $index"); + // log("selected: $selected"); + // log("itemId: $itemId"); + // log("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); serviceItemsFromApi[index].isUpdateOrSelected = selected; serviceItemsFromApi[index].isHomeSelected = isHomeTapped; @@ -554,23 +573,25 @@ class AppointmentsVM extends BaseVM { selectSubServicesError = ""; currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]); allSelectedItemsInAppointments.add(serviceItemsFromApi[index]); - for (var element in allSelectedItemsInAppointments) { - if (!ifItemAlreadySelected(element.id!)) { - if (serviceIndex != -1) { - servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]); - servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0"); - } + // for (var element in allSelectedItemsInAppointments) { + if (!ifItemAlreadySelected(serviceItemsFromApi[index].id!)) { + if (serviceIndex != -1) { + servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]); + servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = + servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0"); } } + // } } else { - log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}"); + // log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}"); selectedSubServicesCounter = selectedSubServicesCounter - 1; currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId); - log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}"); + // log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}"); allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId); if (serviceIndex != -1) { - servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0"); + servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = + servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0"); servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId); } } @@ -714,7 +735,9 @@ class AppointmentsVM extends BaseVM { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - (selectedService.isHomeSelected ? "${(selectedService.currentTotalServicePrice) + ((double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms))}" : "${selectedService.currentTotalServicePrice}") + (selectedService.isHomeSelected + ? "${(selectedService.currentTotalServicePrice) + ((double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms))}" + : "${selectedService.currentTotalServicePrice}") .toText(fontSize: 29, isBold: true), 2.width, LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 822e88c..f558cf5 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -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 indexesForCancelSpecialCarOffer = [0, 1, 6, 7]; + List indexesForCancelSparePartOffer = [2, 7]; + List indexesForRejectOffer = [3, 4, 5, 7]; + List 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 onNewMessageReceivedForRequestOffer({required List 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? arguments) { + logger.i(arguments); + if (arguments == null || arguments.isEmpty) return; List chat = []; for (var message in arguments) { final chatMessage = ChatMessageModel.fromJson(message as Map, isForReqOfferImagesURLs: true); - log("chatMessagechatMessage: ${chatMessage.reqOffer!.reqOfferImages!.first.imageUrl}"); chat.add(chatMessage); } onNewMessageReceivedForRequestOffer(messages: chat, requestsVM: context.read()); - 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 _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 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 closeHubConnection() async { + if (hubConnection != null && hubConnection!.state == HubConnectionState.connected) { + hubConnection!.stop(); + } + } + Future onOfferSendForRequest({ required String receiverId, required ChatMessageTypeEnum chatMessageType, @@ -424,18 +491,24 @@ class ChatVM extends ChangeNotifier { List serviceProviderOffersList = []; - Future getOffersFromProvidersByRequest({required int requestId, required BuildContext context}) async { + Future 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? arguments) { if (arguments == null || arguments.isEmpty) return; List chat = []; @@ -727,7 +800,7 @@ class ChatVM extends ChangeNotifier { notifyListeners(); } - void subscribeToReceiveGeneralMessages(BuildContext context) { + void _subscribeToReceiveGeneralMessages(BuildContext context) { hubConnection!.on(SignalrConsts.receiveMessageGeneral, (List? arguments) { if (arguments == null || arguments.isEmpty) return; List chat = []; diff --git a/lib/view_models/payment_view_model.dart b/lib/view_models/payment_view_model.dart index 1dbaddc..77ab10a 100644 --- a/lib/view_models/payment_view_model.dart +++ b/lib/view_models/payment_view_model.dart @@ -73,7 +73,8 @@ class PaymentVM extends ChangeNotifier { return; } - Future verifyPayments({required BuildContext context, required int id, List? appointmentIds, required int paymentTypeId, required Function() onVerified, bool isForAppointment = false}) async { + Future verifyPayments( + {required BuildContext context, required int id, List? 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()); diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index 9b18b93..d209bc4 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -86,6 +86,7 @@ class RequestsVM extends BaseVM { Future getRequests({bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); applyFilterOnRequestsVM(requestsTypeEnum: RequestsTypeEnum.specialCarRequest); + isOfferSent = false; setState(ViewState.idle); } @@ -111,25 +112,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 providersAcceptedRequestsList = []; + List acceptedRequestsTypeFilterOptions = []; + + Future> 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> getServiceRequestsForProviders() async { @@ -166,8 +205,9 @@ class RequestsVM extends BaseVM { for (var value in requestsTypeFilterOptions) { value.isSelected = false; } - requestsTypeFilterOptions[requestsTypeEnum.getIdFromRequestTypeStatusEnum() - 1].isSelected = true; // -1 to match with the index + requestsTypeFilterOptions[requestsTypeEnum.getIdFromRequestTypeEnum() - 1].isSelected = true; // -1 to match with the index await getRequestsBasedOnFilters(); + isOfferSent = false; notifyListeners(); } @@ -220,9 +260,10 @@ class RequestsVM extends BaseVM { requestStatusesEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.requestStatusesFilterEnumId); } - if (requestsTypeFilterOptions.isEmpty) { + if (requestsTypeFilterOptions.isEmpty || acceptedRequestsTypeFilterOptions.isEmpty) { for (int i = 0; i < myRequestsTypeEnum.length; i++) { requestsTypeFilterOptions.add(FilterListModel(title: myRequestsTypeEnum[i].enumValueStr, isSelected: false, id: myRequestsTypeEnum[i].enumValue)); + acceptedRequestsTypeFilterOptions.add(FilterListModel(title: myRequestsTypeEnum[i].enumValueStr, isSelected: false, id: myRequestsTypeEnum[i].enumValue)); } } @@ -721,7 +762,8 @@ class RequestsVM extends BaseVM { } } catch (e, s) { Utils.hideLoading(context); - log(s.toString()); + log(e.toString()); + Utils.showToast(e.toString()); } } } @@ -904,6 +946,13 @@ class RequestsVM extends BaseVM { return requestImages; } + bool isOfferSent = false; + + updateIsOfferSent(var value) { + isOfferSent = value; + notifyListeners(); + } + Future onSendOfferPressed({ required BuildContext context, required String receiverId, @@ -946,6 +995,8 @@ class RequestsVM extends BaseVM { return; } + updateIsOfferSent(true); + final senderName = AppState().getUser.data!.userInfo!.firstName; final senderId = AppState().getUser.data!.userInfo!.userId; resetSendOfferBottomSheet(); diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index 0ef3af5..82a4973 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -304,6 +304,8 @@ class ServiceVM extends BaseVM { branches = await branchRepo.getBranchAndServices(); if (branches!.data == null) { homePageBranches = []; + } else if (branches!.data!.serviceProviderBranch == null) { + homePageBranches = []; } else { homePageBranches = branches!.data!.serviceProviderBranch!.where((element) => element.branchStatus == BranchStatusEnum.approvedOrActive).toList(); } diff --git a/lib/view_models/shipping_management_view_model.dart b/lib/view_models/shipping_management_view_model.dart index ca5a672..91f541f 100644 --- a/lib/view_models/shipping_management_view_model.dart +++ b/lib/view_models/shipping_management_view_model.dart @@ -42,7 +42,7 @@ class ShippingManagementVM extends BaseVM { } Future 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 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()); diff --git a/lib/view_models/user_view_model.dart b/lib/view_models/user_view_model.dart index 51cf91b..42c7b87 100644 --- a/lib/view_models/user_view_model.dart +++ b/lib/view_models/user_view_model.dart @@ -36,6 +36,7 @@ import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/shared_prefrence.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; +import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/view_models/service_view_model.dart'; import 'package:mc_common_app/view_models/subscriptions_view_model.dart'; import 'package:mc_common_app/views/location_views/map_selection_widget.dart'; @@ -435,7 +436,6 @@ class UserVM extends BaseVM { } } else { Utils.showToast("Please verify your credentials and role type."); - } } else { Utils.showToast(verifiedUser.message ?? ""); @@ -560,7 +560,8 @@ class UserVM extends BaseVM { return await userRepo.getAllCountries(); } - Future performBasicOtpRegisterPage(BuildContext context, {required String countryCode, required String phoneNum, required int role, bool isNeedToPassToken = false, VoidCallback? reloadPage}) async { + Future performBasicOtpRegisterPage(BuildContext context, + {required String countryCode, required String phoneNum, required int role, bool isNeedToPassToken = false, VoidCallback? reloadPage}) async { Utils.showLoading(context); BasicOtpRespModel basicOtp = await userRepo.basicOtp(countryCode + phoneNum, roleId: role, isNeedToPassToken: isNeedToPassToken); Utils.hideLoading(context); @@ -676,8 +677,10 @@ class UserVM extends BaseVM { print(value.body); print("Logout"); } + SharedPrefManager.setPhoneOrEmail(""); SharedPrefManager.setUserPassword(""); + context.read().closeHubConnection(); if (AppState().getUser.data!.userInfo!.userLocalImage != null) { AppState().getUser.data!.userInfo!.userLocalImage = null; AppState().getUser.data!.userInfo!.userImageUrl = null; diff --git a/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart b/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart index ede81cf..b8707e6 100644 --- a/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart +++ b/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart @@ -17,7 +17,7 @@ import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; class AdDuration extends StatelessWidget { - const AdDuration({Key? key}) : super(key: key); + const AdDuration({super.key}); void onAddSpecialServiceButtonTapped(BuildContext context, AdVM adVM) { showModalBottomSheet( diff --git a/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart b/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart index 51f1b7e..286e221 100644 --- a/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart +++ b/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart @@ -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 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 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 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 vehicleModelsDrop = []; for (var element in adVM.vehicleModels) { @@ -148,20 +181,7 @@ class VehicleDetails extends StatelessWidget { ); }), 8.height, - Builder(builder: (context) { - List 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 vehicleCountriesDrop = []; for (var element in adVM.vehicleCountries) { diff --git a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart index a3ae474..0634d1a 100644 --- a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart @@ -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 createState() => _AdsDetailViewState(); @@ -130,6 +131,12 @@ class _AdsDetailViewState extends State { "${widget.adDetails.vehicle!.transmission!.label}".toText(fontSize: 12), ], ), + Row( + children: [ + ("${LocaleKeys.financeAvailable.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), + ((widget.adDetails.vehicle!.isFinanceAvailable ?? false) ? LocaleKeys.yes.tr() : LocaleKeys.no.tr()).toText(fontSize: 12), + ], + ), 8.height, ("${LocaleKeys.description.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), "${widget.adDetails.vehicle!.vehicleDescription}".toText(fontSize: 14), @@ -158,6 +165,43 @@ class _AdsDetailViewState extends State { ).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 { } 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 { @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 { 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 { 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(), diff --git a/lib/views/advertisement/ads_detail_view/components.dart b/lib/views/advertisement/ads_detail_view/components.dart index 0d580b9..a335ec0 100644 --- a/lib/views/advertisement/ads_detail_view/components.dart +++ b/lib/views/advertisement/ads_detail_view/components.dart @@ -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(); 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(); return actionConfirmationBottomSheet( @@ -1054,6 +1055,9 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { case AdPostStatus.sold: return pendingForReviewAction(pendingText: LocaleKeys.sold.tr()); + case AdPostStatus.deActive: + return pendingForReviewAction(pendingText: LocaleKeys.deactivateAd.tr()); + case AdPostStatus.expired: return expiredAdAction(context); case AdPostStatus.allAds: diff --git a/lib/views/advertisement/bottom_sheets/ad_special_service_sheet.dart b/lib/views/advertisement/bottom_sheets/ad_special_service_sheet.dart index ecaec46..af37183 100644 --- a/lib/views/advertisement/bottom_sheets/ad_special_service_sheet.dart +++ b/lib/views/advertisement/bottom_sheets/ad_special_service_sheet.dart @@ -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 createState() => _BottomSheetAdDamagePartsContentState(); @@ -147,7 +147,7 @@ class _BottomSheetAdDamagePartsContentState extends State { int _current = 0; - CarouselSliderController _controller = CarouselSliderController(); + final CarouselSliderController _controller = CarouselSliderController(); @override Widget build(BuildContext context) { @@ -28,11 +33,11 @@ class _CarouselWithIndicatorState extends State { 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 { } 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 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 { 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(), + ), + ], + ), ); } } diff --git a/lib/views/appointments/book_appointment_schedules_view.dart b/lib/views/appointments/book_appointment_schedules_view.dart index b46c46a..05318d2 100644 --- a/lib/views/appointments/book_appointment_schedules_view.dart +++ b/lib/views/appointments/book_appointment_schedules_view.dart @@ -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); }, diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart index 9092246..0957c65 100644 --- a/lib/views/appointments/book_appointment_services_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -1,4 +1,7 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; @@ -14,7 +17,7 @@ import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; class BookAppointmentServicesView extends StatelessWidget { - const BookAppointmentServicesView({Key? key}) : super(key: key); + const BookAppointmentServicesView({super.key}); @override Widget build(BuildContext context) { @@ -57,7 +60,7 @@ class BookAppointmentServicesView extends StatelessWidget { Row( children: [ Expanded(child: (serviceData.serviceDescription ?? "").toText(fontSize: 15, isBold: true)), - IconButton(onPressed: () => appointmentsVM.removeServiceInCurrentAppointment(serviceIndex), icon: const Icon(Icons.delete_outline, color: MyColors.redColor)) + MyAssets.closeWithOrangeBg.buildSvg(height: 30, width: 30).onPress(() => appointmentsVM.removeServiceInCurrentAppointment(serviceIndex)) ], ), if (true) ...[ @@ -105,8 +108,7 @@ class BookAppointmentServicesView extends StatelessWidget { maxHeight: 55, title: LocaleKeys.cancel.tr(), onPressed: () { - appointmentsVM.servicesInCurrentAppointment.clear(); - appointmentsVM.allSelectedItemsInAppointments.clear(); + appointmentsVM.resetAfterBookingAppointment(); Navigator.pop(context); }, backgroundColor: MyColors.greyButtonColor, diff --git a/lib/views/appointments/book_appointments_item_view.dart b/lib/views/appointments/book_appointments_item_view.dart index 65ca7ee..9a949aa 100644 --- a/lib/views/appointments/book_appointments_item_view.dart +++ b/lib/views/appointments/book_appointments_item_view.dart @@ -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) { diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart index 67021ff..d37b235 100644 --- a/lib/views/appointments/review_appointment_view.dart +++ b/lib/views/appointments/review_appointment_view.dart @@ -287,7 +287,7 @@ class ReviewAppointment extends StatelessWidget { ]), Row(children: [ ("${LocaleKeys.location.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), - (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12), + (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.homeLocation.tr() : LocaleKeys.companyLocation.tr()).toText(fontSize: 12), ]), ], ), diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index 397f692..448eb52 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -167,6 +167,7 @@ class _ChatViewState extends State { 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 { 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 { // ), // ).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) ...[ diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 13106b7..d4d2e3e 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -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 createState() => _ChatMessageCustomWidgetState(); @@ -43,8 +44,14 @@ class _ChatMessageCustomWidgetState extends State { 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 { 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 { ); if (status) { + final requestVM = context.read(); 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 { if (status) { final requestVM = context.read(); + ChatVM chatVM = context.read(); + 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 { 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 { ], 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 { 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), ], ); diff --git a/lib/views/common_fragments/requests_fragment.dart b/lib/views/common_fragments/requests_fragment.dart index 4c93f06..4eb2886 100644 --- a/lib/views/common_fragments/requests_fragment.dart +++ b/lib/views/common_fragments/requests_fragment.dart @@ -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, diff --git a/lib/views/requests/create_request_page.dart b/lib/views/requests/create_request_page.dart index 25f2436..555defb 100644 --- a/lib/views/requests/create_request_page.dart +++ b/lib/views/requests/create_request_page.dart @@ -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(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().resetRequestCreationForm(); + } + }, + child: Consumer(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 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 vehicleTypeDrop = []; - for (var element in requestsVM.vehicleTypes) { - vehicleTypeDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleTypeName ?? "", "")); + List 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 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 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 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 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 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 vehicleCitiesDrop = []; - for (var element in requestsVM.vehicleCities) { - vehicleCitiesDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "")); + }), + 8.height, + Builder( + builder: (context) { + List 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().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 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 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().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().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().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().onCreateRequestTapped(context); - }, - ) - ], - ); - }), + ShowFillButton( + title: LocaleKeys.createRequest.tr(), + maxWidth: double.infinity, + maxHeight: 55, + margin: const EdgeInsets.all(16), + onPressed: () async { + await context.read().onCreateRequestTapped(context); + }, + ) + ], + ); + }), + ), ); } } diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart index 8b10f42..785d3f3 100644 --- a/lib/views/requests/offer_list_page.dart +++ b/lib/views/requests/offer_list_page.dart @@ -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 createState() => _OfferListPageState(); +} + +class _OfferListPageState extends State { + @override + void initState() { + _onRefresh(); + super.initState(); + } + + _onRefresh() async { + scheduleMicrotask(() async { + ChatVM chatVM = context.read(); + await chatVM.getOffersFromProvidersByRequest(requestId: widget.requestId); + }); + } @override Widget build(BuildContext context) { - final List 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(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().currentSelectedOffer = offersModel; + ).onPress(() async { + context.read().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().currentSelectedRequest, + requestIndex: -1, + ); - ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest); + ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest); - final chatVM = context.read(); + final chatVM = context.read(); - 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, + ), + ), + ); + }); } } diff --git a/lib/views/requests/request_bottomsheets.dart b/lib/views/requests/request_bottomsheets.dart index 668ec50..01ad4be 100644 --- a/lib/views/requests/request_bottomsheets.dart +++ b/lib/views/requests/request_bottomsheets.dart @@ -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( diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index fea77d6..840f149 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -9,6 +9,7 @@ import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; +import 'package:mc_common_app/view_models/requests_view_model.dart'; import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart'; import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart'; import 'package:mc_common_app/views/requests/request_bottomsheets.dart'; @@ -21,9 +22,10 @@ import 'package:easy_localization/easy_localization.dart'; class RequestDetailPage extends StatelessWidget { final RequestDetailPageArguments requestDetailPageArguments; - const RequestDetailPage({Key? key, required this.requestDetailPageArguments}) : super(key: key); + const RequestDetailPage({super.key, required this.requestDetailPageArguments}); - Widget buildRequestDetailActionFooter({required int requestId, required RequestStatusEnum requestStatus, required RequestsTypeEnum requestTypeEnum, required String statusText, required BuildContext context}) { + Widget buildRequestDetailActionFooter( + {required int requestId, required RequestStatusEnum requestStatus, required RequestsTypeEnum requestTypeEnum, required String statusText, required BuildContext context}) { // final requestsVM = context.read(); switch (requestStatus) { case RequestStatusEnum.submitted: @@ -31,7 +33,7 @@ class RequestDetailPage extends StatelessWidget { maxWidth: double.infinity, margin: const EdgeInsets.all(15), maxHeight: 55, - title: LocaleKeys.viewChat.tr(), + title: LocaleKeys.specialRequestChat.tr(), isBold: false, onPressed: () => onViewChatTapped(context), ); @@ -115,7 +117,7 @@ class RequestDetailPage extends StatelessWidget { appBar: CustomAppBar( title: LocaleKeys.requestDetail.tr(), actions: [ - (requestDetailPageArguments.requestModel.isChatted) + (context.read().isOfferSent || requestDetailPageArguments.requestModel.isChatted) ? Padding( padding: const EdgeInsets.only(top: 8, bottom: 8, right: 21), child: const Icon(Icons.messenger_outline_rounded, color: Colors.black, size: 18).toContainer( @@ -144,7 +146,7 @@ class RequestDetailPage extends StatelessWidget { allPading: 12, margin: const EdgeInsets.only(top: 21, right: 21, left: 21), ), - if (!requestDetailPageArguments.requestModel.isChatted) ...[ + if (!context.read().isOfferSent && !requestDetailPageArguments.requestModel.isChatted) ...[ ShowFillButton( maxWidth: double.infinity, margin: const EdgeInsets.all(15), @@ -160,7 +162,7 @@ class RequestDetailPage extends StatelessWidget { requestStatus: requestDetailPageArguments.requestModel.requestStatus, statusText: "Offer ${requestDetailPageArguments.requestModel.requestStatusName}", context: context, - requestTypeEnum: requestDetailPageArguments.requestModel.requestType.toRequestTypeStatusEnum(), + requestTypeEnum: requestDetailPageArguments.requestModel.requestType.toRequestTypeEnum(), ), ], ], diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index 52afc08..c756441 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -106,10 +106,7 @@ class RequestItem extends StatelessWidget { RequestDetailPageArguments requestDetailPageArguments = RequestDetailPageArguments(requestIndex: requestIndex, requestModel: request); navigateWithName(context, AppRoutes.requestsDetailPage, arguments: requestDetailPageArguments); } else { - ChatVM chatVM = context.read(); - 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); } }); } diff --git a/lib/views/setting_options/provider_accepted_requests_view.dart b/lib/views/setting_options/provider_accepted_requests_view.dart new file mode 100644 index 0000000..b6c8b51 --- /dev/null +++ b/lib/views/setting_options/provider_accepted_requests_view.dart @@ -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 createState() => _ProviderAcceptedRequestsViewState(); +} + +class _ProviderAcceptedRequestsViewState extends State { + @override + void initState() { + scheduleMicrotask(() async => context.read().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), + ), + )) + ], + ), + ), + ); + }); + } +} diff --git a/lib/views/setting_options/setting_options_more.dart b/lib/views/setting_options/setting_options_more.dart index 2bb5078..0907b63 100644 --- a/lib/views/setting_options/setting_options_more.dart +++ b/lib/views/setting_options/setting_options_more.dart @@ -87,6 +87,19 @@ class _SettingOptionsMoreState extends State { 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(), diff --git a/lib/views/shipping_management/shipping_management_view.dart b/lib/views/shipping_management/shipping_management_view.dart index 8f54e8e..4d01e4c 100644 --- a/lib/views/shipping_management/shipping_management_view.dart +++ b/lib/views/shipping_management/shipping_management_view.dart @@ -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 { super.initState(); } - Future buildUpdateShippingStatusBottomSheet({required int shippingRequestId}) { + List getAvailableStatusesList(List list, ShippingRequestStatusEnum shippingRequestStatusEnum) { + List 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 { 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 { ); }, itemBuilder: (BuildContext context, int index) { - List list = shippingManagementVM.shippingRequestStatusesList; + List 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 { 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 { 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, diff --git a/lib/widgets/common_widgets/branch_details_card.dart b/lib/widgets/common_widgets/branch_details_card.dart index f5fd70c..d1f0359 100644 --- a/lib/widgets/common_widgets/branch_details_card.dart +++ b/lib/widgets/common_widgets/branch_details_card.dart @@ -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), ], diff --git a/lib/widgets/image_viewer/image_viewer_screen.dart b/lib/widgets/image_viewer/image_viewer_screen.dart index e4c84d7..48dddd4 100644 --- a/lib/widgets/image_viewer/image_viewer_screen.dart +++ b/lib/widgets/image_viewer/image_viewer_screen.dart @@ -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 { 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 { 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 { }, ), ), - 10.height, ], ), );