From 71ae7cf3e4dcc7561ccf9139d13dc6cf42d1fef0 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Sun, 1 Dec 2024 18:13:18 +0300 Subject: [PATCH 01/10] 1 dec --- assets/langs/ar-SA.json | 4 +- assets/langs/en-US.json | 4 +- lib/classes/consts.dart | 3 +- lib/generated/codegen_loader.g.dart | 8 +- lib/generated/locale_keys.g.dart | 2 + lib/repositories/ads_repo.dart | 4 +- lib/repositories/request_repo.dart | 52 +++++ lib/view_models/ad_view_model.dart | 37 ++-- lib/view_models/chat_view_model.dart | 20 ++ lib/view_models/requests_view_model.dart | 177 +++++++++++++----- lib/views/advertisement/ads_filter_view.dart | 33 ++-- lib/views/chat/chat_view.dart | 18 +- .../chat/widgets/chat_message_widget.dart | 172 +++++++++++++++-- lib/views/requests/request_bottomsheets.dart | 60 ++++-- lib/views/requests/request_detail_page.dart | 7 +- 15 files changed, 492 insertions(+), 109 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 4de7982..17041ca 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -740,5 +740,7 @@ "blockedByAdmin": "تم حظرها من قبل المسؤول", "active": "نشط", "paymentType": "نوع الدفع", - "searchByCreatedDate": "البحث حسب تاريخ الإنشاء" + "searchByCreatedDate": "البحث حسب تاريخ الإنشاء", + "dealCompleted": "تم إتمام الصفقة", + "theDealNotCompleted": "لم تكتمل الصفقة" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index dc824d2..12adf0c 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -738,5 +738,7 @@ "blockedByAdmin": "Blocked by admin", "active": "Active", "paymentType": "Payment Type", - "searchByCreatedDate": "Search By Created Date" + "searchByCreatedDate": "Search By Created Date", + "dealCompleted": "The Deal Completed", + "theDealNotCompleted": "The Deal Not Completed" } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 2ae3e1d..8d591bc 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -163,7 +163,8 @@ class ApiConsts { static String createRequest = "${baseUrlServices}api/RequestManagement/Request_Create"; 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 updateRequestOfferStatus = "${baseUrlServices}api/RequestManagement/RequestOffer_UpdateStatus"; + static String updateRequestOffer = "${baseUrlServices}api/RequestManagement/ReqOffer_Upadte"; 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"; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 4c1e4f7..d98bfba 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -756,7 +756,9 @@ class CodegenLoader extends AssetLoader{ "blockedByAdmin": "تم حظرها من قبل المسؤول", "active": "نشط", "paymentType": "نوع الدفع", - "searchByCreatedDate": "البحث حسب تاريخ الإنشاء" + "searchByCreatedDate": "البحث حسب تاريخ الإنشاء", + "dealCompleted": "تم إتمام الصفقة", + "theDealNotCompleted": "لم تكتمل الصفقة" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1498,7 +1500,9 @@ static const Map en_US = { "blockedByAdmin": "Blocked by admin", "active": "Active", "paymentType": "Payment Type", - "searchByCreatedDate": "Search By Created Date" + "searchByCreatedDate": "Search By Created Date", + "dealCompleted": "The Deal Completed", + "theDealNotCompleted": "The Deal Not Completed" }; 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 cce5331..b866df3 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -720,5 +720,7 @@ abstract class LocaleKeys { static const active = 'active'; static const paymentType = 'paymentType'; static const searchByCreatedDate = 'searchByCreatedDate'; + static const dealCompleted = 'dealCompleted'; + static const theDealNotCompleted = 'theDealNotCompleted'; } diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index a76b314..dfee80b 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -213,9 +213,7 @@ class AdsRepoImp implements AdsRepo { "VehicleModelYearIDs": vehicleModelYearIdsList ?? [], "CreatedByRoles": createdByRolesIdsList ?? [], "AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS - - // TODO: This has to be converted into list - "VehicleNew": (vehicleAdConditionIdsList != null && vehicleAdConditionIdsList.isNotEmpty) ? vehicleAdConditionIdsList.first.toString() : "", + "VehicleNew": vehicleAdConditionIdsList ?? [], "CreatedOn": (vehicleAdCreatedDateList != null && vehicleAdCreatedDateList.isNotEmpty) ? vehicleAdCreatedDateList.first.toString() : "", "isActive": "true", //only Active ADS "isExplore": "true", diff --git a/lib/repositories/request_repo.dart b/lib/repositories/request_repo.dart index 514299d..4e26420 100644 --- a/lib/repositories/request_repo.dart +++ b/lib/repositories/request_repo.dart @@ -6,6 +6,7 @@ import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/dependency_injection.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/main.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/requests_models/offers_model.dart'; import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'; @@ -54,6 +55,19 @@ abstract class RequestRepo { Future updateRequestStatus({required RequestStatusEnum requestStatusEnum, required int requestId}); Future updateRequestOfferStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments}); + + Future updateRequestOfferFromProvider({ + required String message, + required int requestId, + required int offerId, + required String offerPrice, + required bool isDeliveryAvailable, + required String serviceItemName, + required int manufacturedById, + required String manufacturedOn, + required List requestImages, + required RequestOfferStatusEnum requestOfferStatusEnum, + }); } class RequestRepoImp implements RequestRepo { @@ -322,6 +336,44 @@ class RequestRepoImp implements RequestRepo { "offerStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum().toString(), "comments": comments, }; + GenericRespModel genericRespModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.updateRequestOfferStatus, + queryParameters, + token: appState.getUser.data!.accessToken, + ); + + return genericRespModel; + } + + @override + Future updateRequestOfferFromProvider({ + required String message, + required int requestId, + required int offerId, + required String offerPrice, + required bool isDeliveryAvailable, + required String serviceItemName, + required int manufacturedById, + required String manufacturedOn, + required List requestImages, + required RequestOfferStatusEnum requestOfferStatusEnum, + }) async { + final providerId = appState.getUser.data!.userInfo!.providerId; + + var queryParameters = { + "id": offerId, + "requestID": requestId, + "serviceProviderID": providerId, + "offerStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum(), + "serviceItem": serviceItemName, + "comment": message, + "price": offerPrice, + "offeredItemCreatedBy": manufacturedById.toString(), + "offeredItemCreatedOn": manufacturedOn.toString(), + "reqOfferImages": requestImages, + "isDeliveryAvailable": isDeliveryAvailable + }; GenericRespModel genericRespModel = await apiClient.postJsonForObject( (json) => GenericRespModel.fromJson(json), ApiConsts.updateRequestOffer, diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 76b0f09..55f2594 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -114,7 +114,10 @@ class AdVM extends BaseVM { } void removeSpecialServiceCard(int index) { - String option = specialServiceCards.elementAt(index).serviceSelectedId!.selectedOption; + String option = specialServiceCards + .elementAt(index) + .serviceSelectedId! + .selectedOption; for (var value in vehicleAdsSpecialServices) { if (value.name == option) { @@ -1255,7 +1258,10 @@ class AdVM extends BaseVM { } void removeDamagePartCard(int index) { - String option = vehicleDamageCards.elementAt(index).partSelectedId!.selectedOption; + String option = vehicleDamageCards + .elementAt(index) + .partSelectedId! + .selectedOption; for (var value in vehicleDamageParts) { if (value.partName == option) { @@ -1533,7 +1539,9 @@ class AdVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path.split('/').last; + String fileName = file.path + .split('/') + .last; vehiclePostingImages = VehiclePostingImages( imageName: fileName, imageStr: image, @@ -1733,11 +1741,16 @@ class AdVM extends BaseVM { if (vehicleAdCreatedDateSearchHistory.isEmpty) { updateAdsFiltersCounter(adsFiltersCounter + 1); } - vehicleAdCreatedDateSearchHistory.add(value); + if (vehicleAdCreatedDateSearchHistory.isEmpty) { + vehicleAdCreatedDateSearchHistory.add(value); + } else { + vehicleAdCreatedDateSearchHistory.first = value; + } + notifyListeners(); } - // Ad Condition +// Ad Condition List vehicleAdConditionSearchHistory = []; void removeVehicleAdConditionSearchHistory({bool isClear = false, required int index}) { @@ -2028,13 +2041,13 @@ class AdVM extends BaseVM { final chatVM = context.read(); await chatVM .getUsersChatMessagesForAd( - context: context, - isForBuyer: true, - adsChatBuyerId: 1, - adID: adDetailsModel.id, - userID: myUserID, - senderName: adDetailsModel.adOwnerName, - ) + context: context, + isForBuyer: true, + adsChatBuyerId: 1, + adID: adDetailsModel.id, + userID: myUserID, + senderName: adDetailsModel.adOwnerName, + ) .whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments)); } } diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index f558cf5..f7631b6 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -76,6 +76,26 @@ class ChatVM extends BaseVM { } } + List dealOptionsModelList = [ + OfferRequestCommentModel( + index: 0, + isSelected: false, + title: LocaleKeys.dealCompleted.tr(), + ), + OfferRequestCommentModel( + index: 1, + isSelected: false, + title: LocaleKeys.theDealNotCompleted.tr(), + ), + ]; + + updateIsSelectedInDealOptionsModelList(int index, bool value) { + for (var element in dealOptionsModelList) { + element.isSelected = false; + } + dealOptionsModelList[index].isSelected = true; + } + List indexesForCancelSpecialCarOffer = [0, 1, 6, 7]; List indexesForCancelSparePartOffer = [2, 7]; List indexesForRejectOffer = [3, 4, 5, 7]; diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index 079cd9a..d752a7e 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -215,6 +215,11 @@ class RequestsVM extends BaseVM { List pickedVehicleImages = []; String vehicleImageError = ""; + void addImageToPickedVehicleImages(ImageModel imageModel) { + pickedVehicleImages.add(imageModel); + notifyListeners(); + } + void removeImageFromList(String filePath) { int index = pickedVehicleImages.indexWhere((element) => element.filePath == filePath); if (index == -1) { @@ -707,10 +712,17 @@ class RequestsVM extends BaseVM { brandForSearch = v; } - Future convertFileToRequestPostingImages({required File file}) async { - List imageBytes = await file.readAsBytes(); - String image = base64Encode(imageBytes); - RequestPostingImages vehiclePostingImages = RequestPostingImages(requestImage: image); + Future convertFileToRequestPostingImages({required ImageModel imageModel}) async { + RequestPostingImages vehiclePostingImages; + log("imageModelxx: ${imageModel.isFromNetwork}"); + if (imageModel.isFromNetwork == true) { + vehiclePostingImages = RequestPostingImages(id: imageModel.id, requestImage: imageModel.filePath); + } else { + File file = File(imageModel.filePath!); + List imageBytes = await file.readAsBytes(); + String image = base64Encode(imageBytes); + vehiclePostingImages = RequestPostingImages(requestImage: image); + } return vehiclePostingImages; } @@ -718,7 +730,7 @@ class RequestsVM extends BaseVM { List requestImages = []; for (var image in pickedVehicleImages) { - var value = await convertFileToRequestPostingImages(file: File(image.filePath!)); + var value = await convertFileToRequestPostingImages(imageModel: image); requestImages.add(value); } @@ -735,37 +747,37 @@ class RequestsVM extends BaseVM { requestImages.add(element.toJson()); }); - try { - GenericRespModel respModel = await requestRepo.createRequest( - requestTypeId: requestTypeId.selectedId, - vehicleTypeId: vehicleTypeId.selectedId, - brand: brand, - model: model, - year: vehicleYearId.selectedOption, - isNew: vehicleConditionId.selectedId == 1, - // 1 for new, 2 for Used - countryID: vehicleCountryId.selectedId, - cityID: vehicleCityId.selectedId, - price: price.isEmpty ? "1.0" : price, - description: description, - address: address, - isSpecialServiceNeeded: false, - requestImages: requestImages, - ); - Utils.hideLoading(context); - if (respModel.messageStatus == 1) { - Utils.showToast(LocaleKeys.requestSuccessfullyCreated.tr()); - Navigator.pop(context); - resetRequestCreationForm(); - await applyFilterOnRequestsVM(requestsTypeEnum: requestTypeId.selectedId == 1 ? RequestsTypeEnum.specialCarRequest : RequestsTypeEnum.serviceRequest); - } else { - Utils.showToast(respModel.message.toString()); - } - } catch (e, s) { - Utils.hideLoading(context); - log(e.toString()); - Utils.showToast(e.toString()); + // try { + GenericRespModel respModel = await requestRepo.createRequest( + requestTypeId: requestTypeId.selectedId, + vehicleTypeId: vehicleTypeId.selectedId, + brand: brand, + model: model, + year: vehicleYearId.selectedOption, + isNew: vehicleConditionId.selectedId == 1, + // 1 for new, 2 for Used + countryID: vehicleCountryId.selectedId, + cityID: vehicleCityId.selectedId, + price: price.isEmpty ? "1.0" : price, + description: description, + address: address, + isSpecialServiceNeeded: false, + requestImages: requestImages, + ); + Utils.hideLoading(context); + if (respModel.messageStatus == 1) { + Utils.showToast(LocaleKeys.requestSuccessfullyCreated.tr()); + Navigator.pop(context); + await applyFilterOnRequestsVM(requestsTypeEnum: requestTypeId.selectedId.toRequestTypeEnum()); + resetRequestCreationForm(); + } else { + Utils.showToast(respModel.message.toString()); } + // } catch (e, s) { + // Utils.hideLoading(context); + // log(e.toString()); + // Utils.showToast(e.toString()); + // } } } @@ -931,17 +943,36 @@ class RequestsVM extends BaseVM { notifyListeners(); } - MessageImageModel convertFileToMessageImageModel({required File file, required int latestOfferId}) { - List imageBytes = file.readAsBytesSync(); - String image = base64Encode(imageBytes); - MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image, id: 0, isFromNetwork: false, reqOfferID: latestOfferId, imagePath: file.path); - return vehiclePostingImages; + MessageImageModel convertFileToMessageImageModel({required ImageModel imageModel, required int offerId}) { + MessageImageModel offerImages; + + if (imageModel.isFromNetwork ?? false) { + offerImages = MessageImageModel( + id: imageModel.id, + imageStr: imageModel.filePath, + isFromNetwork: true, + reqOfferID: offerId, + ); + } else { + File file = File(imageModel.filePath!); + List imageBytes = file.readAsBytesSync(); + String image = base64Encode(imageBytes); + offerImages = MessageImageModel( + id: 0, + imageStr: image, + isFromNetwork: false, + reqOfferID: offerId, + imagePath: file.path, + ); + } + + return offerImages; } - List getMessagesImageList({required int latestOfferId}) { + List getMessagesImageList({required int offerId}) { List requestImages = []; for (var image in pickedVehicleImages) { - var value = convertFileToMessageImageModel(file: File(image.filePath!), latestOfferId: latestOfferId); + var value = convertFileToMessageImageModel(imageModel: image, offerId: offerId); requestImages.add(value); } return requestImages; @@ -954,6 +985,65 @@ class RequestsVM extends BaseVM { notifyListeners(); } + Future updateOfferFromProvider({ + required String message, + required int requestId, + required int offerId, + required String offerPrice, + required bool isDeliveryAvailable, + required String serviceItemName, + required int manufacturedById, + required String manufacturedOn, + required int receiverId, + required int customerRequestIndex, + required BuildContext context, + }) async { + try { + if (isSendOfferValidated()) { + Utils.showLoading(context); + + List images = getMessagesImageList(offerId: offerId); + List offerImages = []; + images.forEach((element) { + offerImages.add(element.toJson()); + }); + GenericRespModel genericRespModel = await requestRepo.updateRequestOfferFromProvider( + message: message, + requestId: requestId, + offerId: offerId, + offerPrice: offerPrice, + isDeliveryAvailable: isDeliveryAvailable, + serviceItemName: serviceItemName, + manufacturedById: manufacturedById, + manufacturedOn: manufacturedOn, + requestImages: offerImages, + requestOfferStatusEnum: RequestOfferStatusEnum.offer, + ); + + Utils.hideLoading(context); + + if (genericRespModel.messageStatus == 1) { + Utils.showToast(genericRespModel.message ?? ""); + final chatVM = context.read(); + + await chatVM.getRequestsChatMessagesForProvider( + context: context, + customerId: receiverId, + requestOfferId: offerId, + requestId: requestId, + customerRequestIndex: customerRequestIndex, + ); + resetSendOfferBottomSheet(); + pop(context); + } + } + } catch (e, s) { + Utils.hideLoading(context); + log(e.toString()); + Utils.showToast(e.toString()); + } + } + Future onSendOfferPressed({ required BuildContext context, required String receiverId, @@ -967,10 +1057,11 @@ class RequestsVM extends BaseVM { required bool isDeliveryAvailable, required int requestIndex, required bool isFromChatScreen, + required int? offerId, }) async { if (isSendOfferValidated()) { final chatVM = context.read(); - List images = getMessagesImageList(latestOfferId: context.read().latestOfferId); + List images = getMessagesImageList(offerId: context.read().latestOfferId); List offerImages = []; images.forEach((element) { offerImages.add(element.toJson()); diff --git a/lib/views/advertisement/ads_filter_view.dart b/lib/views/advertisement/ads_filter_view.dart index 705ccc0..479195b 100644 --- a/lib/views/advertisement/ads_filter_view.dart +++ b/lib/views/advertisement/ads_filter_view.dart @@ -117,19 +117,21 @@ class _AdsFilterViewState extends State { const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7), SearchEntityWidget( title: LocaleKeys.searchByVehicleYear.tr(), - actionWidget: Builder(builder: (context) { - List vehicleBrandsDrop = []; - for (var element in adVM.vehicleModelYears) { - vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.modelYear ?? "", "")); - } - return DropdownField( - (DropValue value) => adVM.updateSelectionVehicleModelYearId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true), - list: vehicleBrandsDrop, - dropdownValue: adVM.vehicleModelYearId.selectedId != -1 ? DropValue(adVM.vehicleModelYearId.selectedId, adVM.vehicleModelYearId.selectedOption, "") : null, - hint: LocaleKeys.selectYear.tr(), - errorValue: adVM.vehicleModelYearId.errorValue, - ); - }), + actionWidget: Builder( + builder: (context) { + List vehicleBrandsDrop = []; + for (var element in adVM.vehicleModelYears) { + vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.modelYear ?? "", "")); + } + return DropdownField( + (DropValue value) => adVM.updateSelectionVehicleModelYearId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true), + list: vehicleBrandsDrop, + dropdownValue: adVM.vehicleModelYearId.selectedId != -1 ? DropValue(adVM.vehicleModelYearId.selectedId, adVM.vehicleModelYearId.selectedOption, "") : null, + hint: LocaleKeys.selectYear.tr(), + errorValue: adVM.vehicleModelYearId.errorValue, + ); + }, + ), historyContent: adVM.vehicleYearAdSearchHistory, onHistoryItemDeleted: (index) => adVM.removeVehicleYearAdSearchHistory(index: index), onHistoryItemTapped: (DropValue value) => null, @@ -167,7 +169,10 @@ class _AdsFilterViewState extends State { isNeedClickAll: true, postFixDataColor: MyColors.darkTextColor, onTap: () async { - final formattedDate = await Utils.pickDateFromDatePicker(context); + final formattedDate = await Utils.pickDateFromDatePicker(context, firstDate: DateTime(2020), lastDate: DateTime.now()); + if (formattedDate.isEmpty) { + return; + } adVM.updateSelectionDurationStartDate(formattedDate); DropValue value = DropValue(0, formattedDate, ""); adVM.addToVehicleAdCreatedDateSearchHistory(value: value); diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index 448eb52..2799868 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -165,6 +165,7 @@ class _ChatViewState extends State { ChatMessageModel chatMessageModel = chatMessages[index]; return ChatMessageCustomWidget( chatMessageModel: chatMessageModel, + requestModel: chatViewArgumentsForRequest!.requestModel!, requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus, chatTypeEnum: chatTypeEnum, requestsTypeEnum: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel!.requestType.toRequestTypeEnum() : RequestsTypeEnum.specialCarRequest, @@ -218,7 +219,10 @@ class _ChatViewState extends State { ), ), ] else ...[ - if (AppState().currentAppType == AppType.provider && chatTypeEnum == ChatTypeEnum.requestOffer && requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted && chatVM.pickedImagesForMessage.isEmpty) ...[ + if (AppState().currentAppType == AppType.provider && + chatTypeEnum == ChatTypeEnum.requestOffer && + requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted && + chatVM.pickedImagesForMessage.isEmpty) ...[ Expanded( flex: 1, child: const Icon( @@ -227,8 +231,16 @@ class _ChatViewState extends State { size: 30, ).onPress( () { - RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(requestIndex: chatViewArgumentsForRequest!.requestIndex, requestModel: chatViewArgumentsForRequest!.requestModel!); - buildSendOfferBottomSheet(context: context, requestDetailPageArguments: requestDetailArguments, isFromChatScreen: true); + RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments( + requestIndex: chatViewArgumentsForRequest!.requestIndex, + requestModel: chatViewArgumentsForRequest!.requestModel!, + ); + buildSendOfferBottomSheet( + context: context, + requestDetailPageArguments: requestDetailArguments, + isFromChatScreen: true, + offerId: null, // null means creating new offer + ); }, ), ), diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index d4d2e3e..d9bd137 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -9,13 +9,16 @@ 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/models/requests_models/request_model.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/utils/utils.dart'; +import 'package:mc_common_app/view_models/ad_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/requests/request_bottomsheets.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'; @@ -29,14 +32,130 @@ class ChatMessageCustomWidget extends StatefulWidget { final RequestStatusEnum? requestStatusEnum; final ChatTypeEnum chatTypeEnum; final RequestsTypeEnum requestsTypeEnum; + final RequestModel requestModel; - const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum, required this.chatTypeEnum, this.requestsTypeEnum = RequestsTypeEnum.specialCarRequest}); + const ChatMessageCustomWidget({ + super.key, + required this.chatMessageModel, + required this.requestStatusEnum, + required this.chatTypeEnum, + required this.requestModel, + this.requestsTypeEnum = RequestsTypeEnum.specialCarRequest, + }); @override State createState() => _ChatMessageCustomWidgetState(); } class _ChatMessageCustomWidgetState extends State { + Future buildDealBottomSheetForSpecialCar({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { + return InfoBottomSheet( + 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( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 12.height, + ListView( + children: [ + CheckBoxWithTitleDescription( + isSelected: chatVM.dealOptionsModelList[0].isSelected!, + title: '${chatVM.dealOptionsModelList[0].title}', + description: '', + onSelection: (bool value) { + chatVM.updateIsSelectedInDealOptionsModelList(0, value); + }, + ), + CheckBoxWithTitleDescription( + isSelected: chatVM.dealOptionsModelList[1].isSelected ?? false, + title: '${chatVM.dealOptionsModelList[1].title}', + description: '', + onSelection: (bool value) { + chatVM.updateIsSelectedInDealOptionsModelList(1, value); + }, + ), + ], + ), + if (chatVM.dealOptionsModelList[1].isSelected!) ...[ + 12.height, + TxtField( + maxLines: 5, + value: chatVM.rejectOfferDescription, + errorValue: chatVM.rejectOfferDescriptionError, + keyboardType: TextInputType.text, + hint: LocaleKeys.description.tr(), + onChanged: (v) => chatVM.updateRejectOfferDescription(v), + ), + ], + ], + ), + 25.height, + ShowFillButton( + title: LocaleKeys.submit.tr(), + onPressed: () async { + String comments = ""; + if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) //Other + { + comments = chatVM.rejectOfferDescription; + } else { + comments = chatVM.selectedOfferRequestCommentModel.title ?? ""; + } + if (!chatVM.isRejectOfferButtonValidated()) { + return; + } + Navigator.pop(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) { + final requestVM = context.read(); + chatMessageModel.reqOffer!.requestOfferStatusEnum = requestOfferStatusEnum; + int index = chatVM.serviceProviderOffersList + .indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId)); + + if (index != -1) { + chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum; + } + setState(() {}); + // Navigator.pop(context); + chatVM.updateRejectOfferDescription(''); + Utils.showToast("Offer ${requestOfferStatusEnum == RequestOfferStatusEnum.rejected ? "Rejected" : "Cancelled"}"); + // navigateReplaceWithName(context, AppRoutes.dashboard); + } + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + )); + }); + }, + ); + } + Future buildRejectOrCancelOfferBottomSheet({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) { return showModalBottomSheet( context: context, @@ -149,7 +268,8 @@ class _ChatMessageCustomWidgetState extends State { if (status) { final requestVM = context.read(); chatMessageModel.reqOffer!.requestOfferStatusEnum = requestOfferStatusEnum; - int index = chatVM.serviceProviderOffersList.indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId)); + int index = chatVM.serviceProviderOffersList + .indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId)); if (index != -1) { chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum; @@ -207,7 +327,8 @@ class _ChatMessageCustomWidgetState extends State { requestVM.currentSelectedRequest!.requestStatus = RequestStatusEnum.inProgress; requestVM.updateAcceptedReqOffer(chatMessageModel.reqOffer!); requestVM.updateAcceptedRequestOfferProviderName(chatMessageModel.senderName ?? ""); - int index = chatVM.serviceProviderOffersList.indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId)); + int index = chatVM.serviceProviderOffersList + .indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId)); if (index != -1) { chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum; @@ -281,7 +402,8 @@ class _ChatMessageCustomWidgetState extends State { fontSize: 12, isItalic: true, ), - ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: chatMessageModel.isMyMessage! ? MyColors.adPendingStatusColor.withOpacity(0.16) : MyColors.grey98Color.withOpacity(0.1)), + ).toContainer( + borderRadius: 40, width: double.infinity, backgroundColor: chatMessageModel.isMyMessage! ? MyColors.adPendingStatusColor.withOpacity(0.16) : MyColors.grey98Color.withOpacity(0.1)), ], ); } @@ -294,7 +416,8 @@ class _ChatMessageCustomWidgetState extends State { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - "${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: AppState().currentAppType == AppType.provider ? MyColors.white : MyColors.darkTextColor), + "${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}" + .toText(fontSize: 19, isBold: true, color: AppState().currentAppType == AppType.provider ? MyColors.white : MyColors.darkTextColor), 5.width, LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), ], @@ -468,11 +591,37 @@ class _ChatMessageCustomWidgetState extends State { return widget.toCircle(borderRadius: 100); } + Future onOfferEditIconPressed() async { + int index = context.read().myFilteredRequests.indexWhere((request) => request.id == widget.requestModel.id); + RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments( + requestIndex: index, // Not getting used in case of update offer + requestModel: widget.requestModel, + ); + RequestsVM requestVM = context.read(); + requestVM.resetSendOfferBottomSheet(); + ReqOffer offer = widget.chatMessageModel.reqOffer!; + requestVM.updateOfferPrice((offer.price ?? "").toString()); + requestVM.updateServiceItem((offer.serviceItemName ?? "").toString()); + requestVM.updateItemManufacturer((offer.manufacturedById ?? "").toString()); + requestVM.updateServiceItemCreatedOn((offer.manufacturedOn ?? "").toString()); + requestVM.updateOfferDescription((widget.chatMessageModel.chatText ?? "").toString()); + requestVM.updateIsDeliveryAvailableStatus((offer.isDeliveryAvailable ?? false)); + if (offer.reqOfferImages != null && offer.reqOfferImages!.isNotEmpty) { + for (var element in offer.reqOfferImages!) { + if (element.imageUrl != null) { + ImageModel imageModel = ImageModel(id: element.id, filePath: element.imageUrl, isFromNetwork: true); + requestVM.addImageToPickedVehicleImages(imageModel); + } + } + } + + buildSendOfferBottomSheet(context: context, requestDetailPageArguments: requestDetailArguments, isFromChatScreen: true, offerId: offer.id); + } + Widget buildFreeTextDetailsInMessage({required ChatMessageTypeEnum chatMessageTypeEnum}) { return Row( - // crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (chatMessageTypeEnum == ChatMessageTypeEnum.offer && (widget.chatMessageModel.isMyMessage == true) && widget.chatMessageModel.isRead == true) ...[ + if (chatMessageTypeEnum == ChatMessageTypeEnum.offer && (widget.chatMessageModel.isMyMessage == true) && (widget.chatMessageModel.isRead ?? false)) ...[ Row( children: [ const Icon(Icons.remove_red_eye_outlined, size: 12, color: MyColors.lightTextColor), @@ -480,6 +629,8 @@ class _ChatMessageCustomWidgetState extends State { LocaleKeys.viewed.tr().toText(fontSize: 10, color: MyColors.lightTextColor, fontWeight: MyFonts.Medium), ], ), + ] else if (AppState().currentAppType == AppType.provider && chatMessageTypeEnum == ChatMessageTypeEnum.offer && (widget.chatMessageModel.isMyMessage == true)) ...[ + MyAssets.icEdit.buildSvg(color: MyColors.white, height: 15).onPress(() => onOfferEditIconPressed()), ], Expanded( child: Directionality( @@ -487,7 +638,6 @@ class _ChatMessageCustomWidgetState extends State { child: (widget.chatMessageModel.chatText ?? "").toText( color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, fontSize: 12, - // isBold: true, ), ), ), @@ -518,12 +668,12 @@ class _ChatMessageCustomWidgetState extends State { }); } return SizedBox( - // height: (gridItemSize * 2) + (spacing * 2), // Fixed height for 2 rows including spacing +// height: (gridItemSize * 2) + (spacing * 2), // Fixed height for 2 rows including spacing child: GridView.builder( physics: const NeverScrollableScrollPhysics(), - // Prevent scrolling inside grid +// Prevent scrolling inside grid shrinkWrap: true, - // Shrink size to fit the content +// Shrink size to fit the content gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, // Show 2 images per row crossAxisSpacing: spacing, diff --git a/lib/views/requests/request_bottomsheets.dart b/lib/views/requests/request_bottomsheets.dart index 01ad4be..ce83bce 100644 --- a/lib/views/requests/request_bottomsheets.dart +++ b/lib/views/requests/request_bottomsheets.dart @@ -21,8 +21,14 @@ import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; -Future buildSendOfferBottomSheet({required BuildContext context, required RequestDetailPageArguments requestDetailPageArguments, required bool isFromChatScreen}) { +Future buildSendOfferBottomSheet({ + required BuildContext context, + required RequestDetailPageArguments requestDetailPageArguments, + required bool isFromChatScreen, + required int? offerId, +}) { final requestDetail = requestDetailPageArguments.requestModel; + final requestIndex = requestDetailPageArguments.requestIndex; return showModalBottomSheet( context: context, isScrollControlled: true, @@ -134,24 +140,42 @@ Future buildSendOfferBottomSheet({required BuildContext context, required Reques ), 25.height, ShowFillButton( - title: LocaleKeys.submit.tr(), + title: offerId == null ? LocaleKeys.submit.tr() : LocaleKeys.update.tr(), maxHeight: 55, onPressed: () { - requestsVM.onSendOfferPressed( - context: context, - receiverId: requestDetail.customerID, - message: requestsVM.offerDescription, - requestId: requestDetail.id, - offerPrice: requestsVM.offerPrice, - requestModel: requestDetail, - isDeliveryAvailable: requestsVM.isDeliveryAvailableStatus, - requestIndex: requestDetailPageArguments.requestIndex, - isFromChatScreen: isFromChatScreen, - manufacturedById: 1, - // Todo: It should be the ID of the manufacturer - manufacturedOn: requestsVM.serviceItemCreatedOn, - serviceItemName: requestsVM.serviceItem, - ); + if (offerId == null) { + requestsVM.onSendOfferPressed( + context: context, + offerId: offerId, + receiverId: requestDetail.customerID, + message: requestsVM.offerDescription, + requestId: requestDetail.id, + offerPrice: requestsVM.offerPrice, + requestModel: requestDetail, + isDeliveryAvailable: requestsVM.isDeliveryAvailableStatus, + requestIndex: requestDetailPageArguments.requestIndex, + isFromChatScreen: isFromChatScreen, + manufacturedById: 1, + // Todo: It should be the ID of the manufacturer + manufacturedOn: requestsVM.serviceItemCreatedOn, + serviceItemName: requestsVM.serviceItem, + ); + } else { + requestsVM.updateOfferFromProvider( + message: requestsVM.offerDescription, + requestId: requestDetail.id, + offerId: offerId, + offerPrice: requestsVM.offerPrice, + isDeliveryAvailable: requestsVM.isDeliveryAvailableStatus, + serviceItemName: requestsVM.serviceItem, + manufacturedById: 1, + // Todo: It should be the ID of the manufacturer + manufacturedOn: requestsVM.serviceItemCreatedOn, + receiverId: requestDetail.customerId, + customerRequestIndex: requestIndex, + context: context, + ); + } }, maxWidth: double.infinity, ), @@ -164,6 +188,8 @@ Future buildSendOfferBottomSheet({required BuildContext context, required Reques ); } + + Future buildRequestStatusUpdateSheetForProvider({required BuildContext mainContext, required int requestId}) { return showModalBottomSheet( context: mainContext, diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index f1230ff..efb2ddf 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -159,7 +159,12 @@ class RequestDetailPage extends StatelessWidget { title: LocaleKeys.provideOffer.tr(), isBold: false, fontSize: 18, - onPressed: () => buildSendOfferBottomSheet(context: context, requestDetailPageArguments: requestDetailPageArguments, isFromChatScreen: false), + onPressed: () => buildSendOfferBottomSheet( + context: context, + requestDetailPageArguments: requestDetailPageArguments, + isFromChatScreen: false, + offerId: null, // null means creating new offer + ), ), ] else ...[ buildRequestDetailActionFooter( From 58db44969f81d4eb2062b6eaf2c4fce1b89edd2b Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Mon, 2 Dec 2024 01:47:22 +0300 Subject: [PATCH 02/10] dEAL COMPLIETDED --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 7 +- lib/generated/codegen_loader.g.dart | 12 +- lib/generated/locale_keys.g.dart | 4 + lib/view_models/chat_view_model.dart | 25 ++- lib/view_models/requests_view_model.dart | 1 + lib/views/chat/chat_view.dart | 9 +- .../chat/widgets/chat_bottom_sheets.dart | 3 +- .../chat/widgets/chat_message_widget.dart | 164 +++++++++++++----- lib/widgets/checkbox_with_title_desc.dart | 56 +++--- 10 files changed, 198 insertions(+), 89 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 17041ca..51434df 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -742,5 +742,9 @@ "paymentType": "نوع الدفع", "searchByCreatedDate": "البحث حسب تاريخ الإنشاء", "dealCompleted": "تم إتمام الصفقة", - "theDealNotCompleted": "لم تكتمل الصفقة" + "theDealNotCompleted": "لم تكتمل الصفقة", + "cancelRequest": "أريد إلغاء الطلب.", + "highPrice": "سعر مرتفع", + "offerNotMatched": "العرض لم يتطابق مع الطلب.", + "offerRejected": "تم رفض العرض." } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 12adf0c..3e7d6d4 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -740,5 +740,10 @@ "paymentType": "Payment Type", "searchByCreatedDate": "Search By Created Date", "dealCompleted": "The Deal Completed", - "theDealNotCompleted": "The Deal Not Completed" + "theDealNotCompleted": "The Deal Not Completed", + "cancelRequest": "I want to cancel the request.", + "highPrice": "High Price", + "offerNotMatched": "The offer did not match the request.", + "offerRejected": "Offer has been Rejected." + } \ No newline at end of file diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index d98bfba..b3152cc 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -758,7 +758,11 @@ class CodegenLoader extends AssetLoader{ "paymentType": "نوع الدفع", "searchByCreatedDate": "البحث حسب تاريخ الإنشاء", "dealCompleted": "تم إتمام الصفقة", - "theDealNotCompleted": "لم تكتمل الصفقة" + "theDealNotCompleted": "لم تكتمل الصفقة", + "cancelRequest": "أريد إلغاء الطلب.", + "highPrice": "سعر مرتفع", + "offerNotMatched": "العرض لم يتطابق مع الطلب.", + "offerRejected": "تم رفض العرض." }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1502,7 +1506,11 @@ static const Map en_US = { "paymentType": "Payment Type", "searchByCreatedDate": "Search By Created Date", "dealCompleted": "The Deal Completed", - "theDealNotCompleted": "The Deal Not Completed" + "theDealNotCompleted": "The Deal Not Completed", + "cancelRequest": "I want to cancel the request.", + "highPrice": "High Price", + "offerNotMatched": "The offer did not match the request.", + "offerRejected": "Offer has been Rejected." }; 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 b866df3..0813115 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -722,5 +722,9 @@ abstract class LocaleKeys { static const searchByCreatedDate = 'searchByCreatedDate'; static const dealCompleted = 'dealCompleted'; static const theDealNotCompleted = 'theDealNotCompleted'; + static const cancelRequest = 'cancelRequest'; + static const highPrice = 'highPrice'; + static const offerNotMatched = 'offerNotMatched'; + static const offerRejected = 'offerRejected'; } diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index f7631b6..0a9495d 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; @@ -23,7 +24,6 @@ import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; import 'package:provider/provider.dart'; import 'package:signalr_core/signalr_core.dart'; -import 'package:easy_localization/easy_localization.dart'; class ChatVM extends BaseVM { final ChatRepo chatRepo; @@ -94,11 +94,13 @@ class ChatVM extends BaseVM { element.isSelected = false; } dealOptionsModelList[index].isSelected = true; + notifyListeners(); } - List indexesForCancelSpecialCarOffer = [0, 1, 6, 7]; - List indexesForCancelSparePartOffer = [2, 7]; - List indexesForRejectOffer = [3, 4, 5, 7]; + List indexesForCancelSpecialCarOffer = [0, 1, 6, 10]; + List indexesForCancelSparePartOffer = [2, 10]; + List indexesForRejectOffer = [3, 4, 5, 10]; + List indexesForDealNotCompleted = [7, 4, 8, 9, 10]; List offerRejectModelList = [ OfferRequestCommentModel( @@ -139,6 +141,21 @@ class ChatVM extends BaseVM { OfferRequestCommentModel( index: 7, isSelected: false, + title: LocaleKeys.cancelRequest.tr(), + ), + OfferRequestCommentModel( + index: 8, + isSelected: false, + title: LocaleKeys.offerNotMatched.tr(), + ), + OfferRequestCommentModel( + index: 9, + isSelected: false, + title: LocaleKeys.testTheService.tr(), + ), + OfferRequestCommentModel( + index: 10, + isSelected: false, title: LocaleKeys.otherVar.tr(), ), ]; diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index d752a7e..8eb08ab 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/classes/app_state.dart'; diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index 2799868..97435cc 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -1,10 +1,6 @@ -import 'dart:developer'; - -import 'package:flutter/cupertino.dart'; +import 'package:easy_localization/easy_localization.dart' as lcl; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.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'; @@ -21,14 +17,11 @@ import 'package:mc_common_app/views/advertisement/components/picked_images_conta 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/views/setting_options/widgets/custom_setting_options_tile.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/info_bottom_sheet.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' as lcl; class ChatView extends StatefulWidget { final ChatViewArguments chatViewArguments; diff --git a/lib/views/chat/widgets/chat_bottom_sheets.dart b/lib/views/chat/widgets/chat_bottom_sheets.dart index 2c9bc16..c89c2d1 100644 --- a/lib/views/chat/widgets/chat_bottom_sheets.dart +++ b/lib/views/chat/widgets/chat_bottom_sheets.dart @@ -9,7 +9,6 @@ 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/base_view_model.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'; @@ -53,7 +52,7 @@ void dealCompletedConfirmationBottomSheet({required BuildContext mainContext, re if (status) { chatVM.updateAcknowledgePaymentToMowaterStatus(false); mainContext.read().onNavbarTapped(4); - navigateReplaceWithName(mainContext, AppRoutes.dashboard); + navigateReplaceWithNameUntilRoute(mainContext, AppRoutes.dashboard); } }, ), diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index d9bd137..5f3ffa2 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -1,6 +1,5 @@ -import 'dart:developer'; - import 'package:cached_network_image/cached_network_image.dart'; +import 'package:easy_localization/easy_localization.dart' as lcl; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; @@ -18,6 +17,7 @@ import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/view_models/chat_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/requests/request_bottomsheets.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart'; @@ -25,7 +25,6 @@ import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.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' as lcl; class ChatMessageCustomWidget extends StatefulWidget { final ChatMessageModel chatMessageModel; @@ -48,7 +47,7 @@ class ChatMessageCustomWidget extends StatefulWidget { } class _ChatMessageCustomWidgetState extends State { - Future buildDealBottomSheetForSpecialCar({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) { + Future buildDealNotCompletedBottomSheetOptions({required ChatMessageModel chatMessageModel}) { return showModalBottomSheet( context: context, isScrollControlled: true, @@ -62,53 +61,57 @@ class _ChatMessageCustomWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 12.height, - ListView( - children: [ - CheckBoxWithTitleDescription( - isSelected: chatVM.dealOptionsModelList[0].isSelected!, - title: '${chatVM.dealOptionsModelList[0].title}', - description: '', - onSelection: (bool value) { - chatVM.updateIsSelectedInDealOptionsModelList(0, value); - }, - ), - CheckBoxWithTitleDescription( - isSelected: chatVM.dealOptionsModelList[1].isSelected ?? false, - title: '${chatVM.dealOptionsModelList[1].title}', - description: '', - onSelection: (bool value) { - chatVM.updateIsSelectedInDealOptionsModelList(1, value); - }, - ), - ], - ), - if (chatVM.dealOptionsModelList[1].isSelected!) ...[ - 12.height, - TxtField( - maxLines: 5, - value: chatVM.rejectOfferDescription, - errorValue: chatVM.rejectOfferDescriptionError, - keyboardType: TextInputType.text, - hint: LocaleKeys.description.tr(), - onChanged: (v) => chatVM.updateRejectOfferDescription(v), - ), - ], - ], + 12.height, + ListView.separated( + shrinkWrap: true, + itemCount: chatVM.offerRejectModelList.length, + separatorBuilder: (BuildContext context, int index) { + bool indexContains = chatVM.indexesForDealNotCompleted.indexWhere((i) => i == index) != -1; + if ((!indexContains)) { + return const SizedBox(); + } + + return const Divider(thickness: 0.5); + }, + itemBuilder: (BuildContext context, int index) { + bool indexContains = chatVM.indexesForDealNotCompleted.indexWhere((i) => i == index) != -1; + + if (!indexContains) { + return const SizedBox(); + } + + OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index]; + return CircleCheckBoxWithTitle( + isChecked: offerRequestCommentModel.isSelected ?? false, + title: '${offerRequestCommentModel.title}', + onSelected: () { + chatVM.updateSelectionInOfferRejectModelList(index); + }, + selectedColor: MyColors.darkPrimaryColor, + ); + }, ), + if (chatVM.offerRejectModelList.last.isSelected ?? false) ...[ + // comparing if the "other" is selected + 12.height, + TxtField( + maxLines: 5, + value: chatVM.rejectOfferDescription, + errorValue: chatVM.rejectOfferDescriptionError, + keyboardType: TextInputType.text, + hint: LocaleKeys.description.tr(), + onChanged: (v) => chatVM.updateRejectOfferDescription(v), + ), + ], 25.height, ShowFillButton( title: LocaleKeys.submit.tr(), onPressed: () async { String comments = ""; - if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) //Other - { + if (chatVM.offerRejectModelList.last.isSelected ?? false) { comments = chatVM.rejectOfferDescription; } else { - comments = chatVM.selectedOfferRequestCommentModel.title ?? ""; + comments = chatVM.offerRejectModelList.firstWhere((element) => element.isSelected!).title ?? ""; } if (!chatVM.isRejectOfferButtonValidated()) { return; @@ -125,13 +128,13 @@ class _ChatMessageCustomWidgetState extends State { serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "", manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "", manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0, - requestOfferStatusEnum: requestOfferStatusEnum, + requestOfferStatusEnum: RequestOfferStatusEnum.rejected, context: context, ); if (status) { final requestVM = context.read(); - chatMessageModel.reqOffer!.requestOfferStatusEnum = requestOfferStatusEnum; + chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected; int index = chatVM.serviceProviderOffersList .indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId)); @@ -139,9 +142,8 @@ class _ChatMessageCustomWidgetState extends State { chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum; } setState(() {}); - // Navigator.pop(context); chatVM.updateRejectOfferDescription(''); - Utils.showToast("Offer ${requestOfferStatusEnum == RequestOfferStatusEnum.rejected ? "Rejected" : "Cancelled"}"); + Utils.showToast(LocaleKeys.offerRejected.tr()); // navigateReplaceWithName(context, AppRoutes.dashboard); } }, @@ -156,6 +158,68 @@ class _ChatMessageCustomWidgetState extends State { ); } + Future buildDealBottomSheetOptionsForSpecialCar({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { + return InfoBottomSheet( + 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( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 12.height, + CheckBoxWithTitleDescription( + isSelected: chatVM.dealOptionsModelList[0].isSelected!, + title: '${chatVM.dealOptionsModelList[0].title}', + description: '', + onSelection: (bool value) { + chatVM.updateIsSelectedInDealOptionsModelList(0, value); + }, + ), + CheckBoxWithTitleDescription( + isSelected: chatVM.dealOptionsModelList[1].isSelected ?? false, + title: '${chatVM.dealOptionsModelList[1].title}', + description: '', + onSelection: (bool value) { + chatVM.updateIsSelectedInDealOptionsModelList(1, value); + }, + ), + ], + ), + 25.height, + ShowFillButton( + title: LocaleKeys.continu.tr(), + onPressed: () async { + if (chatVM.dealOptionsModelList[0].isSelected ?? false) { + final requestVM = context.read(); + return dealCompletedConfirmationBottomSheet( + mainContext: context, + requestStatusEnum: RequestStatusEnum.completed, + requestId: requestVM.currentSelectedRequest!.id, + ); + } else { + buildDealNotCompletedBottomSheetOptions(chatMessageModel: chatMessageModel); + } + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + )); + }); + }, + ); + } + Future buildRejectOrCancelOfferBottomSheet({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) { return showModalBottomSheet( context: context, @@ -434,7 +498,11 @@ class _ChatMessageCustomWidgetState extends State { borderColor: MyColors.greenColor, isFilled: false, onPressed: () { - offerAcceptConfirmationBottomSheet(chatMessageModel: chatMessageModel); + if (widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest) { + buildDealBottomSheetOptionsForSpecialCar(chatMessageModel: chatMessageModel, requestOfferStatusEnum: requestOfferStatusEnum); + } else { + offerAcceptConfirmationBottomSheet(chatMessageModel: chatMessageModel); + } }, backgroundColor: MyColors.white, txtColor: MyColors.greenColor, diff --git a/lib/widgets/checkbox_with_title_desc.dart b/lib/widgets/checkbox_with_title_desc.dart index 73a9a2a..595c461 100644 --- a/lib/widgets/checkbox_with_title_desc.dart +++ b/lib/widgets/checkbox_with_title_desc.dart @@ -21,31 +21,41 @@ class CheckBoxWithTitleDescription extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox( - width: double.infinity, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Checkbox( - value: isSelected, - activeColor: isDisabled ? MyColors.lightIconColor : MyColors.darkPrimaryColor, - onChanged: (bool? v) { - if (isDisabled) return; - onSelection(v ?? false); + return GestureDetector( + onTap: isDisabled + ? null + : () { + onSelection(!isSelected); }, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - title.toText(fontSize: 14, isBold: true), - description.toText(fontSize: 12, color: MyColors.lightTextColor), - ], + child: SizedBox( + width: double.infinity, + child: Row( + crossAxisAlignment: description.isNotEmpty ? CrossAxisAlignment.start : CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Checkbox( + value: isSelected, + activeColor: isDisabled ? MyColors.lightIconColor : MyColors.darkPrimaryColor, + onChanged: isDisabled + ? null + : (bool? v) { + onSelection(v ?? false); + }, ), - ), - ], + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + title.toText(fontSize: 14, isBold: true), + if (description.isNotEmpty) ...[ + description.toText(fontSize: 12, color: MyColors.lightTextColor), + ] + ], + ), + ), + ], + ), ), ); } From 30fc25c23637fbe6fb999b534046b483e8883d93 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Mon, 2 Dec 2024 18:01:36 +0300 Subject: [PATCH 03/10] Duration Discount --- assets/langs/ar-SA.json | 4 +- assets/langs/en-US.json | 5 +- lib/classes/consts.dart | 2 +- lib/extensions/string_extensions.dart | 2 +- lib/generated/codegen_loader.g.dart | 8 +- lib/generated/locale_keys.g.dart | 2 + .../ads_duration_model.dart | 16 +++ lib/repositories/ads_repo.dart | 2 + lib/view_models/requests_view_model.dart | 19 ++- .../ad_duration_selection_sheet.dart | 73 +++++++++-- .../picked_images_container_widget.dart | 12 +- lib/views/chat/chat_view.dart | 49 ++++---- .../chat/widgets/chat_bottom_sheets.dart | 88 ++++++++++---- .../chat/widgets/chat_message_widget.dart | 115 +++++++++++++++--- lib/views/common_fragments/ads_fragment.dart | 1 + lib/views/requests/request_bottomsheets.dart | 4 +- lib/views/requests/request_detail_page.dart | 7 +- 17 files changed, 308 insertions(+), 101 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 51434df..0dc0ced 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -746,5 +746,7 @@ "cancelRequest": "أريد إلغاء الطلب.", "highPrice": "سعر مرتفع", "offerNotMatched": "العرض لم يتطابق مع الطلب.", - "offerRejected": "تم رفض العرض." + "offerRejected": "تم رفض العرض.", + "offerAccepted": "تم قبول العرض.", + "you": "أنتم" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 3e7d6d4..2f2e648 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -744,6 +744,7 @@ "cancelRequest": "I want to cancel the request.", "highPrice": "High Price", "offerNotMatched": "The offer did not match the request.", - "offerRejected": "Offer has been Rejected." - + "offerRejected": "Offer has been Rejected.", + "offerAccepted": "Offer has been Accepted.", + "you": "You" } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 8d591bc..ea28136 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -164,7 +164,7 @@ class ApiConsts { static String getRequest = "${baseUrlServices}api/RequestManagement/Request_Get"; static String getRequestOffers = "${baseUrlServices}api/RequestManagement/ReqOffer_Get"; static String updateRequestOfferStatus = "${baseUrlServices}api/RequestManagement/RequestOffer_UpdateStatus"; - static String updateRequestOffer = "${baseUrlServices}api/RequestManagement/ReqOffer_Upadte"; + static String updateRequestOffer = "${baseUrlServices}api/RequestManagement/ReqOffer_Update"; 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"; diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 674bf8d..bf4948a 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -796,7 +796,7 @@ extension FormatMonthByNumber on String { extension NameExtensions on String { String getInitials() { final name = trim(); - String initials = "U"; + String initials = ""; if (name.isEmpty) { return initials; } diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index b3152cc..c62ded1 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -762,7 +762,9 @@ class CodegenLoader extends AssetLoader{ "cancelRequest": "أريد إلغاء الطلب.", "highPrice": "سعر مرتفع", "offerNotMatched": "العرض لم يتطابق مع الطلب.", - "offerRejected": "تم رفض العرض." + "offerRejected": "تم رفض العرض.", + "offerAccepted": "تم قبول العرض.", + "you": "أنتم" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1510,7 +1512,9 @@ static const Map en_US = { "cancelRequest": "I want to cancel the request.", "highPrice": "High Price", "offerNotMatched": "The offer did not match the request.", - "offerRejected": "Offer has been Rejected." + "offerRejected": "Offer has been Rejected.", + "offerAccepted": "Offer has been Accepted.", + "you": "You" }; 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 0813115..4ba56cc 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -726,5 +726,7 @@ abstract class LocaleKeys { static const highPrice = 'highPrice'; static const offerNotMatched = 'offerNotMatched'; static const offerRejected = 'offerRejected'; + static const offerAccepted = 'offerAccepted'; + static const you = 'you'; } diff --git a/lib/models/advertisment_models/ads_duration_model.dart b/lib/models/advertisment_models/ads_duration_model.dart index 4602dcf..c435569 100644 --- a/lib/models/advertisment_models/ads_duration_model.dart +++ b/lib/models/advertisment_models/ads_duration_model.dart @@ -7,6 +7,10 @@ class AdsDurationModel { bool? isActive; String? countryName; String? currency; + double? discountValue; + double? discountPercent; + double? priceAfterDiscount; + int? roleID; bool? isSelected; AdsDurationModel({ @@ -18,6 +22,10 @@ class AdsDurationModel { this.isActive, this.countryName, this.currency, + this.discountValue, + this.discountPercent, + this.priceAfterDiscount, + this.roleID, this.isSelected, }); @@ -30,6 +38,10 @@ class AdsDurationModel { isActive = json['isActive']; countryName = json['countryName']; currency = json['currency']; + discountValue = json['discountValue']; + discountPercent = json['discountPercent']; + priceAfterDiscount = json['priceAfterDiscount']; + roleID = json['roleID']; isSelected = false; } @@ -43,6 +55,10 @@ class AdsDurationModel { data['isActive'] = isActive; data['countryName'] = countryName; data['currency'] = currency; + data['discountValue'] = discountValue; + data['discountPercent'] = discountPercent; + data['priceAfterDiscount'] = priceAfterDiscount; + data['roleID'] = roleID; return data; } } diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index dfee80b..d5d83be 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -62,8 +62,10 @@ class AdsRepoImp implements AdsRepo { @override Future> getAdsDuration({required int? countryId}) async { + int roleID = appState.getUser!.data!.userInfo!.roleId ?? 0; var param = { "CountryID": countryId.toString() ?? "0", + "roleID": roleID.toString() ?? "0", }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index 8eb08ab..dcfebf1 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -894,17 +894,23 @@ class RequestsVM extends BaseVM { notifyListeners(); } - Future onActionRequestTapped({required BuildContext context, required RequestStatusEnum requestStatusEnum, required int requestId}) async { - Utils.showLoading(context); + Future onActionRequestTapped({required BuildContext context, required RequestStatusEnum requestStatusEnum, required int requestId, bool needLoading = true}) async { + if (needLoading) { + Utils.showLoading(context); + } try { GenericRespModel genericRespModel = await requestRepo.updateRequestStatus(requestStatusEnum: requestStatusEnum, requestId: requestId); Utils.showToast(genericRespModel.message.toString()); - Utils.hideLoading(context); + if (needLoading) { + Utils.hideLoading(context); + } return genericRespModel.messageStatus == 1; } catch (e) { logger.i(e.toString()); - Utils.showToast(e.toString()); - Utils.hideLoading(context); + // Utils.showToast(e.toString()); + if (needLoading) { + Utils.hideLoading(context); + } return false; } } @@ -1030,7 +1036,8 @@ class RequestsVM extends BaseVM { await chatVM.getRequestsChatMessagesForProvider( context: context, customerId: receiverId, - requestOfferId: offerId, + requestOfferId: 0, + // to get all the messages requestId: requestId, customerRequestIndex: customerRequestIndex, ); diff --git a/lib/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart b/lib/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart index c4fe29b..7f13c89 100644 --- a/lib/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart +++ b/lib/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart @@ -66,17 +66,51 @@ class AdDurationSelectionSheet extends StatelessWidget { ), 12.height, LocaleKeys.adCharges.tr().toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - "${adDuration.price}".toText(fontSize: 22, isBold: true), - 2.width, - Padding( - padding: const EdgeInsets.only(bottom: 4), - child: (adDuration.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - ), - ], - ), + if (adDuration.discountPercent != null && adDuration.discountPercent! > 0.0) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + CustomPaint( + painter: DiagonalLinePainter(), + child: "${adDuration.price}".toText( + fontSize: 16, + isBold: true, + )), + 2.width, + (adDuration.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 8, color: MyColors.lightTextColor, isBold: true), + 10.width, + "${adDuration.priceAfterDiscount}".toText(fontSize: 20, isBold: true), + 2.width, + Padding( + padding: const EdgeInsets.only(bottom: 4), + child: (adDuration.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 10, color: MyColors.lightTextColor, isBold: true), + ), + ], + ), + "${adDuration.discountPercent} % OFF".toText(color: MyColors.white, fontSize: 10).toContainer( + marginAll: 5, + paddingAll: 5, + borderRadius: 4, + backgroundColor: MyColors.greenColor, + ), + ], + ), + ] else ...[ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "${adDuration.price}".toText(fontSize: 22, isBold: true), + 2.width, + Padding( + padding: const EdgeInsets.only(bottom: 4), + child: (adDuration.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + ), + ], + ), + ] ], ), ), @@ -160,3 +194,20 @@ class AdDurationSelectionSheet extends StatelessWidget { ); } } + +class DiagonalLinePainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final Paint paint = Paint() + ..color = MyColors.redColor + ..strokeWidth = 3; + + // Draw the diagonal line from top-left to bottom-right + canvas.drawLine(const Offset(0, 0), Offset(size.width, size.height), paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return false; + } +} diff --git a/lib/views/advertisement/components/picked_images_container_widget.dart b/lib/views/advertisement/components/picked_images_container_widget.dart index 1e134bc..dff6f3f 100644 --- a/lib/views/advertisement/components/picked_images_container_widget.dart +++ b/lib/views/advertisement/components/picked_images_container_widget.dart @@ -1,3 +1,4 @@ +import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -18,7 +19,7 @@ class PickedFilesContainer extends StatelessWidget { final bool allowAdButton; const PickedFilesContainer({ - Key? key, + super.key, required this.pickedFiles, this.onCrossPressedPrimary, this.onCrossPressedSecondary, @@ -28,10 +29,13 @@ class PickedFilesContainer extends StatelessWidget { this.isPdf = false, this.isFromNetwork = false, this.allowAdButton = true, - }) : super(key: key); + }); @override Widget build(BuildContext context) { + pickedFiles.forEach((i) { + log("isFromNetwork: ${i.isFromNetwork}"); + }); return GridView.count( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, @@ -82,14 +86,14 @@ class BuildFilesContainer extends StatelessWidget { final bool isPdf; const BuildFilesContainer({ - Key? key, + super.key, required this.image, this.onCrossPressedPrimary, this.onCrossPressedSecondary, this.index, this.isReview = false, this.isPdf = false, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index 97435cc..94c26ac 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -158,7 +158,7 @@ class _ChatViewState extends State { ChatMessageModel chatMessageModel = chatMessages[index]; return ChatMessageCustomWidget( chatMessageModel: chatMessageModel, - requestModel: chatViewArgumentsForRequest!.requestModel!, + requestModel: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel! : null, requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus, chatTypeEnum: chatTypeEnum, requestsTypeEnum: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel!.requestType.toRequestTypeEnum() : RequestsTypeEnum.specialCarRequest, @@ -187,31 +187,28 @@ class _ChatViewState extends State { }, ), ), - // Center( - // child: LocaleKeys.requestAlreadyInProgress.tr().toText( - // textAlign: TextAlign.center, - // color: MyColors.lightTextColor, - // fontSize: 14, - // ), - // ).paddingAll(15) - ] else if (chatTypeEnum == ChatTypeEnum.requestOffer && - requestVM.currentSelectedRequest!.requestType.toRequestTypeEnum() == RequestsTypeEnum.specialCarRequest && - requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress && - requestVM.currentSelectedOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted && - 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 ...[ + ] + + // else if (chatTypeEnum == ChatTypeEnum.requestOffer && + // requestVM.currentSelectedRequest!.requestType.toRequestTypeEnum() == RequestsTypeEnum.specialCarRequest && + // requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress && + // requestVM.currentSelectedOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted && + // 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 && requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted && diff --git a/lib/views/chat/widgets/chat_bottom_sheets.dart b/lib/views/chat/widgets/chat_bottom_sheets.dart index c89c2d1..1d59e4c 100644 --- a/lib/views/chat/widgets/chat_bottom_sheets.dart +++ b/lib/views/chat/widgets/chat_bottom_sheets.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -16,43 +18,79 @@ 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}) { +void dealCompletedConsentBottomSheet({required BuildContext mainContext, required RequestStatusEnum requestStatusEnum, required int requestId, Function()? acceptRequestOffer}) { final requestVM = mainContext.read(); return actionConfirmationBottomSheet( - isOnlyOneButton: true, + isOnlyOneButton: AppState().currentAppType == AppType.customer, 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(), - checkBoxConfirmationWidget: 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, - ), - ), - ], - ); - }), + checkBoxConfirmationWidget: AppState().currentAppType == AppType.customer + ? 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, + ), + ), + ], + ); + }) + : null, actionButtonYes: Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { return Expanded( child: ShowFillButton( maxHeight: 55, isDisabled: !chatVM.acknowledgePaymentToMowaterStatus, - title: LocaleKeys.submit.tr(), + title: AppState().currentAppType == AppType.customer ? LocaleKeys.submit.tr() : LocaleKeys.yes.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); - navigateReplaceWithNameUntilRoute(mainContext, AppRoutes.dashboard); + pop(context); + bool statusForReqAccept = false; + + if (acceptRequestOffer != null) { + statusForReqAccept = await acceptRequestOffer(); + if (statusForReqAccept) { + bool status = await requestVM.onActionRequestTapped( + context: mainContext, + requestStatusEnum: requestStatusEnum, + requestId: requestId, + needLoading: false, + ); + log("status: $status"); + if (status) { + int index = requestVM.myFilteredRequests.indexWhere((element) => element.id == requestId); + if (index != -1) { + requestVM.myFilteredRequests[index].requestStatus = requestStatusEnum; + } + chatVM.updateAcknowledgePaymentToMowaterStatus(false); + mainContext.read().onNavbarTapped(4); + navigateReplaceWithNameUntilRoute(mainContext, AppRoutes.dashboard); + } + } + } else { + bool status = await requestVM.onActionRequestTapped( + context: mainContext, + requestStatusEnum: requestStatusEnum, + requestId: requestId, + needLoading: false, + ); + log("status: $status"); + if (status) { + int index = requestVM.myFilteredRequests.indexWhere((element) => element.id == requestId); + if (index != -1) { + requestVM.myFilteredRequests[index].requestStatus = requestStatusEnum; + } + chatVM.updateAcknowledgePaymentToMowaterStatus(false); + mainContext.read().onNavbarTapped(4); + navigateReplaceWithNameUntilRoute(mainContext, AppRoutes.dashboard); + } } }, ), diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 5f3ffa2..62a1117 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_localization/easy_localization.dart' as lcl; import 'package:flutter/material.dart'; @@ -31,14 +33,14 @@ class ChatMessageCustomWidget extends StatefulWidget { final RequestStatusEnum? requestStatusEnum; final ChatTypeEnum chatTypeEnum; final RequestsTypeEnum requestsTypeEnum; - final RequestModel requestModel; + final RequestModel? requestModel; const ChatMessageCustomWidget({ super.key, required this.chatMessageModel, required this.requestStatusEnum, required this.chatTypeEnum, - required this.requestModel, + this.requestModel, this.requestsTypeEnum = RequestsTypeEnum.specialCarRequest, }); @@ -158,12 +160,12 @@ class _ChatMessageCustomWidgetState extends State { ); } - Future buildDealBottomSheetOptionsForSpecialCar({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) { + Future buildDealBottomSheetOptionsForSpecialCar({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum, required bool fromCancelOffer}) { return showModalBottomSheet( context: context, isScrollControlled: true, enableDrag: true, - builder: (BuildContext context) { + builder: (BuildContext sheetContext) { return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { return InfoBottomSheet( title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), @@ -198,15 +200,62 @@ class _ChatMessageCustomWidgetState extends State { ShowFillButton( title: LocaleKeys.continu.tr(), onPressed: () async { + pop(sheetContext); if (chatVM.dealOptionsModelList[0].isSelected ?? false) { - final requestVM = context.read(); - return dealCompletedConfirmationBottomSheet( - mainContext: context, - requestStatusEnum: RequestStatusEnum.completed, - requestId: requestVM.currentSelectedRequest!.id, - ); + if (fromCancelOffer) { + final requestVM = context.read(); + return dealCompletedConsentBottomSheet( + mainContext: context, + requestStatusEnum: RequestStatusEnum.completed, + requestId: requestVM.currentSelectedRequest!.id, + ); + } else { + final requestVM = context.read(); + return dealCompletedConsentBottomSheet( + mainContext: context, + requestStatusEnum: RequestStatusEnum.completed, + requestId: requestVM.currentSelectedRequest!.id, + acceptRequestOffer: () async { + bool status = await chatVM.onSendMessageForActionOnRequestOffer( + receiverId: chatMessageModel.senderUserID ?? "", + chatMessageType: ChatMessageTypeEnum.offer, + comments: GlobalConsts.acceptingThisOffer, + 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.accepted, + context: context, + ); + + if (status) { + chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted; + requestVM.currentSelectedRequest!.requestStatus = RequestStatusEnum.inProgress; + requestVM.updateAcceptedReqOffer(chatMessageModel.reqOffer!); + requestVM.updateAcceptedRequestOfferProviderName(chatMessageModel.senderName ?? ""); + int index = chatVM.serviceProviderOffersList.indexWhere( + (element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId)); + + if (index != -1) { + chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum; + } + setState(() {}); + Utils.showToast(LocaleKeys.offerAccepted.tr()); + return true; + } else { + return false; + } + }); + } } else { - buildDealNotCompletedBottomSheetOptions(chatMessageModel: chatMessageModel); + if (fromCancelOffer) { + buildRejectOrCancelOfferBottomSheet(chatMessageModel: chatMessageModel, requestOfferStatusEnum: RequestOfferStatusEnum.cancel); + } else { + buildDealNotCompletedBottomSheetOptions(chatMessageModel: chatMessageModel); + } } }, maxWidth: double.infinity, @@ -399,7 +448,7 @@ class _ChatMessageCustomWidgetState extends State { } setState(() {}); // Navigator.pop(context); - Utils.showToast("Offer Accepted"); + Utils.showToast(LocaleKeys.offerAccepted.tr()); // navigateReplaceWithName(context, AppRoutes.dashboard); } }, @@ -499,7 +548,7 @@ class _ChatMessageCustomWidgetState extends State { isFilled: false, onPressed: () { if (widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest) { - buildDealBottomSheetOptionsForSpecialCar(chatMessageModel: chatMessageModel, requestOfferStatusEnum: requestOfferStatusEnum); + buildDealBottomSheetOptionsForSpecialCar(chatMessageModel: chatMessageModel, requestOfferStatusEnum: requestOfferStatusEnum, fromCancelOffer: false); } else { offerAcceptConfirmationBottomSheet(chatMessageModel: chatMessageModel); } @@ -529,6 +578,23 @@ class _ChatMessageCustomWidgetState extends State { 10.height, Row( children: [ + if (widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest) ...[ + Expanded( + child: ShowFillButton( + maxHeight: 27, + title: LocaleKeys.more.tr(), + fontSize: 9, + borderColor: MyColors.lightTextColor, + isFilled: false, + onPressed: () { + buildDealBottomSheetOptionsForSpecialCar(chatMessageModel: chatMessageModel, requestOfferStatusEnum: requestOfferStatusEnum, fromCancelOffer: true); + }, + backgroundColor: MyColors.white, + txtColor: MyColors.lightTextColor, + ), + ), + 20.width, + ], Expanded( child: ShowFillButton( maxHeight: 27, @@ -660,10 +726,10 @@ class _ChatMessageCustomWidgetState extends State { } Future onOfferEditIconPressed() async { - int index = context.read().myFilteredRequests.indexWhere((request) => request.id == widget.requestModel.id); + int index = context.read().myFilteredRequests.indexWhere((request) => request.id == widget.requestModel!.id); RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments( requestIndex: index, // Not getting used in case of update offer - requestModel: widget.requestModel, + requestModel: widget.requestModel!, ); RequestsVM requestVM = context.read(); requestVM.resetSendOfferBottomSheet(); @@ -676,8 +742,13 @@ class _ChatMessageCustomWidgetState extends State { requestVM.updateIsDeliveryAvailableStatus((offer.isDeliveryAvailable ?? false)); if (offer.reqOfferImages != null && offer.reqOfferImages!.isNotEmpty) { for (var element in offer.reqOfferImages!) { - if (element.imageUrl != null) { - ImageModel imageModel = ImageModel(id: element.id, filePath: element.imageUrl, isFromNetwork: true); + log("element: ${element.imageStr != null && element.imageStr!.isNotEmpty}"); + + if (element.imageUrl != null || element.imageStr != null) { + ImageModel imageModel = ImageModel( + id: element.id, + filePath: element.imageStr != null && element.imageStr!.isNotEmpty ? element.imageStr : element.imageUrl, + isFromNetwork: element.imageStr != null && element.imageStr!.isNotEmpty ? false : true); requestVM.addImageToPickedVehicleImages(imageModel); } } @@ -697,7 +768,10 @@ class _ChatMessageCustomWidgetState extends State { LocaleKeys.viewed.tr().toText(fontSize: 10, color: MyColors.lightTextColor, fontWeight: MyFonts.Medium), ], ), - ] else if (AppState().currentAppType == AppType.provider && chatMessageTypeEnum == ChatMessageTypeEnum.offer && (widget.chatMessageModel.isMyMessage == true)) ...[ + ] else if (AppState().currentAppType == AppType.provider && + widget.chatMessageModel.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.offer && + chatMessageTypeEnum == ChatMessageTypeEnum.offer && + (widget.chatMessageModel.isMyMessage == true)) ...[ MyAssets.icEdit.buildSvg(color: MyColors.white, height: 15).onPress(() => onOfferEditIconPressed()), ], Expanded( @@ -889,7 +963,10 @@ class _ChatMessageCustomWidgetState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - ((widget.chatMessageModel.isMyMessage ?? false) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true), + ((widget.chatMessageModel.isMyMessage ?? false) ? LocaleKeys.you.tr() : widget.chatMessageModel.senderName ?? "").toText( + fontSize: 16, + isBold: true, + ), ], ), 5.height, diff --git a/lib/views/common_fragments/ads_fragment.dart b/lib/views/common_fragments/ads_fragment.dart index 0a61fa2..ec09bc1 100644 --- a/lib/views/common_fragments/ads_fragment.dart +++ b/lib/views/common_fragments/ads_fragment.dart @@ -143,6 +143,7 @@ class AdsFragment extends StatelessWidget { ), floatingActionButton: FloatingActionButton( onPressed: () async { + adVM.getVehicleAdsDuration(); if (AppState().userType != UserType.providerDealer) { if (adVM.vehicleAdDurationId.selectedId == -1 && adVM.vehicleAdsDurations.isNotEmpty) { SelectionModel selection = SelectionModel( diff --git a/lib/views/requests/request_bottomsheets.dart b/lib/views/requests/request_bottomsheets.dart index ce83bce..725760e 100644 --- a/lib/views/requests/request_bottomsheets.dart +++ b/lib/views/requests/request_bottomsheets.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; @@ -188,8 +190,6 @@ Future buildSendOfferBottomSheet({ ); } - - Future buildRequestStatusUpdateSheetForProvider({required BuildContext mainContext, required int requestId}) { return showModalBottomSheet( context: mainContext, diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index efb2ddf..28ce552 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -77,7 +77,12 @@ class RequestDetailPage extends StatelessWidget { maxHeight: 55, title: LocaleKeys.completeDeal.tr(), onPressed: () { - return dealCompletedConfirmationBottomSheet(mainContext: context, requestStatusEnum: RequestStatusEnum.completed, requestId: requestId); + return dealCompletedConsentBottomSheet( + mainContext: context, + requestStatusEnum: RequestStatusEnum.completed, + requestId: requestId, + acceptRequestOffer: () {}, + ); }, ); } From 481dadf0b30262321e43afa66d8c69724f8e42b3 Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Tue, 3 Dec 2024 01:48:18 +0300 Subject: [PATCH 04/10] Lazy Loading --- lib/view_models/ad_view_model.dart | 39 ++++++++-------- .../components/ads_list_widget.dart | 46 +++++++++++-------- lib/views/common_fragments/ads_fragment.dart | 4 +- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 55f2594..c54fa50 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:io'; + +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; @@ -32,7 +34,6 @@ import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:provider/provider.dart'; class AdVM extends BaseVM { @@ -114,10 +115,7 @@ class AdVM extends BaseVM { } void removeSpecialServiceCard(int index) { - String option = specialServiceCards - .elementAt(index) - .serviceSelectedId! - .selectedOption; + String option = specialServiceCards.elementAt(index).serviceSelectedId!.selectedOption; for (var value in vehicleAdsSpecialServices) { if (value.name == option) { @@ -221,6 +219,14 @@ class AdVM extends BaseVM { notifyListeners(); } + int currentPage = 1; + bool isLoadingMore = false; + bool hasMoreData = true; + + fetchMoreAds() async { + + } + Future> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum}) async { return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum); } @@ -1258,10 +1264,7 @@ class AdVM extends BaseVM { } void removeDamagePartCard(int index) { - String option = vehicleDamageCards - .elementAt(index) - .partSelectedId! - .selectedOption; + String option = vehicleDamageCards.elementAt(index).partSelectedId!.selectedOption; for (var value in vehicleDamageParts) { if (value.partName == option) { @@ -1539,9 +1542,7 @@ class AdVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path - .split('/') - .last; + String fileName = file.path.split('/').last; vehiclePostingImages = VehiclePostingImages( imageName: fileName, imageStr: image, @@ -2041,13 +2042,13 @@ class AdVM extends BaseVM { final chatVM = context.read(); await chatVM .getUsersChatMessagesForAd( - context: context, - isForBuyer: true, - adsChatBuyerId: 1, - adID: adDetailsModel.id, - userID: myUserID, - senderName: adDetailsModel.adOwnerName, - ) + context: context, + isForBuyer: true, + adsChatBuyerId: 1, + adID: adDetailsModel.id, + userID: myUserID, + senderName: adDetailsModel.adOwnerName, + ) .whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments)); } } diff --git a/lib/views/advertisement/components/ads_list_widget.dart b/lib/views/advertisement/components/ads_list_widget.dart index fd14790..094baae 100644 --- a/lib/views/advertisement/components/ads_list_widget.dart +++ b/lib/views/advertisement/components/ads_list_widget.dart @@ -1,3 +1,6 @@ +import 'dart:developer'; + +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mc_common_app/classes/consts.dart'; @@ -12,7 +15,6 @@ import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -import 'package:easy_localization/easy_localization.dart'; class AdsListWidget extends StatelessWidget { final List adsList; @@ -21,12 +23,12 @@ class AdsListWidget extends StatelessWidget { final bool shouldShowAdStatus; const AdsListWidget({ - Key? key, + super.key, required this.adsList, this.scrollPhysics, required this.shouldShowAdStatus, this.isAdsFragment = false, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -38,21 +40,29 @@ class AdsListWidget extends StatelessWidget { ], ); } - return ListView.separated( - itemCount: adsList.length, - shrinkWrap: true, - physics: scrollPhysics, - itemBuilder: (BuildContext context, int index) { - return AdCard( - adDetails: adsList[index], - isAdsFragment: isAdsFragment, - shouldShowAdStatus: shouldShowAdStatus, - ).onPress(() => navigateWithName(context, AppRoutes.adsDetailView, arguments: adsList[index])); - }, - separatorBuilder: (BuildContext context, int index) { - return 12.height; + return NotificationListener( + onNotification: (ScrollNotification scrollInfo) { + if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && context.read().hasMoreData) { + log("should call more ads api:"); + } + return false; }, - padding: const EdgeInsets.symmetric(horizontal: 21), + child: ListView.separated( + itemCount: adsList.length, + shrinkWrap: true, + physics: scrollPhysics, + itemBuilder: (BuildContext context, int index) { + return AdCard( + adDetails: adsList[index], + isAdsFragment: isAdsFragment, + shouldShowAdStatus: shouldShowAdStatus, + ).onPress(() => navigateWithName(context, AppRoutes.adsDetailView, arguments: adsList[index])); + }, + separatorBuilder: (BuildContext context, int index) { + return 12.height; + }, + padding: const EdgeInsets.symmetric(horizontal: 21), + ), ); } } @@ -62,7 +72,7 @@ class AdCard extends StatelessWidget { final bool isAdsFragment; final bool shouldShowAdStatus; - const AdCard({Key? key, required this.adDetails, required this.isAdsFragment, required this.shouldShowAdStatus}) : super(key: key); + const AdCard({super.key, required this.adDetails, required this.isAdsFragment, required this.shouldShowAdStatus}); @override Widget build(BuildContext context) { diff --git a/lib/views/common_fragments/ads_fragment.dart b/lib/views/common_fragments/ads_fragment.dart index 0a61fa2..9339751 100644 --- a/lib/views/common_fragments/ads_fragment.dart +++ b/lib/views/common_fragments/ads_fragment.dart @@ -1,3 +1,4 @@ +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/classes/consts.dart'; @@ -18,10 +19,9 @@ 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 AdsFragment extends StatelessWidget { - const AdsFragment({Key? key}) : super(key: key); + const AdsFragment({super.key}); List getAdsList(AdVM adVM) { if (adVM.isExploreAdsTapped) { From 98b6e036783d19cd3e515eb5c5fa65552a198de8 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Tue, 3 Dec 2024 17:21:37 +0300 Subject: [PATCH 05/10] Duration Discount --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 6 +- lib/classes/consts.dart | 2 + lib/generated/codegen_loader.g.dart | 12 +- lib/generated/locale_keys.g.dart | 4 + .../appointment_basic_detail_model.dart | 45 +++++++ lib/repositories/branch_repo.dart | 46 +++++++ lib/repositories/common_repo.dart | 5 +- lib/view_models/ad_view_model.dart | 31 ++--- lib/view_models/chat_view_model.dart | 6 +- lib/view_models/service_view_model.dart | 117 +++++++++++++++--- .../chat/widgets/chat_message_widget.dart | 2 +- 12 files changed, 238 insertions(+), 44 deletions(-) create mode 100644 lib/models/appointments_models/appointment_basic_detail_model.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 0dc0ced..d843d2a 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -748,5 +748,9 @@ "offerNotMatched": "العرض لم يتطابق مع الطلب.", "offerRejected": "تم رفض العرض.", "offerAccepted": "تم قبول العرض.", - "you": "أنتم" + "you": "أنتم", + "youCannotDeactivateThisServiceRightNow": "لا يمكنك تعطيل هذه الخدمة الآن لأن لديك مواعيد معلقة لهذه الخدمة. تفاصيل آخر موعد هي:", + "done": "تم", + "notice": "إشعار", + "serviceDeactivated": "تم تعطيل الخدمة" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 2f2e648..f94f01a 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -746,5 +746,9 @@ "offerNotMatched": "The offer did not match the request.", "offerRejected": "Offer has been Rejected.", "offerAccepted": "Offer has been Accepted.", - "you": "You" + "you": "You", + "youCannotDeactivateThisServiceRightNow": "You cannot deactivate this service right now because you have pending appointments for this service. The last appointment details are:", + "done": "Done", + "notice": "Notice", + "serviceDeactivated": "Service Deactivated" } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index ea28136..d5033d6 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -55,8 +55,10 @@ class ApiConsts { static String servicesGet = "${baseUrlServices}api/ServiceProviders/Services_Get"; static String serviceProviderServiceCreate = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Create"; static String serviceProviderServiceUpdate = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Update"; + static String serviceProviderServiceStatusUpdate = "${baseUrlServices}api/ServiceProviders/CategoryAndServiceDeactivateBySP"; static String getProviderServices = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Get"; static String setScheduleInactive = "${baseUrlServices}api/ServiceProviders/BranchAppointmentSchedule_IsActiveUpdate"; + static String serviceProviderAppointmentGetByCategoryOrService = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_GetByCategoryOrService"; static String serviceProviderServiceGet = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Get"; static String branchesAndServices = "${baseUrlServices}api/ServiceProviders/ServiceProviderDetail_Get"; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index c62ded1..b1c8418 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -764,7 +764,11 @@ class CodegenLoader extends AssetLoader{ "offerNotMatched": "العرض لم يتطابق مع الطلب.", "offerRejected": "تم رفض العرض.", "offerAccepted": "تم قبول العرض.", - "you": "أنتم" + "you": "أنتم", + "youCannotDeactivateThisServiceRightNow": "لا يمكنك تعطيل هذه الخدمة الآن لأن لديك مواعيد معلقة لهذه الخدمة. تفاصيل آخر موعد هي:", + "done": "تم", + "notice": "إشعار", + "serviceDeactivated": "تم تعطيل الخدمة" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1514,7 +1518,11 @@ static const Map en_US = { "offerNotMatched": "The offer did not match the request.", "offerRejected": "Offer has been Rejected.", "offerAccepted": "Offer has been Accepted.", - "you": "You" + "you": "You", + "youCannotDeactivateThisServiceRightNow": "You cannot deactivate this service right now because you have pending appointments for this service. The last appointment details are:", + "done": "Done", + "notice": "Notice", + "serviceDeactivated": "Service Deactivated" }; 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 4ba56cc..411a854 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -728,5 +728,9 @@ abstract class LocaleKeys { static const offerRejected = 'offerRejected'; static const offerAccepted = 'offerAccepted'; static const you = 'you'; + static const youCannotDeactivateThisServiceRightNow = 'youCannotDeactivateThisServiceRightNow'; + static const done = 'done'; + static const notice = 'notice'; + static const serviceDeactivated = 'serviceDeactivated'; } diff --git a/lib/models/appointments_models/appointment_basic_detail_model.dart b/lib/models/appointments_models/appointment_basic_detail_model.dart new file mode 100644 index 0000000..041315d --- /dev/null +++ b/lib/models/appointments_models/appointment_basic_detail_model.dart @@ -0,0 +1,45 @@ +class AppointmentBasicDetailsModel { + int? serviceSlotID; + String? slotDate; + String? startTime; + String? endTime; + int? appointmentStatusID; + String? appointmentStatusText; + int? serviceProviderID; + int? customerID; + + AppointmentBasicDetailsModel({ + this.serviceSlotID, + this.slotDate, + this.startTime, + this.endTime, + this.appointmentStatusID, + this.appointmentStatusText, + this.serviceProviderID, + this.customerID, + }); + + AppointmentBasicDetailsModel.fromJson(Map json) { + serviceSlotID = json['serviceSlotID']; + slotDate = json['slotDate']; + startTime = json['startTime']; + endTime = json['endTime']; + appointmentStatusID = json['appointmentStatusID']; + appointmentStatusText = json['appointmentStatusText']; + serviceProviderID = json['serviceProviderID']; + customerID = json['customerID']; + } + + Map toJson() { + final Map data = {}; + data['serviceSlotID'] = serviceSlotID; + data['slotDate'] = slotDate; + data['startTime'] = startTime; + data['endTime'] = endTime; + data['appointmentStatusID'] = appointmentStatusID; + data['appointmentStatusText'] = appointmentStatusText; + data['serviceProviderID'] = serviceProviderID; + data['customerID'] = customerID; + return data; + } +} diff --git a/lib/repositories/branch_repo.dart b/lib/repositories/branch_repo.dart index 2cb2f77..730cfd9 100644 --- a/lib/repositories/branch_repo.dart +++ b/lib/repositories/branch_repo.dart @@ -5,6 +5,8 @@ import 'package:mc_common_app/api/api_client.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/dependency_injection.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/models/appointments_models/appointment_basic_detail_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/models/provider_branches_models/branch_review_model.dart'; @@ -16,6 +18,7 @@ import 'package:mc_common_app/models/provider_branches_models/provider_model.dar import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.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/utils/enums.dart'; abstract class BranchRepo { Future createBranch({ @@ -70,6 +73,10 @@ abstract class BranchRepo { Future updateService(List> map); + Future updateServiceStatus({required int branchId, required List serviceIds, required ServiceStatusEnum serviceStatusEnum}); + + Future> getAppointmentsByCategoryOrService({required int branchId, required int serviceId}); + Future getMatchedServices(int oldBranchId, int newBranchId, int categoryId); Future duplicateItems({required String providerBranchID, required List items}); @@ -305,6 +312,45 @@ class BranchRepoImp implements BranchRepo { return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceUpdate, map, token: t); } + @override + Future updateServiceStatus({required int branchId, required List serviceIds, required ServiceStatusEnum serviceStatusEnum}) async { + List ids = []; + + for (var id in serviceIds) { + ids.add(id.toString()); + } + + int providerID = AppState().getUser.data!.userInfo!.providerId; + var map = { + "ServiceProviderID": providerID.toString(), + "ProviderBranchID": branchId.toString(), + "ProviderServiceIDs": ids, + "Status": serviceStatusEnum.getIdFromServiceStatusEnum().toString(), + }; + String t = AppState().getUser.data!.accessToken ?? ""; + return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceStatusUpdate, queryParameters: map, token: t); + } + + @override + Future> getAppointmentsByCategoryOrService({required int branchId, required int serviceId}) async { + var map = { + "ProviderBranchID": branchId.toString(), + "ServiceProviderServiceID": serviceId.toString(), + }; + + String t = AppState().getUser.data!.accessToken ?? ""; + GenericRespModel genericRespModel = await apiClient.getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.serviceProviderAppointmentGetByCategoryOrService, + queryParameters: map, + token: t, + ); + + List basicAppointmentList = List.generate(genericRespModel.data.length, (index) => AppointmentBasicDetailsModel.fromJson(genericRespModel.data[index])); + + return basicAppointmentList; + } + @override Future getMatchedServices(int oldBranchId, int newBranchId, int categoryId) async { var postParams = { diff --git a/lib/repositories/common_repo.dart b/lib/repositories/common_repo.dart index 24d07f2..d823a16 100644 --- a/lib/repositories/common_repo.dart +++ b/lib/repositories/common_repo.dart @@ -48,7 +48,7 @@ abstract class CommonRepo { Future getCarCheckServiceScheduleDetails({required double lat, required double long}); - Future> getPhotographyServiceScheduleListByOffices({required double lat, required double long}); + Future> getPhotographyServiceScheduleListByOffices({required double lat, required double long, required int cityID}); //TODO: Needs to remove common methods from AD's repo and delete all repeated methods. Future getVehicleDetails({int? vehicleTypeId, int? vehicleBrandId}); @@ -97,10 +97,11 @@ class CommonRepoImp implements CommonRepo { } @override - Future> getPhotographyServiceScheduleListByOffices({required double lat, required double long}) async { + Future> getPhotographyServiceScheduleListByOffices({required double lat, required double long, required int cityID}) async { var params = { "Latitude": lat.toString(), "Longitude": long.toString(), + "cityID": cityID.toString(), }; GenericRespModel genericRespModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 55f2594..d541c72 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -114,10 +114,7 @@ class AdVM extends BaseVM { } void removeSpecialServiceCard(int index) { - String option = specialServiceCards - .elementAt(index) - .serviceSelectedId! - .selectedOption; + String option = specialServiceCards.elementAt(index).serviceSelectedId!.selectedOption; for (var value in vehicleAdsSpecialServices) { if (value.name == option) { @@ -1258,10 +1255,7 @@ class AdVM extends BaseVM { } void removeDamagePartCard(int index) { - String option = vehicleDamageCards - .elementAt(index) - .partSelectedId! - .selectedOption; + String option = vehicleDamageCards.elementAt(index).partSelectedId!.selectedOption; for (var value in vehicleDamageParts) { if (value.partName == option) { @@ -1394,8 +1388,9 @@ class AdVM extends BaseVM { Future getPhotographyServiceScheduleListByOffices({required double latitude, required double longitude, bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); + int cityID = AppState().getUser.data!.userInfo!.cityId ?? 1; try { - photoSSSchedulesByOffices = await commonRepo.getPhotographyServiceScheduleListByOffices(lat: latitude, long: longitude); + photoSSSchedulesByOffices = await commonRepo.getPhotographyServiceScheduleListByOffices(lat: latitude, long: longitude, cityID: cityID); if (isNeedToRebuild) setState(ViewState.idle); } catch (e) { if (isNeedToRebuild) setState(ViewState.idle); @@ -1539,9 +1534,7 @@ class AdVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path - .split('/') - .last; + String fileName = file.path.split('/').last; vehiclePostingImages = VehiclePostingImages( imageName: fileName, imageStr: image, @@ -2041,13 +2034,13 @@ class AdVM extends BaseVM { final chatVM = context.read(); await chatVM .getUsersChatMessagesForAd( - context: context, - isForBuyer: true, - adsChatBuyerId: 1, - adID: adDetailsModel.id, - userID: myUserID, - senderName: adDetailsModel.adOwnerName, - ) + context: context, + isForBuyer: true, + adsChatBuyerId: 1, + adID: adDetailsModel.id, + userID: myUserID, + senderName: adDetailsModel.adOwnerName, + ) .whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments)); } } diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 0a9495d..f2c8bf2 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -97,9 +97,9 @@ class ChatVM extends BaseVM { notifyListeners(); } - List indexesForCancelSpecialCarOffer = [0, 1, 6, 10]; - List indexesForCancelSparePartOffer = [2, 10]; - List indexesForRejectOffer = [3, 4, 5, 10]; + List indexesForCancelSpecialCarOffer = [0, 1, 5, 6, 10]; + List indexesForCancelSparePartOffer = [2, 5, 10]; + List indexesForRejectOffer = [3, 4, 10]; List indexesForDealNotCompleted = [7, 4, 8, 9, 10]; List offerRejectModelList = [ diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index 1068494..377f38f 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; @@ -6,8 +7,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/appointments_models/appointment_basic_detail_model.dart'; import 'package:mc_common_app/models/general_models/enums_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; @@ -24,12 +27,17 @@ import 'package:mc_common_app/models/user_models/country.dart'; import 'package:mc_common_app/repositories/branch_repo.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/services/common_services.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/utils/utils.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; +import 'package:mc_common_app/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'; class ServiceVM extends BaseVM { final BranchRepo branchRepo; @@ -201,7 +209,6 @@ class ServiceVM extends BaseVM { } branchServicesFilterOptions[serviceStatusEnum.getIdFromServiceStatusEnum() - 1].isSelected = true; // -1 to match with the index - } // Future selectFile(BuildContext context, int index) async { @@ -264,11 +271,7 @@ class ServiceVM extends BaseVM { context, allowMultiple: false, ); - if (files != null && files.any((element) => - element.path - .split('.') - .last - .toLowerCase() != 'pdf')) { + if (files != null && files.any((element) => element.path.split('.').last.toLowerCase() != 'pdf')) { Utils.showToast("Only PDF Files are allowed"); return; } @@ -282,8 +285,8 @@ class ServiceVM extends BaseVM { documentID == 1 ? commerceCertificates.addAll(imageModels) : documentID == 2 - ? commercialCertificates.addAll(imageModels) - : vatCertificates.addAll(imageModels); + ? commercialCertificates.addAll(imageModels) + : vatCertificates.addAll(imageModels); document!.data![index].document = Utils.convertFileToBase64(files.first); document!.data![index].fileExt = Utils.checkFileExt(files.first.path); document!.data![index].documentUrl = files.first.path; @@ -459,10 +462,10 @@ class ServiceVM extends BaseVM { DropValue( element.id ?? 0, ((element.categoryName!.isEmpty - ? "N/A" - : countryCode == "SA" - ? element.categoryNameN - : element.categoryName) ?? + ? "N/A" + : countryCode == "SA" + ? element.categoryNameN + : element.categoryName) ?? "N/A"), "", ), @@ -528,6 +531,92 @@ class ServiceVM extends BaseVM { return await branchRepo.updateService(map); } + Future> getAppointmentsByServiceID({required BuildContext context, required int branchId, required int serviceId}) async { + try { + Utils.showLoading(context); + List appointmentList = await branchRepo.getAppointmentsByCategoryOrService(branchId: branchId, serviceId: serviceId); + // log("list: ${mResponse.}"); + Utils.hideLoading(context); + + return appointmentList; + } catch (e) { + Utils.hideLoading(context); + log(e.toString()); + Utils.showToast(e.toString() ?? ""); + return []; + } + } + + Future updateServiceStatus({required BuildContext context, required ServiceStatusEnum serviceStatusEnum, required int branchId, required List providerServiceIds}) async { + try { + Utils.showLoading(context); + GenericRespModel genericRespModel = await branchRepo.updateServiceStatus(branchId: branchId, serviceIds: providerServiceIds, serviceStatusEnum: serviceStatusEnum); + Utils.hideLoading(context); + Utils.showToast(genericRespModel.message ?? ""); + return genericRespModel.messageStatus == 1; + } catch (e) { + Utils.hideLoading(context); + log(e.toString()); + Utils.showToast(e.toString()); + return false; + } + } + + Future buildDealNotCompletedBottomSheetOptions({required BuildContext mainContext, required List appointments, required String branchName}) async { + return actionConfirmationBottomSheet( + isOnlyOneButton: true, + context: mainContext, + title: LocaleKeys.notice.tr().toText(fontSize: 26, isBold: true, letterSpacing: -1.44), + subtitle: LocaleKeys.youCannotDeactivateThisServiceRightNow.tr(), + checkBoxConfirmationWidget: ListView.builder( + shrinkWrap: true, + itemCount: appointments.length, + itemBuilder: (BuildContext context, int index) { + AppointmentBasicDetailsModel appointmentBasicDetailsModel = appointments[index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (appointmentBasicDetailsModel.appointmentStatusText != null && appointmentBasicDetailsModel.appointmentStatusText!.isNotEmpty) ...[ + Utils.statusContainerChip(text: appointmentBasicDetailsModel.appointmentStatusText!, chipColor: MyColors.black), + ], + 6.height, + "${appointmentBasicDetailsModel.customerID}".toText(fontSize: 16, letterSpacing: -0.64), + showItem("${LocaleKeys.branchName.tr()}:", "${appointmentBasicDetailsModel.slotDate}"), + showItem("${LocaleKeys.date.tr()}:", "${appointmentBasicDetailsModel.slotDate}"), + showItem("${LocaleKeys.time.tr()}:", "${appointmentBasicDetailsModel.startTime} - ${appointmentBasicDetailsModel.startTime}"), + ], + ).toContainer(isShadowEnabled: true); + }), + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.done.tr(), + fontSize: 15, + onPressed: () => Navigator.pop(mainContext), + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.done.tr(), + fontSize: 15, + onPressed: () => Navigator.pop(mainContext), + ), + ), + ); + } + + Widget showItem(String title, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.toText(color: MyColors.lightTextColor, letterSpacing: -0.48), + 3.width, + Flexible(child: value.toText(isBold: true, overflow: TextOverflow.ellipsis)), + ], + ); + } + void updateSelectedBranchType(int status) { selectedBranchStatus = status; notifyListeners(); @@ -657,9 +746,7 @@ class ServiceVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path - .split('/') - .last; + String fileName = file.path.split('/').last; branchPostingImages = BranchPostingImages( imageName: fileName, imageStr: image, diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 62a1117..49b73c3 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -769,8 +769,8 @@ class _ChatMessageCustomWidgetState extends State { ], ), ] else if (AppState().currentAppType == AppType.provider && - widget.chatMessageModel.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.offer && chatMessageTypeEnum == ChatMessageTypeEnum.offer && + widget.chatMessageModel.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.offer && (widget.chatMessageModel.isMyMessage == true)) ...[ MyAssets.icEdit.buildSvg(color: MyColors.white, height: 15).onPress(() => onOfferEditIconPressed()), ], From 30cffc364b9abe74cdc49245fbea30740baa429a Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Wed, 4 Dec 2024 17:49:03 +0300 Subject: [PATCH 06/10] addnew service fix --- assets/langs/ar-SA.json | 4 +- assets/langs/en-US.json | 4 +- lib/generated/codegen_loader.g.dart | 8 +++- lib/generated/locale_keys.g.dart | 2 + .../ss_photo_schedule_model.dart | 2 +- .../profile/categroy.dart | 7 ++- lib/view_models/appointments_view_model.dart | 33 +++----------- lib/view_models/requests_view_model.dart | 24 +++++++++-- lib/view_models/service_view_model.dart | 1 + .../ads_detail_view/components.dart | 36 ++++++++-------- .../picked_images_container_widget.dart | 40 +++++++++++------ lib/views/chat/chat_view.dart | 1 + .../chat/widgets/chat_message_widget.dart | 4 +- .../common_fragments/requests_fragment.dart | 25 ++++++++--- lib/views/requests/offer_list_page.dart | 43 +++++++++++-------- lib/views/requests/widget/request_item.dart | 25 +++-------- .../provider_accepted_requests_view.dart | 20 ++++++++- .../widgets/custom_setting_options_tile.dart | 8 ---- 18 files changed, 163 insertions(+), 124 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index d843d2a..04b6118 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -752,5 +752,7 @@ "youCannotDeactivateThisServiceRightNow": "لا يمكنك تعطيل هذه الخدمة الآن لأن لديك مواعيد معلقة لهذه الخدمة. تفاصيل آخر موعد هي:", "done": "تم", "notice": "إشعار", - "serviceDeactivated": "تم تعطيل الخدمة" + "serviceDeactivated": "تم تعطيل الخدمة", + "totalNumberOfServices": "إجمالي عدد الخدمات:", + "noAvailableOfficesInCity": "لا توجد مكاتب متاحة في مدينتك لخدمتك." } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index f94f01a..e15f863 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -750,5 +750,7 @@ "youCannotDeactivateThisServiceRightNow": "You cannot deactivate this service right now because you have pending appointments for this service. The last appointment details are:", "done": "Done", "notice": "Notice", - "serviceDeactivated": "Service Deactivated" + "serviceDeactivated": "Service Deactivated", + "totalNumberOfServices": "Total number of services:", + "noAvailableOfficesInCity": "There are no available offices in your city for your service." } \ No newline at end of file diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index b1c8418..7006615 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -768,7 +768,9 @@ class CodegenLoader extends AssetLoader{ "youCannotDeactivateThisServiceRightNow": "لا يمكنك تعطيل هذه الخدمة الآن لأن لديك مواعيد معلقة لهذه الخدمة. تفاصيل آخر موعد هي:", "done": "تم", "notice": "إشعار", - "serviceDeactivated": "تم تعطيل الخدمة" + "serviceDeactivated": "تم تعطيل الخدمة", + "totalNumberOfServices": "إجمالي عدد الخدمات:", + "noAvailableOfficesInCity": "لا توجد مكاتب متاحة في مدينتك لخدمتك." }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1522,7 +1524,9 @@ static const Map en_US = { "youCannotDeactivateThisServiceRightNow": "You cannot deactivate this service right now because you have pending appointments for this service. The last appointment details are:", "done": "Done", "notice": "Notice", - "serviceDeactivated": "Service Deactivated" + "serviceDeactivated": "Service Deactivated", + "totalNumberOfServices": "Total number of services:", + "noAvailableOfficesInCity": "There are no available offices in your city for your 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 411a854..9aa89f5 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -732,5 +732,7 @@ abstract class LocaleKeys { static const done = 'done'; static const notice = 'notice'; static const serviceDeactivated = 'serviceDeactivated'; + static const totalNumberOfServices = 'totalNumberOfServices'; + static const noAvailableOfficesInCity = 'noAvailableOfficesInCity'; } diff --git a/lib/models/advertisment_models/ss_photo_schedule_model.dart b/lib/models/advertisment_models/ss_photo_schedule_model.dart index 6aa7f54..8852e2b 100644 --- a/lib/models/advertisment_models/ss_photo_schedule_model.dart +++ b/lib/models/advertisment_models/ss_photo_schedule_model.dart @@ -12,7 +12,7 @@ class SSPhotoOfficeScheduleModel { String? areaName; String? latitude; String? longitude; - int? distanceKM; + double? distanceKM; int? totalItemsCount; List? photoOfficeScheduleSlots; List? customTimeDateSlotList; diff --git a/lib/models/provider_branches_models/profile/categroy.dart b/lib/models/provider_branches_models/profile/categroy.dart index 1d0a0c1..8945da0 100644 --- a/lib/models/provider_branches_models/profile/categroy.dart +++ b/lib/models/provider_branches_models/profile/categroy.dart @@ -51,15 +51,17 @@ class CategoryData extends Equatable { this.services, this.branchId, this.branchName, + this.isDeactivated, }); int? id; String? categoryName; String? categoryNameN; - dynamic? serviceCategoryIconUrl; - dynamic? serviceCategoryImageUrl; + String? serviceCategoryIconUrl; + String? serviceCategoryImageUrl; String? branchId; String? branchName; + bool? isDeactivated; List? services; factory CategoryData.fromJson(Map json) => CategoryData( @@ -69,6 +71,7 @@ class CategoryData extends Equatable { serviceCategoryIconUrl: json["serviceCategoryIconUrl"], serviceCategoryImageUrl: json["serviceCategoryImageUrl"], services: [], + isDeactivated: false, ); Map toJson() => { diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 7547d28..c29df67 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -252,17 +252,6 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - // String pickedHomeLocation = ""; - // - // void updatePickedHomeLocation(String value) { - // pickedHomeLocation = value; - // pickHomeLocationError = ""; - // if (currentServiceSelection != null) { - // currentServiceSelection!.homeLocation = value; - // } - // notifyListeners(); - // } - SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateProviderCategoryId(SelectionModel id) { @@ -305,9 +294,6 @@ class AppointmentsVM extends BaseVM { return; } - servicesInCurrentAppointment.forEach((i) { - log("hlo1: ${i.serviceDescription} ${i.servicelocationInfo.homeChargesInCurrentService}"); - }); if (!(currentServiceSelection!.isAllowAppointmentHome ?? false)) { updateIsHomeTapped(false); return; @@ -321,19 +307,8 @@ class AppointmentsVM extends BaseVM { distanceToBranch: currentLocationInfoModel!.distanceToBranch, homeChargesInCurrentService: double.parse(currentServiceSelection!.rangePricePerKm ?? "0.0"), ); - - // for (var service in servicesInCurrentAppointment) { - // service.servicelocationInfo.address = currentLocationInfoModel!.address; - // service.servicelocationInfo.latitude = currentLocationInfoModel!.latitude; - // service.servicelocationInfo.longitude = currentLocationInfoModel!.longitude; - // service.servicelocationInfo.distanceToBranch = currentLocationInfoModel!.distanceToBranch; - // } } - servicesInCurrentAppointment.forEach((i) { - log("hlo2: ${i.serviceDescription} ${i.servicelocationInfo.homeChargesInCurrentService}"); - }); - notifyListeners(); } @@ -384,7 +359,7 @@ class AppointmentsVM extends BaseVM { } applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) { - log("appointmentStatusEnum: ${appointmentStatusEnum}"); + log("appointmentStatusEnum: $appointmentStatusEnum"); // isNeedCustomerFilter IS ONLY FOR THE PROVIDER APP if (appointmentsFilterOptions.isEmpty) return; for (var value in appointmentsFilterOptions) { @@ -467,13 +442,15 @@ class AppointmentsVM extends BaseVM { myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters(); // myFilteredAppointments = myAppointments; + + setState(ViewState.idle); + applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); + myUpComingAppointments = myAppointments .where((element) => (element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) && (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now()))) .toList(); - setState(ViewState.idle); - applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); notifyListeners(); } diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index dcfebf1..9a1e7f1 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -950,6 +950,14 @@ class RequestsVM extends BaseVM { notifyListeners(); } + bool isBase64String(String input) { + // This regex checks if the string is a valid Base64 string + final base64Regex = RegExp(r'^[A-Za-z0-9+/=]+$'); + + // Check if the string matches the Base64 pattern + return base64Regex.hasMatch(input) && (input.length % 4 == 0); + } + MessageImageModel convertFileToMessageImageModel({required ImageModel imageModel, required int offerId}) { MessageImageModel offerImages; @@ -961,15 +969,23 @@ class RequestsVM extends BaseVM { reqOfferID: offerId, ); } else { - File file = File(imageModel.filePath!); - List imageBytes = file.readAsBytesSync(); - String image = base64Encode(imageBytes); + File? file; + List imageBytes = []; + String image = ''; + if (!isBase64String(imageModel.filePath!)) { + file = File(imageModel.filePath!); + imageBytes = file.readAsBytesSync(); + image = base64Encode(imageBytes); + } else { + image = imageModel.filePath!; + } + offerImages = MessageImageModel( id: 0, imageStr: image, isFromNetwork: false, reqOfferID: offerId, - imagePath: file.path, + imagePath: file?.path, ); } diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index 377f38f..a6d1172 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -164,6 +164,7 @@ class ServiceVM extends BaseVM { final BranchDetailModel currentBranch = branches!.data!.serviceProviderBranch!.firstWhere((element) => element.id == selectedBranchId); for (var element in currentBranch.branchServices!) { + // TODO: Here , we need to add the category deactivated status. categories.add( CategoryData( id: element.categoryId, diff --git a/lib/views/advertisement/ads_detail_view/components.dart b/lib/views/advertisement/ads_detail_view/components.dart index a2eddec..fd3be94 100644 --- a/lib/views/advertisement/ads_detail_view/components.dart +++ b/lib/views/advertisement/ads_detail_view/components.dart @@ -293,24 +293,24 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { 20.height, adVM.state == ViewState.busy ? const Center(child: CircularProgressIndicator()) - : Builder( - builder: (context) { - List vehicleCitiesDrop = []; - for (int i = 0; i < adVM.photoSSSchedulesByOffices.length; i++) { - var element = adVM.photoSSSchedulesByOffices[i]; - vehicleCitiesDrop.add(DropValue(element.photoOfficeID?.toInt() ?? 0, element.photoOfficeName ?? "", i.toString())); - } - - return DropdownField( - (DropValue value) => adVM.updatePhotoOfficeSelectedId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), - // here the item price is the index of the selected option - list: vehicleCitiesDrop, - dropdownValue: adVM.photoOfficeSelectedId.selectedId != -1 ? DropValue(adVM.photoOfficeSelectedId.selectedId, adVM.photoOfficeSelectedId.selectedOption, "") : null, - hint: LocaleKeys.selectOffice.tr(), - errorValue: adVM.photoOfficeSelectedId.errorValue, - ); - }, - ), + : adVM.photoSSSchedulesByOffices.isEmpty + ? LocaleKeys.noAvailableOfficesInCity.tr().toText(fontSize: 14, height: 1.2) + : Builder( + builder: (context) { + List vehicleCitiesDrop = []; + for (int i = 0; i < adVM.photoSSSchedulesByOffices.length; i++) { + var element = adVM.photoSSSchedulesByOffices[i]; + vehicleCitiesDrop.add(DropValue(element.photoOfficeID?.toInt() ?? 0, element.photoOfficeName ?? "", i.toString())); + } + return DropdownField( + (DropValue value) => adVM.updatePhotoOfficeSelectedId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), + list: vehicleCitiesDrop, + dropdownValue: adVM.photoOfficeSelectedId.selectedId != -1 ? DropValue(adVM.photoOfficeSelectedId.selectedId, adVM.photoOfficeSelectedId.selectedOption, "") : null, + hint: LocaleKeys.selectOffice.tr(), + errorValue: adVM.photoOfficeSelectedId.errorValue, + ); + }, + ), if (adVM.photoOfficeSelectedId.selectedId != -1) ...[ 9.height, CustomCalenderAppointmentWidget( diff --git a/lib/views/advertisement/components/picked_images_container_widget.dart b/lib/views/advertisement/components/picked_images_container_widget.dart index dff6f3f..91e3454 100644 --- a/lib/views/advertisement/components/picked_images_container_widget.dart +++ b/lib/views/advertisement/components/picked_images_container_widget.dart @@ -1,5 +1,5 @@ -import 'dart:developer'; -import 'dart:io'; +import 'dart:convert'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; @@ -33,9 +33,6 @@ class PickedFilesContainer extends StatelessWidget { @override Widget build(BuildContext context) { - pickedFiles.forEach((i) { - log("isFromNetwork: ${i.isFromNetwork}"); - }); return GridView.count( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, @@ -95,8 +92,20 @@ class BuildFilesContainer extends StatelessWidget { this.isPdf = false, }); + bool isBase64String(String input) { + // This regex checks if the string is a valid Base64 string + final base64Regex = RegExp(r'^[A-Za-z0-9+/=]+$'); + + // Check if the string matches the Base64 pattern + return base64Regex.hasMatch(input) && (input.length % 4 == 0); + } + @override Widget build(BuildContext context) { + Uint8List? bytes; + if ((image.isFromNetwork == null || image.isFromNetwork == false) && isBase64String(image.filePath ?? "")) { + bytes = (base64Decode(image.filePath!)) as Uint8List?; + } return Stack( children: [ SizedBox( @@ -124,13 +133,20 @@ class BuildFilesContainer extends StatelessWidget { width: 73, ) .paddingAll(8) - : image.filePath! - .buildFileImage( - fit: BoxFit.fill, - height: 72, - width: 70, - ) - .paddingAll(8), + : isBase64String(image.filePath!) + ? Image.memory( + bytes!, + fit: BoxFit.fill, + height: 72, + width: 70, + ).paddingAll(8) + : image.filePath! + .buildFileImage( + fit: BoxFit.fill, + height: 72, + width: 70, + ) + .paddingAll(8), if (!isReview) ...[ Align( alignment: Alignment.topRight, diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index 94c26ac..60861cc 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -221,6 +221,7 @@ class _ChatViewState extends State { size: 30, ).onPress( () { + requestVM.resetSendOfferBottomSheet(); RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments( requestIndex: chatViewArgumentsForRequest!.requestIndex, requestModel: chatViewArgumentsForRequest!.requestModel!, diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 49b73c3..5ede78f 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -742,13 +742,13 @@ class _ChatMessageCustomWidgetState extends State { requestVM.updateIsDeliveryAvailableStatus((offer.isDeliveryAvailable ?? false)); if (offer.reqOfferImages != null && offer.reqOfferImages!.isNotEmpty) { for (var element in offer.reqOfferImages!) { - log("element: ${element.imageStr != null && element.imageStr!.isNotEmpty}"); - if (element.imageUrl != null || element.imageStr != null) { ImageModel imageModel = ImageModel( id: element.id, filePath: element.imageStr != null && element.imageStr!.isNotEmpty ? element.imageStr : element.imageUrl, isFromNetwork: element.imageStr != null && element.imageStr!.isNotEmpty ? false : true); + + log("imageModelimageModel: ${imageModel.toString()}"); requestVM.addImageToPickedVehicleImages(imageModel); } } diff --git a/lib/views/common_fragments/requests_fragment.dart b/lib/views/common_fragments/requests_fragment.dart index 7ff9a4b..3d7544c 100644 --- a/lib/views/common_fragments/requests_fragment.dart +++ b/lib/views/common_fragments/requests_fragment.dart @@ -3,6 +3,7 @@ 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'; @@ -156,11 +157,25 @@ class MyRequestsFragment extends StatelessWidget { return; }, child: RequestItem( - request: requestsVM.myFilteredRequests[index], - appType: AppState().currentAppType, - requestIndex: index, - shouldShowStatuses: AppState().currentAppType == AppType.customer, - ), + request: requestsVM.myFilteredRequests[index], + shouldShowStatuses: AppState().currentAppType == AppType.customer, + onTap: () async { + RequestModel request = requestsVM.myFilteredRequests[index]; + requestsVM.updateCurrentSelectedRequest(request); + if (request.requestStatus == RequestStatusEnum.pending || + request.requestStatus == RequestStatusEnum.cancelled || + request.requestStatus == RequestStatusEnum.expired) { + return; + } + if (AppState().currentAppType == AppType.provider) { + RequestDetailPageArguments requestDetailPageArguments = RequestDetailPageArguments(requestIndex: index, requestModel: request); + navigateWithName(context, AppRoutes.requestsDetailPage, arguments: requestDetailPageArguments); + } else { + requestsVM.myFilteredRequests[index].offerCount = 0; + requestsVM.notifyListeners(); + navigateWithName(context, AppRoutes.offersListPage, arguments: request.id); + } + }), ); }, separatorBuilder: (context, index) { diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart index 785d3f3..dab5a41 100644 --- a/lib/views/requests/offer_list_page.dart +++ b/lib/views/requests/offer_list_page.dart @@ -80,19 +80,21 @@ class _OfferListPageState extends State { fontSize: 16, isBold: true, ), - Center( - child: "${offersModel.offerCount}".toText( - color: Colors.white, - isBold: true, - fontSize: 10, + if (offersModel.offerCount != null && offersModel.offerCount! > 0) ...[ + Center( + child: "${offersModel.offerCount}".toText( + color: Colors.white, + isBold: true, + fontSize: 10, + ), + ).toContainer( + backgroundColor: MyColors.cancelledColor, + borderRadius: 100, + paddingAll: 1, + width: 22, + height: 22, ), - ).toContainer( - backgroundColor: MyColors.cancelledColor, - borderRadius: 100, - paddingAll: 1, - width: 22, - height: 22, - ), + ], ], ), 8.height, @@ -131,13 +133,16 @@ class _OfferListPageState extends State { await chatVM .getRequestsChatMessagesForCustomer( - context: context, - providerId: offersModel.providerId ?? 0, - requestOfferId: 0, - requestId: widget.requestId, - providerOfferIndex: index, - ) - .whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments)); + context: context, + providerId: offersModel.providerId ?? 0, + requestOfferId: 0, + requestId: widget.requestId, + providerOfferIndex: index, + ) + .whenComplete(() { + chatVM.serviceProviderOffersList[index].offerCount = 0; + navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments); + }); }).toContainer(isShadowEnabled: true); }, separatorBuilder: (context, index) => 16.height, diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index 57e3c4d..724abda 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -2,27 +2,22 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:mc_common_app/config/routes.dart'; +import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/requests_models/request_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/requests_view_model.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; -import 'package:provider/provider.dart'; class RequestItem extends StatelessWidget { final RequestModel request; - final AppType appType; - final int requestIndex; final bool shouldShowStatuses; + final Function() onTap; - const RequestItem({super.key, required this.request, required this.appType, required this.requestIndex, this.shouldShowStatuses = true}); + const RequestItem({super.key, required this.request, this.shouldShowStatuses = true, required this.onTap}); @override Widget build(BuildContext context) { @@ -65,7 +60,7 @@ class RequestItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ - if (request.offerCount > 0 && appType == AppType.customer && request.requestStatus == RequestStatusEnum.submitted) ...[ + if (request.offerCount > 0 && AppState().currentAppType == AppType.customer && request.requestStatus == RequestStatusEnum.submitted) ...[ Center( child: "${request.offerCount}".toText( color: Colors.white, @@ -112,17 +107,7 @@ class RequestItem extends StatelessWidget { // ), ], ).toContainer(isShadowEnabled: true).onPress(() async { - RequestsVM requestsVM = context.read(); - requestsVM.updateCurrentSelectedRequest(request); - if (request.requestStatus == RequestStatusEnum.pending || request.requestStatus == RequestStatusEnum.cancelled || request.requestStatus == RequestStatusEnum.expired) { - return; - } - if (appType == AppType.provider) { - RequestDetailPageArguments requestDetailPageArguments = RequestDetailPageArguments(requestIndex: requestIndex, requestModel: request); - navigateWithName(context, AppRoutes.requestsDetailPage, arguments: requestDetailPageArguments); - } else { - navigateWithName(context, AppRoutes.offersListPage, arguments: request.id); - } + onTap(); }); } diff --git a/lib/views/setting_options/provider_accepted_requests_view.dart b/lib/views/setting_options/provider_accepted_requests_view.dart index b6c8b51..ce38770 100644 --- a/lib/views/setting_options/provider_accepted_requests_view.dart +++ b/lib/views/setting_options/provider_accepted_requests_view.dart @@ -5,6 +5,7 @@ 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'; @@ -80,7 +81,24 @@ class _ProviderAcceptedRequestsViewState extends State Date: Wed, 4 Dec 2024 22:02:37 +0300 Subject: [PATCH 07/10] From MBP --- assets/langs/ar-SA.json | 4 +- assets/langs/en-US.json | 3 +- lib/generated/codegen_loader.g.dart | 6 +- lib/generated/locale_keys.g.dart | 1 + lib/repositories/appointment_repo.dart | 3 + lib/repositories/branch_repo.dart | 6 +- lib/view_models/ad_view_model.dart | 4 +- lib/view_models/appointments_view_model.dart | 83 +++++++++++++++++-- .../ad_duration_selection_sheet.dart | 18 ++-- .../damage_pictures_bottom_sheet.dart | 2 +- .../appointments_filter_view.dart | 33 ++++++-- .../book_appointment_services_view.dart | 6 +- ...appointment_service_pick_bottom_sheet.dart | 2 +- 13 files changed, 131 insertions(+), 40 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 0dc0ced..d4df718 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -748,5 +748,7 @@ "offerNotMatched": "العرض لم يتطابق مع الطلب.", "offerRejected": "تم رفض العرض.", "offerAccepted": "تم قبول العرض.", - "you": "أنتم" + "you": "أنتم", + "searchByItem": "البحث حسب العنصر" + } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 2f2e648..63b1010 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -746,5 +746,6 @@ "offerNotMatched": "The offer did not match the request.", "offerRejected": "Offer has been Rejected.", "offerAccepted": "Offer has been Accepted.", - "you": "You" + "you": "You", + "searchByItem": "Search By Item" } \ No newline at end of file diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index c62ded1..f3a6bd9 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -764,7 +764,8 @@ class CodegenLoader extends AssetLoader{ "offerNotMatched": "العرض لم يتطابق مع الطلب.", "offerRejected": "تم رفض العرض.", "offerAccepted": "تم قبول العرض.", - "you": "أنتم" + "you": "أنتم", + "searchByItem": "البحث حسب العنصر" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1514,7 +1515,8 @@ static const Map en_US = { "offerNotMatched": "The offer did not match the request.", "offerRejected": "Offer has been Rejected.", "offerAccepted": "Offer has been Accepted.", - "you": "You" + "you": "You", + "searchByItem": "Search By Item" }; 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 4ba56cc..3d4daeb 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -728,5 +728,6 @@ abstract class LocaleKeys { static const offerRejected = 'offerRejected'; static const offerAccepted = 'offerAccepted'; static const you = 'you'; + static const searchByItem = 'searchByItem'; } diff --git a/lib/repositories/appointment_repo.dart b/lib/repositories/appointment_repo.dart index f13fb45..cf99abe 100644 --- a/lib/repositories/appointment_repo.dart +++ b/lib/repositories/appointment_repo.dart @@ -19,6 +19,7 @@ abstract class AppointmentRepo { List? providerIdsList, List? categoryIdsList, List? serviceIdsList, + List? itemIdsList, List? branchIdsList, }); @@ -223,6 +224,7 @@ class AppointmentRepoImp implements AppointmentRepo { List? providerIdsList, List? categoryIdsList, List? serviceIdsList, + List? itemIdsList, List? branchIdsList, }) async { var params = { @@ -230,6 +232,7 @@ class AppointmentRepoImp implements AppointmentRepo { "ServiceProviderIDs": providerIdsList ?? [], "ProviderBranchIDs": branchIdsList ?? [], "ServiceIDs": serviceIdsList ?? [], + "ServiceItemIDs": itemIdsList ?? [], "CategoryIDs": categoryIdsList ?? [], }; GenericRespModel genericRespModel = await apiClient.getJsonForObject( diff --git a/lib/repositories/branch_repo.dart b/lib/repositories/branch_repo.dart index 2cb2f77..75fec0a 100644 --- a/lib/repositories/branch_repo.dart +++ b/lib/repositories/branch_repo.dart @@ -86,7 +86,7 @@ abstract class BranchRepo { Future> getAllNearBranchAndServices({required double latitude, required double longitude}); - Future> getServiceItems(int serviceId); + Future> getServiceItems({required int serviceId}); Future getBranchAndServicesByProviderId(int providerId, {double? latitude, double? longitude}); @@ -418,7 +418,7 @@ class BranchRepoImp implements BranchRepo { } @override - Future> getServiceItems(int serviceId) async { + Future> getServiceItems({required int serviceId}) async { var queryParameters = { "ServiceProviderServiceID": serviceId.toString(), }; @@ -427,7 +427,7 @@ class BranchRepoImp implements BranchRepo { (json) => GenericRespModel.fromJson(json), ApiConsts.getServiceItems, token: appState.getUser.data!.accessToken, - queryParameters: queryParameters, + queryParameters: serviceId.toString() != "-1" ? queryParameters : null, ); List serviceItems = List.generate(adsGenericModel.data.length, (index) => ItemData.fromJson(adsGenericModel.data[index])); return serviceItems; diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index c54fa50..947df6d 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -223,9 +223,7 @@ class AdVM extends BaseVM { bool isLoadingMore = false; bool hasMoreData = true; - fetchMoreAds() async { - - } + fetchMoreAds() async {} Future> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum}) async { return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum); diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 7547d28..a908f56 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -1,6 +1,6 @@ import 'dart:developer'; -import 'package:flutter/cupertino.dart'; +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/classes/consts.dart'; @@ -44,7 +44,6 @@ 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'; class AppointmentsVM extends BaseVM { final CommonRepo commonRepo; @@ -660,10 +659,10 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - Future> getServiceItems(int serviceId) async { + Future> getServiceItems({required int serviceId}) async { setState(ViewState.busy); serviceItemsFromApi.clear(); - serviceItemsFromApi = await branchRepo.getServiceItems(serviceId); + serviceItemsFromApi = await branchRepo.getServiceItems(serviceId: serviceId); selectedSubServicesCounter = 0; for (var item in serviceItemsFromApi) { if (ifItemAlreadySelected(item.id!)) { @@ -1301,8 +1300,9 @@ class AppointmentsVM extends BaseVM { } List categoryDropList = []; - List servicesDropList = []; List providersDropList = []; + List servicesDropList = []; + List itemsDropList = []; Future fetchAllProviders() async { if (providersDropList.isNotEmpty) return; @@ -1349,10 +1349,30 @@ class AppointmentsVM extends BaseVM { setState(ViewState.idle); } + Future fetchAllItems() async { + if (itemsDropList.isNotEmpty) return; + itemsDropList.clear(); + setState(ViewState.busy); + List itemsList = await branchRepo.getServiceItems(serviceId: -1); // to get all services + + for (ItemData element in itemsList) { + if (element.name != null && element.name!.isNotEmpty) { + itemsDropList.add( + DropValue( + element.id ?? 0, + element.name ?? "", + "", + ), + ); + } + } + setState(ViewState.idle); + } + Future populateDataForBranchesFilter() async { - await fetchAllProviders(); // saudi arabia - await fetchAllCategories(); // saudi arabia - await fetchAllServices(); // saudi arabia + await fetchAllProviders(); + await fetchAllCategories(); + await fetchAllServices(); updateBranchFilterCurrentDistance(25.0); } @@ -1446,6 +1466,7 @@ class AppointmentsVM extends BaseVM { await fetchAllProviders(); await fetchAllCategories(); await fetchAllServices(); + await fetchAllItems(); } int appointmentFiltersCounter = 0; @@ -1621,14 +1642,53 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } + List appointmentFilterItemsSearchHistory = []; + + void removeAppointmentFilterItemsSearchHistory({bool isClear = false, required int index}) { + if (isClear) { + appointmentFilterItemsSearchHistory.clear(); + notifyListeners(); + return; + } + appointmentFilterItemsSearchHistory.removeAt(index); + if (appointmentFilterItemsSearchHistory.isEmpty) { + updateAppointmentFiltersCounter(appointmentFiltersCounter - 1); + // appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + } + notifyListeners(); + } + + void addAppointmentFilterItemsSearchHistory({required DropValue value}) { + if (appointmentFilterItemsSearchHistory.isEmpty) { + updateAppointmentFiltersCounter(appointmentFiltersCounter + 1); + } + appointmentFilterItemsSearchHistory.add(value); + notifyListeners(); + } + + SelectionModel appointmentFilterSelectedItemId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + void updateAppointmentFilterSelectedItemId(SelectionModel id, {bool isForSearch = false}) async { + if (isForSearch) { + DropValue itemsDrop = itemsDropList.firstWhere((element) => element.id == id.selectedId); + if (!ifAlreadyExist(list: appointmentFilterItemsSearchHistory, value: itemsDrop)) { + addAppointmentFilterItemsSearchHistory(value: itemsDrop); + } + } + // appointmentFilterSelectedServiceId = id; + notifyListeners(); + } + void clearAppointmentFilterSelections() { appointmentFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); appointmentFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + appointmentFilterSelectedItemId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); appointmentFilterSelectedBranchId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); } void clearAppointmentFilters() { + appointmentFilterItemsSearchHistory.clear(); appointmentFilterServicesSearchHistory.clear(); appointmentFilterCategorySearchHistory.clear(); appointmentFilterProviderSearchHistory.clear(); @@ -1659,6 +1719,12 @@ class AppointmentsVM extends BaseVM { servicesIdsList.add(element.id.toString()); } } + List itemIdsList = []; + if (appointmentFilterItemsSearchHistory.isNotEmpty) { + for (var element in appointmentFilterItemsSearchHistory) { + itemIdsList.add(element.id.toString()); + } + } List branchesIdsList = []; if (appointmentFilterBranchSearchHistory.isNotEmpty) { for (var element in appointmentFilterBranchSearchHistory) { @@ -1670,6 +1736,7 @@ class AppointmentsVM extends BaseVM { providerIdsList: providersIdsList, categoryIdsList: categoryIdsList, serviceIdsList: servicesIdsList, + itemIdsList: itemIdsList, branchIdsList: branchesIdsList, ); applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); diff --git a/lib/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart b/lib/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart index 7f13c89..f6d224d 100644 --- a/lib/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart +++ b/lib/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart @@ -1,11 +1,11 @@ import 'dart:developer'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/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/main.dart'; import 'package:mc_common_app/models/advertisment_models/ads_duration_model.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; @@ -15,7 +15,6 @@ import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -import 'package:easy_localization/easy_localization.dart'; class AdDurationSelectionSheet extends StatelessWidget { final bool isFromExtendAd; @@ -76,12 +75,15 @@ class AdDurationSelectionSheet extends StatelessWidget { CustomPaint( painter: DiagonalLinePainter(), child: "${adDuration.price}".toText( - fontSize: 16, + fontSize: 20, isBold: true, )), 2.width, - (adDuration.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 8, color: MyColors.lightTextColor, isBold: true), - 10.width, + Padding( + padding: const EdgeInsets.only(bottom: 4), + child: (adDuration.currency ?? LocaleKeys.sar.tr()).toText(fontSize: 10, color: MyColors.lightTextColor, isBold: true), + ), + 14.width, "${adDuration.priceAfterDiscount}".toText(fontSize: 20, isBold: true), 2.width, Padding( @@ -126,7 +128,7 @@ class AdDurationSelectionSheet extends StatelessWidget { SelectionModel( selectedId: adDuration.id ?? 0, selectedOption: ("${adDuration.days} ${LocaleKeys.daysVar.tr()}"), - itemPrice: adDuration.price!.toInt().toString(), + itemPrice: adDuration.priceAfterDiscount!.toInt().toString(), ), ); return; @@ -135,7 +137,7 @@ class AdDurationSelectionSheet extends StatelessWidget { SelectionModel( selectedId: adDuration.id ?? 0, selectedOption: ("${adDuration.days} ${LocaleKeys.daysVar.tr()}"), - itemPrice: adDuration.price!.toString(), + itemPrice: adDuration.priceAfterDiscount!.toString(), currency: adDuration.currency!.toString(), ), ); @@ -200,7 +202,7 @@ class DiagonalLinePainter extends CustomPainter { void paint(Canvas canvas, Size size) { final Paint paint = Paint() ..color = MyColors.redColor - ..strokeWidth = 3; + ..strokeWidth = 2.5; // Draw the diagonal line from top-left to bottom-right canvas.drawLine(const Offset(0, 0), Offset(size.width, size.height), paint); diff --git a/lib/views/advertisement/components/damage_pictures_bottom_sheet.dart b/lib/views/advertisement/components/damage_pictures_bottom_sheet.dart index c3572d2..6e4e55a 100644 --- a/lib/views/advertisement/components/damage_pictures_bottom_sheet.dart +++ b/lib/views/advertisement/components/damage_pictures_bottom_sheet.dart @@ -24,7 +24,7 @@ class _DamagePicturesListState extends State { @override void initState() { super.initState(); - context.read().getServiceItems(widget.serviceId); + context.read().getServiceItems(serviceId: widget.serviceId); } @override diff --git a/lib/views/appointments/appointments_filter_view.dart b/lib/views/appointments/appointments_filter_view.dart index 45b2faf..3523174 100644 --- a/lib/views/appointments/appointments_filter_view.dart +++ b/lib/views/appointments/appointments_filter_view.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; @@ -13,7 +14,6 @@ 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'; import 'package:mc_common_app/widgets/common_widgets/search_entity_widget.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; @@ -33,14 +33,14 @@ class _AppointmentsFilterViewState extends State { void initState() { appointmentsVM = context.read(); scheduleMicrotask(() async { - await appointmentsVM.populateDataForBranchesFilter(); + await appointmentsVM.populateDataForAppointmentsFilter(); }); super.initState(); } @override void dispose() { - appointmentsVM.clearBranchFilterSelections(); + appointmentsVM.clearAppointmentFilterSelections(); super.dispose(); } @@ -96,7 +96,7 @@ class _AppointmentsFilterViewState extends State { children: [ SizedBox( height: 5.h, - width: 5.h, + width: 5.h, child: const CircularProgressIndicator(), ), ], @@ -165,6 +165,23 @@ class _AppointmentsFilterViewState extends State { onHistoryItemDeleted: (index) => appointmentsVM.removeAppointmentFilterServicesSearchHistory(index: index), onHistoryItemTapped: (DropValue value) => null, ), + const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5), + SearchEntityWidget( + title: LocaleKeys.searchByItem.tr(), + actionWidget: Builder(builder: (context) { + return DropdownField( + (DropValue value) => appointmentsVM.updateAppointmentFilterSelectedItemId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true), + list: appointmentsVM.itemsDropList, + dropdownValue: appointmentsVM.appointmentFilterSelectedItemId.selectedId != -1 + ? DropValue(appointmentsVM.appointmentFilterSelectedItemId.selectedId, appointmentsVM.appointmentFilterSelectedItemId.selectedOption, "") + : null, + hint: LocaleKeys.selectItems.tr(), + ); + }), + historyContent: appointmentsVM.appointmentFilterItemsSearchHistory, + onHistoryItemDeleted: (index) => appointmentsVM.removeAppointmentFilterItemsSearchHistory(index: index), + onHistoryItemTapped: (DropValue value) => null, + ), ], ).expand(), Container( @@ -193,10 +210,10 @@ class _AppointmentsFilterViewState extends State { InkWell( onTap: () => appointmentsVM.clearAppointmentFilters(), child: LocaleKeys.clearFilters.tr().toText( - fontSize: 14, - isBold: true, - color: MyColors.darkPrimaryColor, - ), + fontSize: 14, + isBold: true, + color: MyColors.darkPrimaryColor, + ), ), 10.height, ], diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart index eb9c4bc..95d5fea 100644 --- a/lib/views/appointments/book_appointment_services_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -1,5 +1,4 @@ -import 'dart:developer'; - +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; @@ -17,7 +16,6 @@ import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/common_widgets/custom_add_button_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -import 'package:easy_localization/easy_localization.dart'; class BookAppointmentServicesView extends StatelessWidget { const BookAppointmentServicesView({super.key}); @@ -73,7 +71,7 @@ class BookAppointmentServicesView extends StatelessWidget { itemPrice: "", ), ); - appointmentsVM.getServiceItems(serviceData.serviceProviderServiceId!); + appointmentsVM.getServiceItems(serviceId: serviceData.serviceProviderServiceId!); navigateWithName(context, AppRoutes.bookAppointmentsItemView); }), 10.width, 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 29ed87d..a3d3c1f 100644 --- a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -238,7 +238,7 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { onPressed: () { bool isValidated = appointmentsVM.isServiceSelectionValidated(); if (isValidated) { - appointmentsVM.getServiceItems(appointmentsVM.branchSelectedServiceId.selectedId); + appointmentsVM.getServiceItems(serviceId: appointmentsVM.branchSelectedServiceId.selectedId); Navigator.pop(context); navigateWithName(context, AppRoutes.bookAppointmentsItemView); } From 803e81dbc96f151062200a961f5c110c80b16cdc Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Thu, 5 Dec 2024 01:57:34 +0300 Subject: [PATCH 08/10] Added Services Widget on HomeScreen --- assets/langs/ar-SA.json | 7 ++++--- assets/langs/en-US.json | 6 ++++-- lib/generated/codegen_loader.g.dart | 18 +++++++++------- lib/generated/locale_keys.g.dart | 2 ++ lib/models/general_models/widgets_models.dart | 3 ++- lib/view_models/appointments_view_model.dart | 21 ++++++++++--------- .../components/ads_list_widget.dart | 4 +++- .../common_appointment_slider_widget.dart | 6 +++--- lib/views/common_fragments/ads_fragment.dart | 7 ++++++- lib/widgets/dropdown/dropdown_text.dart | 10 ++++----- 10 files changed, 50 insertions(+), 34 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 7b4cfb3..5a4f114 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -753,8 +753,9 @@ "done": "تم", "notice": "إشعار", "serviceDeactivated": "تم تعطيل الخدمة", - "totalNumberOfServices": "إجمالي عدد الخدمات:", + "totalNumberOfServices": "عدد الخدمات:", "noAvailableOfficesInCity": "لا توجد مكاتب متاحة في مدينتك لخدمتك.", - "searchByItem": "البحث حسب العنصر" - + "searchByItem": "البحث حسب العنصر", + "selectBranchToCopyServices": "حدد الفرع لنسخ العناصر الخاصة بهم إلى هذا الفرع. يمكنك تعديل الاختيار في أي وقت.", + "tapToSelect": "Tap to select" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 2548f81..731a962 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -751,7 +751,9 @@ "done": "Done", "notice": "Notice", "serviceDeactivated": "Service Deactivated", - "totalNumberOfServices": "Total number of services:", + "totalNumberOfServices": "Number of services:", "noAvailableOfficesInCity": "There are no available offices in your city for your service.", - "searchByItem": "Search By Item" + "searchByItem": "Search By Item", + "selectBranchToCopyServices": "Select the branch to copy their items to this branch. You can modify the selection at any time.", + "tapToSelect": "Tap to select" } \ No newline at end of file diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index e86e38d..73ef7d0 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -769,10 +769,11 @@ class CodegenLoader extends AssetLoader{ "done": "تم", "notice": "إشعار", "serviceDeactivated": "تم تعطيل الخدمة", - "totalNumberOfServices": "إجمالي عدد الخدمات:", - "noAvailableOfficesInCity": "لا توجد مكاتب متاحة في مدينتك لخدمتك." - "you": "أنتم", - "searchByItem": "البحث حسب العنصر" + "totalNumberOfServices": "عدد الخدمات:", + "noAvailableOfficesInCity": "لا توجد مكاتب متاحة في مدينتك لخدمتك.", + "searchByItem": "البحث حسب العنصر", + "selectBranchToCopyServices": "حدد الفرع لنسخ العناصر الخاصة بهم إلى هذا الفرع. يمكنك تعديل الاختيار في أي وقت.", + "tapToSelect": "Tap to select" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1527,10 +1528,11 @@ static const Map en_US = { "done": "Done", "notice": "Notice", "serviceDeactivated": "Service Deactivated", - "totalNumberOfServices": "Total number of services:", - "noAvailableOfficesInCity": "There are no available offices in your city for your service." - "you": "You", - "searchByItem": "Search By Item" + "totalNumberOfServices": "Number of services:", + "noAvailableOfficesInCity": "There are no available offices in your city for your service.", + "searchByItem": "Search By Item", + "selectBranchToCopyServices": "Select the branch to copy their items to this branch. You can modify the selection at any time.", + "tapToSelect": "Tap to select" }; 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 b668b9e..1d161b3 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -735,5 +735,7 @@ abstract class LocaleKeys { static const totalNumberOfServices = 'totalNumberOfServices'; static const noAvailableOfficesInCity = 'noAvailableOfficesInCity'; static const searchByItem = 'searchByItem'; + static const selectBranchToCopyServices = 'selectBranchToCopyServices'; + static const tapToSelect = 'tapToSelect'; } diff --git a/lib/models/general_models/widgets_models.dart b/lib/models/general_models/widgets_models.dart index d4271c9..ae5e061 100644 --- a/lib/models/general_models/widgets_models.dart +++ b/lib/models/general_models/widgets_models.dart @@ -1,9 +1,10 @@ class FilterListModel { String title; + String iconUrl; int id; bool isSelected; - FilterListModel({required this.id, required this.isSelected, required this.title}); + FilterListModel({required this.id, required this.isSelected, this.iconUrl = "", required this.title}); } class SelectionModel { diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 010a5cd..891e253 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -259,7 +259,7 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - List branchesFilterOptions = []; + List branchesCategoriesFilterOptions = []; List servicesInCurrentAppointment = []; ServiceModel? currentServiceSelection; @@ -478,6 +478,7 @@ class AppointmentsVM extends BaseVM { (element.appointmentStatusEnum == AppointmentStatusEnum.confirmed || element.appointmentStatusEnum == AppointmentStatusEnum.booked) && (DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now()))) .toList(); + notifyListeners(); applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true); setState(ViewState.idle); } @@ -626,13 +627,13 @@ class AppointmentsVM extends BaseVM { } Future applyFilterOnBranches({required int index}) async { - if (branchesFilterOptions.isEmpty) return; - for (var value in branchesFilterOptions) { + if (branchesCategoriesFilterOptions.isEmpty) return; + for (var value in branchesCategoriesFilterOptions) { value.isSelected = false; } - branchesFilterOptions[index].isSelected = true; + branchesCategoriesFilterOptions[index].isSelected = true; - await getBranchesBasedOnCategoryFilters(categoryId: branchesFilterOptions[index].id); + await getBranchesBasedOnCategoryFilters(categoryId: branchesCategoriesFilterOptions[index].id); notifyListeners(); } @@ -1019,14 +1020,14 @@ class AppointmentsVM extends BaseVM { List branchServices = []; Future populateBranchesFilterList() async { - if (branchesFilterOptions.isNotEmpty) return; - branchesFilterOptions.clear(); + if (branchesCategoriesFilterOptions.isNotEmpty) return; + branchesCategoriesFilterOptions.clear(); setOnlyState(ViewState.busy); Category category = await branchRepo.fetchBranchCategory(); category.data?.forEach((element) { - branchesFilterOptions.add(FilterListModel(id: element.id ?? 0, isSelected: false, title: element.categoryName ?? "N/A")); + branchesCategoriesFilterOptions.add(FilterListModel(id: element.id ?? 0, isSelected: false, iconUrl: element.serviceCategoryIconUrl ?? "", title: element.categoryName ?? "N/A")); }); - branchesFilterOptions.insert(0, FilterListModel(id: 0, isSelected: true, title: "All Branches")); + branchesCategoriesFilterOptions.insert(0, FilterListModel(id: 0, isSelected: true, title: "All Branches")); notifyListeners(); setState(ViewState.idle); } @@ -1036,7 +1037,7 @@ class AppointmentsVM extends BaseVM { if (isNeedToRebuild) setState(ViewState.busy); if (isFromRefresh) { - var selectedFilter = branchesFilterOptions.firstWhere((element) => element.isSelected); + var selectedFilter = branchesCategoriesFilterOptions.firstWhere((element) => element.isSelected); log("selectedFilter: ${selectedFilter.id}"); if (selectedFilter.id == 0) { diff --git a/lib/views/advertisement/components/ads_list_widget.dart b/lib/views/advertisement/components/ads_list_widget.dart index 094baae..919937c 100644 --- a/lib/views/advertisement/components/ads_list_widget.dart +++ b/lib/views/advertisement/components/ads_list_widget.dart @@ -21,12 +21,14 @@ class AdsListWidget extends StatelessWidget { final ScrollPhysics? scrollPhysics; final bool isAdsFragment; final bool shouldShowAdStatus; + final Function() onFetchMoreAds; const AdsListWidget({ super.key, required this.adsList, - this.scrollPhysics, required this.shouldShowAdStatus, + required this.onFetchMoreAds, + this.scrollPhysics, this.isAdsFragment = false, }); diff --git a/lib/views/appointments/widgets/common_appointment_slider_widget.dart b/lib/views/appointments/widgets/common_appointment_slider_widget.dart index ce7cb80..fc08420 100644 --- a/lib/views/appointments/widgets/common_appointment_slider_widget.dart +++ b/lib/views/appointments/widgets/common_appointment_slider_widget.dart @@ -26,7 +26,7 @@ import 'package:sizer/sizer.dart'; class CommonAppointmentSliderWidget extends StatelessWidget { final Function(AppointmentListModel)? onAppointmentClick; - const CommonAppointmentSliderWidget({Key? key, this.onAppointmentClick}) : super(key: key); + const CommonAppointmentSliderWidget({super.key, this.onAppointmentClick}); Widget getCarouselSliderWidget(AppType appType, AppointmentsVM appointmentsVM) { return CarouselSlider.builder( @@ -95,7 +95,7 @@ class AppointmentHomeTileWidget extends StatelessWidget { final AppointmentListModel? appointmentListModel; final Function()? onTapped; - const AppointmentHomeTileWidget({Key? key, this.isForProvider = false, this.onTapped, required this.appointmentListModel, this.isForCustomerHome = false}) : super(key: key); + const AppointmentHomeTileWidget({super.key, this.isForProvider = false, this.onTapped, required this.appointmentListModel, this.isForCustomerHome = false}); Widget showServices(String title, String icon, {bool isMoreText = false}) { return Row( @@ -227,7 +227,7 @@ class AppointmentFragmentTileWidget extends StatelessWidget { final AppointmentListModel? appointmentListModel; final Function()? onTapped; - const AppointmentFragmentTileWidget({Key? key, this.onTapped, required this.appointmentListModel}) : super(key: key); + const AppointmentFragmentTileWidget({super.key, this.onTapped, required this.appointmentListModel}); Widget showServices(String title, String icon, {bool isMoreText = false}) { return Row( diff --git a/lib/views/common_fragments/ads_fragment.dart b/lib/views/common_fragments/ads_fragment.dart index 547ff96..024b677 100644 --- a/lib/views/common_fragments/ads_fragment.dart +++ b/lib/views/common_fragments/ads_fragment.dart @@ -135,7 +135,12 @@ class AdsFragment extends StatelessWidget { }, child: adVM.state == ViewState.busy ? const Center(child: CircularProgressIndicator()) - : AdsListWidget(isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM)), + : AdsListWidget( + isAdsFragment: true, + shouldShowAdStatus: !adVM.isExploreAdsTapped, + adsList: getAdsList(adVM), + onFetchMoreAds: () {}, + ), ), ) ], diff --git a/lib/widgets/dropdown/dropdown_text.dart b/lib/widgets/dropdown/dropdown_text.dart index 079ee26..056d2b3 100644 --- a/lib/widgets/dropdown/dropdown_text.dart +++ b/lib/widgets/dropdown/dropdown_text.dart @@ -1,14 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.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/widgets/extensions/extensions_widget.dart'; class DropDownText extends StatelessWidget { - String title; + final String title; + final bool showDropDownIcon; - DropDownText(this.title, {super.key}); + DropDownText({required this.title, required this.showDropDownIcon, super.key}); @override Widget build(BuildContext context) { @@ -20,7 +19,8 @@ class DropDownText extends StatelessWidget { fontSize: 16, letterSpacing: -0.64, ), - const Icon(Icons.keyboard_arrow_down_outlined), + if (showDropDownIcon) + const Icon(Icons.keyboard_arrow_down_outlined), ], ).toContainer( isEnabledBorder: true, From 07c3fa416433caa3fdb6b76a7b3eb1fc4d2794e2 Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Thu, 5 Dec 2024 01:58:22 +0300 Subject: [PATCH 09/10] Added Services Widget on HomeScreen --- .../advertisement/components/ads_list_widget.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/views/advertisement/components/ads_list_widget.dart b/lib/views/advertisement/components/ads_list_widget.dart index 919937c..344738e 100644 --- a/lib/views/advertisement/components/ads_list_widget.dart +++ b/lib/views/advertisement/components/ads_list_widget.dart @@ -21,13 +21,13 @@ class AdsListWidget extends StatelessWidget { final ScrollPhysics? scrollPhysics; final bool isAdsFragment; final bool shouldShowAdStatus; - final Function() onFetchMoreAds; + final Function()? onFetchMoreAds; const AdsListWidget({ super.key, required this.adsList, required this.shouldShowAdStatus, - required this.onFetchMoreAds, + this.onFetchMoreAds, this.scrollPhysics, this.isAdsFragment = false, }); @@ -44,7 +44,9 @@ class AdsListWidget extends StatelessWidget { } return NotificationListener( onNotification: (ScrollNotification scrollInfo) { - if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && context.read().hasMoreData) { + if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && context + .read() + .hasMoreData) { log("should call more ads api:"); } return false; @@ -102,7 +104,9 @@ class AdCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - if (isAdsFragment && !context.read().isExploreAdsTapped) ...[ + if (isAdsFragment && !context + .read() + .isExploreAdsTapped) ...[ Utils.statusContainerChip(text: adDetails.statuslabel!, chipColor: Utils.getChipColorByAdStatus(adDetails.adPostStatus!)), ], (adDetails.vehicle!.vehicleTitle ?? "").toText( From 618a27ab4da49797df747f553e867d1dad87cf2e Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Thu, 5 Dec 2024 18:00:48 +0300 Subject: [PATCH 10/10] addnew service fix --- assets/langs/ar-SA.json | 5 +- assets/langs/en-US.json | 5 +- lib/generated/codegen_loader.g.dart | 10 +++- lib/generated/locale_keys.g.dart | 3 ++ lib/repositories/branch_repo.dart | 29 ++++++------ lib/view_models/ad_view_model.dart | 3 +- lib/view_models/appointments_view_model.dart | 4 ++ lib/view_models/chat_view_model.dart | 2 +- lib/view_models/service_view_model.dart | 24 ++++++---- .../components/ads_list_widget.dart | 12 ++--- .../chat/widgets/chat_message_widget.dart | 19 ++++---- lib/views/common_fragments/ads_fragment.dart | 6 ++- lib/views/requests/review_request_offer.dart | 47 ++++++++++++------- lib/widgets/checkbox_with_title_desc.dart | 4 +- 14 files changed, 109 insertions(+), 64 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 5a4f114..9eba80f 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -757,5 +757,8 @@ "noAvailableOfficesInCity": "لا توجد مكاتب متاحة في مدينتك لخدمتك.", "searchByItem": "البحث حسب العنصر", "selectBranchToCopyServices": "حدد الفرع لنسخ العناصر الخاصة بهم إلى هذا الفرع. يمكنك تعديل الاختيار في أي وقت.", - "tapToSelect": "Tap to select" + "tapToSeeItems": "اضغط لرؤية العناصر", + "tapToSelect": "اضغط للاختيار", + "noteCopyItemsExplanation": "ملاحظة: ستتمكن من نسخ العناصر من خدمة إلى أخرى في الفئة المحددة. يجب عليك إنشاء الخدمات أولاً ويجب أن تكون معتمدة. ثم ستتمكن من الحصول على الخدمات المتاحة التي يمكنك نسخ جميع العناصر منها أو تحديد العناصر التي تريد نسخها.", + "requestCreatedOn": "تم إنشاء الطلب في" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 731a962..7027c81 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -755,5 +755,8 @@ "noAvailableOfficesInCity": "There are no available offices in your city for your service.", "searchByItem": "Search By Item", "selectBranchToCopyServices": "Select the branch to copy their items to this branch. You can modify the selection at any time.", - "tapToSelect": "Tap to select" + "tapToSelect": "Tap to select", + "tapToSeeItems": "Tap to see items", + "noteCopyItemsExplanation": "Note: You will be able to copy items from one service to another in a selected category. You must create the services first and they should be approved. Then you will be able to get the available services from which you can copy all or selected items.", + "requestCreatedOn": "Request created on" } \ No newline at end of file diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 73ef7d0..905622f 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -773,7 +773,10 @@ class CodegenLoader extends AssetLoader{ "noAvailableOfficesInCity": "لا توجد مكاتب متاحة في مدينتك لخدمتك.", "searchByItem": "البحث حسب العنصر", "selectBranchToCopyServices": "حدد الفرع لنسخ العناصر الخاصة بهم إلى هذا الفرع. يمكنك تعديل الاختيار في أي وقت.", - "tapToSelect": "Tap to select" + "tapToSeeItems": "اضغط لرؤية العناصر", + "tapToSelect": "اضغط للاختيار", + "noteCopyItemsExplanation": "ملاحظة: ستتمكن من نسخ العناصر من خدمة إلى أخرى في الفئة المحددة. يجب عليك إنشاء الخدمات أولاً ويجب أن تكون معتمدة. ثم ستتمكن من الحصول على الخدمات المتاحة التي يمكنك نسخ جميع العناصر منها أو تحديد العناصر التي تريد نسخها.", + "requestCreatedOn": "تم إنشاء الطلب في" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1532,7 +1535,10 @@ static const Map en_US = { "noAvailableOfficesInCity": "There are no available offices in your city for your service.", "searchByItem": "Search By Item", "selectBranchToCopyServices": "Select the branch to copy their items to this branch. You can modify the selection at any time.", - "tapToSelect": "Tap to select" + "tapToSelect": "Tap to select", + "tapToSeeItems": "Tap to see items", + "noteCopyItemsExplanation": "Note: You will be able to copy items from one service to another in a selected category. You must create the services first and they should be approved. Then you will be able to get the available services from which you can copy all or selected items.", + "requestCreatedOn": "Request created on" }; 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 1d161b3..396b4fa 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -736,6 +736,9 @@ abstract class LocaleKeys { static const noAvailableOfficesInCity = 'noAvailableOfficesInCity'; static const searchByItem = 'searchByItem'; static const selectBranchToCopyServices = 'selectBranchToCopyServices'; + static const tapToSeeItems = 'tapToSeeItems'; static const tapToSelect = 'tapToSelect'; + static const noteCopyItemsExplanation = 'noteCopyItemsExplanation'; + static const requestCreatedOn = 'requestCreatedOn'; } diff --git a/lib/repositories/branch_repo.dart b/lib/repositories/branch_repo.dart index 89f85ff..068c285 100644 --- a/lib/repositories/branch_repo.dart +++ b/lib/repositories/branch_repo.dart @@ -236,7 +236,7 @@ class BranchRepoImp implements BranchRepo { GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: t, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderDDLGet, ); List providersList = List.generate(adsGenericModel.data.length, (index) => ProviderBasicDataModel.fromJson(adsGenericModel.data[index])); @@ -268,7 +268,8 @@ class BranchRepoImp implements BranchRepo { serviceProviderBranchImages.add(imageMap); } - String lat = "0", long = "0"; + String lat = "0", + long = "0"; try { lat = latitude.toString().substring(0, 9); long = longitude.toString().substring(0, 9); @@ -340,7 +341,7 @@ class BranchRepoImp implements BranchRepo { String t = AppState().getUser.data!.accessToken ?? ""; GenericRespModel genericRespModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderAppointmentGetByCategoryOrService, queryParameters: map, token: t, @@ -415,7 +416,7 @@ class BranchRepoImp implements BranchRepo { String t = AppState().getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getAllNearBranches, token: appState.getUser.data!.accessToken, queryParameters: queryParameters, @@ -443,7 +444,7 @@ class BranchRepoImp implements BranchRepo { "longitude": longitude.toString(), }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getAllNearBranches, token: appState.getUser.data!.accessToken, queryParameters: queryParameters, @@ -455,7 +456,7 @@ class BranchRepoImp implements BranchRepo { @override Future> getMyRecentBranchesWithServices() async { GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getMyRecentBranches, token: appState.getUser.data!.accessToken, ); @@ -470,7 +471,7 @@ class BranchRepoImp implements BranchRepo { }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getServiceItems, token: appState.getUser.data!.accessToken, queryParameters: serviceId.toString() != "-1" ? queryParameters : null, @@ -490,7 +491,7 @@ class BranchRepoImp implements BranchRepo { postParams.addAll({"Latitude": "$latitude"}); } GenericRespModel adsGenericModel = - await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams); + await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams); return ProviderProfileModel.fromJson(adsGenericModel.data); } @@ -535,7 +536,7 @@ class BranchRepoImp implements BranchRepo { }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getAllNearBranches, token: appState.getUser.data!.accessToken, queryParameters: postParams, @@ -549,7 +550,7 @@ class BranchRepoImp implements BranchRepo { var postParams = {"ServiceProviderBranchID": serviceProviderBranchID.toString()}; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getBranchRatings, token: appState.getUser.data!.accessToken, queryParameters: postParams, @@ -563,7 +564,7 @@ class BranchRepoImp implements BranchRepo { final customerID = appState.getUser.data!.userInfo!.customerId; final parameters = {"title": title, "review": review, "ratNo": ratingNo, "serviceProviderBranchID": serviceProviderBranchID, "customerID": "$customerID"}; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.createBranchRatings, parameters, token: appState.getUser.data!.accessToken, @@ -579,7 +580,7 @@ class BranchRepoImp implements BranchRepo { "customerID": customerID.toString(), }; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.favouriteServiceProviderCreate, parameters, token: appState.getUser.data!.accessToken, @@ -591,7 +592,7 @@ class BranchRepoImp implements BranchRepo { Future removeProviderFromFavourite({required int providerID}) async { final parameters = {"id": providerID.toString()}; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.unFavouriteServiceProvider, parameters, token: appState.getUser.data!.accessToken, @@ -606,7 +607,7 @@ class BranchRepoImp implements BranchRepo { var postParams = {"customerID": customerID.toString()}; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.favouriteServiceProviderGet, token: appState.getUser.data!.accessToken, queryParameters: postParams, diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 3d5dfa7..c7b4568 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -219,7 +219,8 @@ class AdVM extends BaseVM { notifyListeners(); } - int currentPage = 1; + int currentPageForExploreAds = 1; + int currentPageForMyAds = 1; bool isLoadingMore = false; bool hasMoreData = true; diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 891e253..e5f6606 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -398,10 +398,14 @@ class AppointmentsVM extends BaseVM { // Create a list of CustomerData instances myFilteredAppointments2 = uniqueCustomerIDs.map((id) { List list = myFilteredAppointments.where((item) => item.customerID == id).toList(); + list.sort((a, b) => DateHelper.parseStringToDate(DateHelper.formatDateT(b.appointmentDate!)) + .millisecondsSinceEpoch + .compareTo(DateHelper.parseStringToDate(DateHelper.formatDateT(a.appointmentDate!)).millisecondsSinceEpoch)); AppointmentListModel model = list.first; model.customerAppointmentList = list; return model; }).toList(); + // customersAppointments = uniqueCustomerIDs.map((id) { // List list = myFilteredAppointments // .where((item) => item.customerID == id) diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index f2c8bf2..17d4654 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -98,7 +98,7 @@ class ChatVM extends BaseVM { } List indexesForCancelSpecialCarOffer = [0, 1, 5, 6, 10]; - List indexesForCancelSparePartOffer = [2, 5, 10]; + List indexesForCancelSparePartOffer = [2, 5, 6, 10]; List indexesForRejectOffer = [3, 4, 10]; List indexesForDealNotCompleted = [7, 4, 8, 9, 10]; diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index a6d1172..11a5015 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -272,7 +272,11 @@ class ServiceVM extends BaseVM { context, allowMultiple: false, ); - if (files != null && files.any((element) => element.path.split('.').last.toLowerCase() != 'pdf')) { + if (files != null && files.any((element) => + element.path + .split('.') + .last + .toLowerCase() != 'pdf')) { Utils.showToast("Only PDF Files are allowed"); return; } @@ -286,8 +290,8 @@ class ServiceVM extends BaseVM { documentID == 1 ? commerceCertificates.addAll(imageModels) : documentID == 2 - ? commercialCertificates.addAll(imageModels) - : vatCertificates.addAll(imageModels); + ? commercialCertificates.addAll(imageModels) + : vatCertificates.addAll(imageModels); document!.data![index].document = Utils.convertFileToBase64(files.first); document!.data![index].fileExt = Utils.checkFileExt(files.first.path); document!.data![index].documentUrl = files.first.path; @@ -463,10 +467,10 @@ class ServiceVM extends BaseVM { DropValue( element.id ?? 0, ((element.categoryName!.isEmpty - ? "N/A" - : countryCode == "SA" - ? element.categoryNameN - : element.categoryName) ?? + ? "N/A" + : countryCode == "SA" + ? element.categoryNameN + : element.categoryName) ?? "N/A"), "", ), @@ -626,7 +630,7 @@ class ServiceVM extends BaseVM { List? matchedServices; bool isAllSelected = false; - Future getAllMatchedServices(int oldBranchId, int newBranchId, int categoryId) async { + Future getAllMatchedServices({required int oldBranchId, required int newBranchId, required int categoryId}) async { matchedServices = null; final GenericRespModel response = await branchRepo.getMatchedServices(oldBranchId, newBranchId, categoryId); matchedServices = []; @@ -747,7 +751,9 @@ class ServiceVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path.split('/').last; + String fileName = file.path + .split('/') + .last; branchPostingImages = BranchPostingImages( imageName: fileName, imageStr: image, diff --git a/lib/views/advertisement/components/ads_list_widget.dart b/lib/views/advertisement/components/ads_list_widget.dart index 344738e..fff78fb 100644 --- a/lib/views/advertisement/components/ads_list_widget.dart +++ b/lib/views/advertisement/components/ads_list_widget.dart @@ -44,10 +44,10 @@ class AdsListWidget extends StatelessWidget { } return NotificationListener( onNotification: (ScrollNotification scrollInfo) { - if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && context - .read() - .hasMoreData) { - log("should call more ads api:"); + if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && context.read().hasMoreData) { + if (onFetchMoreAds != null) { + onFetchMoreAds!(); + } } return false; }, @@ -104,9 +104,7 @@ class AdCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - if (isAdsFragment && !context - .read() - .isExploreAdsTapped) ...[ + if (isAdsFragment && !context.read().isExploreAdsTapped) ...[ Utils.statusContainerChip(text: adDetails.statuslabel!, chipColor: Utils.getChipColorByAdStatus(adDetails.adPostStatus!)), ], (adDetails.vehicle!.vehicleTitle ?? "").toText( diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 5ede78f..05ef366 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -776,7 +776,7 @@ class _ChatMessageCustomWidgetState extends State { ], Expanded( child: Directionality( - textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.ltr : TextDirection.rtl, + textDirection: TextDirection.ltr, child: (widget.chatMessageModel.chatText ?? "").toText( color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, fontSize: 12, @@ -901,19 +901,18 @@ class _ChatMessageCustomWidgetState extends State { case ChatMessageTypeEnum.file: case ChatMessageTypeEnum.offer: messageTypeWidget = Column( + crossAxisAlignment: (widget.chatMessageModel.isMyMessage ?? false) ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ buildFreeTextDetailsInMessage(chatMessageTypeEnum: chatMessageTypeEnum), if (widget.requestsTypeEnum == RequestsTypeEnum.serviceRequest) ...[ 2.height, - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - "${LocaleKeys.deliveryAvailable.tr()} : ${(widget.chatMessageModel.reqOffer!.isDeliveryAvailable ?? false) ? LocaleKeys.yes.tr() : LocaleKeys.no.tr()}".toText( - fontSize: 10, - color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, - fontWeight: MyFonts.Medium, - ), - ], + Directionality( + textDirection: TextDirection.ltr, + child: "${LocaleKeys.deliveryAvailable.tr()} : ${(widget.chatMessageModel.reqOffer!.isDeliveryAvailable ?? false) ? LocaleKeys.yes.tr() : LocaleKeys.no.tr()}".toText( + fontSize: 10, + color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, + fontWeight: MyFonts.Medium, + ), ), ], if (widget.chatMessageModel.reqOffer!.reqOfferImages != null && widget.chatMessageModel.reqOffer!.reqOfferImages!.isNotEmpty) ...[ diff --git a/lib/views/common_fragments/ads_fragment.dart b/lib/views/common_fragments/ads_fragment.dart index 024b677..b0e29d4 100644 --- a/lib/views/common_fragments/ads_fragment.dart +++ b/lib/views/common_fragments/ads_fragment.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; @@ -139,7 +141,9 @@ class AdsFragment extends StatelessWidget { isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM), - onFetchMoreAds: () {}, + onFetchMoreAds: () { + log("fetch more ads"); + }, ), ), ) diff --git a/lib/views/requests/review_request_offer.dart b/lib/views/requests/review_request_offer.dart index 9af95fe..4fef205 100644 --- a/lib/views/requests/review_request_offer.dart +++ b/lib/views/requests/review_request_offer.dart @@ -94,20 +94,25 @@ class _ReviewRequestOfferState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - LocaleKeys.locationInformation.tr().toText(fontSize: 18), - MyAssets.icEdit.buildSvg().onPress(() => buildLocationInformationEditBottomSheet(context, requestVM)), - ], - ), + LocaleKeys.locationInformation.tr().toText(fontSize: 18), + + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // LocaleKeys.locationInformation.tr().toText(fontSize: 18), + // MyAssets.icEdit.buildSvg().onPress(() => buildLocationInformationEditBottomSheet(context, requestVM)), + // ], + // ), 8.height, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SingleDetailWidget(text: requestVM.currentSelectedRequest!.address ?? "", type: LocaleKeys.location.tr()), - 16.height, - SingleDetailWidget(text: requestVM.additionalAddressSparePartRequestDelivery.isNotEmpty ? requestVM.additionalAddressSparePartRequestDelivery : "N/A", type: LocaleKeys.additionalAddressDetails.tr()), + // 16.height, + // SingleDetailWidget( + // text: requestVM.additionalAddressSparePartRequestDelivery.isNotEmpty ? requestVM.additionalAddressSparePartRequestDelivery : "N/A", + // type: LocaleKeys.additionalAddressDetails.tr(), + // ), ], ), ], @@ -117,9 +122,13 @@ class _ReviewRequestOfferState extends State { Widget buildServiceInformation(BuildContext context) { final requestVM = context.read(); - String formattedDate = ""; + String manufacturedOnFormattedDate = ""; if (requestVM.acceptedRequestOffer!.manufacturedOn != null) { - formattedDate = DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(requestVM.acceptedRequestOffer!.manufacturedOn.toString() ?? ""))); + manufacturedOnFormattedDate = DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(requestVM.acceptedRequestOffer!.manufacturedOn.toString() ?? ""))); + } + String requestCreatedOn = ""; + if (requestVM.currentSelectedRequest!.createdOn != null) { + requestCreatedOn = DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(requestVM.currentSelectedRequest!.createdOn.toString() ?? ""))); } return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -148,8 +157,8 @@ class _ReviewRequestOfferState extends State { 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.acceptedRequestOffer!.price.toString()} ${LocaleKeys.sar.tr()}", type: LocaleKeys.offerPrice.tr()), 16.height, SingleDetailWidget(text: requestVM.acceptedRequestOfferProviderName ?? "", type: LocaleKeys.providerName.tr()), 16.height, @@ -165,12 +174,18 @@ class _ReviewRequestOfferState extends State { SingleDetailWidget(text: requestVM.currentSelectedRequest!.brand, type: LocaleKeys.vehicleBrand.tr()), 16.height, SingleDetailWidget(text: requestVM.acceptedRequestOffer!.serviceItemName ?? "", type: LocaleKeys.serviceName.tr()), - if (formattedDate.isNotEmpty) ...[ + if (manufacturedOnFormattedDate.isNotEmpty) ...[ 16.height, - SingleDetailWidget(text: formattedDate, type: LocaleKeys.manufacturedOn.tr()), + SingleDetailWidget(text: manufacturedOnFormattedDate, type: LocaleKeys.manufacturedOn.tr()), ], 16.height, - SingleDetailWidget(text: "${requestVM.currentSelectedRequest!.price.toString()} SAR", type: LocaleKeys.totalPrice.tr()), + // SingleDetailWidget(text: "${requestVM.currentSelectedRequest!.price.toString()} ${LocaleKeys.sar.tr()}", type: LocaleKeys.totalPrice.tr()), + SingleDetailWidget(text: "${requestVM.acceptedRequestOffer!.price.toString()} ${LocaleKeys.sar.tr()}", type: LocaleKeys.offerPrice.tr()), + + if (requestCreatedOn.isNotEmpty) ...[ + 16.height, + SingleDetailWidget(text: requestCreatedOn, type: LocaleKeys.requestCreatedOn.tr()), + ], ], ), ), diff --git a/lib/widgets/checkbox_with_title_desc.dart b/lib/widgets/checkbox_with_title_desc.dart index 595c461..a3d67bb 100644 --- a/lib/widgets/checkbox_with_title_desc.dart +++ b/lib/widgets/checkbox_with_title_desc.dart @@ -9,12 +9,14 @@ class CheckBoxWithTitleDescription extends StatelessWidget { final String title, description; final Function(bool) onSelection; final bool isDisabled; + final CrossAxisAlignment? crossAxisAlignment; const CheckBoxWithTitleDescription({ required this.isSelected, required this.title, required this.description, required this.onSelection, + this.crossAxisAlignment, this.isDisabled = false, super.key, }); @@ -30,7 +32,7 @@ class CheckBoxWithTitleDescription extends StatelessWidget { child: SizedBox( width: double.infinity, child: Row( - crossAxisAlignment: description.isNotEmpty ? CrossAxisAlignment.start : CrossAxisAlignment.center, + crossAxisAlignment: crossAxisAlignment == null ? (description.isNotEmpty ? CrossAxisAlignment.start : CrossAxisAlignment.center) : crossAxisAlignment!, mainAxisAlignment: MainAxisAlignment.start, children: [ Checkbox(