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(