From 892f52ced821fdfb2ba282c9d1f7e07f96d03c33 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Mon, 25 Nov 2024 17:40:45 +0300 Subject: [PATCH] Fixing JIRA Issues --- assets/langs/ar-SA.json | 12 +- assets/langs/en-US.json | 15 +- lib/classes/consts.dart | 44 ++-- lib/extensions/string_extensions.dart | 5 + lib/generated/codegen_loader.g.dart | 26 ++- lib/generated/locale_keys.g.dart | 10 + lib/main.dart | 1 + lib/models/services_models/service_model.dart | 3 + lib/models/user_models/user.dart | 12 +- lib/repositories/ads_repo.dart | 42 ++-- lib/repositories/common_repo.dart | 58 +++--- lib/repositories/user_repo.dart | 38 ++-- lib/services/common_services.dart | 2 +- lib/utils/date_helper.dart | 42 ++++ lib/utils/enums.dart | 1 + lib/utils/utils.dart | 31 ++- lib/view_models/ad_view_model.dart | 6 +- lib/view_models/appointments_view_model.dart | 191 ++++++++++++++---- lib/view_models/base_view_model.dart | 3 +- .../ads_detail_view/components.dart | 1 + .../book_appointment_services_view.dart | 30 ++- .../book_appointments_item_view.dart | 4 +- .../appointments/review_appointment_view.dart | 23 ++- ...appointment_service_pick_bottom_sheet.dart | 79 ++++++-- .../common_fragments/requests_fragment.dart | 7 +- lib/views/requests/request_detail_page.dart | 35 +++- lib/views/requests/widget/request_item.dart | 21 +- 27 files changed, 554 insertions(+), 188 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index aeac85a..e5b4816 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -722,5 +722,15 @@ "noImagesToShow": "لا توجد صور للعرض", "updateGroupServices": "تحديث خدمات المجموعة", "addCommentsHere": "أضف التعليقات هنا", - "waitForBranchVerification": "يرجى الانتظار حتى يتم التحقق من الفرع." + "waitForBranchVerification": "يرجى الانتظار حتى يتم التحقق من الفرع.", + "reportComplaint": "تقديم شكوى", + "descriptionCannotBeEmpty": "وصف لا يمكن أن يكون فارغًا.", + "adMarkedAsSold": "تم وسم الإعلان كـ 'مباع' بنجاح!", + "chargesAndServiceRangeGreaterThanZero": "يجب أن تكون الرسوم ونطاق الخدمة أكبر من الصفر.", + "cannotSelectAllDaysAsOff": "لا يمكنك اختيار جميع الأيام كأيام عطلة.", + "endDateAfterStartDate": "يجب أن تكون تاريخ النهاية بعد تاريخ البدء.", + "pleaseEnterStartDateFirst": "من فضلك أدخل تاريخ البدء أولاً.", + "endTimeAfterStartTime": "يجب أن يكون وقت الانتهاء بعد وقت البدء.", + "pickedLocationNotice": "سيتم استخدام هذا الموقع لجميع الخدمات المنزلية في هذه الموعد", + "serviceNotProvided": "لسوء الحظ، هذه الخدمة غير متوفرة في الفرع الذي اخترته في هذا الموقع المحدد" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index bccf819..eae787f 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -646,7 +646,7 @@ "customerCarePrompt": "Got any question? We are here to service. Click here to talk to our customer care right now.", "callNow": "Call Now", "condition": "Condition", - "servicesNotAvailableHomeLocation": "Some services are not available on home location.", + "servicesNotAvailableHomeLocation": "This location will be same for all services", "additionalChargesNotice": "These charges are additional to the actual service charges. For heavy items the charges may vary.", "selectServiceLocation": "Select Service Location", "apptOn": "Appt On", @@ -720,6 +720,15 @@ "noImagesToShow": "No Images to Show", "updateGroupServices": "Update Group Services", "addCommentsHere": "Add comments here", - "waitForBranchVerification": "Please wait for the branch verification." - + "waitForBranchVerification": "Please wait for the branch verification.", + "reportComplaint": "Report a complaint", + "descriptionCannotBeEmpty": "Description cannot be empty.", + "adMarkedAsSold": "Ad has been marked as sold successfully!", + "chargesAndServiceRangeGreaterThanZero": "Charges and Service range should be greater than zero.", + "cannotSelectAllDaysAsOff": "You cannot select all days as Off days.", + "endDateAfterStartDate": "End Date should be after start date.", + "pleaseEnterStartDateFirst": "Please enter start date first.", + "endTimeAfterStartTime": "End Time should be after start time.", + "pickedLocationNotice": "This picked location will be used for all home-based services in this appointment", + "serviceNotProvided": "Unfortunately, this service is not provided by your selected branch in this picked location." } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 33757fd..2ae3e1d 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -17,28 +17,29 @@ class ApiConsts { static String signalRUrl = "${baseUrl}McHub"; //User - static String Login_V1 = "${baseUrlServices}api/Account/Login_V1"; - static String Login_V2_OTP = "${baseUrlServices}api/Account/Login_V2_OTP"; - static String Login_V2_OTPVerify = "${baseUrlServices}api/Account/Login_V2_OTPVerify"; + static String loginV1 = "${baseUrlServices}api/Account/Login_V1"; + static String loginV2OTP = "${baseUrlServices}api/Account/Login_V2_OTP"; + static String loginV2OTPVerify = "${baseUrlServices}api/Account/Login_V2_OTPVerify"; static String user = "${baseUrlServices}api/User/"; - static String GetAllCountry = "${baseUrlServices}api/Master/Country_Get"; - static String GetProviderRoles = "${baseUrlServices}api/Master/RoleServiceProvider_Get"; - static String GetAllCities = "${baseUrlServices}api/Master/City_Get"; - static String ForgetPasswordOTPRequest = "${baseUrlServices}api/Account/ForgetPasswordOTPRequest"; - static String ForgetPasswordOTPCompare = "${baseUrlServices}api/Account/ForgetPasswordOTPCompare"; - static String ForgetPassword = "${baseUrlServices}api/Account/ForgetPassword"; - static String Login_Email_OTP = "${baseUrlServices}api/Account/EmailVerify"; - static String Login_Email_OTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify"; - static String ChangePassword = "${baseUrlServices}api/Account/ChangePassword"; - static String ChangeMobileNoOTPRequest = "${baseUrlServices}api/Account/ChangeMobileNoOTPRequest"; - static String ChangeMobileNo = "${baseUrlServices}api/Account/ChangeMobileNo"; - static String ChangeEmailOTPRequest = "${baseUrlServices}api/Account/ChangeEmailOTPRequest"; - static String ChangeEmail = "${baseUrlServices}api/Account/ChangeEmail"; - static String EmailVerify = "${baseUrlServices}api/Account/EmailVerify"; - static String EmailVerifyOTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify"; - static String LogoutUser = "${baseUrlServices}api/Account/Logout"; - static String UpdateUserImage = "${baseUrlServices}api/User_UpdateProfileImage"; - static String GetUserImage = "${baseUrlServices}api/ProfileImage"; + static String getAllCountry = "${baseUrlServices}api/Master/Country_Get"; + static String getProviderRoles = "${baseUrlServices}api/Master/RoleServiceProvider_Get"; + static String getAllCities = "${baseUrlServices}api/Master/City_Get"; + static String forgetPasswordOTPRequest = "${baseUrlServices}api/Account/ForgetPasswordOTPRequest"; + static String forgetPasswordOTPCompare = "${baseUrlServices}api/Account/ForgetPasswordOTPCompare"; + static String forgetPassword = "${baseUrlServices}api/Account/ForgetPassword"; + static String loginEmailOTP = "${baseUrlServices}api/Account/EmailVerify"; + static String loginEmailOTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify"; + static String changePassword = "${baseUrlServices}api/Account/ChangePassword"; + static String changeMobileNoOTPRequest = "${baseUrlServices}api/Account/ChangeMobileNoOTPRequest"; + static String changeMobileNo = "${baseUrlServices}api/Account/ChangeMobileNo"; + static String changeEmailOTPRequest = "${baseUrlServices}api/Account/ChangeEmailOTPRequest"; + static String changeEmail = "${baseUrlServices}api/Account/ChangeEmail"; + static String emailVerify = "${baseUrlServices}api/Account/EmailVerify"; + static String emailVerifyOTPVerify = "${baseUrlServices}api/Account/EmailVerifyOTPVerify"; + static String logoutUser = "${baseUrlServices}api/Account/Logout"; + static String updateUserImage = "${baseUrlServices}api/User_UpdateProfileImage"; + static String getUserImage = "${baseUrlServices}api/ProfileImage"; + static String providerComplaintCreate = "${baseUrlServices}api/ServiceProviders/ProviderComplaint_Create"; //Profile static String fetProviderDocument = "${baseUrlServices}api/ServiceProviders/ServiceProviderDocument_Get"; @@ -151,7 +152,6 @@ class ApiConsts { //Branch Users static String getAllProviderDealers = "${baseUrlServices}api/ServiceProviders/DealershipUserBranchWise_Get"; - static String getBranchUser = "${baseUrlServices}api/ServiceProviders/BranchUser_Get"; static String assignDealerToBranch = "${baseUrlServices}api/ServiceProviders/BranchUser_Create"; static String removeDealerFromBranch = "${baseUrlServices}api/ServiceProviders/BranchUser_Update"; diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index c9f94d8..a95bb50 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -240,6 +240,8 @@ extension AdPostEnum on int { return AdPostStatus.reserveCancel; } else if (this == 12) { return AdPostStatus.deActive; + } else if (this == 13) { + return AdPostStatus.pendingForActive; } else if (this == 0) { return AdPostStatus.allAds; } else { @@ -488,6 +490,9 @@ extension AdPostStatusToInt on AdPostStatus { case AdPostStatus.deActive: return 12; + + case AdPostStatus.pendingForActive: + return 13; default: return 0; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index fa1d932..1abcd67 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -738,7 +738,17 @@ class CodegenLoader extends AssetLoader{ "noImagesToShow": "لا توجد صور للعرض", "updateGroupServices": "تحديث خدمات المجموعة", "addCommentsHere": "أضف التعليقات هنا", - "waitForBranchVerification": "يرجى الانتظار حتى يتم التحقق من الفرع." + "waitForBranchVerification": "يرجى الانتظار حتى يتم التحقق من الفرع.", + "reportComplaint": "تقديم شكوى", + "descriptionCannotBeEmpty": "وصف لا يمكن أن يكون فارغًا.", + "adMarkedAsSold": "تم وسم الإعلان كـ 'مباع' بنجاح!", + "chargesAndServiceRangeGreaterThanZero": "يجب أن تكون الرسوم ونطاق الخدمة أكبر من الصفر.", + "cannotSelectAllDaysAsOff": "لا يمكنك اختيار جميع الأيام كأيام عطلة.", + "endDateAfterStartDate": "يجب أن تكون تاريخ النهاية بعد تاريخ البدء.", + "pleaseEnterStartDateFirst": "من فضلك أدخل تاريخ البدء أولاً.", + "endTimeAfterStartTime": "يجب أن يكون وقت الانتهاء بعد وقت البدء.", + "pickedLocationNotice": "سيتم استخدام هذا الموقع لجميع الخدمات المنزلية في هذه الموعد", + "serviceNotProvided": "لسوء الحظ، هذه الخدمة غير متوفرة في الفرع الذي اخترته في هذا الموقع المحدد" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1388,7 +1398,7 @@ static const Map en_US = { "customerCarePrompt": "Got any question? We are here to service. Click here to talk to our customer care right now.", "callNow": "Call Now", "condition": "Condition", - "servicesNotAvailableHomeLocation": "Some services are not available on home location.", + "servicesNotAvailableHomeLocation": "This location will be same for all services", "additionalChargesNotice": "These charges are additional to the actual service charges. For heavy items the charges may vary.", "selectServiceLocation": "Select Service Location", "apptOn": "Appt On", @@ -1462,7 +1472,17 @@ static const Map en_US = { "noImagesToShow": "No Images to Show", "updateGroupServices": "Update Group Services", "addCommentsHere": "Add comments here", - "waitForBranchVerification": "Please wait for the branch verification." + "waitForBranchVerification": "Please wait for the branch verification.", + "reportComplaint": "Report a complaint", + "descriptionCannotBeEmpty": "Description cannot be empty.", + "adMarkedAsSold": "Ad has been marked as sold successfully!", + "chargesAndServiceRangeGreaterThanZero": "Charges and Service range should be greater than zero.", + "cannotSelectAllDaysAsOff": "You cannot select all days as Off days.", + "endDateAfterStartDate": "End Date should be after start date.", + "pleaseEnterStartDateFirst": "Please enter start date first.", + "endTimeAfterStartTime": "End Time should be after start time.", + "pickedLocationNotice": "This picked location will be used for all home-based services in this appointment", + "serviceNotProvided": "Unfortunately, this service is not provided by your selected branch in this picked location." }; 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 3cc4d69..5198393 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -702,5 +702,15 @@ abstract class LocaleKeys { static const updateGroupServices = 'updateGroupServices'; static const addCommentsHere = 'addCommentsHere'; static const waitForBranchVerification = 'waitForBranchVerification'; + static const reportComplaint = 'reportComplaint'; + static const descriptionCannotBeEmpty = 'descriptionCannotBeEmpty'; + static const adMarkedAsSold = 'adMarkedAsSold'; + static const chargesAndServiceRangeGreaterThanZero = 'chargesAndServiceRangeGreaterThanZero'; + static const cannotSelectAllDaysAsOff = 'cannotSelectAllDaysAsOff'; + static const endDateAfterStartDate = 'endDateAfterStartDate'; + static const pleaseEnterStartDateFirst = 'pleaseEnterStartDateFirst'; + static const endTimeAfterStartTime = 'endTimeAfterStartTime'; + static const pickedLocationNotice = 'pickedLocationNotice'; + static const serviceNotProvided = 'serviceNotProvided'; } diff --git a/lib/main.dart b/lib/main.dart index 4d3fd7e..85a9d62 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,3 +12,4 @@ bool disableThingsForQA = true; // todo terminal command to genertate translation keys // flutter pub run easy_localization:generate --source-dir ./assets/langs -f keys -o locale_keys.g.dart // command to generate languages data from json + diff --git a/lib/models/services_models/service_model.dart b/lib/models/services_models/service_model.dart index d158bee..9128bd9 100644 --- a/lib/models/services_models/service_model.dart +++ b/lib/models/services_models/service_model.dart @@ -21,6 +21,7 @@ class ServiceModel { String providerServiceName; bool isHomeSelected; String homeLocation; + double distanceToHomeInKms; double currentTotalServicePrice; ServiceModel({ @@ -41,6 +42,7 @@ class ServiceModel { this.serviceItems, this.isHomeSelected = false, this.homeLocation = "", + this.distanceToHomeInKms = 0.0, this.currentTotalServicePrice = 0.0, required this.isExpandedOrSelected, required this.providerServiceId, @@ -74,6 +76,7 @@ class ServiceModel { providerServiceName: "", isHomeSelected: false, homeLocation: "", + distanceToHomeInKms: 0.0, ); Map toJson() => { diff --git a/lib/models/user_models/user.dart b/lib/models/user_models/user.dart index 1efe6db..c8c055b 100644 --- a/lib/models/user_models/user.dart +++ b/lib/models/user_models/user.dart @@ -34,7 +34,7 @@ class User { Map toJson() => { "totalItemsCount": totalItemsCount, - "data": data == null ? null : data!.toJson(), + "data": data?.toJson(), "messageStatus": messageStatus, "message": message, }; @@ -63,8 +63,8 @@ class UserData { Map toJson() => { "accessToken": accessToken, "refreshToken": refreshToken, - "expiryDate": expiryDate == null ? null : expiryDate!.toIso8601String(), - "userInfo": userInfo == null ? null : userInfo!.toJson(), + "expiryDate": expiryDate?.toIso8601String(), + "userInfo": userInfo?.toJson(), }; } @@ -88,6 +88,8 @@ class UserInfo { this.isDealershipUser, this.providerId, this.customerId, + this.countryId, + this.cityId, this.dealershipId, this.userLocalImage}); @@ -109,6 +111,8 @@ class UserInfo { bool? isDealershipUser; dynamic providerId; int? customerId; + int? cityId; + int? countryId; dynamic dealershipId; File? userLocalImage; @@ -140,6 +144,8 @@ class UserInfo { isDealershipUser = json["isDealershipUser"]; providerId = json["providerID"]; customerId = json["customerID"]; + countryId = json["countryId"] ?? 1; + cityId = json["cityId"] ?? 1; dealershipId = json["dealershipID"]; userLocalImage = null; } diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index 9d97121..79f12d8 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -16,7 +16,7 @@ import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/utils/enums.dart'; abstract class AdsRepo { - Future> getAdsDuration(); + Future> getAdsDuration({required int? countryId}); Future> getSpecialServices({required int specialServiceType}); @@ -54,10 +54,14 @@ class AdsRepoImp implements AdsRepo { AppState appState = injector.get(); @override - Future> getAdsDuration() async { + Future> getAdsDuration({required int? countryId}) async { + var param = { + "CountryID": countryId.toString() ?? "0", + }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), + queryParameters: param, ApiConsts.vehicleAdsDurationGet, ); List vehicleAdsDuration = List.generate(adsGenericModel.data.length, (index) => AdsDurationModel.fromJson(adsGenericModel.data[index])); @@ -70,7 +74,7 @@ class AdsRepoImp implements AdsRepo { "SpecialServiceType": specialServiceType.toString(), }; GenericRespModel adsGenericModel = - await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsSpecialServicesGet, queryParameters: params); + await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsSpecialServicesGet, queryParameters: params); List vehicleAdsDuration = List.generate(adsGenericModel.data.length, (index) => SpecialServiceModel.fromJson(adsGenericModel.data[index])); return vehicleAdsDuration; } @@ -145,7 +149,7 @@ class AdsRepoImp implements AdsRepo { String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), isCreateNew ? ApiConsts.vehicleAdsSingleStepCreate : ApiConsts.vehicleAdsSingleStepUpdate, postParams, token: token, @@ -179,7 +183,7 @@ class AdsRepoImp implements AdsRepo { } GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsGet, queryParameters: isMyAds ? onlyMyAdsParams : allAdsParams, ); @@ -207,7 +211,7 @@ class AdsRepoImp implements AdsRepo { GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsGet, queryParameters: parameters, ); @@ -223,7 +227,7 @@ class AdsRepoImp implements AdsRepo { GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.myAdsReserveGet, queryParameters: params, ); @@ -245,7 +249,7 @@ class AdsRepoImp implements AdsRepo { }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), queryParameters: params, ApiConsts.vehicleAdsGet, ); @@ -267,7 +271,7 @@ class AdsRepoImp implements AdsRepo { }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), queryParameters: params, ApiConsts.vehicleAdsGet, ); @@ -283,7 +287,7 @@ class AdsRepoImp implements AdsRepo { }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), queryParameters: params, ApiConsts.adsMCBankAccountAdGet, ); @@ -305,7 +309,7 @@ class AdsRepoImp implements AdsRepo { String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.adsUpdateStatus, postParams, token: token, @@ -324,7 +328,7 @@ class AdsRepoImp implements AdsRepo { String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.adsExtendDurationCreate, postParams, token: token, @@ -341,7 +345,7 @@ class AdsRepoImp implements AdsRepo { String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.deleteAd, postParams, token: token, @@ -363,7 +367,7 @@ class AdsRepoImp implements AdsRepo { String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.deleteAd, postParams, token: token, @@ -384,7 +388,7 @@ class AdsRepoImp implements AdsRepo { String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.adsReserveCreate, postParams, token: token, @@ -412,7 +416,7 @@ class AdsRepoImp implements AdsRepo { String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.adsPhotoOfficeAppointmentCreate, postParams, token: token, @@ -445,7 +449,7 @@ class AdsRepoImp implements AdsRepo { String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.adsPhotoOfficeAppointmentCreate, postParams, token: token, @@ -458,7 +462,7 @@ class AdsRepoImp implements AdsRepo { Future> getChatBuyersForAds({required int adsID}) async { var queryParameters = {"AdsID": adsID.toString()}; GenericRespModel genericRespModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getChatBuyersForAds, queryParameters: queryParameters, token: appState.getUser.data!.accessToken, diff --git a/lib/repositories/common_repo.dart b/lib/repositories/common_repo.dart index df6275b..24d07f2 100644 --- a/lib/repositories/common_repo.dart +++ b/lib/repositories/common_repo.dart @@ -54,6 +54,8 @@ abstract class CommonRepo { Future getVehicleDetails({int? vehicleTypeId, int? vehicleBrandId}); Future> getEnumTypeValues({int? enumTypeID, String? enumTypeName}); + + Future createComplainFromProvider({required int customerId, required String comment, required int complainType}); } class CommonRepoImp implements CommonRepo { @@ -62,7 +64,7 @@ class CommonRepoImp implements CommonRepo { @override Future getAllCountries() async { - return await apiClient.getJsonForObject((json) => Country.fromJson(json), ApiConsts.GetAllCountry); + return await apiClient.getJsonForObject((json) => Country.fromJson(json), ApiConsts.getAllCountry); } @override @@ -70,12 +72,12 @@ class CommonRepoImp implements CommonRepo { var postParams = { "CountryID": countryId, }; - return await apiClient.getJsonForObject((json) => Cities.fromJson(json), ApiConsts.GetAllCities, queryParameters: postParams); + return await apiClient.getJsonForObject((json) => Cities.fromJson(json), ApiConsts.getAllCities, queryParameters: postParams); } @override Future getRoles() async { - return await apiClient.getJsonForObject((json) => Role.fromJson(json), ApiConsts.GetProviderRoles); + return await apiClient.getJsonForObject((json) => Role.fromJson(json), ApiConsts.getProviderRoles); } @override @@ -166,7 +168,8 @@ class CommonRepoImp implements CommonRepo { var postParams = { "VehicleType": vehicleTypeId.toString(), }; - GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleCategoryGet, queryParameters: postParams); + GenericRespModel adsGenericModel = + await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleCategoryGet, queryParameters: postParams); List vehicleCategories = List.generate(adsGenericModel.data.length, (index) => VehicleCategoryModel.fromJson(adsGenericModel.data[index])); return vehicleCategories; } @@ -176,7 +179,8 @@ class CommonRepoImp implements CommonRepo { var postParams = { "CountryID": countryId.toString(), }; - GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleCityGet, queryParameters: countryId != -1 ? postParams : null); + GenericRespModel adsGenericModel = await apiClient.getJsonForObject( + token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleCityGet, queryParameters: countryId != -1 ? postParams : null); List vehicleCities = List.generate(adsGenericModel.data.length, (index) => VehicleCityModel.fromJson(adsGenericModel.data[index])); return vehicleCities; } @@ -186,7 +190,8 @@ class CommonRepoImp implements CommonRepo { var postParams = { "VehicleType": vehicleTypeId.toString(), }; - GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleColorGet, queryParameters: postParams); + GenericRespModel adsGenericModel = + await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleColorGet, queryParameters: postParams); List vehicleColors = List.generate(adsGenericModel.data.length, (index) => VehicleColorModel.fromJson(adsGenericModel.data[index])); return vehicleColors; } @@ -196,7 +201,8 @@ class CommonRepoImp implements CommonRepo { var postParams = { "VehicleType": vehicleTypeId.toString(), }; - GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleConditionGet, queryParameters: postParams); + GenericRespModel adsGenericModel = + await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleConditionGet, queryParameters: postParams); List vehicleConditions = List.generate(adsGenericModel.data.length, (index) => VehicleConditionModel.fromJson(adsGenericModel.data[index])); return vehicleConditions; } @@ -217,7 +223,8 @@ class CommonRepoImp implements CommonRepo { var postParams = { "VehicleType": vehicleTypeId.toString(), }; - GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleMileageGet, queryParameters: postParams); + GenericRespModel adsGenericModel = + await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleMileageGet, queryParameters: postParams); List vehicleMileages = List.generate(adsGenericModel.data.length, (index) => VehicleMileageModel.fromJson(adsGenericModel.data[index])); return vehicleMileages; } @@ -338,22 +345,21 @@ class CommonRepoImp implements CommonRepo { return vehicleParts; } -// -// @override -// Future> getProviderServiceCategories() async { -// GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.serviceCategoryGet); -// List providerCategories = List.generate(adsGenericModel.data.length, (index) => ProviderCategoryModel.fromJson(adsGenericModel.data[index])); -// return providerCategories; -// } - -// @override -// Future> getProviderServices({required int categoryId}) async { -// var postParams = { -// "ServiceCategoryID": categoryId.toString(), -// }; -// GenericRespModel adsGenericModel = -// await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.serviceCategoryGet, queryParameters: postParams); -// List providerServices = List.generate(adsGenericModel.data.length, (index) => ProviderServiceModel.fromJson(adsGenericModel.data[index])); -// return providerServices; -// } + @override + Future createComplainFromProvider({required int customerId, required String comment, required int complainType}) async { + final providerId = appState.getUser.data!.userInfo!.providerId; + final parameters = { + "serviceProviderID": providerId, + "customerID": customerId, + "detail": comment, + "complaintType": complainType, + }; + GenericRespModel adsGenericModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.providerComplaintCreate, + parameters, + token: appState.getUser.data!.accessToken, + ); + return adsGenericModel; + } } diff --git a/lib/repositories/user_repo.dart b/lib/repositories/user_repo.dart index 5f5bd31..0ae8a6a 100644 --- a/lib/repositories/user_repo.dart +++ b/lib/repositories/user_repo.dart @@ -125,7 +125,7 @@ class UserRepoImp implements UserRepo { "mobileorEmail": phoneNo, "password": password, }; - return await injector.get().postJsonForResponse(ApiConsts.Login_V1, postParams); + return await injector.get().postJsonForResponse(ApiConsts.loginV1, postParams); //return await injector.get().postJsonForObject((json) => BasicOtp.fromJson(json), ApiConsts.Login_V1, postParams); } @@ -136,7 +136,7 @@ class UserRepoImp implements UserRepo { "loginType": loginType, }; - Response response = await injector.get().postJsonForResponse(ApiConsts.Login_V2_OTP, postParams); + Response response = await injector.get().postJsonForResponse(ApiConsts.loginV2OTP, postParams); LoginPasswordRespModel user = LoginPasswordRespModel.fromJson(jsonDecode(response.body)); return user; } @@ -149,7 +149,7 @@ class UserRepoImp implements UserRepo { String deviceToken, ) async { var postParams = {"userToken": userToken, "userOTP": otp, "deviceType": deviceType, "deviceToken": deviceToken}; - return await injector.get().postJsonForResponse(ApiConsts.Login_V2_OTPVerify, postParams); + return await injector.get().postJsonForResponse(ApiConsts.loginV2OTPVerify, postParams); } @override @@ -161,7 +161,7 @@ class UserRepoImp implements UserRepo { @override Future getAllCountries() async { - return await injector.get().getJsonForObject((json) => Country.fromJson(json), ApiConsts.GetAllCountry); + return await injector.get().getJsonForObject((json) => Country.fromJson(json), ApiConsts.getAllCountry); } @override @@ -169,12 +169,12 @@ class UserRepoImp implements UserRepo { var postParams = { "CountryID": countryId, }; - return await injector.get().getJsonForObject((json) => Cities.fromJson(json), ApiConsts.GetAllCities, queryParameters: postParams); + return await injector.get().getJsonForObject((json) => Cities.fromJson(json), ApiConsts.getAllCities, queryParameters: postParams); } @override Future getRoles() async { - return await injector.get().getJsonForObject((json) => Role.fromJson(json), ApiConsts.GetProviderRoles); + return await injector.get().getJsonForObject((json) => Role.fromJson(json), ApiConsts.getProviderRoles); } @override @@ -183,14 +183,14 @@ class UserRepoImp implements UserRepo { "userName": userName, "otpType": 1, }; - return await injector.get().postJsonForResponse(ApiConsts.ForgetPasswordOTPRequest, postParams); + return await injector.get().postJsonForResponse(ApiConsts.forgetPasswordOTPRequest, postParams); //return await injector.get().postJsonForObject((json) => PasswordOTPRequest.fromJson(json), ApiConsts.ForgetPasswordOTPRequest, postParams); } @override Future forgetPasswordOTPCompare(String userToken, String userOTP) async { var postParams = {"userToken": userToken, "userOTP": userOTP}; - Response response = await injector.get().postJsonForResponse(ApiConsts.ForgetPasswordOTPCompare, postParams); + Response response = await injector.get().postJsonForResponse(ApiConsts.forgetPasswordOTPCompare, postParams); PasswordOTPCompareResModel otpCompare = PasswordOTPCompareResModel.fromJson(jsonDecode(response.body)); return otpCompare; @@ -203,7 +203,7 @@ class UserRepoImp implements UserRepo { "newPassword": newPassword, }; - Response response = await injector.get().postJsonForResponse(ApiConsts.ForgetPassword, postParams); + Response response = await injector.get().postJsonForResponse(ApiConsts.forgetPassword, postParams); ConfirmPasswordRespModel data = ConfirmPasswordRespModel.fromJson(jsonDecode(response.body)); return data; @@ -220,14 +220,14 @@ class UserRepoImp implements UserRepo { String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token $t"); - return await injector.get().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.ChangePassword, postParams, token: t); + return await injector.get().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.changePassword, postParams, token: t); } @override Future changeMobileNoOTPRequest(countryID, String mobileNo, String password) async { var postParams = {"countryID": 1, "mobileNo": mobileNo, "password": password}; String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => ChangeMobileRespModel.fromJson(json), ApiConsts.ChangeMobileNoOTPRequest, postParams, token: t); + return await injector.get().postJsonForObject((json) => ChangeMobileRespModel.fromJson(json), ApiConsts.changeMobileNoOTPRequest, postParams, token: t); } @override @@ -237,21 +237,21 @@ class UserRepoImp implements UserRepo { "userOTP": userOTP, }; String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => ConfirmMobileRespModel.fromJson(json), ApiConsts.ChangeMobileNo, postParams, token: t); + return await injector.get().postJsonForObject((json) => ConfirmMobileRespModel.fromJson(json), ApiConsts.changeMobileNo, postParams, token: t); } @override Future changeEmailOTPRequest(String email, String password) async { var postParams = {"email": email, "password": password}; String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => ChanEmailRespModel.fromJson(json), ApiConsts.ChangeEmailOTPRequest, postParams, token: t); + return await injector.get().postJsonForObject((json) => ChanEmailRespModel.fromJson(json), ApiConsts.changeEmailOTPRequest, postParams, token: t); } @override Future changeEmail(String userToken, String userOTP) async { var postParams = {"userToken": userToken, "userOTP": userOTP}; String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => ConfirmEmailRespModel.fromJson(json), ApiConsts.ChangeEmail, postParams, token: t); + return await injector.get().postJsonForObject((json) => ConfirmEmailRespModel.fromJson(json), ApiConsts.changeEmail, postParams, token: t); } @override @@ -264,7 +264,7 @@ class UserRepoImp implements UserRepo { }; String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => VerifyEmailRespModel.fromJson(json), ApiConsts.EmailVerify, postParams, token: t); + return await injector.get().postJsonForObject((json) => VerifyEmailRespModel.fromJson(json), ApiConsts.emailVerify, postParams, token: t); } @override @@ -272,7 +272,7 @@ class UserRepoImp implements UserRepo { var postParams = {"userToken": userToken, "userOTP": userOTP}; String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.EmailVerifyOTPVerify, postParams, token: t); + return await injector.get().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.emailVerifyOTPVerify, postParams, token: t); } @override @@ -284,7 +284,7 @@ class UserRepoImp implements UserRepo { debugPrint("token $t"); return await injector.get().postJsonForObject( (json) => ImageResponse.fromJson(json), - ApiConsts.UpdateUserImage, + ApiConsts.updateUserImage, postParams, token: t, ); @@ -297,7 +297,7 @@ class UserRepoImp implements UserRepo { String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token $t"); - return await injector.get().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.UpdateUserImage, postParams, token: t); + return await injector.get().postJsonForObject((json) => ImageResponse.fromJson(json), ApiConsts.updateUserImage, postParams, token: t); } @override @@ -319,6 +319,6 @@ class UserRepoImp implements UserRepo { Future logoutUser() async { String t = AppState().getUser.data!.accessToken ?? ""; var postParams = {"token": AppState().getUser.data!.accessToken ?? ""}; - return await injector.get().postJsonForResponse(ApiConsts.LogoutUser, postParams, token: t); + return await injector.get().postJsonForResponse(ApiConsts.logoutUser, postParams, token: t); } } diff --git a/lib/services/common_services.dart b/lib/services/common_services.dart index 53ffe31..d4bf272 100644 --- a/lib/services/common_services.dart +++ b/lib/services/common_services.dart @@ -103,7 +103,7 @@ class CommonServicesImp implements CommonAppServices { destLongitude, ); - return distance ?? 0.0; + return (distance / 1000) ?? 0.0; } catch (e) { logger.e(e.toString()); return 0.0; diff --git a/lib/utils/date_helper.dart b/lib/utils/date_helper.dart index e9cd7ab..9c11007 100644 --- a/lib/utils/date_helper.dart +++ b/lib/utils/date_helper.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; class DateHelper { @@ -50,3 +51,44 @@ class DateHelper { return formatter.format(date); } } + +extension TimeOfDayUtils on TimeOfDay { + bool isAfter(TimeOfDay other) { + final thisTime = DateTime(0, 1, 1, hour, minute); + final otherTime = DateTime(0, 1, 1, other.hour, other.minute); + return thisTime.isAfter(otherTime); + } + + bool isBefore(TimeOfDay other) { + final thisTime = DateTime(0, 1, 1, hour, minute); + final otherTime = DateTime(0, 1, 1, other.hour, other.minute); + return thisTime.isBefore(otherTime); + } + + static TimeOfDay convertStringToTimeOfDay(String timeString) { + bool is12HourFormat = timeString.contains(RegExp(r'AM|PM', caseSensitive: false)); + + int hour; + int minute; + + if (is12HourFormat) { + List parts = timeString.split(' '); // Split time and AM/PM part + List timeParts = parts[0].split(':'); // Split hours and minutes + + hour = int.parse(timeParts[0]); + minute = int.parse(timeParts[1]); + + if (parts[1].toUpperCase() == 'PM' && hour != 12) { + hour += 12; + } else if (parts[1].toUpperCase() == 'AM' && hour == 12) { + hour = 0; + } + } else { + List timeParts = timeString.split(':'); + hour = int.parse(timeParts[0]); + minute = int.parse(timeParts[1]); + } + + return TimeOfDay(hour: hour, minute: minute); + } +} diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index 3d558e1..f168af0 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -63,6 +63,7 @@ enum AdPostStatus { buyingService, reserveCancel, deActive, + pendingForActive, allAds, } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index e39d9fe..040f778 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -30,13 +30,7 @@ class Utils { static void showToast(String message) { Fluttertoast.showToast( - msg: message, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - timeInSecForIosWeb: 2, - backgroundColor: Colors.black54, - textColor: Colors.white, - fontSize: 16.0); + msg: message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.black54, textColor: Colors.white, fontSize: 16.0); } static Future openNumberViaCaller({required String phoneNumber}) async { @@ -69,10 +63,10 @@ class Utils { await launchUrlString(url, mode: LaunchMode.externalApplication); } - static Future pickDateFromDatePicker(BuildContext context, {DateTime? firstDate, DateTime? lastDate}) async { + static Future pickDateFromDatePicker(BuildContext context, {DateTime? initial, DateTime? firstDate, DateTime? lastDate}) async { DateTime? pickedDate = await showDatePicker( context: context, - initialDate: DateTime.now(), //get today's date + initialDate: initial ?? DateTime.now(), //get today's date firstDate: firstDate ?? DateTime.now(), //DateTime.now() - not to allow to choose before today. lastDate: lastDate ?? DateTime(2101)); @@ -85,17 +79,16 @@ class Utils { } static Future pickTime(BuildContext context, {TimeOfDay? initialTime}) async { - TimeOfDay? timeOfDay = await showTimePicker(context: context, initialTime: initialTime ?? TimeOfDay.now()); + TimeOfDay? timeOfDay = await showTimePicker( + context: context, + initialTime: initialTime ?? TimeOfDay.now(), + ); if (timeOfDay == null) { return ""; } - return ("${timeOfDay.hour - .toString() - .length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute - .toString() - .length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); + return ("${timeOfDay.hour.toString().length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute.toString().length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); } static dynamic getNotNullValue(List list, int index) { @@ -231,6 +224,7 @@ class Utils { return MyColors.adCancelledStatusColor; case AdPostStatus.pendingForPost: + case AdPostStatus.pendingForActive: return MyColors.inProgressColor; case AdPostStatus.active: @@ -546,7 +540,7 @@ class Utils { border: Border.all( width: w, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -569,7 +563,7 @@ class Utils { border: Border.all( width: 1, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -587,8 +581,7 @@ class Utils { } static String getAdsPaymentBrowserForm({required int paymentId, required int adId}) { - return '
'; + return '
'; } // BOTTOM SHEETS diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index cc28983..941170c 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -297,7 +297,8 @@ class AdVM extends BaseVM { } Future getVehicleAdsDuration() async { - vehicleAdsDurations = await adsRepo.getAdsDuration(); + final countryId = AppState().getUser.data!.userInfo!.countryId; + vehicleAdsDurations = await adsRepo.getAdsDuration(countryId: countryId); notifyListeners(); } @@ -329,7 +330,7 @@ class AdVM extends BaseVM { return; } Utils.hideLoading(context); - Utils.showToast(respModel.message ?? "Ad has been marked as sold successfully!"); + Utils.showToast(respModel.message ?? LocaleKeys.adMarkedAsSold.tr()); updateIsExploreAds(false); applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.sold); //pending for review navigateReplaceWithName(context, AppRoutes.dashboard); @@ -723,7 +724,6 @@ class AdVM extends BaseVM { SelectionModel vehicleAdDurationId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateVehicleAdDurationId(SelectionModel id) { - log("vehicleAdDurationId is updated: ${vehicleAdDurationId.selectedId}"); vehicleAdDurationId = id; notifyListeners(); } diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 4906905..1e48f60 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -38,9 +38,11 @@ import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/payment_view_model.dart'; import 'package:mc_common_app/views/appointments/book_appointment_schedules_view.dart'; import 'package:mc_common_app/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -81,6 +83,7 @@ class AppointmentsVM extends BaseVM { bool ifItemAlreadySelected(int id) { int indexFound = allSelectedItemsInAppointments.indexWhere((element) => element.id == id); + log("already selected at: $indexFound"); if (indexFound != -1) { return true; } @@ -101,6 +104,7 @@ class AppointmentsVM extends BaseVM { } resetCategorySelectionBottomSheet(); + notifyListeners(); } } @@ -248,16 +252,16 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - String pickedHomeLocation = ""; - - void updatePickedHomeLocation(String value) { - pickedHomeLocation = value; - pickHomeLocationError = ""; - if (currentServiceSelection != null) { - currentServiceSelection!.homeLocation = value; - } - notifyListeners(); - } + // String pickedHomeLocation = ""; + // + // void updatePickedHomeLocation(String value) { + // pickedHomeLocation = value; + // pickHomeLocationError = ""; + // if (currentServiceSelection != null) { + // currentServiceSelection!.homeLocation = value; + // } + // notifyListeners(); + // } SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); @@ -272,10 +276,23 @@ class AppointmentsVM extends BaseVM { List servicesInCurrentAppointment = []; ServiceModel? currentServiceSelection; + CurrentLocationInfoModel? currentLocationInfoModel; + + void updateCurrentLocationInfoModel(CurrentLocationInfoModel? value) { + currentLocationInfoModel = value; + pickHomeLocationError = ""; + if (currentServiceSelection != null && value != null) { + currentServiceSelection!.homeLocation = value.address; + } + notifyListeners(); + } + void updateBranchServiceId(SelectionModel id) async { branchSelectedServiceId = id; currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId); - + if (currentLocationInfoModel != null) { + currentLocationInfoModel!.homeChargesInCurrentService = double.parse(currentServiceSelection!.rangePricePerKm ?? "0.0"); + } log("currentServiceSelection isAllowAppointmentHome: ${currentServiceSelection!.isAllowAppointmentHome}"); log("currentServiceSelection serviceDescription: ${currentServiceSelection!.serviceDescription}"); notifyListeners(); @@ -308,6 +325,7 @@ class AppointmentsVM extends BaseVM { } servicesInCurrentAppointment.clear(); servicesInCurrentAppointment = []; + currentLocationInfoModel = null; log("here servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); } @@ -547,6 +565,7 @@ class AppointmentsVM extends BaseVM { double amountToPayForAppointment = 0.0; double totalAmount = 0.0; + double totalLocationCharges = 0.0; List serviceItemsFromApi = []; @@ -557,42 +576,35 @@ class AppointmentsVM extends BaseVM { void onItemUpdateOrSelected(int index, bool selected, int itemId) { int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!); - // log("currentServiceSelection!.allSelectedItemsInAppointments: ${allSelectedItemsInAppointments.length}"); - // log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}"); - // log("index: $index"); - // log("selected: $selected"); - // log("itemId: $itemId"); - // log("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); - serviceItemsFromApi[index].isUpdateOrSelected = selected; serviceItemsFromApi[index].isHomeSelected = isHomeTapped; - log(" serviceItemsFromApi[index].isHomeSelected: ${serviceItemsFromApi[index].isHomeSelected}"); + log(" serviceIndex: $serviceIndex"); if (selected) { selectedSubServicesCounter = selectedSubServicesCounter + 1; selectSubServicesError = ""; - currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]); - allSelectedItemsInAppointments.add(serviceItemsFromApi[index]); - // for (var element in allSelectedItemsInAppointments) { + if (!ifItemAlreadySelected(serviceItemsFromApi[index].id!)) { + currentServiceSelection!.serviceItems!.add(serviceItemsFromApi[index]); + allSelectedItemsInAppointments.add(serviceItemsFromApi[index]); + log("Here with: ${serviceItemsFromApi[index].description}"); if (serviceIndex != -1) { - servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]); + // servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]); servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0"); } } - // } } else { - // log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}"); selectedSubServicesCounter = selectedSubServicesCounter - 1; currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId); - // log("currentServiceSelection!.serviceItems: ${currentServiceSelection!.serviceItems!.length}"); - allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId); if (serviceIndex != -1) { servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0"); - servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId); + if (servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice < 0.0) { + servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice * -1; + } + // servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId); } } @@ -614,9 +626,11 @@ class AppointmentsVM extends BaseVM { setState(ViewState.busy); serviceItemsFromApi.clear(); serviceItemsFromApi = await branchRepo.getServiceItems(serviceId); + selectedSubServicesCounter = 0; for (var item in serviceItemsFromApi) { if (ifItemAlreadySelected(item.id!)) { item.isUpdateOrSelected = true; + selectedSubServicesCounter++; } } setState(ViewState.idle); @@ -639,7 +653,7 @@ class AppointmentsVM extends BaseVM { } if (isHomeTapped) { - if (pickedHomeLocation == "") { + if (currentLocationInfoModel == null || currentLocationInfoModel!.address == "") { updatePickHomeLocationError(GlobalConsts.homeLocationEmptyError); return false; } @@ -683,8 +697,7 @@ class AppointmentsVM extends BaseVM { isScrollControlled: true, enableDrag: true, builder: (BuildContext context) { - // TODO: It should be calculated from API - double totalKms = 15.3; + double totalKms = double.parse(selectedService.distanceToHomeInKms.toStringAsFixed(2)); return InfoBottomSheet( title: LocaleKeys.chargesBreakdown.tr().toText(fontSize: 24, isBold: true), description: Column( @@ -723,7 +736,7 @@ class AppointmentsVM extends BaseVM { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - "${(double.parse(selectedService.rangePricePerKm!)) * totalKms} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true), + "${((double.parse(selectedService.rangePricePerKm!)) * totalKms).toStringAsFixed(2)} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true), ], ), ], @@ -736,7 +749,7 @@ class AppointmentsVM extends BaseVM { crossAxisAlignment: CrossAxisAlignment.end, children: [ (selectedService.isHomeSelected - ? "${(selectedService.currentTotalServicePrice) + ((double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms))}" + ? ((selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)).toStringAsFixed(2) : "${selectedService.currentTotalServicePrice}") .toText(fontSize: 29, isBold: true), 2.width, @@ -813,10 +826,20 @@ class AppointmentsVM extends BaseVM { return; } totalAmount = 0.0; + totalLocationCharges = 0.0; amountToPayForAppointment = 0.0; for (var schedule in serviceAppointmentScheduleList) { amountToPayForAppointment = amountToPayForAppointment + (schedule.amountToPay ?? 0.0); totalAmount = totalAmount + (schedule.amountTotal ?? 0.0); + for (var service in schedule.servicesListInAppointment!) { + int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceProviderServiceId == service.serviceProviderServiceId); + if (index != -1) { + if (servicesInCurrentAppointment[index].isHomeSelected) { + totalAmount = totalAmount + ((servicesInCurrentAppointment[index].distanceToHomeInKms) * double.parse(servicesInCurrentAppointment[index].rangePricePerKm!)); + totalLocationCharges = totalLocationCharges + (servicesInCurrentAppointment[index].distanceToHomeInKms * double.parse(servicesInCurrentAppointment[index].rangePricePerKm!)); + } + } + } } Utils.hideLoading(context); @@ -890,7 +913,90 @@ class AppointmentsVM extends BaseVM { } } -// BRANCHES RELATED + // REPORT A COMPLAINT + + String complaintDescription = ""; + String complaintDescriptionError = ""; + + void updateComplaintDescription(String value) { + complaintDescription = value; + if (value.isNotEmpty) { + complaintDescriptionError = ""; + } + } + + Future buildComplaintBottomSheet({required int customerID, required int complainType, required String customerName, required BuildContext context}) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return InfoBottomSheet( + title: LocaleKeys.reportComplaint.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + description: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (customerName.isNotEmpty) ...[ + ("${LocaleKeys.customerName.tr()}: $customerName").toText(fontSize: 15, color: MyColors.lightTextColor), + ], + 10.height, + TxtField( + maxLines: 5, + value: complaintDescription, + errorValue: complaintDescriptionError, + keyboardType: TextInputType.text, + hint: LocaleKeys.description.tr(), + onChanged: (v) => updateComplaintDescription(v), + ), + 25.height, + ShowFillButton( + title: LocaleKeys.submit.tr(), + onPressed: () async { + if (complaintDescription.isEmpty) { + Utils.showToast(LocaleKeys.descriptionCannotBeEmpty.tr()); + return; + } + final status = await reportComplaintAgainstCustomer( + customerID: customerID, + comment: complaintDescription, + complaintType: complainType, // 1 for Appointment and 2 for Requests + context: context, + ); + + if (status) { + updateComplaintDescription(""); + pop(context); + } + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + ), + ); + }, + ); + } + + Future reportComplaintAgainstCustomer({required int customerID, required String comment, required int complaintType, required BuildContext context}) async { + Utils.showLoading(context); + try { + GenericRespModel genericRespModel = await commonRepo.createComplainFromProvider(customerId: customerID, comment: comment, complainType: complaintType); + Utils.showToast(genericRespModel.message.toString()); + Utils.hideLoading(context); + return genericRespModel.messageStatus == 1; + } catch (e, s) { + logger.e(s); + Utils.hideLoading(context); + Utils.showToast(e.toString()); + return false; + } + } + + // BRANCHES RELATED List nearbyBranches = []; List myRecentBranches = []; @@ -960,7 +1066,6 @@ class AppointmentsVM extends BaseVM { Future getBranchServices({required int categoryId}) async { branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); isHomeTapped = false; - pickedHomeLocation = ""; pickHomeLocationError = ""; if (categoryId != -1) { isFetchingServices = true; @@ -1643,3 +1748,19 @@ class AppointmentsVM extends BaseVM { // } } } + +class CurrentLocationInfoModel { + final String address; + final double latitude; + final double longitude; + final double distanceFromCurrent; + double homeChargesInCurrentService; + + CurrentLocationInfoModel({ + required this.address, + required this.latitude, + required this.longitude, + required this.distanceFromCurrent, + required this.homeChargesInCurrentService, + }); +} diff --git a/lib/view_models/base_view_model.dart b/lib/view_models/base_view_model.dart index 8386b7a..b91e680 100644 --- a/lib/view_models/base_view_model.dart +++ b/lib/view_models/base_view_model.dart @@ -1,6 +1,5 @@ import 'package:flutter/cupertino.dart'; -import 'package:mc_common_app/classes/app_state.dart'; -import 'package:mc_common_app/main.dart'; +import 'package:flutter/material.dart'; import 'package:mc_common_app/utils/enums.dart'; class BaseVM extends ChangeNotifier { diff --git a/lib/views/advertisement/ads_detail_view/components.dart b/lib/views/advertisement/ads_detail_view/components.dart index a335ec0..a2eddec 100644 --- a/lib/views/advertisement/ads_detail_view/components.dart +++ b/lib/views/advertisement/ads_detail_view/components.dart @@ -1047,6 +1047,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { case AdPostStatus.cancelled: return pendingForReviewAction(pendingText: LocaleKeys.cancelledByOwner.tr()); case AdPostStatus.pendingForPost: + case AdPostStatus.pendingForActive: return pendingForReviewAction(pendingText: LocaleKeys.waitingAdminPost.tr()); case AdPostStatus.pendingForReview: diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart index 0957c65..66103fe 100644 --- a/lib/views/appointments/book_appointment_services_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -2,12 +2,15 @@ import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/models/services_models/item_model.dart'; import 'package:mc_common_app/models/services_models/service_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -60,7 +63,25 @@ class BookAppointmentServicesView extends StatelessWidget { Row( children: [ Expanded(child: (serviceData.serviceDescription ?? "").toText(fontSize: 15, isBold: true)), - MyAssets.closeWithOrangeBg.buildSvg(height: 30, width: 30).onPress(() => appointmentsVM.removeServiceInCurrentAppointment(serviceIndex)) + Row( + children: [ + MyAssets.icEdit.buildSvg(height: 17, width: 17).onPress(() { + log("serviceId: ${serviceData.serviceProviderServiceId}"); + + appointmentsVM.updateBranchServiceId( + SelectionModel( + selectedId: serviceData.serviceProviderServiceId!, + selectedOption: serviceData.serviceDescription ?? "", + itemPrice: "", + ), + ); + appointmentsVM.getServiceItems(serviceData.serviceProviderServiceId!); + navigateWithName(context, AppRoutes.bookAppointmentsItemView); + }), + 10.width, + MyAssets.closeWithOrangeBg.buildSvg(height: 30, width: 30).onPress(() => appointmentsVM.removeServiceInCurrentAppointment(serviceIndex)), + ], + ), ], ), if (true) ...[ @@ -89,7 +110,12 @@ class BookAppointmentServicesView extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - ((serviceData.currentTotalServicePrice).toString()).toText(fontSize: 32, isBold: true), + if (serviceData.isHomeSelected) ...[ + ((serviceData.currentTotalServicePrice + (serviceData.distanceToHomeInKms * double.parse(serviceData.rangePricePerKm!))).toStringAsFixed(2)) + .toText(fontSize: 32, isBold: true), + ] else ...[ + ((serviceData.currentTotalServicePrice).toString()).toText(fontSize: 32, isBold: true), + ], 2.width, LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), const Icon(Icons.arrow_drop_down, size: 30) diff --git a/lib/views/appointments/book_appointments_item_view.dart b/lib/views/appointments/book_appointments_item_view.dart index 9a949aa..823d8f8 100644 --- a/lib/views/appointments/book_appointments_item_view.dart +++ b/lib/views/appointments/book_appointments_item_view.dart @@ -65,9 +65,9 @@ class BookAppointmentsItemView extends StatelessWidget { ], ).horPaddingMain(), appointmentsVM.state == ViewState.busy - ? const Center(child: CircularProgressIndicator()) + ? const Expanded(child: Center(child: CircularProgressIndicator())) : appointmentsVM.serviceItemsFromApi.isEmpty - ? Expanded(child: Center(child: LocaleKeys.noAvailableItems.toText(fontSize: 16, color: MyColors.lightTextColor))) + ? Expanded(child: Center(child: LocaleKeys.noAvailableItems.tr().toText(fontSize: 16, color: MyColors.lightTextColor))) : ListView.separated( separatorBuilder: (BuildContext context, int index) => const Divider(), itemCount: appointmentsVM.serviceItemsFromApi.length, diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart index d37b235..c896e37 100644 --- a/lib/views/appointments/review_appointment_view.dart +++ b/lib/views/appointments/review_appointment_view.dart @@ -206,6 +206,7 @@ class ReviewAppointment extends StatelessWidget { AppointmentsVM appointmentsVM = context.read(); return ListView.builder( shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), itemCount: appointmentsVM.serviceAppointmentScheduleList.length, itemBuilder: (BuildContext context, int scheduleIndex) { ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex]; @@ -223,7 +224,7 @@ class ReviewAppointment extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ ListView.separated( - // physics: const NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: scheduleData.servicesListInAppointment!.length, itemBuilder: (BuildContext context, int serviceIndex) { @@ -295,10 +296,14 @@ class ReviewAppointment extends StatelessWidget { 10.height, const Divider(thickness: 0.7), Builder(builder: (BuildContext context) { - //TODO: THIS SHOULD BE FINALIZED FROM API double totalServicePrice = 0.0; - double totalKms = 15.3; - double rangePricePerKm = 5; + double totalKms = 0.0; + double rangePricePerKm = 0; + + if (appointmentsVM.currentLocationInfoModel != null) { + totalKms = appointmentsVM.currentLocationInfoModel!.distanceFromCurrent; + rangePricePerKm = appointmentsVM.currentLocationInfoModel!.homeChargesInCurrentService; + } totalServicePrice = rangePricePerKm * totalKms; return Column( @@ -316,8 +321,9 @@ class ReviewAppointment extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - ("${LocaleKeys.locationCharges.tr()} ($rangePricePerKm x $totalKms )").toText(fontSize: 14, color: MyColors.lightTextColor), - ("${totalServicePrice.toString()} ${LocaleKeys.sar.tr()}").toText(fontSize: 16, isBold: true), + ("${LocaleKeys.locationCharges.tr()} (${rangePricePerKm.toStringAsFixed(2)} x ${totalKms.toStringAsFixed(2)} )") + .toText(fontSize: 14, color: MyColors.lightTextColor), + ("${totalServicePrice.toStringAsFixed(2)} ${LocaleKeys.sar.tr()}").toText(fontSize: 16, isBold: true), ], ), ], @@ -369,7 +375,7 @@ class ReviewAppointment extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - (appointmentsVM.totalAmount - appointmentsVM.amountToPayForAppointment).toString().toText(fontSize: 16, isBold: true), + (appointmentsVM.totalAmount - appointmentsVM.amountToPayForAppointment).toStringAsFixed(2).toText(fontSize: 16, isBold: true), 2.width, LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2), ], @@ -384,7 +390,7 @@ class ReviewAppointment extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - appointmentsVM.totalAmount.toString().toText(fontSize: 29, isBold: true), + appointmentsVM.totalAmount.toStringAsFixed(2).toText(fontSize: 29, isBold: true), 2.width, LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), ], @@ -436,6 +442,7 @@ class ReviewAppointment extends StatelessWidget { buildServicesInfoCard(context: context), 10.height, buildPersonalInformationCard(context: context), + 20.height, ], ).expand(), buildNextButtonFooter(context: context), diff --git a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart index add3b01..4012d56 100644 --- a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -1,10 +1,15 @@ +import 'dart:developer'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/config/dependency_injection.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; +import 'package:mc_common_app/services/common_services.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; @@ -18,7 +23,7 @@ import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; class AppointmentServicePickBottomSheet extends StatelessWidget { - const AppointmentServicePickBottomSheet({Key? key}) : super(key: key); + const AppointmentServicePickBottomSheet({super.key}); @override Widget build(BuildContext context) { @@ -50,7 +55,9 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { (DropValue value) => appointmentsVM.updateProviderCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), list: appointmentsVM.branchCategories, hint: LocaleKeys.selectCategory.tr(), - dropdownValue: appointmentsVM.branchSelectedCategoryId.selectedId != -1 ? DropValue(appointmentsVM.branchSelectedCategoryId.selectedId, appointmentsVM.branchSelectedCategoryId.selectedOption, "") : null, + dropdownValue: appointmentsVM.branchSelectedCategoryId.selectedId != -1 + ? DropValue(appointmentsVM.branchSelectedCategoryId.selectedId, appointmentsVM.branchSelectedCategoryId.selectedOption, "") + : null, ); }, ), @@ -78,7 +85,9 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { (DropValue value) => appointmentsVM.updateBranchServiceId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), list: serviceCategories, hint: LocaleKeys.selectService.tr(), - dropdownValue: appointmentsVM.branchSelectedServiceId.selectedId != -1 ? DropValue(appointmentsVM.branchSelectedServiceId.selectedId, appointmentsVM.branchSelectedServiceId.selectedOption, "") : null, + dropdownValue: appointmentsVM.branchSelectedServiceId.selectedId != -1 + ? DropValue(appointmentsVM.branchSelectedServiceId.selectedId, appointmentsVM.branchSelectedServiceId.selectedOption, "") + : null, ); }, ), @@ -91,6 +100,10 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { children: [ Expanded( child: ShowFillButton( + borderColor: !(appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) + ? Colors.transparent + : MyColors.darkPrimaryColor, + isDisabled: !(appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)), isFilled: appointmentsVM.isHomeTapped, maxHeight: 48, title: LocaleKeys.home.tr(), @@ -117,11 +130,11 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { ], ), if (appointmentsVM.isHomeTapped) ...[ - 8.height, + 12.height, TxtField( hint: LocaleKeys.pickLocation.tr(), errorValue: appointmentsVM.pickHomeLocationError, - value: appointmentsVM.pickedHomeLocation, + value: appointmentsVM.currentLocationInfoModel == null ? "" : appointmentsVM.currentLocationInfoModel!.address, isNeedClickAll: true, postfixData: Icons.add_location_outlined, postFixDataColor: MyColors.darkPrimaryColor, @@ -129,9 +142,35 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { navigateTo( context, PickLocationPage( - onPickAddress: (double latitude, double longitude, String address) { - appointmentsVM.updatePickedHomeLocation(address); + onPickAddress: (double latitude, double longitude, String address) async { appointmentsVM.setState(ViewState.idle); + + double distance = injector.get().getDistanceBetween( + curLatitude: double.parse(appointmentsVM.selectedBranchModel!.latitude!), + curLongitude: double.parse(appointmentsVM.selectedBranchModel!.longitude!), + destLatitude: latitude, + destLongitude: longitude, + ); + if (distance > (appointmentsVM.currentServiceSelection!.customerLocationRange ?? 0)) { + Utils.showToast(LocaleKeys.serviceNotProvided.tr()); + appointmentsVM.updateCurrentLocationInfoModel(null); + return; + } + + log("distance: $distance"); + log("customerLocationRange : ${appointmentsVM.currentServiceSelection!.customerLocationRange}"); + + if (appointmentsVM.currentServiceSelection != null) { + appointmentsVM.currentServiceSelection!.distanceToHomeInKms = distance; + } + CurrentLocationInfoModel temp = CurrentLocationInfoModel( + address: address, + latitude: latitude, + longitude: longitude, + distanceFromCurrent: distance, + homeChargesInCurrentService: double.parse(appointmentsVM.currentServiceSelection!.rangePricePerKm ?? "0.0"), + ); + appointmentsVM.updateCurrentLocationInfoModel(temp); }, ), ); @@ -139,11 +178,11 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { ), 14.height, Row( - crossAxisAlignment: CrossAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, children: [ const Icon(Icons.warning, color: MyColors.adPendingStatusColor, size: 19).paddingOnly(bottom: 2), 3.width, - LocaleKeys.servicesNotAvailableHomeLocation.tr().toText(color: MyColors.adPendingStatusColor, fontSize: 12, isItalic: true), + Flexible(child: LocaleKeys.pickedLocationNotice.tr().toText(color: MyColors.adPendingStatusColor, fontSize: 12, isItalic: true)), ], ), ] @@ -155,14 +194,28 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { width: double.infinity, child: Column( children: [ - if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices && appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) ...[ + if (appointmentsVM.isHomeTapped && + !appointmentsVM.isFetchingServices && + appointmentsVM.currentServiceSelection != null && + (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) ...[ const Divider(thickness: 1, height: 1), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - // TODO: This Price will be decided according to the service selected, We will calculate the KMs and multiple it with price per KMs - appointmentsVM.currentServiceSelection!.rangePricePerKm.toString().toText(fontSize: 30, isBold: true), - "${LocaleKeys.sar.tr()}/km".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + if (appointmentsVM.currentLocationInfoModel == null) ...[ + appointmentsVM.currentServiceSelection!.rangePricePerKm.toString().toText(fontSize: 30, isBold: true), + "${LocaleKeys.sar.tr()}/km".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + ] else ...[ + (appointmentsVM.currentLocationInfoModel!.homeChargesInCurrentService * appointmentsVM.currentLocationInfoModel!.distanceFromCurrent).toStringAsFixed(2).toText( + fontSize: 30, + isBold: true, + ), + LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + 10.width, + ("(${(appointmentsVM.currentLocationInfoModel!.homeChargesInCurrentService).toStringAsFixed(2)} ${LocaleKeys.sar.tr()} x ${appointmentsVM.currentLocationInfoModel!.distanceFromCurrent.toStringAsFixed(2)} KMs )") + .toText(fontSize: 13, color: MyColors.lightTextColor) + .paddingOnly(bottom: 5), + ], ], ), LocaleKeys.additionalChargesNotice.tr().toText(fontSize: 12, color: MyColors.lightTextColor), diff --git a/lib/views/common_fragments/requests_fragment.dart b/lib/views/common_fragments/requests_fragment.dart index 4eb2886..7ff9a4b 100644 --- a/lib/views/common_fragments/requests_fragment.dart +++ b/lib/views/common_fragments/requests_fragment.dart @@ -155,7 +155,12 @@ class MyRequestsFragment extends StatelessWidget { cancelRequestBottomSheet(context, requestsVM.myFilteredRequests[index].id); return; }, - child: RequestItem(request: requestsVM.myFilteredRequests[index], appType: AppState().currentAppType, requestIndex: index), + child: RequestItem( + request: requestsVM.myFilteredRequests[index], + appType: AppState().currentAppType, + requestIndex: index, + shouldShowStatuses: AppState().currentAppType == AppType.customer, + ), ); }, separatorBuilder: (context, index) { diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index 840f149..f1230ff 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -8,6 +8,7 @@ import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart'; @@ -24,9 +25,13 @@ class RequestDetailPage extends StatelessWidget { const RequestDetailPage({super.key, required this.requestDetailPageArguments}); - Widget buildRequestDetailActionFooter( - {required int requestId, required RequestStatusEnum requestStatus, required RequestsTypeEnum requestTypeEnum, required String statusText, required BuildContext context}) { - // final requestsVM = context.read(); + Widget buildRequestDetailActionFooter({ + required int requestId, + required RequestStatusEnum requestStatus, + required RequestsTypeEnum requestTypeEnum, + required String statusText, + required BuildContext context, + }) { switch (requestStatus) { case RequestStatusEnum.submitted: return ShowFillButton( @@ -213,6 +218,30 @@ class RequestDetailPage extends StatelessWidget { ], showItem("${LocaleKeys.description.tr()}: ", requestDetail.description), 16.height, + + if (AppState().currentAppType == AppType.provider && + (requestDetailPageArguments.requestModel.requestStatus == RequestStatusEnum.inProgress || requestDetailPageArguments.requestModel.requestStatus == RequestStatusEnum.submitted)) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ("${LocaleKeys.reportComplaint.tr()}?").toText(fontSize: 14, color: MyColors.darkTextColor), + ], + ).onPress( + () { + int customerID = requestDetailPageArguments.requestModel.customerId; + int complainType = 2; + String customerName = requestDetailPageArguments.requestModel.customerName; + + context.read().buildComplaintBottomSheet( + customerID: customerID, + customerName: customerName, + complainType: complainType, + context: context, + ); + }, + ), + ], + // showItem("${LocaleKeys.priceRange.tr()}:", ""), // Row( // children: [ diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index c756441..57e3c4d 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -20,28 +20,44 @@ class RequestItem extends StatelessWidget { final RequestModel request; final AppType appType; final int requestIndex; + final bool shouldShowStatuses; - const RequestItem({super.key, required this.request, required this.appType, required this.requestIndex}); + const RequestItem({super.key, required this.request, required this.appType, required this.requestIndex, this.shouldShowStatuses = true}); @override Widget build(BuildContext context) { + String imageUrl = ""; + if (request.requestImages != null && request.requestImages!.isNotEmpty) { + imageUrl = request.requestImages!.first.imageUrl ?? ""; + } return Column( children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ + imageUrl.buildNetworkImage( + width: 80, + height: 80, + fit: BoxFit.cover, + ), + 12.width, Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.statusContainerChip(text: request.requestStatusName.toLowerCase() == "InProgress".toLowerCase() ? "In Progress" : request.requestStatusName, chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)), + if (shouldShowStatuses) ...[ + Utils.statusContainerChip( + text: request.requestStatusName.toLowerCase() == "InProgress".toLowerCase() ? "In Progress" : request.requestStatusName, + chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)), + ], 6.height, "${request.brand} ${request.model} | ${request.id}".toText(fontSize: 16, letterSpacing: -0.64), showItem("${LocaleKeys.year.tr()}:", "${request.year}"), if (request.customerName.isNotEmpty) ...[ showItem("${LocaleKeys.customerName.tr()}:", request.customerName), ], + showItem("${LocaleKeys.description.tr()}:", request.description), ], ), ), @@ -72,7 +88,6 @@ class RequestItem extends StatelessWidget { ) ], ), - showItem("${LocaleKeys.description.tr()}:", request.description), // showItem("${LocaleKeys.priceRange.tr()}: ", ""), // Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween,