From f290f2e04f754a3148ae4f2a09b6fcb1a4aed78d Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Tue, 17 Sep 2024 17:26:31 +0300 Subject: [PATCH] Request Module Completed --- assets/langs/ar-SA.json | 40 +++- assets/langs/en-US.json | 38 +++- lib/classes/consts.dart | 4 +- lib/config/routes.dart | 6 +- lib/extensions/string_extensions.dart | 52 ++++- lib/generated/codegen_loader.g.dart | 76 ++++++- lib/generated/locale_keys.g.dart | 36 ++++ .../chat_models/chat_message_model.dart | 6 +- lib/models/requests_models/request_model.dart | 2 +- lib/repositories/request_repo.dart | 34 ++- lib/theme/colors.dart | 12 +- lib/utils/dialogs_and_bottomsheets.dart | 35 +++- lib/utils/enums.dart | 17 +- lib/utils/utils.dart | 49 ++--- lib/view_models/chat_view_model.dart | 78 +++++-- lib/view_models/payment_view_model.dart | 79 ++++--- lib/view_models/requests_view_model.dart | 82 +++++++- lib/view_models/subscriptions_view_model.dart | 8 + .../ad_review_containers.dart | 4 +- .../appointments/review_appointment_view.dart | 178 ++++------------ lib/views/chat/chat_view.dart | 63 ++++-- .../chat/widgets/chat_bottom_sheets.dart | 70 +++++++ .../chat/widgets/chat_message_widget.dart | 138 +++++++++--- .../my_requests_fragment.dart | 82 -------- .../common_fragments/requests_fragment.dart | 156 ++++++++++++++ .../location_views/pick_location_page.dart | 32 +-- lib/views/payments/payment_methods_view.dart | 6 +- lib/views/requests/create_request_page.dart | 48 +++-- lib/views/requests/request_bottomsheets.dart | 79 ++++++- lib/views/requests/request_detail_page.dart | 120 +++++++---- lib/views/requests/review_request_offer.dart | 197 ++++++++++++++++++ lib/views/requests/widget/request_item.dart | 26 +-- .../requests/widget/request_status_chip.dart | 4 +- lib/widgets/checkbox_with_title_desc.dart | 4 +- 34 files changed, 1371 insertions(+), 490 deletions(-) create mode 100644 lib/views/chat/widgets/chat_bottom_sheets.dart delete mode 100644 lib/views/common_fragments/my_requests_fragment.dart create mode 100644 lib/views/common_fragments/requests_fragment.dart create mode 100644 lib/views/requests/review_request_offer.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 6235242..fce9fc2 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -594,7 +594,41 @@ "reserved": "محجوز", "delete": "يمسح", "deleteBranchConfirmation": "هل تريد حذف الفرع؟", - "deleteBranchConfirmationMessage": "سيتم حذف فرعك بشكل دائم ولن تتمكن من التراجع عن هذا الإجراء" - - + "deleteBranchConfirmationMessage": "سيتم حذف فرعك بشكل دائم ولن تتمكن من التراجع عن هذا الإجراء", + "reviewAndPayment": "المراجعة والدفع", + "awaitingPaymentFromCustomer": "في انتظار الدفع من العميل", + "awaitingResponseFromProvider": "في انتظار الرد من المزود", + "offerNoLongerAvailable": "العرض لم يعد متاحاً", + "doYouWantToCompleteThisDeal": "هل تريد إكمال هذه الصفقة؟", + "customerCompletingDealMeansThat": "إكمال هذه الصفقة يعني أن الطلب الذي قمت بنشره قد تم استلامه بنجاح ولا يمكن التراجع عن هذا الإجراء.", + "providerCompletingDealMeansThat": "إكمال هذه الصفقة يعني أنك قد قدمت الخدمة للعميل بنجاح وفقاً لطلبه ولا يمكن التراجع عن هذا الإجراء.", + "offerSummary": "ملخص العرض", + "personalInformation": "المعلومات الشخصية", + "name": "الاسم", + "phone": "الهاتف", + "serviceInformation": "معلومات الخدمة", + "locationInformation": "معلومات الموقع", + "additionalAddressDetails": "تفاصيل العنوان الإضافية", + "serviceName": "اسم الخدمة", + "serviceCategory": "فئة الخدمة", + "offerPrice": "سعر العرض", + "totalPrice": "السعر الإجمالي", + "providerName": "اسم المزود", + "createdOn": "تم الإنشاء في", + "manufacturedBy": "صُنع بواسطة", + "manufacturedOn": "تاريخ الصنع", + "shipping": "الشحن", + "delivery": "التسليم", + "selectDeliveryType": "اختر نوع التسليم", + "shippingManagementInstruction": "يرجى الذهاب إلى الإعدادات > شاشة إدارة الشحن لإدارة عناصر الشحن/التسليم بشكل أكبر.", + "withdrawOffer": "سحب العرض", + "manufacturer": "الصانع", + "priceRange": "نطاق السعر", + "cancelOffer": "إلغاء العرض", + "pleaseSpecify": "يرجى التحديد", + "customerNotResponding": "العميل غير متجاوب", + "cancelRequestPrompt": "هل تريد إلغاء هذا الطلب؟", + "requestPermanentlyCancelled": "سيتم إلغاء طلبك بشكل نهائي. لا يمكنك التراجع عن هذا الإجراء.", + "awaitingResponseFromCustomer": "في انتظار الرد من العميل", + "acknowledgePaymentToMowater": "أقر بأنه عند إكمال هذه الصفقة، سأدفع 2٪ من إجمالي هذه الصفقة لشركة مواتر مقابل خدماتهم." } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 8845eca..9f0821f 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -595,5 +595,41 @@ "reserved": "Reserved", "delete": "Delete", "deleteBranchConfirmation": "Do you want to delete the Branch?", - "deleteBranchConfirmationMessage": "Your branch will be permanently deleted and you cannot undo this action" + "deleteBranchConfirmationMessage": "Your branch will be permanently deleted and you cannot undo this action", + "reviewAndPayment": "Review & Payment", + "awaitingPaymentFromCustomer": "Awaiting Payment from Customer", + "awaitingResponseFromProvider": "Awaiting Response From Provider", + "offerNoLongerAvailable": "Offer is No Longer Available", + "doYouWantToCompleteThisDeal": "Do you want to complete this deal?", + "customerCompletingDealMeansThat": "Completing this deal means that the you request that you have posted has been successfully received and this action cannot be undone.", + "providerCompletingDealMeansThat": "Completing this deal means that you have successfully provided the customer according to his request and this action cannot be undone.", + "offerSummary": "Offer Summary", + "personalInformation": "Personal Information", + "name": "Name", + "phone": "Phone", + "locationInformation": "Location Information", + "serviceInformation": "Service Information", + "additionalAddressDetails": "Additional Address Details", + "serviceName": "Service Name", + "serviceCategory": "Service Category", + "offerPrice": "Offer Price", + "totalPrice": "Total Price", + "providerName": "Provider Name", + "createdOn": "Created On", + "manufacturedBy": "Manufactured By", + "manufacturedOn": "Manufactured On", + "shipping": "Shipping", + "delivery": "Delivery", + "selectDeliveryType": "Select Delivery Type", + "shippingManagementInstruction": "Please go to Settings > Shipping Management screen to manage shipping/delivery items further.", + "withdrawOffer": "Withdraw Offer", + "manufacturer": "Manufacturer", + "priceRange": "Price Range", + "cancelOffer": "Cancel Offer", + "pleaseSpecify": "Please Specify", + "customerNotResponding": "Customer 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", + "acknowledgePaymentToMowater": "I acknowledge that upon completion of this deal, I will pay 2% of this total deal to the Mowater company for their service." } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 279b622..05bd880 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -157,6 +157,8 @@ class ApiConsts { static String getRequest = "${baseUrlServices}api/RequestManagement/Request_Get"; static String getRequestOffers = "${baseUrlServices}api/RequestManagement/ReqOffer_Get"; static String updateRequestOffer = "${baseUrlServices}api/RequestManagement/RequestOffer_UpdateStatus"; + static String updateRequestCustomer = "${baseUrlServices}api/RequestManagement/Request_StatusUpdate_Customer"; + static String updateRequestProvider = "${baseUrlServices}api/RequestManagement/Request_StatusUpdate_Provider"; static String requestOffersSpsGet = "${baseUrlServices}api/RequestManagement/Request_OfferSPs_Get"; static String getServiceRequestsForProvider = "${baseUrlServices}api/RequestManagement/Request_ServiceProvider"; @@ -321,8 +323,6 @@ class MyFonts { static const FontWeight ItalicBold = FontWeight.w700; static const FontWeight ItalicExtraBold = FontWeight.w800; static const FontWeight ItalicBlack = FontWeight.w900; - - } class SignalrConsts { diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 7d5d3b9..3b7de2d 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -15,6 +15,7 @@ import 'package:mc_common_app/views/profile/profile_view.dart'; import 'package:mc_common_app/views/requests/create_request_page.dart'; import 'package:mc_common_app/views/requests/offer_list_page.dart'; import 'package:mc_common_app/views/requests/request_detail_page.dart'; +import 'package:mc_common_app/views/requests/review_request_offer.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_faqs.dart'; @@ -99,6 +100,7 @@ class AppRoutes { static const String offersListPage = "/offersListPage"; static const String requestsDetailPage = "/requestsDetailPage"; static const String sendOfferPage = "/sendOfferPage"; + static const String reviewRequestOffer = "/reviewRequestOffer"; //Setting Options static const String settingOptionsFaqs = "/settingOptionsFaqs"; @@ -157,9 +159,7 @@ class AppRoutes { 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.reviewRequestOffer: (context) => const ReviewRequestOffer(), }; } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 7d3f796..677490d 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -25,7 +25,7 @@ extension EmailValidator on String { int? maxLines}) { bool isArabic = AppState().postParamsObject!.languageID == 1; // arabic -> 1 , English -> 2 return AutoSizeText( - isUpperCase ? this.toUpperCase() : this, + isUpperCase ? toUpperCase() : this, textAlign: textAlign, maxLines: maxLines, style: TextStyle( @@ -163,23 +163,53 @@ extension FormatDate on String { } extension RequestEnum on int { - RequestStatus toRequestStatusEnum() { + RequestStatusEnum toRequestStatusEnum() { if (this == 1) { - return RequestStatus.submitted; + return RequestStatusEnum.submitted; } else if (this == 2) { - return RequestStatus.inProgress; + return RequestStatusEnum.inProgress; } else if (this == 3) { - return RequestStatus.completed; + return RequestStatusEnum.completed; } else if (this == 4) { - return RequestStatus.cancelled; + return RequestStatusEnum.cancelled; } else if (this == 5) { - return RequestStatus.paid; + return RequestStatusEnum.paid; } else if (this == 6) { - return RequestStatus.expired; + return RequestStatusEnum.expired; } else if (this == 7) { - return RequestStatus.shipping; + return RequestStatusEnum.shipping; + } else if (this == 8) { + return RequestStatusEnum.delivery; } else { - return RequestStatus.pending; + return RequestStatusEnum.pending; + } + } +} + +extension RequestStatusEnumToInt on RequestStatusEnum { + int getIdFromRequestStatusEnum() { + switch (this) { + case RequestStatusEnum.submitted: + return 1; + case RequestStatusEnum.inProgress: + return 2; + case RequestStatusEnum.completed: + return 3; + case RequestStatusEnum.cancelled: + return 4; + case RequestStatusEnum.paid: + return 5; + case RequestStatusEnum.expired: + return 6; + case RequestStatusEnum.shipping: + return 7; + case RequestStatusEnum.delivery: + return 8; + case RequestStatusEnum.pending: + return 0; + + default: + return 0; } } } @@ -353,7 +383,7 @@ extension AppointmentPaymentEnum on int { } } -extension RequestTypeTypeEnum on int { +extension RequestTypeTypEnum on int { RequestsTypeEnum toRequestTypeStatusEnum() { if (this == 1) { return RequestsTypeEnum.specialCarRequest; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index b830764..be36937 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -610,7 +610,43 @@ class CodegenLoader extends AssetLoader{ "reserved": "محجوز", "delete": "يمسح", "deleteBranchConfirmation": "هل تريد حذف الفرع؟", - "deleteBranchConfirmationMessage": "سيتم حذف فرعك بشكل دائم ولن تتمكن من التراجع عن هذا الإجراء" + "deleteBranchConfirmationMessage": "سيتم حذف فرعك بشكل دائم ولن تتمكن من التراجع عن هذا الإجراء", + "reviewAndPayment": "المراجعة والدفع", + "awaitingPaymentFromCustomer": "في انتظار الدفع من العميل", + "awaitingResponseFromProvider": "في انتظار الرد من المزود", + "offerNoLongerAvailable": "العرض لم يعد متاحاً", + "doYouWantToCompleteThisDeal": "هل تريد إكمال هذه الصفقة؟", + "customerCompletingDealMeansThat": "إكمال هذه الصفقة يعني أن الطلب الذي قمت بنشره قد تم استلامه بنجاح ولا يمكن التراجع عن هذا الإجراء.", + "providerCompletingDealMeansThat": "إكمال هذه الصفقة يعني أنك قد قدمت الخدمة للعميل بنجاح وفقاً لطلبه ولا يمكن التراجع عن هذا الإجراء.", + "offerSummary": "ملخص العرض", + "personalInformation": "المعلومات الشخصية", + "name": "الاسم", + "phone": "الهاتف", + "serviceInformation": "معلومات الخدمة", + "locationInformation": "معلومات الموقع", + "additionalAddressDetails": "تفاصيل العنوان الإضافية", + "serviceName": "اسم الخدمة", + "serviceCategory": "فئة الخدمة", + "offerPrice": "سعر العرض", + "totalPrice": "السعر الإجمالي", + "providerName": "اسم المزود", + "createdOn": "تم الإنشاء في", + "manufacturedBy": "صُنع بواسطة", + "manufacturedOn": "تاريخ الصنع", + "shipping": "الشحن", + "delivery": "التسليم", + "selectDeliveryType": "اختر نوع التسليم", + "shippingManagementInstruction": "يرجى الذهاب إلى الإعدادات > شاشة إدارة الشحن لإدارة عناصر الشحن/التسليم بشكل أكبر.", + "withdrawOffer": "سحب العرض", + "manufacturer": "الصانع", + "priceRange": "نطاق السعر", + "cancelOffer": "إلغاء العرض", + "pleaseSpecify": "يرجى التحديد", + "customerNotResponding": "العميل غير متجاوب", + "cancelRequestPrompt": "هل تريد إلغاء هذا الطلب؟", + "requestPermanentlyCancelled": "سيتم إلغاء طلبك بشكل نهائي. لا يمكنك التراجع عن هذا الإجراء.", + "awaitingResponseFromCustomer": "في انتظار الرد من العميل", + "acknowledgePaymentToMowater": "أقر بأنه عند إكمال هذه الصفقة، سأدفع 2٪ من إجمالي هذه الصفقة لشركة مواتر مقابل خدماتهم." }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1209,7 +1245,43 @@ static const Map en_US = { "reserved": "Reserved", "delete": "Delete", "deleteBranchConfirmation": "Do you want to delete the Branch?", - "deleteBranchConfirmationMessage": "Your branch will be permanently deleted and you cannot undo this action" + "deleteBranchConfirmationMessage": "Your branch will be permanently deleted and you cannot undo this action", + "reviewAndPayment": "Review & Payment", + "awaitingPaymentFromCustomer": "Awaiting Payment from Customer", + "awaitingResponseFromProvider": "Awaiting Response From Provider", + "offerNoLongerAvailable": "Offer is No Longer Available", + "doYouWantToCompleteThisDeal": "Do you want to complete this deal?", + "customerCompletingDealMeansThat": "Completing this deal means that the you request that you have posted has been successfully received and this action cannot be undone.", + "providerCompletingDealMeansThat": "Completing this deal means that you have successfully provided the customer according to his request and this action cannot be undone.", + "offerSummary": "Offer Summary", + "personalInformation": "Personal Information", + "name": "Name", + "phone": "Phone", + "locationInformation": "Location Information", + "serviceInformation": "Service Information", + "additionalAddressDetails": "Additional Address Details", + "serviceName": "Service Name", + "serviceCategory": "Service Category", + "offerPrice": "Offer Price", + "totalPrice": "Total Price", + "providerName": "Provider Name", + "createdOn": "Created On", + "manufacturedBy": "Manufactured By", + "manufacturedOn": "Manufactured On", + "shipping": "Shipping", + "delivery": "Delivery", + "selectDeliveryType": "Select Delivery Type", + "shippingManagementInstruction": "Please go to Settings > Shipping Management screen to manage shipping/delivery items further.", + "withdrawOffer": "Withdraw Offer", + "manufacturer": "Manufacturer", + "priceRange": "Price Range", + "cancelOffer": "Cancel Offer", + "pleaseSpecify": "Please Specify", + "customerNotResponding": "Customer 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", + "acknowledgePaymentToMowater": "I acknowledge that upon completion of this deal, I will pay 2% of this total deal to the Mowater company for their service." }; 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 51b9e5c..44c3c36 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -574,5 +574,41 @@ abstract class LocaleKeys { static const delete = 'delete'; static const deleteBranchConfirmation = 'deleteBranchConfirmation'; static const deleteBranchConfirmationMessage = 'deleteBranchConfirmationMessage'; + static const reviewAndPayment = 'reviewAndPayment'; + static const awaitingPaymentFromCustomer = 'awaitingPaymentFromCustomer'; + static const awaitingResponseFromProvider = 'awaitingResponseFromProvider'; + static const offerNoLongerAvailable = 'offerNoLongerAvailable'; + static const doYouWantToCompleteThisDeal = 'doYouWantToCompleteThisDeal'; + static const customerCompletingDealMeansThat = 'customerCompletingDealMeansThat'; + static const providerCompletingDealMeansThat = 'providerCompletingDealMeansThat'; + static const offerSummary = 'offerSummary'; + static const personalInformation = 'personalInformation'; + static const name = 'name'; + static const phone = 'phone'; + static const serviceInformation = 'serviceInformation'; + static const locationInformation = 'locationInformation'; + static const additionalAddressDetails = 'additionalAddressDetails'; + static const serviceName = 'serviceName'; + static const serviceCategory = 'serviceCategory'; + static const offerPrice = 'offerPrice'; + static const totalPrice = 'totalPrice'; + static const providerName = 'providerName'; + static const createdOn = 'createdOn'; + static const manufacturedBy = 'manufacturedBy'; + static const manufacturedOn = 'manufacturedOn'; + static const shipping = 'shipping'; + static const delivery = 'delivery'; + static const selectDeliveryType = 'selectDeliveryType'; + static const shippingManagementInstruction = 'shippingManagementInstruction'; + static const withdrawOffer = 'withdrawOffer'; + static const manufacturer = 'manufacturer'; + static const priceRange = 'priceRange'; + static const cancelOffer = 'cancelOffer'; + static const pleaseSpecify = 'pleaseSpecify'; + static const customerNotResponding = 'customerNotResponding'; + static const cancelRequestPrompt = 'cancelRequestPrompt'; + static const requestPermanentlyCancelled = 'requestPermanentlyCancelled'; + static const awaitingResponseFromCustomer = 'awaitingResponseFromCustomer'; + static const acknowledgePaymentToMowater = 'acknowledgePaymentToMowater'; } diff --git a/lib/models/chat_models/chat_message_model.dart b/lib/models/chat_models/chat_message_model.dart index debc179..f4eacb4 100644 --- a/lib/models/chat_models/chat_message_model.dart +++ b/lib/models/chat_models/chat_message_model.dart @@ -101,11 +101,11 @@ class ReqOffer { offerStatusText = json['offerStatusText']; comment = json['comment']; serviceItemName = json['serviceItem']; - manufacturedById = json['itemManufacturer']; - manufacturedOn = json['manufacturedDate']; + manufacturedById = json['offeredItemCreatedBy']; + manufacturedOn = json['offeredItemCreatedOn']; price = json['price']; requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum(); - requestsTypeEnum = RequestsTypeEnum.serviceRequest; // TODO: THIS SHOULD COME FROM API + requestsTypeEnum = RequestsTypeEnum.serviceRequest; } } diff --git a/lib/models/requests_models/request_model.dart b/lib/models/requests_models/request_model.dart index 8bd7c5b..845fb40 100644 --- a/lib/models/requests_models/request_model.dart +++ b/lib/models/requests_models/request_model.dart @@ -7,7 +7,7 @@ class RequestModel { int requestType; String requestTypeName; String requestStatusName; - RequestStatus requestStatus; + RequestStatusEnum requestStatus; String cityName; String vehicleTypeName; String countryName; diff --git a/lib/repositories/request_repo.dart b/lib/repositories/request_repo.dart index d4e7005..e74631f 100644 --- a/lib/repositories/request_repo.dart +++ b/lib/repositories/request_repo.dart @@ -20,6 +20,7 @@ abstract class RequestRepo { required int cityID, required String price, required String description, + required String address, required bool isSpecialServiceNeeded, required List requestImages, }); @@ -32,7 +33,9 @@ abstract class RequestRepo { Future> getServiceRequestsForProviders({required int serviceProviderID}); - Future updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments}); + Future updateRequestStatus({required RequestStatusEnum requestStatusEnum, required int requestId}); + + Future updateRequestOfferStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments}); } class RequestRepoImp implements RequestRepo { @@ -50,6 +53,7 @@ class RequestRepoImp implements RequestRepo { required int cityID, required String price, required String description, + required String address, required bool isSpecialServiceNeeded, required List requestImages, }) async { @@ -65,6 +69,7 @@ class RequestRepoImp implements RequestRepo { "cityID": cityID, "price": price, "description": description, + "address": address, "isSpecialServiceNeeded": false, "requestImages": requestImages, }; @@ -106,9 +111,18 @@ class RequestRepoImp implements RequestRepo { @override Future> getServiceRequestsForProviders({required int serviceProviderID}) async { + List requestStatusList = [ + RequestStatusEnum.submitted.getIdFromRequestStatusEnum().toString(), + RequestStatusEnum.completed.getIdFromRequestStatusEnum().toString(), + RequestStatusEnum.inProgress.getIdFromRequestStatusEnum().toString(), + RequestStatusEnum.paid.getIdFromRequestStatusEnum().toString(), + RequestStatusEnum.delivery.getIdFromRequestStatusEnum().toString(), + RequestStatusEnum.shipping.getIdFromRequestStatusEnum().toString(), + ]; final parameters = { "ServiceProviderId": serviceProviderID.toString(), "RequestTypeId": "2", // Only to get the Service Request + "RequestStatuses": requestStatusList, }; GenericRespModel genericRespModel = await apiClient.getJsonForObject( (json) => GenericRespModel.fromJson(json), @@ -165,7 +179,23 @@ class RequestRepoImp implements RequestRepo { } @override - Future updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments}) async { + Future updateRequestStatus({required RequestStatusEnum requestStatusEnum, required int requestId}) async { + var queryParameters = { + "requestId": requestId.toString(), + "requestStatus": requestStatusEnum.getIdFromRequestStatusEnum().toString(), + }; + GenericRespModel genericRespModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + appState.currentAppType == AppType.provider ? ApiConsts.updateRequestProvider : ApiConsts.updateRequestCustomer, + queryParameters, + token: appState.getUser.data!.accessToken, + ); + + return genericRespModel; + } + + @override + Future updateRequestOfferStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments}) async { var queryParameters = { "requestOfferId": requestOfferId.toString(), "offerStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum().toString(), diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index 872d41a..af0f4c0 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -42,7 +42,7 @@ class MyColors { static const Color lightGreyDDColor = Color(0xffDDDDDD); static const Color greyColor = Color(0xff787878); - static const Color provTextColor = Color(0xFF28323A); + static const Color provTextColor = Color(0xFF28323A); //AdStatusColors: static const Color adActiveStatusColor = Color(0xff5FC16A); @@ -50,6 +50,16 @@ class MyColors { static const Color adSoldStatusColor = Color(0xff22742B); static const Color adPendingStatusColor = Color(0xffEDB947); + static const Color pendingColor = Colors.amber; + static const Color submittedColor = Colors.blue; + static const Color inProgressColor = Colors.deepOrange; + static const Color completedColor = Colors.green; + static const Color cancelledColor = Colors.red; + static const Color paidColor = Colors.teal; + static const Color expiredColor = Colors.grey; + static const Color shippingColor = Colors.purple; + static const Color deliveryColor = Colors.brown; + static Decoration gradient = BoxDecoration( gradient: const LinearGradient(colors: [ darkPrimaryColor, diff --git a/lib/utils/dialogs_and_bottomsheets.dart b/lib/utils/dialogs_and_bottomsheets.dart index 4360164..cc4896f 100644 --- a/lib/utils/dialogs_and_bottomsheets.dart +++ b/lib/utils/dialogs_and_bottomsheets.dart @@ -1,4 +1,6 @@ +import 'package:flutter/cupertino.dart'; 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/utils/app_permission_handler.dart'; @@ -11,7 +13,7 @@ Future showConfirmDialogs(context, msg, positiveText, negativeTe builder: (context) { return AlertDialog( backgroundColor: Colors.white, - title: Text(msg, style: TextStyle(fontSize: 16)), + title: Text(msg, style: const TextStyle(fontSize: 16)), actions: [ TextButton( child: Text( @@ -35,19 +37,30 @@ Future showConfirmDialogs(context, msg, positiveText, negativeTe ); } -void actionConfirmationBottomSheet({required BuildContext context, required Widget title, required subtitle, required Widget actionButtonYes, required Widget actionButtonNo}) { +void actionConfirmationBottomSheet({required BuildContext context, required Widget title, required subtitle, required Widget actionButtonYes, required Widget actionButtonNo, Widget? confirmationWidget, bool isOnlyOneButton = false}) { showModalBottomSheet( context: context, isScrollControlled: true, enableDrag: true, builder: (BuildContext context) { return InfoBottomSheet( - title: title, - description: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "$subtitle".toText(fontSize: 16), - 25.height, + title: title, + description: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "$subtitle".toText(fontSize: 14, fontWeight: MyFonts.Medium), + if (confirmationWidget != null) ...[ + 10.height, + confirmationWidget, + ], + 25.height, + if (isOnlyOneButton) ...[ + Row( + children: [ + actionButtonYes, + ], + ), + ] else ...[ Row( children: [ actionButtonYes, @@ -55,9 +68,11 @@ void actionConfirmationBottomSheet({required BuildContext context, required Widg actionButtonNo, ], ), - 19.height, ], - )); + 19.height, + ], + ), + ); }, ); } diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index 47c23e0..94224e5 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -56,7 +56,7 @@ enum AdPostStatus { allAds, } -enum RequestStatus { +enum RequestStatusEnum { pending, submitted, inProgress, @@ -65,21 +65,8 @@ enum RequestStatus { paid, expired, shipping, + delivery, } -//TODO: Needs to remove -// public enum RequestType : int -// { -// Special_Car_Request = 1, -// Service_Request = 2 //SparePart -// } -// public enum OfferStatus : int -// { -// Offer = 1,//SP -// Negotiate = 2, -// Accepted = 3, -// Rejected = 4, -// Cancelled = 5 //SP -// } enum PaymentMethods { mada, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index a4e5f9e..00eb22e 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -222,30 +222,26 @@ class Utils { } } - static Color getChipColorByRequestStatus(RequestStatus requestStatus) { + static Color getChipColorByRequestStatus(RequestStatusEnum requestStatus) { switch (requestStatus) { - case RequestStatus.pending: - return MyColors.adPendingStatusColor; - case RequestStatus.submitted: - return MyColors.lightTextColor; - - case RequestStatus.inProgress: - return MyColors.lightTextColor; - - case RequestStatus.completed: - return MyColors.greenColor; - - case RequestStatus.cancelled: - return MyColors.redColor; - - case RequestStatus.paid: - return MyColors.greenColor; - - case RequestStatus.expired: - return MyColors.redColor; - - case RequestStatus.shipping: - return MyColors.greenColor; + case RequestStatusEnum.pending: + return MyColors.pendingColor; + case RequestStatusEnum.submitted: + return MyColors.submittedColor; + case RequestStatusEnum.inProgress: + return MyColors.inProgressColor; + case RequestStatusEnum.completed: + return MyColors.completedColor; + case RequestStatusEnum.cancelled: + return MyColors.cancelledColor; + case RequestStatusEnum.paid: + return MyColors.paidColor; + case RequestStatusEnum.expired: + return MyColors.expiredColor; + case RequestStatusEnum.shipping: + return MyColors.shippingColor; + case RequestStatusEnum.delivery: + return MyColors.deliveryColor; } } @@ -271,7 +267,6 @@ class Utils { } } - static String getChipStatusTextByBranchStatus(BranchStatusEnum branchStatusEnum) { switch (branchStatusEnum) { case BranchStatusEnum.pending: @@ -301,11 +296,7 @@ class Utils { borderRadius: const BorderRadius.all(Radius.circular(200)), ), padding: padding, - child: text.toText( - fontSize: 10, - color: textColor, - letterSpacing: -0.50 - )); + child: text.toText(fontSize: 10, color: textColor, letterSpacing: -0.50)); } static InputDecoration txtField(String label) { diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 87c9483..961a05c 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -46,6 +46,13 @@ class ChatVM extends ChangeNotifier { notifyListeners(); } + bool acknowledgePaymentToMowaterStatus = false; + + updateAcknowledgePaymentToMowaterStatus(var value) { + acknowledgePaymentToMowaterStatus = value; + notifyListeners(); + } + bool isUserOnChatScreen = false; final ScrollController scrollController = ScrollController(); @@ -66,7 +73,7 @@ class ChatVM extends ChangeNotifier { List offerRejectModelList = [ OfferRequestCommentModel( index: 0, - isSelected: false, + isSelected: true, title: LocaleKeys.changedMind.tr(), ), OfferRequestCommentModel( @@ -81,15 +88,20 @@ class ChatVM extends ChangeNotifier { ), OfferRequestCommentModel( index: 3, - isSelected: true, + isSelected: false, + title: LocaleKeys.customerNotResponding.tr(), + ), + OfferRequestCommentModel( + index: 4, + isSelected: false, title: LocaleKeys.otherVar.tr(), ), ]; OfferRequestCommentModel selectedOfferRequestCommentModel = OfferRequestCommentModel( - index: 3, + index: 0, isSelected: true, - title: LocaleKeys.otherVar.tr(), + title: LocaleKeys.changedMind.tr(), ); void updateSelectionInOfferRejectModelList(int index) { @@ -131,26 +143,44 @@ class ChatVM extends ChangeNotifier { Future onNewMessageReceivedForRequestOffer({required List messages, bool isMyOwnOffer = false, required RequestsVM requestsVM}) async { if (AppState().currentAppType == AppType.customer) { - for (var msg in messages) { - int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == msg.senderUserID); + for (var currentMessage in messages) { + int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == currentMessage.senderUserID); if (providerIndex != -1) { - serviceProviderOffersList[providerIndex].chatMessages!.add(msg); + if (currentMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer) { + for (var chatMessage in serviceProviderOffersList[providerIndex].chatMessages!) { + if (chatMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer && chatMessage.reqOffer!.id == currentMessage.reqOffer!.id) { + logger.i("chatMessage reqOfferID: ${chatMessage.reqOffer!.id.toString()}"); + logger.i("currentMessage reqOfferID: ${currentMessage.reqOffer!.id.toString()}"); + chatMessage.reqOffer!.requestOfferStatusEnum = currentMessage.reqOffer!.requestOfferStatusEnum; + } + } + } + serviceProviderOffersList[providerIndex].chatMessages!.add(currentMessage); scrollChatDown(); } } } else { - for (var msg in messages) { - logger.i(msg); + for (var currentMessage in messages) { + logger.i(currentMessage); // Where we need to call this function for saving a message in chat we will use receiverUserID and in those cases where received ID is null , it means it is a signal R call - int providerIndex = -1; + int requestIndex = -1; if (isMyOwnOffer) { - providerIndex = requestsVM.myFilteredRequests.indexWhere((element) => (element.customerUserID == msg.receiverUserID && element.id == msg.requestID)); + requestIndex = requestsVM.myFilteredRequests.indexWhere((element) => (element.customerUserID == currentMessage.receiverUserID && element.id == currentMessage.requestID)); } else { - providerIndex = requestsVM.myFilteredRequests.indexWhere((element) => (element.customerUserID == msg.senderUserID && element.id == msg.requestID)); + requestIndex = requestsVM.myFilteredRequests.indexWhere((element) => (element.customerUserID == currentMessage.senderUserID && element.id == currentMessage.requestID)); } - log("providerIndex: $providerIndex"); - if (providerIndex != -1) { - requestsVM.addChatMessagesInRequestsModel(msg: msg, index: providerIndex); + log("requestIndex: $requestIndex"); + if (requestIndex != -1) { + if (currentMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer) { + for (var chatMessage in requestsVM.myFilteredRequests[requestIndex].chatMessages) { + if (chatMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer && chatMessage.reqOffer!.id == currentMessage.reqOffer!.id) { + logger.i("chatMessage reqOfferID: ${chatMessage.reqOffer!.id.toString()}"); + logger.i("currentMessage reqOfferID: ${currentMessage.reqOffer!.id.toString()}"); + chatMessage.reqOffer!.requestOfferStatusEnum = currentMessage.reqOffer!.requestOfferStatusEnum; + } + } + } + requestsVM.addChatMessagesInRequestsModel(msg: currentMessage, index: requestIndex); scrollChatDown(); } } @@ -158,8 +188,6 @@ class ChatVM extends ChangeNotifier { notifyListeners(); } - - Future onNewMessageReceivedForAds({required List messages}) async { for (var msg in messages) { currentMessagesForAds.add(msg); @@ -323,10 +351,7 @@ class ChatVM extends ChangeNotifier { serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel); } } else { - int providerIndex = context - .read() - .myFilteredRequests - .indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId)); + int providerIndex = context.read().myFilteredRequests.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId)); log("providerIndex2:$providerIndex"); if (providerIndex != -1) { context.read().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex); @@ -370,6 +395,10 @@ class ChatVM extends ChangeNotifier { for (var message in serviceProviderOffersList[providerOfferIndex].chatMessages!) { if (message.chatMessageTypeEnum == ChatMessageTypeEnum.offer) { updateLatestOfferId(message.reqOfferID ?? 0); + if (message.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted) { + context.read().updateAcceptedReqOffer(message.reqOffer!); + context.read().updateAcceptedRequestOfferProviderName(serviceProviderOffersList[providerOfferIndex].name ?? ""); + } log("latestOfferId: $latestOfferId"); } } @@ -404,9 +433,11 @@ class ChatVM extends ChangeNotifier { } Future onActionOfferTapped({required BuildContext context, required RequestOfferStatusEnum requestOfferStatusEnum, required int reqOfferId, required String comments}) async { + Utils.showLoading(context); try { - GenericRespModel genericRespModel = await requestRepo.updateOfferRequestStatus(requestOfferStatusEnum: requestOfferStatusEnum, requestOfferId: reqOfferId, comments: comments); + GenericRespModel genericRespModel = await requestRepo.updateRequestOfferStatus(requestOfferStatusEnum: requestOfferStatusEnum, requestOfferId: reqOfferId, comments: comments); Utils.showToast(genericRespModel.message.toString()); + Utils.showLoading(context); return genericRespModel.messageStatus == 1 ? reqOfferId : -1; } catch (e) { logger.i(e.toString()); @@ -563,7 +594,8 @@ class ChatVM extends ChangeNotifier { notifyListeners(); - if (AppState().currentAppType == AppType.customer) {} else {} + if (AppState().currentAppType == AppType.customer) { + } else {} return true; } return false; diff --git a/lib/view_models/payment_view_model.dart b/lib/view_models/payment_view_model.dart index f873c03..4dc7d98 100644 --- a/lib/view_models/payment_view_model.dart +++ b/lib/view_models/payment_view_model.dart @@ -10,6 +10,7 @@ import 'package:mc_common_app/services/payments_service.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/subscriptions_view_model.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -56,19 +57,19 @@ class PaymentVM extends ChangeNotifier { Future onContinuePressed(BuildContext context, {required PaymentTypes paymentType}) async { switch (selectedPaymentMethod) { case PaymentMethods.mada: - // TODO: Handle this case. + // TODO: Handle this case. break; case PaymentMethods.visa: await onVisaCardSelected(context, paymentType); break; case PaymentMethods.applePay: - // TODO: Handle this case. + // TODO: Handle this case. break; case PaymentMethods.masterCard: - // TODO: Handle this case. + // TODO: Handle this case. break; case PaymentMethods.tamara: - // TODO: Handle this case. + // TODO: Handle this case. break; } @@ -132,35 +133,46 @@ class PaymentVM extends ChangeNotifier { Future placeThePayment({required PaymentTypes paymentTypeEnum, required BuildContext context}) async { await paymentService.placePayment( - // This will be request ID for request payment || ad ID for Ad Related Payment || OrderProviderSubscriptionID for SubscriptionPayment id: getIdTypeByPaymentType(paymentTypeEnum), + // This will be request ID for request payment || ad ID for Ad Related Payment || OrderProviderSubscriptionID for SubscriptionPayment appointmentIds: appointmentIdsForPayment, paymentType: paymentTypeEnum, onFailure: () { Utils.showToast(LocaleKeys.paymentFailed.tr()); switch (paymentTypeEnum) { case PaymentTypes.subscription: - // TODO: Handle this case. + // TODO: Handle this case. + Utils.showToast("Appointment Payment has been Failed!!"); break; - case PaymentTypes.appointment: - // TODO: Handle this case. - log("Appointment Payment has been Failed!!"); + + case PaymentTypes.request: + // TODO: Handle this case. + Utils.showToast("Request Payment has been Failed!!"); break; + case PaymentTypes.adReserve: - // TODO: Handle this case. + // TODO: Handle this case. + Utils.showToast("adReserve Payment has been Failed!!"); break; + case PaymentTypes.ads: - // TODO: Handle this case. - break; - case PaymentTypes.request: - // TODO: Handle this case. + // TODO: Handle this case. + Utils.showToast("ads Payment has been Failed!!"); break; + case PaymentTypes.extendAds: - // TODO: Handle this case. + // TODO: Handle this case. + Utils.showToast("extendAds Payment has been Failed!!"); + + break; + + case PaymentTypes.appointment: + // TODO: Handle this case. + Utils.showToast("Appointment Payment has been Failed!!"); break; - case PaymentTypes.partialAppointment: - log("Partial Appointment Payment has been Failed!!"); + case PaymentTypes.partialAppointment: + Utils.showToast("partialAppointment Payment has been Failed!!"); break; } }, @@ -168,35 +180,39 @@ class PaymentVM extends ChangeNotifier { // TOD0: we have to take payment confirmation methods from Backend team and make success callbacks like onAdsPaymentSuccess switch (paymentTypeEnum) { case PaymentTypes.subscription: - onSubscriptionSuccess(context); + onSubscriptionPaymentSuccess(context); break; + case PaymentTypes.appointment: - log("Appointment Payment has been Succeeded"); + Utils.showToast("Appointment Payment has been Succeeded"); + break; + + case PaymentTypes.partialAppointment: + Utils.showToast("Partial Appointment Payment has been Succeeded"); break; + case PaymentTypes.request: + Utils.showToast("Request Payment has been Succeeded"); + onRequestPaymentSuccess(context); break; + case PaymentTypes.adReserve: case PaymentTypes.ads: case PaymentTypes.extendAds: await onAdsPaymentSuccess(context: context, paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), currentAdId: currentAdId); break; - case PaymentTypes.partialAppointment: - log("Partial Appointment Payment has been Succeeded"); - break; } }, ); } - onSubscriptionSuccess(BuildContext context) { - // pop(context); - // pop(context); - // pop(context); + void onRequestPaymentSuccess(BuildContext context) { + context.read().onNavbarTapped(4); + navigateReplaceWithName(context, AppRoutes.dashboard); + } - // context.read().getMySubscriptions(AppState().getUser.data?.userInfo?.providerId.toString() ?? ""); - context - .read() - .mySubscriptionsBySp = []; + void onSubscriptionPaymentSuccess(BuildContext context) { + context.read().mySubscriptionsBySp = []; context.read().getMySubscriptionsBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? ""); navigateReplaceWithNameUntilRoute(context, AppRoutes.mySubscriptionsPage); } @@ -210,7 +226,8 @@ class PaymentVM extends ChangeNotifier { await placeThePayment(context: context, paymentTypeEnum: paymentType); break; case PaymentTypes.request: - // TODO: Handle this case. + if (requestId == -1) return; + await placeThePayment(context: context, paymentTypeEnum: paymentType); break; case PaymentTypes.subscription: await placeThePayment(context: context, paymentTypeEnum: paymentType); diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index 6ab6057..56c9ba8 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -9,6 +9,7 @@ import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart'; import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/general_models/enums_model.dart'; @@ -19,6 +20,7 @@ import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/repositories/request_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; +import 'package:mc_common_app/utils/date_helper.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; @@ -48,6 +50,20 @@ class RequestsVM extends BaseVM { currentSelectedRequest = value; } + ReqOffer? acceptedRequestOffer; + + updateAcceptedReqOffer(ReqOffer value) { + acceptedRequestOffer = value; + notifyListeners(); + } + + String? acceptedRequestOfferProviderName; + + updateAcceptedRequestOfferProviderName(String value) { + acceptedRequestOfferProviderName = value; + notifyListeners(); + } + populateRequestsFilterList() async { if (requestsTypeFilterOptions.isNotEmpty) return; @@ -95,6 +111,10 @@ class RequestsVM extends BaseVM { for (var message in myFilteredRequests[index].chatMessages) { if (message.chatMessageTypeEnum == ChatMessageTypeEnum.offer) { context.read().updateLatestOfferId(message.reqOfferID ?? 0); + if (message.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted) { + updateAcceptedReqOffer(message.reqOffer!); + updateAcceptedRequestOfferProviderName(message.senderName ?? ""); + } } } } @@ -227,10 +247,10 @@ class RequestsVM extends BaseVM { notifyListeners(); } - bool isShippingDeliveryEnabled = false; + bool shippingEnabled = false; - void updateShippingDeliverEnabled(bool v) { - isShippingDeliveryEnabled = v; + void updateShippingEnabled(bool v) { + shippingEnabled = v; notifyListeners(); } @@ -256,15 +276,20 @@ class RequestsVM extends BaseVM { //Request Management String price = ""; String description = ""; + String address = ""; - updatePrice(String v) { + void updatePrice(String v) { price = v; } - updateDescription(String v) { + void updateDescription(String v) { description = v; } + void updateAddress(String v) { + address = v; + } + Future convertFileToRequestPostingImages({required File file}) async { List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); @@ -305,6 +330,7 @@ class RequestsVM extends BaseVM { cityID: vehicleCityId.selectedId, price: price, description: description, + address: address, isSpecialServiceNeeded: false, requestImages: requestImages, ); @@ -353,6 +379,9 @@ class RequestsVM extends BaseVM { } else if (description.isEmpty) { Utils.showToast(LocaleKeys.addValidDescription.tr()); isValid = false; + } else if (address.isEmpty && requestTypeId.selectedId == 2) { + Utils.showToast(LocaleKeys.addValidDescription.tr()); + isValid = false; } return isValid; } @@ -410,6 +439,49 @@ class RequestsVM extends BaseVM { } } + List providerDeliveryActionsList = [ + OfferRequestCommentModel( + index: 0, + isSelected: false, + title: LocaleKeys.shipping.tr(), + ), + OfferRequestCommentModel( + index: 1, + isSelected: true, + title: LocaleKeys.delivery.tr(), + ), + ]; + + OfferRequestCommentModel selectedProviderDeliveryAction = OfferRequestCommentModel( + index: 1, + isSelected: true, + title: LocaleKeys.delivery.tr(), + ); + + void updateSelectedProviderDeliveryAction(int index) { + for (var value in providerDeliveryActionsList) { + value.isSelected = false; + } + selectedProviderDeliveryAction = providerDeliveryActionsList[index]; + providerDeliveryActionsList[index].isSelected = true; + notifyListeners(); + } + + Future onActionRequestTapped({required BuildContext context, required RequestStatusEnum requestStatusEnum, required int requestId}) async { + Utils.showLoading(context); + try { + GenericRespModel genericRespModel = await requestRepo.updateRequestStatus(requestStatusEnum: requestStatusEnum, requestId: requestId); + Utils.showToast(genericRespModel.message.toString()); + Utils.hideLoading(context); + return genericRespModel.messageStatus == 1; + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + return false; + } + } + //SENDING OFFER bool isSendOfferValidated() { bool isValidated = true; diff --git a/lib/view_models/subscriptions_view_model.dart b/lib/view_models/subscriptions_view_model.dart index 29f48ae..7046d5d 100644 --- a/lib/view_models/subscriptions_view_model.dart +++ b/lib/view_models/subscriptions_view_model.dart @@ -175,6 +175,14 @@ class SubscriptionsVM extends BaseVM { } } + // Rights + // Branch CRUD + // Services in Branch (CRUD) + // Appointment Management + // Request Management with Chat + // Ads CRUD with Chat + // General Chat + // My Provider Subscription getMySubscriptionsBySP(String? serviceProviderID) async { setState(ViewState.busy); diff --git a/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart b/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart index 96b3840..01a5af0 100644 --- a/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart +++ b/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart @@ -37,8 +37,8 @@ class SingleDetailWidget extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "$type:".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - text.toText(fontSize: 14, color: MyColors.black, isBold: true), + (type.isEmpty ? type : "$type:").toText(fontSize: 12, color: MyColors.lightTextColor), + text.toText(fontSize: 14, color: MyColors.darkTextColor), ], ); } diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart index 75e7e39..ba2a56e 100644 --- a/lib/views/appointments/review_appointment_view.dart +++ b/lib/views/appointments/review_appointment_view.dart @@ -1,6 +1,5 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.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'; @@ -22,7 +21,7 @@ class ReviewAppointment extends StatelessWidget { AppointmentsVM appointmentsVM = context.read(); return Padding( padding: const EdgeInsets.only( - bottom: 10, + bottom: 0, left: 21, right: 21, ), @@ -48,17 +47,12 @@ class ReviewAppointment extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - (appointmentsVM.selectedBranchModel!.branchName ?? - "") - .toText(fontSize: 16, isBold: true), + (appointmentsVM.selectedBranchModel!.branchName ?? "").toText(fontSize: 16, isBold: true), Row( children: [ - LocaleKeys.location.tr().toText( - color: MyColors.lightTextColor, - fontSize: 12), + LocaleKeys.location.tr().toText(color: MyColors.lightTextColor, fontSize: 12), 2.width, - ": ${appointmentsVM.selectedBranchModel!.branchDescription ?? ""}" - .toText(fontSize: 12), + ": ${appointmentsVM.selectedBranchModel!.branchDescription ?? ""}".toText(fontSize: 12), ], ), ], @@ -82,9 +76,7 @@ class ReviewAppointment extends StatelessWidget { ), ), ], - ) - .onPress(() {}) - .toWhiteContainer(width: double.infinity, allPading: 12)); + ).onPress(() {}).toWhiteContainer(width: double.infinity, allPading: 12)); } Widget buildServicesInfoCard({required BuildContext context}) { @@ -93,18 +85,14 @@ class ReviewAppointment extends StatelessWidget { shrinkWrap: true, itemCount: appointmentsVM.serviceAppointmentScheduleList.length, itemBuilder: (BuildContext context, int scheduleIndex) { - ServiceAppointmentScheduleModel scheduleData = - appointmentsVM.serviceAppointmentScheduleList[scheduleIndex]; + ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex]; return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: LocaleKeys.services.tr().toText( - fontSize: 14, - isBold: true, - color: MyColors.lightTextColor), + child: LocaleKeys.services.tr().toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), ), ], ), @@ -112,60 +100,38 @@ class ReviewAppointment extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ ListView.separated( - physics: NeverScrollableScrollPhysics(), + // physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: scheduleData.servicesListInAppointment!.length, itemBuilder: (BuildContext context, int serviceIndex) { String selectedTimeSlot = ""; - if (scheduleData - .selectedCustomTimeDateSlotModel!.availableSlots != - null) { - selectedTimeSlot = scheduleData - .selectedCustomTimeDateSlotModel!.availableSlots! - .firstWhere((element) => element.isSelected) - .slot; + if (scheduleData.selectedCustomTimeDateSlotModel!.availableSlots != null) { + selectedTimeSlot = scheduleData.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected).slot; } return Column( children: [ - if (scheduleData - .servicesListInAppointment!.isNotEmpty) ...[ + if (scheduleData.servicesListInAppointment!.isNotEmpty) ...[ Column( - children: List.generate( - scheduleData.servicesListInAppointment!.length, - (serviceIndex) { - ServiceModel serviceData = scheduleData - .servicesListInAppointment![serviceIndex]; + children: List.generate(scheduleData.servicesListInAppointment!.length, (serviceIndex) { + ServiceModel serviceData = scheduleData.servicesListInAppointment![serviceIndex]; return Column( children: [ Row( - crossAxisAlignment: - CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - "${serviceData.providerServiceDescription}" - .toText(fontSize: 16, isBold: true), + "${serviceData.providerServiceDescription}".toText(fontSize: 16, isBold: true), ], ), Column( - children: List.generate( - serviceData.serviceItems!.length, - (itemIndex) { - ItemData itemData = - serviceData.serviceItems![itemIndex]; + children: List.generate(serviceData.serviceItems!.length, (itemIndex) { + ItemData itemData = serviceData.serviceItems![itemIndex]; return Column( children: [ Row( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: - MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ - ("${itemData.name}: ${itemData.price} " + - LocaleKeys.sar.tr()) - .toText( - fontSize: 13, - isBold: true, - color: MyColors - .lightTextColor), + ("${itemData.name}: ${itemData.price} ${LocaleKeys.sar.tr()}").toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor), ], ), ], @@ -183,33 +149,21 @@ class ReviewAppointment extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.timeLocation - .tr() - .toText(fontSize: 16, isBold: true), + LocaleKeys.timeLocation.tr().toText(fontSize: 16, isBold: true), 3.height, Row(children: [ - LocaleKeys.dateAndTime.tr().toText( - fontSize: 12, - color: MyColors.lightTextColor, - isBold: true), - "${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at ${selectedTimeSlot}" - .toText(fontSize: 12, isBold: true), + LocaleKeys.dateAndTime.tr().toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + "${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at ${selectedTimeSlot}".toText(fontSize: 12, isBold: true), ]), Row(children: [ - (LocaleKeys.location.tr() + " ").toText( - fontSize: 12, - color: MyColors.lightTextColor, - isBold: true), - (scheduleData.appointmentType == 2 - ? LocaleKeys.home.tr() - : LocaleKeys.workshop.tr()) - .toText(fontSize: 12, isBold: true), + ("${LocaleKeys.location.tr()} ").toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + (scheduleData.appointmentType == 2 ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true), ]), ], ), ), 10.height, - Divider(thickness: 0.7), + const Divider(thickness: 0.7), Builder(builder: (BuildContext context) { double totalServicePrice = 0.0; double totalKms = 15.3; @@ -219,35 +173,20 @@ class ReviewAppointment extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.amountVAR - .tr() - .toText(fontSize: 16, isBold: true), + LocaleKeys.amountVAR.tr().toText(fontSize: 16, isBold: true), Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.serviceCharges.tr().toText( - fontSize: 14, - color: MyColors.lightTextColor, - isBold: true), - "${scheduleData.amountTotal.toString()} SAR" - .toText(fontSize: 16, isBold: true), + LocaleKeys.serviceCharges.tr().toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true), + "${scheduleData.amountTotal.toString()} SAR".toText(fontSize: 16, isBold: true), ], ), if (scheduleData.appointmentType == 1) ...[ Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - (LocaleKeys.locationCharges.tr() + - " ($rangePricePerKm x $totalKms )") - .toText( - fontSize: 14, - color: MyColors.lightTextColor, - isBold: true), - ("${totalServicePrice.toString()} " + - LocaleKeys.sar.tr()) - .toText(fontSize: 16, isBold: true), + ("${LocaleKeys.locationCharges.tr()} ($rangePricePerKm x $totalKms )").toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true), + ("${totalServicePrice.toString()} ${LocaleKeys.sar.tr()}").toText(fontSize: 16, isBold: true), ], ), ], @@ -258,16 +197,12 @@ class ReviewAppointment extends StatelessWidget { ], ); }, - separatorBuilder: (BuildContext context, int index) => - Divider(thickness: 2), + separatorBuilder: (BuildContext context, int index) => Divider(thickness: 2), ).paddingOnly(bottom: 10), ], ), ], - ).toWhiteContainer( - width: double.infinity, - allPading: 12, - margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 0)); + ).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.fromLTRB(21 , 0, 21, 12)); }, ); } @@ -289,17 +224,9 @@ class ReviewAppointment extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - appointmentsVM.amountToPayForAppointment - .toString() - .toText(fontSize: 16, isBold: true), + appointmentsVM.amountToPayForAppointment.toString().toText(fontSize: 16, isBold: true), 2.width, - LocaleKeys.sar - .tr() - .toText( - color: MyColors.lightTextColor, - fontSize: 12, - isBold: true) - .paddingOnly(bottom: 2), + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2), ], ) ], @@ -307,24 +234,13 @@ class ReviewAppointment extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.remainingAmount - .tr() - .toText(fontSize: 14, isBold: true), + LocaleKeys.remainingAmount.tr().toText(fontSize: 14, isBold: true), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - (appointmentsVM.totalAmount - - appointmentsVM.amountToPayForAppointment) - .toString() - .toText(fontSize: 16, isBold: true), + (appointmentsVM.totalAmount - appointmentsVM.amountToPayForAppointment).toString().toText(fontSize: 16, isBold: true), 2.width, - LocaleKeys.sar - .tr() - .toText( - color: MyColors.lightTextColor, - fontSize: 12, - isBold: true) - .paddingOnly(bottom: 2), + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2), ], ) ], @@ -337,17 +253,9 @@ class ReviewAppointment extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - appointmentsVM.totalAmount - .toString() - .toText(fontSize: 29, isBold: true), + appointmentsVM.totalAmount.toString().toText(fontSize: 29, isBold: true), 2.width, - LocaleKeys.sar - .tr() - .toText( - color: MyColors.lightTextColor, - fontSize: 16, - isBold: true) - .paddingOnly(bottom: 5), + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), ], ) ], @@ -383,7 +291,9 @@ class ReviewAppointment extends StatelessWidget { children: [ ListView( children: [ + 10.height, buildBranchInfoCard(context: context), + 10.height, buildServicesInfoCard(context: context), ], ).expand(), diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index c9f6f10..1e4405b 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/config/routes.dart'; @@ -7,10 +8,14 @@ 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'; import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; +import 'package:mc_common_app/view_models/payment_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; +import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart'; import 'package:mc_common_app/views/chat/widgets/chat_message_widget.dart'; import 'package:mc_common_app/views/requests/request_bottomsheets.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/txt_field.dart'; @@ -90,7 +95,7 @@ class _ChatViewState extends State { if (chatTypeEnum == ChatTypeEnum.ads) { chatMessages = chatVM.currentMessagesForAds; } else if (chatTypeEnum == ChatTypeEnum.requestOffer) { - if (AppState().currentAppType == AppType.customer) { + if (AppState().currentAppType == AppType.customer && chatVM.serviceProviderOffersList.isNotEmpty) { chatMessages = chatVM.serviceProviderOffersList[chatViewArgumentsForRequest!.providerIndex].chatMessages ?? []; } else { chatMessages = requestVM.myFilteredRequests[chatViewArgumentsForRequest!.requestIndex].chatMessages; @@ -107,7 +112,7 @@ class _ChatViewState extends State { separatorBuilder: (BuildContext context, int index) => 20.height, itemBuilder: (BuildContext context, int index) { ChatMessageModel chatMessageModel = chatMessages[index]; - return ChatMessageCustomWidget(chatMessageModel: chatMessageModel); + return ChatMessageCustomWidget(chatMessageModel: chatMessageModel, requestStatusEnum: requestVM.currentSelectedRequest!.requestStatus); }, ).horPaddingMain(), ), @@ -115,16 +120,48 @@ class _ChatViewState extends State { Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (chatTypeEnum == ChatTypeEnum.requestOffer && requestVM.currentSelectedRequest!.requestStatus == RequestStatus.inProgress) ...[ - Center( - child: LocaleKeys.requestAlreadyInProgress.tr().toText( - textAlign: TextAlign.center, - color: MyColors.lightTextColor, - fontSize: 14, - ), - ).paddingAll(15) + if (chatTypeEnum == ChatTypeEnum.requestOffer && + requestVM.currentSelectedRequest!.requestType.toRequestTypeStatusEnum() == RequestsTypeEnum.serviceRequest && + requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress && + AppState().currentAppType == AppType.customer) ...[ + Expanded( + child: ShowFillButton( + maxWidth: double.infinity, + margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), + maxHeight: 55, + title: LocaleKeys.reviewAndPayment.tr(), + isBold: false, + onPressed: () { + context.read().updateRequestId(id: requestVM.currentSelectedRequest!.id); + navigateWithName(context, AppRoutes.reviewRequestOffer, arguments: PaymentTypes.request); + }, + ), + ), + // Center( + // child: LocaleKeys.requestAlreadyInProgress.tr().toText( + // textAlign: TextAlign.center, + // color: MyColors.lightTextColor, + // fontSize: 14, + // ), + // ).paddingAll(15) + ] else if (chatTypeEnum == ChatTypeEnum.requestOffer && + requestVM.currentSelectedRequest!.requestType.toRequestTypeStatusEnum() == RequestsTypeEnum.specialCarRequest && + requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress && + AppState().currentAppType == AppType.customer) ...[ + Expanded( + child: ShowFillButton( + maxWidth: double.infinity, + margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), + maxHeight: 55, + title: LocaleKeys.completeDeal.tr(), + isBold: false, + onPressed: () { + return dealCompletedConfirmationBottomSheet(mainContext: context, requestStatusEnum: RequestStatusEnum.completed, requestId: requestVM.currentSelectedRequest!.id); + }, + ), + ), ] else ...[ - if (AppState().currentAppType == AppType.provider && chatTypeEnum == ChatTypeEnum.requestOffer) ...[ + if (AppState().currentAppType == AppType.provider && chatTypeEnum == ChatTypeEnum.requestOffer && requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted) ...[ Expanded( flex: 1, child: const Icon( @@ -134,7 +171,7 @@ class _ChatViewState extends State { ).onPress( () { RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(requestIndex: chatViewArgumentsForRequest!.requestIndex, requestModel: chatViewArgumentsForRequest!.requestModel!); - buildSendOfferBottomSheet(context, requestDetailArguments); + buildSendOfferBottomSheet(context: context, requestDetailPageArguments: requestDetailArguments, isFromChatScreen: true); }, ), ), @@ -167,7 +204,7 @@ class _ChatViewState extends State { ), ], ], - ).toContainer(isShadowEnabled: true), + ).toContainer(isShadowEnabled: true) ], ); }), diff --git a/lib/views/chat/widgets/chat_bottom_sheets.dart b/lib/views/chat/widgets/chat_bottom_sheets.dart new file mode 100644 index 0000000..eb80295 --- /dev/null +++ b/lib/views/chat/widgets/chat_bottom_sheets.dart @@ -0,0 +1,70 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/config/routes.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/view_models/chat_view_model.dart'; +import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; +import 'package:mc_common_app/view_models/requests_view_model.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart'; +import 'package:provider/provider.dart'; + +void dealCompletedConfirmationBottomSheet({required BuildContext mainContext, required RequestStatusEnum requestStatusEnum, required int requestId}) { + final requestVM = mainContext.read(); + return actionConfirmationBottomSheet( + isOnlyOneButton: true, + context: mainContext, + title: LocaleKeys.doYouWantToCompleteThisDeal.tr().toText(fontSize: 26, isBold: true, letterSpacing: -1.44), + subtitle: AppState().currentAppType == AppType.provider ? LocaleKeys.providerCompletingDealMeansThat.tr() : LocaleKeys.customerCompletingDealMeansThat.tr(), + confirmationWidget: Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { + return Row( + children: [ + Expanded( + child: CircleCheckBoxWithTitle( + isChecked: chatVM.acknowledgePaymentToMowaterStatus, + title: LocaleKeys.acknowledgePaymentToMowater.tr(), + onSelected: () { + chatVM.updateAcknowledgePaymentToMowaterStatus(!chatVM.acknowledgePaymentToMowaterStatus); + }, + selectedColor: MyColors.darkPrimaryColor, + ), + ), + ], + ); + }), + actionButtonYes: Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { + return Expanded( + child: ShowFillButton( + maxHeight: 55, + isDisabled: !chatVM.acknowledgePaymentToMowaterStatus, + title: LocaleKeys.submit.tr(), + fontSize: 15, + onPressed: () async { + Navigator.pop(context); + bool status = await requestVM.onActionRequestTapped(context: mainContext, requestStatusEnum: requestStatusEnum, requestId: requestId); + if (status) { + chatVM.updateAcknowledgePaymentToMowaterStatus(false); + mainContext.read().onNavbarTapped(4); + navigateReplaceWithName(mainContext, AppRoutes.dashboard); + } + }, + ), + ); + }), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.no.tr(), + fontSize: 15, + onPressed: () => Navigator.pop(mainContext), + ), + ), + ); +} diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 05c8d4b..b693e7d 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -1,4 +1,3 @@ -import 'dart:developer'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; @@ -12,6 +11,7 @@ import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; +import 'package:mc_common_app/view_models/requests_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; @@ -22,15 +22,16 @@ import 'package:easy_localization/easy_localization.dart' as lcl; class ChatMessageCustomWidget extends StatefulWidget { final ChatMessageModel chatMessageModel; + final RequestStatusEnum requestStatusEnum; - const ChatMessageCustomWidget({super.key, required this.chatMessageModel}); + const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum}); @override State createState() => _ChatMessageCustomWidgetState(); } class _ChatMessageCustomWidgetState extends State { - Future buildRejectOfferBottomSheet({required ChatMessageModel chatMessageModel}) { + Future buildRejectOrCancelOfferBottomSheet({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) { return showModalBottomSheet( context: context, isScrollControlled: true, @@ -38,7 +39,7 @@ class _ChatMessageCustomWidgetState extends State { builder: (BuildContext context) { return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { return InfoBottomSheet( - title: LocaleKeys.makeAnOffer.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), description: Padding( padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), child: Column( @@ -51,8 +52,16 @@ class _ChatMessageCustomWidgetState extends State { ListView.separated( shrinkWrap: true, itemCount: chatVM.offerRejectModelList.length, - separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5), + separatorBuilder: (BuildContext context, int index) { + if (chatVM.offerRejectModelList[index].index == 3 && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) { + return const SizedBox(); + } + return const Divider(thickness: 0.5); + }, itemBuilder: (BuildContext context, int index) { + if (chatVM.offerRejectModelList[index].index == 3 && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) { + return const SizedBox(); + } OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index]; return CircleCheckBoxWithTitle( isChecked: offerRequestCommentModel.isSelected ?? false, @@ -93,26 +102,28 @@ class _ChatMessageCustomWidgetState extends State { return; } Navigator.pop(context); - bool status = await context.read().onSendMessageForActionOnRequestOffer( - receiverId: chatMessageModel.senderUserID ?? "", - chatMessageType: ChatMessageTypeEnum.offer, - comments: comments, - requestId: chatMessageModel.reqOffer!.requestID ?? -1, - serviceProviderID: chatMessageModel.serviceProviderID ?? 0, - requestOfferID: chatMessageModel.reqOffer!.id ?? -1, - offerPrice: chatMessageModel.reqOffer!.price.toString(), - serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "", - manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "", - manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0, - requestOfferStatusEnum: RequestOfferStatusEnum.rejected, - context: context, - ); + + bool status = await chatVM.onSendMessageForActionOnRequestOffer( + receiverId: (chatMessageModel.isMyMessage ?? false) ? chatMessageModel.receiverUserID ?? "" : chatMessageModel.senderUserID ?? "", + chatMessageType: ChatMessageTypeEnum.offer, + comments: comments, + requestId: chatMessageModel.reqOffer!.requestID ?? -1, + serviceProviderID: chatMessageModel.serviceProviderID ?? 0, + requestOfferID: chatMessageModel.reqOffer!.id ?? -1, + offerPrice: chatMessageModel.reqOffer!.price.toString(), + serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "", + manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "", + manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0, + requestOfferStatusEnum: requestOfferStatusEnum, + context: context, + ); if (status) { - chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected; + chatMessageModel.reqOffer!.requestOfferStatusEnum = requestOfferStatusEnum; setState(() {}); // Navigator.pop(context); - Utils.showToast("Offer Rejected"); + chatVM.updateRejectOfferDescription(''); + Utils.showToast("Offer ${requestOfferStatusEnum == RequestOfferStatusEnum.rejected ? "Rejected" : "Cancelled"}"); // navigateReplaceWithName(context, AppRoutes.dashboard); } }, @@ -155,11 +166,15 @@ class _ChatMessageCustomWidgetState extends State { ); if (status) { + final requestVM = context.read(); chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted; + requestVM.currentSelectedRequest!.requestStatus = RequestStatusEnum.inProgress; + requestVM.updateAcceptedReqOffer(chatMessageModel.reqOffer!); + requestVM.updateAcceptedRequestOfferProviderName(chatMessageModel.senderName ?? ""); + setState(() {}); // Navigator.pop(context); Utils.showToast("Offer Accepted"); - // navigateReplaceWithName(context, AppRoutes.dashboard); } }, @@ -181,9 +196,32 @@ class _ChatMessageCustomWidgetState extends State { ); } - Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) { + Widget buildOfferDetailsInChatMessage({required RequestStatusEnum requestStatusEnum, required ChatMessageModel chatMessageModel, required BuildContext context}) { final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer; + if (requestStatusEnum != RequestStatusEnum.submitted && requestOfferStatusEnum != RequestOfferStatusEnum.accepted) { + return Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor), + 5.width, + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), + ], + ), + 5.height, + Center( + child: LocaleKeys.offerNoLongerAvailable.tr().toText( + color: chatMessageModel.isMyMessage! ? MyColors.adPendingStatusColor : MyColors.lightTextColor, + fontSize: 12, + isItalic: true, + ), + ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: chatMessageModel.isMyMessage! ? MyColors.adPendingStatusColor.withOpacity(0.16) : MyColors.grey98Color.withOpacity(0.1)), + ], + ); + } + //offerNoLongerAvailable switch (requestOfferStatusEnum) { case RequestOfferStatusEnum.offer: return Column( @@ -225,13 +263,33 @@ class _ChatMessageCustomWidgetState extends State { backgroundColor: MyColors.white, txtColor: MyColors.redColor, fontSize: 9, - onPressed: () async { - buildRejectOfferBottomSheet(chatMessageModel: chatMessageModel); + onPressed: () { + buildRejectOrCancelOfferBottomSheet(chatMessageModel: chatMessageModel, requestOfferStatusEnum: RequestOfferStatusEnum.rejected); }, ), ) ], ), + ] else if (requestStatusEnum == RequestStatusEnum.submitted) ...[ + 10.height, + Row( + children: [ + Expanded( + child: ShowFillButton( + maxHeight: 27, + title: LocaleKeys.cancelOffer.tr(), + fontSize: 9, + borderColor: MyColors.lightTextColor, + isFilled: false, + onPressed: () { + buildRejectOrCancelOfferBottomSheet(chatMessageModel: chatMessageModel, requestOfferStatusEnum: RequestOfferStatusEnum.cancel); + }, + backgroundColor: MyColors.white, + txtColor: MyColors.lightTextColor, + ), + ), + ], + ), ], ], ); @@ -250,6 +308,15 @@ class _ChatMessageCustomWidgetState extends State { case RequestOfferStatusEnum.accepted: return Column( children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor), + 5.width, + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), + ], + ), + 5.height, Center( child: LocaleKeys.offerHasBeenAccepted.tr().toText( color: MyColors.adPendingStatusColor, @@ -262,6 +329,15 @@ class _ChatMessageCustomWidgetState extends State { case RequestOfferStatusEnum.rejected: return Column( children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor), + 5.width, + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), + ], + ), + 5.height, Center( child: LocaleKeys.offerHasBeenRejected.tr().toText( color: MyColors.adPendingStatusColor, @@ -274,6 +350,15 @@ class _ChatMessageCustomWidgetState extends State { case RequestOfferStatusEnum.cancel: return Column( children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor), + 5.width, + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), + ], + ), + 5.height, Center( child: LocaleKeys.offerHasBeenCancelled.tr().toText( color: MyColors.adPendingStatusColor, @@ -369,8 +454,9 @@ class _ChatMessageCustomWidgetState extends State { ), ], ), + 10.height, if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[ - buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context), + buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum, chatMessageModel: widget.chatMessageModel, context: context), ], ], ).toContainer( diff --git a/lib/views/common_fragments/my_requests_fragment.dart b/lib/views/common_fragments/my_requests_fragment.dart deleted file mode 100644 index 141aa2c..0000000 --- a/lib/views/common_fragments/my_requests_fragment.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:mc_common_app/classes/app_state.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/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 MyRequestsFragment extends StatelessWidget { - const MyRequestsFragment({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: CustomAppBar( - title: LocaleKeys.myRequests.tr(), - isRemoveBackButton: true, - - ), - body: Container( - color: MyColors.backgroundColor, - width: double.infinity, - height: double.infinity, - child: Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) { - return Column( - children: [ - 16.height, - FiltersList( - filterList: requestsVM.requestsTypeFilterOptions, - onFilterTapped: (index, selectedFilterId) { - requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: selectedFilterId.toRequestTypeStatusEnum()); - }, - ), - 8.height, - Expanded( - child: RefreshIndicator( - onRefresh: () async => await requestsVM.getRequests(isNeedToRebuild: true, appType: AppType.provider), - child: requestsVM.state == ViewState.busy - ? const Center(child: CircularProgressIndicator()) - : requestsVM.myFilteredRequests.isEmpty - ? Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - LocaleKeys.noRequeststoShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor), - ], - ) - : ListView.separated( - itemBuilder: (context, index) { - return RequestItem(request: requestsVM.myFilteredRequests[index], appType: AppState().currentAppType, requestIndex: index); - }, - separatorBuilder: (context, index) { - return 16.height; - }, - itemCount: requestsVM.myFilteredRequests.length, - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8), - ), - )) - ], - ); - }), - ), - floatingActionButton: AppState().currentAppType == AppType.customer - ? FloatingActionButton( - onPressed: () => navigateWithName(context, AppRoutes.createRequestPage), - backgroundColor: MyColors.darkPrimaryColor, - child: const Icon( - Icons.add, - color: MyColors.white, - ), - ) - : null, - ); - } -} diff --git a/lib/views/common_fragments/requests_fragment.dart b/lib/views/common_fragments/requests_fragment.dart new file mode 100644 index 0000000..5980ab9 --- /dev/null +++ b/lib/views/common_fragments/requests_fragment.dart @@ -0,0 +1,156 @@ +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/models/requests_models/request_model.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/button/show_fill_button.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:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; +import 'package:easy_localization/easy_localization.dart'; + +class MyRequestsFragment extends StatelessWidget { + const MyRequestsFragment({super.key}); + + void cancelRequestBottomSheet(BuildContext context, int requestId) { + return actionConfirmationBottomSheet( + context: context, + title: LocaleKeys.cancelRequestPrompt.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: LocaleKeys.requestPermanentlyCancelled.tr(), + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.yes.tr(), + fontSize: 15, + onPressed: () async { + Navigator.pop(context); + + final requestVM = context.read(); + + bool status = await requestVM.onActionRequestTapped(context: context, requestStatusEnum: RequestStatusEnum.cancelled, requestId: requestId); + if (status) { + await requestVM.getRequests(isNeedToRebuild: true, appType: AppType.provider); + } + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: LocaleKeys.no.tr(), + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } + + bool shouldDismiss(RequestStatusEnum requestStatusEnum) { + if (AppState().currentAppType == AppType.provider) { + return false; + } + + if (requestStatusEnum == RequestStatusEnum.pending || requestStatusEnum == RequestStatusEnum.submitted) { + return true; + } + return false; + ; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: LocaleKeys.myRequests.tr(), + isRemoveBackButton: true, + ), + body: Container( + color: MyColors.backgroundColor, + width: double.infinity, + height: double.infinity, + child: Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) { + return Column( + children: [ + 16.height, + FiltersList( + filterList: requestsVM.requestsTypeFilterOptions, + onFilterTapped: (index, selectedFilterId) { + requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: selectedFilterId.toRequestTypeStatusEnum()); + }, + ), + 8.height, + Expanded( + child: RefreshIndicator( + onRefresh: () async => await requestsVM.getRequests(isNeedToRebuild: true, appType: AppType.provider), + child: requestsVM.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) + : requestsVM.myFilteredRequests.isEmpty + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + LocaleKeys.noRequeststoShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor), + ], + ) + : ListView.separated( + itemBuilder: (context, index) { + return Dismissible( + key: Key(index.toString()), + background: const SizedBox(), + secondaryBackground: Container( + color: const Color(0xFFDD0505), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + LocaleKeys.cancel.tr().toText(fontSize: 15, fontWeight: MyFonts.Medium, height: 23 / 25, color: MyColors.white).margin(right: 23), + ], + ), + ), + dragStartBehavior: DragStartBehavior.start, + direction: shouldDismiss(requestsVM.myFilteredRequests[index].requestStatus) ? DismissDirection.endToStart : DismissDirection.none, + confirmDismiss: (DismissDirection direction) async { + cancelRequestBottomSheet(context, requestsVM.myFilteredRequests[index].id); + }, + child: RequestItem(request: requestsVM.myFilteredRequests[index], appType: AppState().currentAppType, requestIndex: index), + ); + }, + separatorBuilder: (context, index) { + return 16.height; + }, + itemCount: requestsVM.myFilteredRequests.length, + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8), + ), + )) + ], + ); + }), + ), + floatingActionButton: AppState().currentAppType == AppType.customer + ? FloatingActionButton( + onPressed: () => navigateWithName(context, AppRoutes.createRequestPage), + backgroundColor: MyColors.darkPrimaryColor, + child: const Icon( + Icons.add, + color: MyColors.white, + ), + ) + : null, + ); + } +} diff --git a/lib/views/location_views/pick_location_page.dart b/lib/views/location_views/pick_location_page.dart index 72105d3..a4d4176 100644 --- a/lib/views/location_views/pick_location_page.dart +++ b/lib/views/location_views/pick_location_page.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; @@ -16,9 +17,9 @@ import 'map_selection_widget.dart'; import 'package:geocoding/geocoding.dart'; class PickLocationPage extends StatefulWidget { - Function(double, double, String) onPickAddress; + final Function(double, double, String) onPickAddress; - PickLocationPage({required this.onPickAddress}); + const PickLocationPage({super.key, required this.onPickAddress}); @override State createState() => _PickLocationPageState(); @@ -28,14 +29,14 @@ class _PickLocationPageState extends State { double latitude = 0; double longitude = 0; - final Set markers = new Set(); + final Set markers = {}; late AppMap appMap; - static CameraPosition _kGooglePlex = CameraPosition( + static CameraPosition _kGooglePlex = const CameraPosition( target: LatLng(37.42796133580664, -122.085749655962), zoom: 14.4746, ); - late LatLng currentPostion; + late LatLng currentPosition; Completer mapController = Completer(); final ValueNotifier _counter = ValueNotifier(""); @@ -80,7 +81,7 @@ class _PickLocationPageState extends State { return value.isNotEmpty ? Container( width: double.infinity, - margin: EdgeInsets.all(12), + margin: const EdgeInsets.all(12), child: Card( child: Padding( padding: const EdgeInsets.all(12.0), @@ -102,11 +103,12 @@ class _PickLocationPageState extends State { ], ), ), - Container( + SizedBox( width: double.infinity, child: Padding( padding: const EdgeInsets.all(12.0), child: ShowFillButton( + maxHeight: 55, title: LocaleKeys.pickAddress.tr(), onPressed: () { widget.onPickAddress(latitude, longitude, _counter.value); @@ -120,18 +122,18 @@ class _PickLocationPageState extends State { ); } - Future _updatePosition(CameraPosition _position) async { - latitude = _position.target.latitude; - longitude = _position.target.longitude; - print(latitude); - print(latitude); + Future _updatePosition(CameraPosition position) async { + latitude = position.target.latitude; + longitude = position.target.longitude; + log(latitude.toString()); + log(latitude.toString()); updateAddress(latitude, longitude); } void _getUserLocation() async { if (await Permission.location.request().isGranted) { var position = await GeolocatorPlatform.instance.getCurrentPosition(); - currentPostion = LatLng(position.latitude, position.longitude); + currentPosition = LatLng(position.latitude, position.longitude); latitude = position.latitude; longitude = position.longitude; setMap(); @@ -141,7 +143,7 @@ class _PickLocationPageState extends State { (value) async { if (value[Permission.location]!.isGranted) { var position = await GeolocatorPlatform.instance.getCurrentPosition(); - currentPostion = LatLng(position.latitude, position.longitude); + currentPosition = LatLng(position.latitude, position.longitude); latitude = position.latitude; longitude = position.longitude; setMap(); @@ -160,7 +162,7 @@ class _PickLocationPageState extends State { setMap() { setState(() { _kGooglePlex = CameraPosition( - target: currentPostion, + target: currentPosition, zoom: 14.4746, ); appMap.moveTo(cameraPostion: _kGooglePlex); diff --git a/lib/views/payments/payment_methods_view.dart b/lib/views/payments/payment_methods_view.dart index 7b7c1f5..d7b72db 100644 --- a/lib/views/payments/payment_methods_view.dart +++ b/lib/views/payments/payment_methods_view.dart @@ -99,7 +99,7 @@ class PaymentMethodContainer extends StatelessWidget { return InkWell( onTap: onTap, child: Container( - margin: EdgeInsets.symmetric(vertical: 10), + margin: const EdgeInsets.symmetric(vertical: 10), width: double.infinity, height: 80, decoration: BoxDecoration( @@ -120,11 +120,11 @@ class PaymentMethodContainer extends StatelessWidget { Container( height: 80.0, width: 80.0, - padding: EdgeInsets.all(7.0), + padding: const EdgeInsets.all(7.0), child: Image.asset(cardAsset), ), Utils.mFlex(1), - isSelected ? Utils.statusContainerChip(text: "Selected", padding: EdgeInsets.symmetric(vertical: 6, horizontal: 10)) : SizedBox(), + isSelected ? Utils.statusContainerChip(text: "Selected", padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10)) : const SizedBox(), ], ), ), diff --git a/lib/views/requests/create_request_page.dart b/lib/views/requests/create_request_page.dart index 3b08da8..fe289d6 100644 --- a/lib/views/requests/create_request_page.dart +++ b/lib/views/requests/create_request_page.dart @@ -1,4 +1,6 @@ import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/utils/enums.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/classes/consts.dart'; @@ -8,6 +10,7 @@ import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.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'; +import 'package:mc_common_app/views/location_views/pick_location_page.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; @@ -173,11 +176,40 @@ class CreateRequestPage extends StatelessWidget { 8.height, TxtField( hint: LocaleKeys.description.tr(), - maxLines: 5, + 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, + ], DottedRectContainer( onTap: () => context.read().pickMultipleImages(), text: LocaleKeys.attachImage.tr(), @@ -202,19 +234,6 @@ class CreateRequestPage extends StatelessWidget { }, ), ], - 16.height, - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Checkbox( - value: requestsVM.isShippingDeliveryEnabled, - onChanged: null, - ), - LocaleKeys.shippingDelivery.tr().toText(color: MyColors.darkPrimaryColor, isBold: true, fontSize: 18) - ], - ).onPress(() { - requestsVM.updateShippingDeliverEnabled(!requestsVM.isShippingDeliveryEnabled); - }), ], ), ] @@ -225,6 +244,7 @@ class CreateRequestPage extends StatelessWidget { 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/request_bottomsheets.dart b/lib/views/requests/request_bottomsheets.dart index 9480fce..8837df5 100644 --- a/lib/views/requests/request_bottomsheets.dart +++ b/lib/views/requests/request_bottomsheets.dart @@ -3,17 +3,22 @@ import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/chat_view_model.dart'; +import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; -Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArguments requestDetailPageArguments) { +Future buildSendOfferBottomSheet({required BuildContext context, required RequestDetailPageArguments requestDetailPageArguments, required bool isFromChatScreen}) { final requestDetail = requestDetailPageArguments.requestModel; return showModalBottomSheet( context: context, @@ -54,13 +59,13 @@ Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArgument value: requestsVM.itemManufacturer, errorValue: requestsVM.offerPriceError, keyboardType: TextInputType.number, - hint: "Manufacturer", + hint: LocaleKeys.manufacturer.tr(), onChanged: (v) => requestsVM.updateItemManufacturer(v), ), 12.height, TxtField( errorValue: "", - hint: "Manufactured On", + hint: LocaleKeys.manufacturedOn.tr(), value: requestsVM.serviceItemCreatedOn, isNeedClickAll: true, postfixData: Icons.calendar_month_rounded, @@ -95,7 +100,7 @@ Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArgument offerPrice: requestsVM.offerPrice, requestModel: requestDetail, requestIndex: requestDetailPageArguments.requestIndex, - isFromChatScreen: false, + isFromChatScreen: isFromChatScreen, manufacturedById: 1, // Todo: It should be the ID of the manufacturer manufacturedOn: requestsVM.serviceItemCreatedOn, @@ -111,3 +116,69 @@ Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArgument }, ); } + +Future buildRequestStatusUpdateSheetForProvider({required BuildContext mainContext, required int requestId}) { + return showModalBottomSheet( + context: mainContext, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return Consumer(builder: (BuildContext context, RequestsVM requestVM, Widget? child) { + return InfoBottomSheet( + title: LocaleKeys.selectDeliveryType.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + description: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 12.height, + ListView.separated( + shrinkWrap: true, + itemCount: requestVM.providerDeliveryActionsList.length, + separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5), + itemBuilder: (BuildContext context, int index) { + OfferRequestCommentModel offerRequestCommentModel = requestVM.providerDeliveryActionsList[index]; + return CircleCheckBoxWithTitle( + isChecked: offerRequestCommentModel.isSelected ?? false, + title: '${offerRequestCommentModel.title}', + onSelected: () { + requestVM.updateSelectedProviderDeliveryAction(index); + }, + selectedColor: MyColors.darkPrimaryColor, + ); + }, + ), + ], + ), + 25.height, + ShowFillButton( + title: LocaleKeys.submit.tr(), + maxHeight: 55, + onPressed: () async { + // index 0-> shipping + // index 1-> delivery + pop(context); + bool status = false; + if (requestVM.selectedProviderDeliveryAction.index == 0) { + status = await requestVM.onActionRequestTapped(context: mainContext, requestStatusEnum: RequestStatusEnum.shipping, requestId: requestId); + } else if (requestVM.selectedProviderDeliveryAction.index == 1) { + status = await requestVM.onActionRequestTapped(context: mainContext, requestStatusEnum: RequestStatusEnum.delivery, requestId: requestId); + } + if (status) { + await requestVM.getRequests(isNeedToRebuild: true, appType: AppType.provider); + pop(mainContext); + } + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + )); + }); + }, + ); +} diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index 02c0a97..6c03702 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -9,7 +9,9 @@ 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'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -22,43 +24,78 @@ class RequestDetailPage extends StatelessWidget { const RequestDetailPage({Key? key, required this.requestDetailPageArguments}) : super(key: key); - Widget buildBottomButton({ - required RequestStatus requestStatus, - required String statusText, - required BuildContext context, - required Function() onViewChatTapped, - }) { + Widget buildRequestDetailActionFooter({required int requestId, required RequestStatusEnum requestStatus, required RequestsTypeEnum requestTypeEnum, required String statusText, required BuildContext context}) { + final requestsVM = context.read(); switch (requestStatus) { - case RequestStatus.submitted: - case RequestStatus.inProgress: - case RequestStatus.paid: - case RequestStatus.shipping: + case RequestStatusEnum.submitted: return ShowFillButton( maxWidth: double.infinity, margin: const EdgeInsets.all(15), maxHeight: 55, title: LocaleKeys.viewChat.tr(), isBold: false, - onPressed: onViewChatTapped, + onPressed: () => onViewChatTapped(context), ); - case RequestStatus.completed: - case RequestStatus.cancelled: - case RequestStatus.expired: - case RequestStatus.pending: - return buildDisabledButton(statusText); + + case RequestStatusEnum.inProgress: + if (requestTypeEnum == RequestsTypeEnum.serviceRequest) { + return buildStatusContainer(LocaleKeys.awaitingPaymentFromCustomer.tr()); + } else { + return buildStatusContainer(LocaleKeys.awaitingResponseFromCustomer.tr()); + } + + case RequestStatusEnum.paid: + if (AppState().currentAppType == AppType.provider) { + return ShowFillButton( + maxWidth: double.infinity, + margin: const EdgeInsets.all(15), + maxHeight: 55, + title: LocaleKeys.selectDeliveryType.tr(), + isBold: false, + onPressed: () { + buildRequestStatusUpdateSheetForProvider(mainContext: context, requestId: requestId); + }, + ); + } else { + return buildStatusContainer(LocaleKeys.awaitingResponseFromProvider.tr()); + } + + case RequestStatusEnum.shipping: + case RequestStatusEnum.delivery: + if (AppState().currentAppType == AppType.provider) { + return buildStatusContainer(LocaleKeys.shippingManagementInstruction.tr(), isForShippingOrDelivery: true); + } else { + return ShowFillButton( + maxWidth: double.infinity, + margin: const EdgeInsets.all(15), + maxHeight: 55, + title: LocaleKeys.completeDeal.tr(), + onPressed: () { + return dealCompletedConfirmationBottomSheet(mainContext: context, requestStatusEnum: RequestStatusEnum.completed, requestId: requestId); + }, + ); + } + case RequestStatusEnum.completed: + case RequestStatusEnum.cancelled: + case RequestStatusEnum.expired: + case RequestStatusEnum.pending: + return buildStatusContainer(statusText); } } - Widget buildDisabledButton(String text) { - return ShowFillButton( - backgroundColor: MyColors.grey98Color.withOpacity(0.3), - txtColor: MyColors.lightTextColor, - maxWidth: double.infinity, - margin: const EdgeInsets.all(15), - maxHeight: 55, - title: text, - isBold: false, - onPressed: () {}, + Widget buildStatusContainer(String text, {bool isForShippingOrDelivery = true}) { + return Center( + child: text.toText( + color: isForShippingOrDelivery ? MyColors.lightTextColor : MyColors.adPendingStatusColor, + fontSize: isForShippingOrDelivery ? 14 : 16, + // isItalic: true, + ), + ).toContainer( + marginAll: 12, + paddingAll: 12, + borderRadius: 8, + width: double.infinity, + backgroundColor: isForShippingOrDelivery ? MyColors.grey98Color.withOpacity(0.1) : MyColors.adPendingStatusColor.withOpacity(0.16), ); } @@ -94,6 +131,18 @@ class RequestDetailPage extends StatelessWidget { return Scaffold( appBar: CustomAppBar( title: LocaleKeys.requestDetail.tr(), + actions: [ + (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( + borderRadius: 100, + borderColor: MyColors.lightGreyEFColor, + isEnabledBorder: true, + ), + ).onPress(() => onViewChatTapped(context)) + : const SizedBox(), + ], ), body: SizedBox( width: double.infinity, @@ -120,14 +169,15 @@ class RequestDetailPage extends StatelessWidget { title: LocaleKeys.sendOffer.tr(), isBold: false, fontSize: 18, - onPressed: () => buildSendOfferBottomSheet(context, requestDetailPageArguments), + onPressed: () => buildSendOfferBottomSheet(context: context, requestDetailPageArguments: requestDetailPageArguments, isFromChatScreen: false), ), ] else ...[ - buildBottomButton( + buildRequestDetailActionFooter( + requestId: requestDetailPageArguments.requestModel.id, requestStatus: requestDetailPageArguments.requestModel.requestStatus, statusText: "Offer ${requestDetailPageArguments.requestModel.requestStatusName}", context: context, - onViewChatTapped: () async => onViewChatTapped(context), + requestTypeEnum: requestDetailPageArguments.requestModel.requestType.toRequestTypeStatusEnum(), ), ], ], @@ -151,8 +201,8 @@ class RequestDetailPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ requestDetail.vehicleTypeName.toText(fontSize: 16, letterSpacing: -0.64), - showItem("Manufacturer:", requestDetail.brand), - showItem("Model:", "${requestDetail.year}"), + showItem("${LocaleKeys.manufacturer.tr()}: ", requestDetail.brand), + showItem("${LocaleKeys.model.tr()}: ", "${requestDetail.year}"), ], ), ), @@ -172,10 +222,10 @@ class RequestDetailPage extends StatelessWidget { ), ], ), - showItem("Customer Name: ", requestDetail.customerName), - showItem("Description: ", requestDetail.description), + showItem("${LocaleKeys.customerName.tr()}: ", requestDetail.customerName), + showItem("${LocaleKeys.description.tr()}: ", requestDetail.description), 16.height, - showItem("Price Range:", ""), + showItem("${LocaleKeys.priceRange.tr()}:", ""), Row( children: [ Expanded( @@ -195,7 +245,7 @@ class RequestDetailPage extends StatelessWidget { children: [ Utils.statusContainerChip( text: (requestDetail.requestStatusName), - chipColor: MyColors.grey98Color.withOpacity(0.3), + chipColor: MyColors.grey98Color.withOpacity(0.1), textColor: MyColors.lightTextColor, ), ], diff --git a/lib/views/requests/review_request_offer.dart b/lib/views/requests/review_request_offer.dart new file mode 100644 index 0000000..28d4f7d --- /dev/null +++ b/lib/views/requests/review_request_offer.dart @@ -0,0 +1,197 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/routes.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/user_models/user.dart'; +import 'package:mc_common_app/utils/date_helper.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/view_models/requests_view_model.dart'; +import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_review_containers.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; + +class ReviewRequestOffer extends StatelessWidget { + const ReviewRequestOffer({super.key}); + + Widget buildPersonalInformation() { + UserInfo userInfo = AppState().getUser.data!.userInfo!; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.personalInformation.tr().toText(fontSize: 18), + MyAssets.icEdit.buildSvg().onPress(() {}), + ], + ), + 8.height, + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + flex: 5, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleDetailWidget(text: "${userInfo.firstName ?? ""} ${userInfo.lastName ?? ""}", type: LocaleKeys.name.tr()), + 16.height, + ], + ), + ), + Expanded( + flex: 5, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleDetailWidget(text: userInfo.email ?? "", type: "${LocaleKeys.email.tr()} / ${LocaleKeys.phone.tr()}"), + 16.height, + ], + ), + ), + ], + ), + ], + ); + } + + Widget buildLocationInformation(BuildContext context) { + // final requestVM = context.read(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.locationInformation.tr().toText(fontSize: 18), + MyAssets.icEdit.buildSvg().onPress(() {}), + ], + ), + 8.height, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleDetailWidget(text: "${"Will get from API"} }", type: LocaleKeys.location.tr()), + 16.height, + SingleDetailWidget(text: "N/A", type: "${LocaleKeys.email.tr()} / ${LocaleKeys.additionalAddressDetails.tr()}"), + ], + ), + ], + ); + } + + Widget buildServiceInformation(BuildContext context) { + final requestVM = context.read(); + + String formattedDate = ""; + if (requestVM.acceptedRequestOffer!.manufacturedOn != null) { + formattedDate = DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(requestVM.acceptedRequestOffer!.manufacturedOn.toString() ?? ""))); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.serviceInformation.tr().toText(fontSize: 18), + MyAssets.icEdit.buildSvg().onPress(() {}), + ], + ), + 8.height, + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 6, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleDetailWidget(text: requestVM.currentSelectedRequest!.vehicleTypeName, type: LocaleKeys.vehicleType.tr()), + 16.height, + SingleDetailWidget(text: '${requestVM.currentSelectedRequest!.model} ${requestVM.currentSelectedRequest!.year}', type: LocaleKeys.model.tr()), + if (requestVM.acceptedRequestOffer!.manufacturedById != null) ...[ + 16.height, + SingleDetailWidget(text: (requestVM.acceptedRequestOffer!.manufacturedById ?? "").toString(), type: LocaleKeys.manufacturedBy.tr()), + ], + 16.height, + SingleDetailWidget(text: "${requestVM.acceptedRequestOffer!.price.toString()} SAR", type: LocaleKeys.offerPrice.tr()), + 16.height, + SingleDetailWidget(text: requestVM.acceptedRequestOfferProviderName ?? "", type: LocaleKeys.providerName.tr()), + 16.height, + ], + ), + ), + Expanded( + flex: 5, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SingleDetailWidget(text: requestVM.currentSelectedRequest!.brand, type: LocaleKeys.vehicleBrand.tr()), + 16.height, + SingleDetailWidget(text: requestVM.acceptedRequestOffer!.serviceItemName ?? "", type: LocaleKeys.serviceName.tr()), + if (formattedDate.isNotEmpty) ...[ + 16.height, + SingleDetailWidget(text: formattedDate, type: LocaleKeys.manufacturedOn.tr()), + ], + 16.height, + SingleDetailWidget(text: "${requestVM.currentSelectedRequest!.price.toString()} SAR", type: LocaleKeys.totalPrice.tr()), + ], + ), + ), + ], + ), + SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: LocaleKeys.offerSummary.tr(), + isRemoveBackButton: false, + isDrawerEnabled: false, + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleChildScrollView( + child: Column( + children: [ + 10.height, + buildPersonalInformation().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)), + 10.height, + buildLocationInformation(context).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)), + 10.height, + buildServiceInformation(context).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)), + ], + ), + ).expand(), + ShowFillButton( + maxWidth: double.infinity, + margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 21), + maxHeight: 55, + title: LocaleKeys.payNow.tr(), + isBold: false, + onPressed: () { + // context.read().updateRequestId(id: requestVM.currentSelectedRequest!.id); + navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.request); + }, + ), + ], + ), + ); + } +} diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index 244aa26..d2ccdce 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -53,7 +53,7 @@ class RequestItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ - if (request.offerCount > 0 && appType == AppType.customer) ...[ + if (request.offerCount > 0 && appType == AppType.customer && request.requestStatus == RequestStatusEnum.submitted) ...[ Center( child: "${request.offerCount}".toText( color: Colors.white, @@ -77,7 +77,7 @@ class RequestItem extends StatelessWidget { ], ), showItem("${LocaleKeys.description.tr()}:", request.description), - showItem("Price Range: ", ""), + showItem("${LocaleKeys.priceRange.tr()}: ", ""), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, @@ -85,23 +85,17 @@ class RequestItem extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - request.price.toInt().toString().toText( - fontSize: 19, - color: MyColors.darkTextColor, - isBold: true, - letterSpacing: -1.16 - - ), + request.price.toInt().toString().toText(fontSize: 19, color: MyColors.darkTextColor, isBold: true, letterSpacing: -1.16), 2.width, LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, letterSpacing: -0.4).paddingOnly(bottom: 3), ], ), - request.requestStatus == RequestStatus.submitted - ? SvgPicture.asset( - MyAssets.arrowRight, - height: 9.69, - width: 13, - ) + request.requestStatus == RequestStatusEnum.submitted + ? SvgPicture.asset( + MyAssets.arrowRight, + height: 9.69, + width: 13, + ) : const SizedBox() ], ), @@ -109,7 +103,7 @@ class RequestItem extends StatelessWidget { ).toContainer(isShadowEnabled: true).onPress(() async { RequestsVM requestsVM = context.read(); requestsVM.updateCurrentSelectedRequest(request); - if (request.requestStatus == RequestStatus.pending) { + if (request.requestStatus == RequestStatusEnum.pending || request.requestStatus == RequestStatusEnum.cancelled) { return; } if (appType == AppType.provider) { diff --git a/lib/views/requests/widget/request_status_chip.dart b/lib/views/requests/widget/request_status_chip.dart index 3c0b462..2bc5e86 100644 --- a/lib/views/requests/widget/request_status_chip.dart +++ b/lib/views/requests/widget/request_status_chip.dart @@ -19,9 +19,9 @@ class RequestStatusChip extends StatelessWidget { ) .toContainer( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - backgroundColor: request.requestStatus == RequestStatus.pending + backgroundColor: request.requestStatus == RequestStatusEnum.pending ? MyColors.adPendingStatusColor - : request.requestStatus == RequestStatus.cancelled + : request.requestStatus == RequestStatusEnum.cancelled ? MyColors.redColor : MyColors.greenColor, borderRadius: 100, diff --git a/lib/widgets/checkbox_with_title_desc.dart b/lib/widgets/checkbox_with_title_desc.dart index b874944..4cf774d 100644 --- a/lib/widgets/checkbox_with_title_desc.dart +++ b/lib/widgets/checkbox_with_title_desc.dart @@ -75,12 +75,12 @@ class CircleCheckBoxWithTitle extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - title.toText(fontSize: 14,), + title.toText(fontSize: 14), ], ), ), ], - ).onPress((){ + ).onPress(() { onSelected(); }), );