From c3696586f39882a0c99842f712a639d7005fb11b Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Wed, 4 Sep 2024 15:34:55 +0300 Subject: [PATCH] Service Request Done --- lib/config/routes.dart | 12 +- lib/extensions/string_extensions.dart | 22 +- .../chat_models/chat_message_model.dart | 18 +- lib/repositories/chat_repo.dart | 1 + lib/view_models/ad_view_model.dart | 39 +- lib/view_models/chat_view_model.dart | 21 +- lib/view_models/requests_view_model.dart | 42 +- lib/view_models/service_view_model.dart | 6 + .../advertisement/ads_buyer_chats_view.dart | 7 +- .../ads_detail_view/ads_detail_view.dart | 371 ++++++++++++++++ .../components.dart} | 357 +-------------- .../common_appointment_slider_widget.dart | 12 +- lib/views/chat/chat_view.dart | 409 +----------------- .../chat/widgets/chat_message_widget.dart | 389 +++++++++++++++++ lib/views/requests/request_bottomsheets.dart | 21 +- lib/views/requests/request_detail_page.dart | 222 ++++++++++ lib/views/requests/widget/request_item.dart | 3 +- lib/widgets/button/show_fill_button.dart | 40 +- .../common_widgets/branch_details_card.dart | 2 - lib/widgets/extensions/extensions_widget.dart | 37 +- 20 files changed, 1155 insertions(+), 876 deletions(-) create mode 100644 lib/views/advertisement/ads_detail_view/ads_detail_view.dart rename lib/views/advertisement/{ads_detail_view.dart => ads_detail_view/components.dart} (75%) create mode 100644 lib/views/chat/widgets/chat_message_widget.dart create mode 100644 lib/views/requests/request_detail_page.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index ba06962..7d5d3b9 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -5,7 +5,7 @@ import 'package:mc_common_app/models/requests_models/request_model.dart'; import 'package:mc_common_app/models/user_models/register_user.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/views/advertisement/ads_buyer_chats_view.dart'; -import 'package:mc_common_app/views/advertisement/ads_detail_view.dart'; +import 'package:mc_common_app/views/advertisement/ads_detail_view/ads_detail_view.dart'; import 'package:mc_common_app/views/advertisement/ads_filter_view.dart'; import 'package:mc_common_app/views/advertisement/create_ad_view.dart'; import 'package:mc_common_app/views/advertisement/select_ad_type_view.dart'; @@ -14,6 +14,7 @@ import 'package:mc_common_app/views/payments/payment_methods_view.dart'; import 'package:mc_common_app/views/profile/profile_view.dart'; import 'package:mc_common_app/views/requests/create_request_page.dart'; import 'package:mc_common_app/views/requests/offer_list_page.dart'; +import 'package:mc_common_app/views/requests/request_detail_page.dart'; import 'package:mc_common_app/views/setting_options/provider_license_page.dart'; import 'package:mc_common_app/views/setting_options/setting_option_help.dart'; import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart'; @@ -148,12 +149,17 @@ class AppRoutes { AppRoutes.adsFilterView: (context) => const AdsFilterView(), AppRoutes.selectAdTypeView: (context) => SelectAdTypeView(arguments: ModalRoute.of(context)!.settings.arguments as List), AppRoutes.chatView: (context) => ChatView(chatViewArguments: ModalRoute.of(context)!.settings.arguments as ChatViewArguments), - AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments), AppRoutes.adsBuyerChatsListView: (context) => AdsBuyerChatsView(buyersListViewArguments: ModalRoute.of(context)!.settings.arguments as List), - AppRoutes.createRequestPage: (context) => const CreateRequestPage(), AppRoutes.settingOptionsFaqs: (context) => const SettingOptionsFAQs(), AppRoutes.settingOptionsInviteFriends: (context) => const SettingOptionsInviteFriends(), AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypes), + //Requests + AppRoutes.requestsDetailPage: (context) => RequestDetailPage(requestDetailPageArguments: ModalRoute.of(context)!.settings.arguments as RequestDetailPageArguments), + AppRoutes.createRequestPage: (context) => const CreateRequestPage(), + AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments), + + + }; } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index cd08596..7d3f796 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -3,6 +3,7 @@ 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'; +import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; @@ -32,7 +33,7 @@ extension EmailValidator on String { fontStyle: isItalic ? FontStyle.italic : null, height: height, decoration: isUnderLine ? TextDecoration.underline : textDecoration ?? TextDecoration.none, - decorationColor: decorationColor ?? MyColors.provTextColor, + decorationColor: decorationColor ?? color ?? MyColors.darkTextColor, fontSize: fontSize ?? 10, fontFamily: isArabic ? MyFonts.gessTwoFont : MyFonts.poppinsFont, fontWeight: isBold ? FontWeight.bold : fontWeight ?? MyFonts.SemiBold, @@ -378,8 +379,6 @@ extension RequestTypeStatusToInt on RequestsTypeEnum { } } -// 290223243016 - extension AdPostStatusToInt on AdPostStatus { int getIdFromAdPostStatusEnum() { switch (this) { @@ -665,6 +664,23 @@ extension FormatMonthByNumber on String { } } +extension NameExtensions on String { + String getInitials() { + final name = trim(); + String initials = "U"; + if (name.isEmpty) { + return initials; + } + final list = name.split(' '); + + if (list.length > 1) { + initials = list[0][0].toUpperCase() + list[1][0].toUpperCase(); + } + + return initials; + } +} + extension ChatMessageTypeEnumExt on int { //FreeText = 1, // Image = 2, diff --git a/lib/models/chat_models/chat_message_model.dart b/lib/models/chat_models/chat_message_model.dart index 07efb9d..debc179 100644 --- a/lib/models/chat_models/chat_message_model.dart +++ b/lib/models/chat_models/chat_message_model.dart @@ -71,9 +71,9 @@ class ReqOffer { int? offerStatus; String? offerStatusText; String? comment; - String? serviceItem; - String? itemManufacturer; - String? manufacturedDate; + String? serviceItemName; + int? manufacturedById; + String? manufacturedOn; double? price; RequestsTypeEnum? requestsTypeEnum; RequestOfferStatusEnum? requestOfferStatusEnum; @@ -85,9 +85,9 @@ class ReqOffer { this.offerStatus, this.offerStatusText, this.comment, - this.serviceItem, - this.itemManufacturer, - this.manufacturedDate, + this.serviceItemName, + this.manufacturedById, + this.manufacturedOn, this.price, this.requestOfferStatusEnum, this.requestsTypeEnum, @@ -100,9 +100,9 @@ class ReqOffer { offerStatus = json['offerStatus']; offerStatusText = json['offerStatusText']; comment = json['comment']; - serviceItem = json['serviceItem']; - itemManufacturer = json['itemManufacturer']; - manufacturedDate = json['manufacturedDate']; + serviceItemName = json['serviceItem']; + manufacturedById = json['itemManufacturer']; + manufacturedOn = json['manufacturedDate']; price = json['price']; requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum(); requestsTypeEnum = RequestsTypeEnum.serviceRequest; // TODO: THIS SHOULD COME FROM API diff --git a/lib/repositories/chat_repo.dart b/lib/repositories/chat_repo.dart index 7b8b7f7..b23999f 100644 --- a/lib/repositories/chat_repo.dart +++ b/lib/repositories/chat_repo.dart @@ -53,6 +53,7 @@ class ChatRepoImp implements ChatRepo { client: IOClient(HttpClient()..badCertificateCallback = (x, y, z) => true), logging: (level, message) {}, )) + .withAutomaticReconnect() .build(); return hub; } diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index beafaee..fcd7756 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -232,7 +232,7 @@ class AdVM extends BaseVM { return; } // this means if the filter is reserved ads - if (adPostStatusEnum.getIdFromAdPostStatusEnum() == 9) { + if (adPostStatusEnum == AdPostStatus.reserved) { myAdsFilteredList = myReservedAds; for (var ad in myAdsFilteredList) { ad.isReservedByMe = true; @@ -245,24 +245,6 @@ class AdVM extends BaseVM { notifyListeners(); } - Future getMyAds() async { - setState(ViewState.busy); - myAds = await adsRepo.getAllAds(isMyAds: true); - final myActiveAds = myAds.where((element) => element.adPostStatus == AdPostStatus.active).toList(); - myActiveAdsForHome = myActiveAds.length >= 3 ? myActiveAds.take(3).toList() : myActiveAds; - await getMyReservedAds(); - notifyListeners(); - setState(ViewState.idle); - } - - Future> getMyReservedAds() async { - setState(ViewState.busy); - myReservedAds = await adsRepo.getMyReservedAds(); - isFetchingLists = false; - setState(ViewState.idle); - return myReservedAds; - } - Future createReserveAd({required int adId, required BuildContext context}) async { Utils.showLoading(context); GenericRespModel genericRespModel = await adsRepo.createReserveAd(adId: adId); @@ -284,6 +266,25 @@ class AdVM extends BaseVM { setState(ViewState.idle); } + Future getMyAds() async { + setState(ViewState.busy); + myAds = await adsRepo.getAllAds(isMyAds: true); + myAdsFilteredList = myAds; + final myActiveAds = myAds.where((element) => element.adPostStatus == AdPostStatus.active).toList(); + myActiveAdsForHome = myActiveAds.length >= 3 ? myActiveAds.take(3).toList() : myActiveAds; + await getMyReservedAds(); + notifyListeners(); + setState(ViewState.idle); + } + + Future> getMyReservedAds() async { + setState(ViewState.busy); + myReservedAds = await adsRepo.getMyReservedAds(); + isFetchingLists = false; + setState(ViewState.idle); + return myReservedAds; + } + Future getVehicleTypes() async { vehicleTypes = await commonRepo.getVehicleTypes(); notifyListeners(); diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 7323e48..1c3089d 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -221,12 +221,16 @@ class ChatVM extends ChangeNotifier { required String message, required int requestId, required String offerPrice, + required String serviceItemName, + required int manufacturedById, + required String manufacturedOn, required BuildContext context, }) async { if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) { logger.i("Hub Not Connected!!"); await buildHubConnection(context); } + if (hubConnection!.state == HubConnectionState.connected) { final providerId = AppState().getUser.data!.userInfo!.providerId; final obj = { @@ -238,6 +242,9 @@ class ChatVM extends ChangeNotifier { "ReqOffer": { "RequestID": requestId, "Price": double.parse(offerPrice), + "ServiceItem": serviceItemName, + "OfferedItemCreatedBy": manufacturedById, // TODO: The ID should be used from the manufacturer database + // "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime "ServiceProviderID": providerId, "OfferStatus": RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(), "Comment": message, @@ -314,7 +321,10 @@ class ChatVM extends ChangeNotifier { serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel); } } else { - int providerIndex = context.read().myFilteredRequests.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId)); + int providerIndex = context + .read() + .myFilteredRequests + .indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId)); log("providerIndex2:$providerIndex"); if (providerIndex != -1) { context.read().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex); @@ -412,6 +422,9 @@ class ChatVM extends ChangeNotifier { required int serviceProviderID, required int requestOfferID, required String offerPrice, + required String serviceItemName, + required int manufacturedById, + required String manufacturedOn, required RequestOfferStatusEnum requestOfferStatusEnum, required BuildContext context, }) async { @@ -431,6 +444,9 @@ class ChatVM extends ChangeNotifier { "ID": requestOfferID, "RequestID": requestId, "Price": double.parse(offerPrice), + "ServiceItem": serviceItemName, + "OfferedItemCreatedBy": manufacturedById, // TODO: The ID should be used from the manufacturer database + // "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime "ServiceProviderID": serviceProviderID, "OfferStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum(), "Comment": comments, @@ -545,8 +561,7 @@ class ChatVM extends ChangeNotifier { notifyListeners(); - if (AppState().currentAppType == AppType.customer) { - } else {} + if (AppState().currentAppType == AppType.customer) {} else {} return true; } return false; diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index bb34445..6ab6057 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -449,6 +449,9 @@ class RequestsVM extends BaseVM { required String message, required int requestId, required String offerPrice, + required String serviceItemName, + required int manufacturedById, + required String manufacturedOn, required RequestModel requestModel, required int requestIndex, required bool isFromChatScreen, @@ -461,6 +464,9 @@ class RequestsVM extends BaseVM { message: message, requestId: requestId, offerPrice: offerPrice, + manufacturedById: manufacturedById, + manufacturedOn: manufacturedOn, + serviceItemName: serviceItemName, context: context, ); @@ -470,23 +476,27 @@ class RequestsVM extends BaseVM { resetSendOfferBottomSheet(); Navigator.pop(context); ChatMessageModel chatMessageModel = ChatMessageModel( - isMyMessage: true, - chatText: message, - messageType: ChatMessageTypeEnum.offer.getIdFromChatMessageTypeEnum(), - senderName: senderName, - senderUserID: senderId, - receiverUserID: receiverId, - chatMessageTypeEnum: ChatMessageTypeEnum.offer, - requestID: requestModel.id, + isMyMessage: true, + chatText: message, + messageType: ChatMessageTypeEnum.offer.getIdFromChatMessageTypeEnum(), + senderName: senderName, + senderUserID: senderId, + receiverUserID: receiverId, + chatMessageTypeEnum: ChatMessageTypeEnum.offer, + requestID: requestModel.id, + offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(), + reqOffer: ReqOffer( offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(), - reqOffer: ReqOffer( - offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(), - requestID: requestModel.id, - price: double.parse(offerPrice), - requestOfferStatusEnum: RequestOfferStatusEnum.offer, - comment: message, - offerStatusText: "", - )); + requestID: requestModel.id, + price: double.parse(offerPrice), + manufacturedById: manufacturedById, + manufacturedOn: manufacturedOn, + serviceItemName: serviceItemName, + requestOfferStatusEnum: RequestOfferStatusEnum.offer, + comment: message, + offerStatusText: "", + ), + ); context.read().onNewMessageReceivedForRequestOffer(messages: [chatMessageModel], requestsVM: this, isMyOwnOffer: true); if (!isFromChatScreen) { diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index f2d1545..bf7e6cd 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -207,6 +207,12 @@ class ServiceVM extends BaseVM { countryCode == "SA" ? (element.cityNameN ?? "") : (element.cityName ?? ""), (element.id ?? "").toString(), ); + if (branchData.branchImages != null && branchData.branchImages!.isNotEmpty) { + for (var element in branchData.branchImages!) { + ImageModel image = ImageModel(id: element.id, isFromNetwork: true, filePath: element.imageUrl); + pickedBranchImages.add(image); + } + } } } citiesDropList.add( diff --git a/lib/views/advertisement/ads_buyer_chats_view.dart b/lib/views/advertisement/ads_buyer_chats_view.dart index 37eba85..520896c 100644 --- a/lib/views/advertisement/ads_buyer_chats_view.dart +++ b/lib/views/advertisement/ads_buyer_chats_view.dart @@ -39,11 +39,12 @@ class AdsBuyerChatsView extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset( - MyAssets.bnCar, + Container( width: 34, height: 34, - fit: BoxFit.fill, + alignment: Alignment.center, + color: MyColors.darkTextColor, + child: ((chatForAdsModel.buyerName ?? "").getInitials()).toText(color: MyColors.white, fontSize: 15), ).toCircle(borderRadius: 100), SizedBox(width: 2.w), Expanded( diff --git a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart new file mode 100644 index 0000000..ba25f52 --- /dev/null +++ b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart @@ -0,0 +1,371 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; +import 'package:mc_common_app/models/general_models/widgets_models.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/ad_view_model.dart'; +import 'package:mc_common_app/view_models/payment_view_model.dart'; +import 'package:mc_common_app/views/advertisement/ads_detail_view/components.dart'; +import 'package:mc_common_app/views/advertisement/bottom_sheets/ads_damage_part_pictures_sheet.dart'; +import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart'; +import 'package:mc_common_app/widgets/bottom_sheet.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; +import 'package:easy_localization/easy_localization.dart'; + +class AdsDetailView extends StatefulWidget { + final AdDetailsModel adDetails; + + const AdsDetailView({Key? key, required this.adDetails}) : super(key: key); + + @override + State createState() => _AdsDetailViewState(); +} + +class _AdsDetailViewState extends State { + late AdVM adVM; + late List damagePartsPictures; + + @override + void initState() { + adVM = context.read(); + damagePartsPictures = []; + scheduleMicrotask(() { + onAdDetailsLoaded(); + populateDamagePartPictures(); + }); + super.initState(); + } + + Future onAdDetailsLoaded() async { + context.read().updateCurrentAdId(id: widget.adDetails.id!); + if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus == AdPostStatus.reserved)) { + await adVM.getAdBankingAccountInfo(adId: widget.adDetails.id!); + } + } + + void populateDamagePartPictures() { + if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) { + for (var element in widget.adDetails.vehicle!.damagereport!) { + int index = -1; + ImageModel imageModel = ImageModel(id: element.id!, filePath: element.imageUrl!, isFromNetwork: true); + + VehicleDamageCard vehicleDamageCard = VehicleDamageCard( + partSelectedId: SelectionModel( + selectedId: element.vehicleDamagePartID!, + selectedOption: element.partName ?? "", + ), + partImages: [imageModel], + ); + damagePartsPictures.add(vehicleDamageCard); + } + } + } + + void deleteAdBottomSheet(BuildContext context) { + return actionConfirmationBottomSheet( + context: context, + title: LocaleKeys.deleteAdConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: LocaleKeys.deleteAdConfirmationMessage.tr(), + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.yes.tr(), + fontSize: 15, + onPressed: () { + Navigator.pop(context); + adVM.deleteMyAd(context, adId: widget.adDetails.id!); + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: LocaleKeys.no.tr(), + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } + + Widget buildVehicleDetailsWidget() { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ImagesCorouselWidget(imagesList: widget.adDetails.vehicle!.image!), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: "${widget.adDetails.vehicle!.vehicleTitle} | ${widget.adDetails.vehicle!.color!.label} | ${widget.adDetails.id}".toText( + fontSize: 18, + isBold: true, + maxLines: 2, + ), + ), + (widget.adDetails.vehicle!.cityName ?? "").toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + ("${LocaleKeys.model.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + "${widget.adDetails.vehicle!.modelyear!.label}".toText( + fontSize: 14, + isBold: true, + ), + ], + ), + "${widget.adDetails.vehicle!.countryID}".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + ("${LocaleKeys.mileage.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + "${widget.adDetails.vehicle!.mileage!.mileageEnd}Km".toText( + fontSize: 14, + isBold: true, + ), + ], + ), + widget.adDetails.createdOn != null ? DateTime.parse(widget.adDetails.createdOn!).getTimeAgo().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor) : const SizedBox(), + ], + ), + Row( + children: [ + ("${LocaleKeys.transmission.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + "${widget.adDetails.vehicle!.transmission!.label}".toText( + fontSize: 14, + isBold: true, + ), + ], + ), + 8.height, + ("${LocaleKeys.description.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + "${widget.adDetails.vehicle!.vehicleDescription}".toText( + fontSize: 14, + isBold: true, + ), + if (widget.adDetails.isMyAd ?? false) ...[ + 8.height, + ("${LocaleKeys.demand.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, height: 1.2, isBold: true), + LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + ], + ), + if (widget.adDetails.adPostStatus == AdPostStatus.expired) ...[ + 8.height, + const Divider(thickness: 1, height: 1), + 8.height, + LocaleKeys.adDurationExpired.tr().toText( + color: MyColors.redColor, + fontSize: 12, + isItalic: true, + ), + ], + ] + ], + ).toWhiteContainer(width: double.infinity, allPading: 12); + } + + Widget buildBankDetailsIfAvailable() { + return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) { + if (adVM.adsBankDetailsModel != null) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.bankDetails.tr().toText(fontSize: 18, isBold: true), + // Row( + // children: [ + // "Full Name: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + // "${widget.adDetails.vehicle!.vehicleDescription}".toText( + // fontSize: 14, + // isBold: true, + // ), + // ], + // ), + Row( + children: [ + ("${LocaleKeys.bankName.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + (adVM.adsBankDetailsModel!.bankName ?? "").toText( + fontSize: 14, + isBold: true, + ), + ], + ), + Row( + children: [ + ("${LocaleKeys.iban.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + (adVM.adsBankDetailsModel!.iban ?? "").toText( + fontSize: 14, + isBold: true, + ), + ], + ), + ], + ).toWhiteContainer(width: double.infinity, allPading: 12); + } + return const SizedBox.shrink(); + }); + } + + Widget buildAdRejectionDetails() { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.rejectionComments.tr().toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor), + Row( + children: [ + LocaleKeys.editAd.tr().toText(fontSize: 10, isBold: true), + 2.width, + const Icon(Icons.edit, size: 15), + ], + ).onPress(() async { + Utils.showLoading(context); + await adVM.getVehicleTypes(); + await adVM.getVehicleBrandsByVehicleTypeId(vehicleIdForEditAd: widget.adDetails.vehicle!.vehicleType ?? -1); + Utils.hideLoading(context); + adVM.onEditUpdateAdPressed(context: context, previousDetails: widget.adDetails, isFromExtendAd: false); + }), + ], + ).paddingOnly(bottom: 5), + Row( + children: [ + Flexible( + child: (widget.adDetails.comment ?? "").toText( + color: MyColors.redColor, + fontSize: 12, + isItalic: true, + ), + ) + ], + ), + ], + ).toWhiteContainer(width: double.infinity, allPading: 12); + } + + Widget buildDamagePartDetails() { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + MyAssets.carHitIcon.buildSvg(color: MyColors.darkTextColor), + 10.width, + LocaleKeys.damagePartPictures.tr().toText(fontSize: 16), + ], + ), + const Icon(Icons.arrow_forward_ios_rounded, size: 20) + ], + ).paddingOnly(top: 5, bottom: 5).onPress(() { + showMyBottomSheet(context, child: AdDamagePartPicturesSheet(adDamageReportList: widget.adDetails.vehicle!.damagereport ?? [])); + }).toWhiteContainer(width: double.infinity, allPading: 12); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: LocaleKeys.vehicleDetails.tr(), + profileImageUrl: MyAssets.bnCar, + isRemoveBackButton: false, + isDrawerEnabled: false, + actions: [ + ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active) + ? IconButton( + icon: const Icon(Icons.delete_outline, color: MyColors.redColor), + onPressed: () { + return deleteAdBottomSheet(context); + }, + ) + : IconButton(icon: const Icon(Icons.chat_outlined, color: Colors.black), onPressed: () => adVM.onMessagesButtonPressed(context: context, adDetailsModel: widget.adDetails))) + .toContainer( + margin: const EdgeInsets.fromLTRB(0, 8, 21, 8), + paddingAll: 0, + padding: const EdgeInsets.only(right: 21), + borderRadius: 100, + borderColor: MyColors.lightGreyEFColor, + isEnabledBorder: true, + height: 40, + width: 42, + ) + ], + onTap: () {}, + ), + body: Container( + padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), + child: Column( + children: [ + Expanded( + child: ListView( + shrinkWrap: true, + children: [ + buildVehicleDetailsWidget(), + 12.height, + buildBankDetailsIfAvailable(), + if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) ...[ + buildDamagePartDetails(), + ], + 12.height, + if (widget.adDetails.adPostStatus == AdPostStatus.rejected) ...[ + buildAdRejectionDetails(), + ], + ], + ), + ), + SizedBox( + child: Column( + children: [ + if (!(widget.adDetails.isMyAd ?? false)) ...[ + const Divider(thickness: 1, height: 1), + 18.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true), + LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + ], + ), + 14.height, + ], + widget.adDetails.isMyAd ?? false ? BuildAdDetailsActionButtonForMyAds(adDetailsModel: widget.adDetails) : BuildAdDetailsActionButtonForExploreAds(adDetailsModel: widget.adDetails), + ], + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/views/advertisement/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view/components.dart similarity index 75% rename from lib/views/advertisement/ads_detail_view.dart rename to lib/views/advertisement/ads_detail_view/components.dart index 115de7f..10b8f39 100644 --- a/lib/views/advertisement/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view/components.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; @@ -15,15 +14,11 @@ 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/payment_view_model.dart'; import 'package:mc_common_app/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart'; -import 'package:mc_common_app/views/advertisement/bottom_sheets/ads_damage_part_pictures_sheet.dart'; -import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart'; import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart'; import 'package:mc_common_app/widgets/bottom_sheet.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/common_widgets/time_slots.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; @@ -32,354 +27,6 @@ import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; -class AdsDetailView extends StatefulWidget { - final AdDetailsModel adDetails; - - const AdsDetailView({Key? key, required this.adDetails}) : super(key: key); - - @override - State createState() => _AdsDetailViewState(); -} - -class _AdsDetailViewState extends State { - late AdVM adVM; - late List damagePartsPictures; - - @override - void initState() { - adVM = context.read(); - damagePartsPictures = []; - scheduleMicrotask(() { - onAdDetailsLoaded(); - populateDamagePartPictures(); - }); - super.initState(); - } - - Future onAdDetailsLoaded() async { - context.read().updateCurrentAdId(id: widget.adDetails.id!); - if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus == AdPostStatus.reserved)) { - await adVM.getAdBankingAccountInfo(adId: widget.adDetails.id!); - } - } - - void populateDamagePartPictures() { - if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) { - for (var element in widget.adDetails.vehicle!.damagereport!) { - int index = -1; - ImageModel imageModel = ImageModel(id: element.id!, filePath: element.imageUrl!, isFromNetwork: true); - - VehicleDamageCard vehicleDamageCard = VehicleDamageCard( - partSelectedId: SelectionModel( - selectedId: element.vehicleDamagePartID!, - selectedOption: element.partName ?? "", - ), - partImages: [imageModel], - ); - damagePartsPictures.add(vehicleDamageCard); - } - } - } - - void deleteAdBottomSheet(BuildContext context) { - return actionConfirmationBottomSheet( - context: context, - title: LocaleKeys.deleteAdConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), - subtitle: LocaleKeys.deleteAdConfirmationMessage.tr(), - actionButtonYes: Expanded( - child: ShowFillButton( - maxHeight: 55, - title: LocaleKeys.yes.tr(), - fontSize: 15, - onPressed: () { - Navigator.pop(context); - adVM.deleteMyAd(context, adId: widget.adDetails.id!); - }, - ), - ), - actionButtonNo: Expanded( - child: ShowFillButton( - maxHeight: 55, - isFilled: false, - borderColor: MyColors.darkPrimaryColor, - title: LocaleKeys.no.tr(), - txtColor: MyColors.darkPrimaryColor, - fontSize: 15, - onPressed: () { - Navigator.pop(context); - }, - ), - ), - ); - } - - Widget buildVehicleDetailsWidget() { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ImagesCorouselWidget(imagesList: widget.adDetails.vehicle!.image!), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: "${widget.adDetails.vehicle!.vehicleTitle} | ${widget.adDetails.vehicle!.color!.label} | ${widget.adDetails.id}".toText( - fontSize: 18, - isBold: true, - maxLines: 2, - ), - ), - (widget.adDetails.vehicle!.cityName ?? "").toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - ("${LocaleKeys.model.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "${widget.adDetails.vehicle!.modelyear!.label}".toText( - fontSize: 14, - isBold: true, - ), - ], - ), - "${widget.adDetails.vehicle!.countryID}".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - ("${LocaleKeys.mileage.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "${widget.adDetails.vehicle!.mileage!.mileageEnd}Km".toText( - fontSize: 14, - isBold: true, - ), - ], - ), - widget.adDetails.createdOn != null ? DateTime.parse(widget.adDetails.createdOn!).getTimeAgo().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor) : const SizedBox(), - ], - ), - Row( - children: [ - ("${LocaleKeys.transmission.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "${widget.adDetails.vehicle!.transmission!.label}".toText( - fontSize: 14, - isBold: true, - ), - ], - ), - 8.height, - ("${LocaleKeys.description.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "${widget.adDetails.vehicle!.vehicleDescription}".toText( - fontSize: 14, - isBold: true, - ), - if (widget.adDetails.isMyAd ?? false) ...[ - 8.height, - ("${LocaleKeys.demand.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, height: 1.2, isBold: true), - LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), - ], - ), - if (widget.adDetails.adPostStatus == AdPostStatus.expired) ...[ - 8.height, - const Divider(thickness: 1, height: 1), - 8.height, - LocaleKeys.adDurationExpired.tr().toText( - color: MyColors.redColor, - fontSize: 12, - isItalic: true, - ), - ], - ] - ], - ).toWhiteContainer(width: double.infinity, allPading: 12); - } - - Widget buildBankDetailsIfAvailable() { - return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) { - if (adVM.adsBankDetailsModel != null) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.bankDetails.tr().toText(fontSize: 18, isBold: true), - // Row( - // children: [ - // "Full Name: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - // "${widget.adDetails.vehicle!.vehicleDescription}".toText( - // fontSize: 14, - // isBold: true, - // ), - // ], - // ), - Row( - children: [ - ("${LocaleKeys.bankName.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - (adVM.adsBankDetailsModel!.bankName ?? "").toText( - fontSize: 14, - isBold: true, - ), - ], - ), - Row( - children: [ - ("${LocaleKeys.iban.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - (adVM.adsBankDetailsModel!.iban ?? "").toText( - fontSize: 14, - isBold: true, - ), - ], - ), - ], - ).toWhiteContainer(width: double.infinity, allPading: 12); - } - return const SizedBox.shrink(); - }); - } - - Widget buildAdRejectionDetails() { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - LocaleKeys.rejectionComments.tr().toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor), - Row( - children: [ - LocaleKeys.editAd.tr().toText(fontSize: 10, isBold: true), - 2.width, - const Icon(Icons.edit, size: 15), - ], - ).onPress(() async { - Utils.showLoading(context); - await adVM.getVehicleTypes(); - await adVM.getVehicleBrandsByVehicleTypeId(vehicleIdForEditAd: widget.adDetails.vehicle!.vehicleType ?? -1); - Utils.hideLoading(context); - adVM.onEditUpdateAdPressed(context: context, previousDetails: widget.adDetails, isFromExtendAd: false); - }), - ], - ).paddingOnly(bottom: 5), - Row( - children: [ - Flexible( - child: (widget.adDetails.comment ?? "").toText( - color: MyColors.redColor, - fontSize: 12, - isItalic: true, - ), - ) - ], - ), - ], - ).toWhiteContainer(width: double.infinity, allPading: 12); - } - - Widget buildDamagePartDetails() { - return Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - MyAssets.carHitIcon.buildSvg(color: MyColors.darkTextColor), - 10.width, - LocaleKeys.damagePartPictures.tr().toText(fontSize: 16), - ], - ), - const Icon(Icons.arrow_forward_ios_rounded, size: 20) - ], - ).paddingOnly(top: 5, bottom: 5).onPress(() { - showMyBottomSheet(context, child: AdDamagePartPicturesSheet(adDamageReportList: widget.adDetails.vehicle!.damagereport ?? [])); - }).toWhiteContainer(width: double.infinity, allPading: 12); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: CustomAppBar( - title: LocaleKeys.ads.tr(), - profileImageUrl: MyAssets.bnCar, - isRemoveBackButton: false, - isDrawerEnabled: false, - actions: [ - ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active) - ? IconButton( - icon: const Icon(Icons.delete_outline, color: MyColors.redColor), - onPressed: () { - return deleteAdBottomSheet(context); - }, - ) - : IconButton(icon: const Icon(Icons.chat_outlined, color: Colors.black), onPressed: () => adVM.onMessagesButtonPressed(context: context, adDetailsModel: widget.adDetails))) - .toContainer( - margin: const EdgeInsets.fromLTRB(0, 8, 21, 8), - paddingAll: 0, - padding: const EdgeInsets.only(right: 21), - borderRadius: 100, - borderColor: MyColors.lightGreyEFColor, - isEnabledBorder: true, - height: 40, - width: 42, - ) - ], - onTap: () {}, - ), - body: Container( - padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), - child: Column( - children: [ - Expanded( - child: ListView( - shrinkWrap: true, - children: [ - buildVehicleDetailsWidget(), - 12.height, - buildBankDetailsIfAvailable(), - if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) ...[ - buildDamagePartDetails(), - ], - 12.height, - if (widget.adDetails.adPostStatus == AdPostStatus.rejected) ...[ - buildAdRejectionDetails(), - ], - ], - ), - ), - SizedBox( - child: Column( - children: [ - if (!(widget.adDetails.isMyAd ?? false)) ...[ - const Divider(thickness: 1, height: 1), - 18.height, - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true), - LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), - ], - ), - 14.height, - ], - widget.adDetails.isMyAd ?? false ? BuildAdDetailsActionButtonForMyAds(adDetailsModel: widget.adDetails) : BuildAdDetailsActionButtonForExploreAds(adDetailsModel: widget.adDetails), - ], - ), - ) - ], - ), - ), - ); - } -} - class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { final AdDetailsModel adDetailsModel; @@ -856,11 +503,9 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { children: [ Expanded( child: ShowFillButton( - backgroundColor: MyColors.grey98Color.withOpacity(0.3), - txtColor: MyColors.lightTextColor, + isDisabled: true, maxHeight: 55, title: pendingText, - isBold: false, onPressed: () {}, ), ), diff --git a/lib/views/appointments/widgets/common_appointment_slider_widget.dart b/lib/views/appointments/widgets/common_appointment_slider_widget.dart index 689112d..36c2d40 100644 --- a/lib/views/appointments/widgets/common_appointment_slider_widget.dart +++ b/lib/views/appointments/widgets/common_appointment_slider_widget.dart @@ -28,7 +28,7 @@ class CommonAppointmentSliderWidget extends StatelessWidget { return CarouselSlider.builder( options: CarouselOptions( height: appType == AppType.provider ? 121 : 140, - viewportFraction: 0.99999, // Todo: Dont do anything + viewportFraction: 1, // Todo: Dont do anything enlargeCenterPage: false, enableInfiniteScroll: false, ), @@ -57,7 +57,7 @@ class CommonAppointmentSliderWidget extends StatelessWidget { if (model.myUpComingAppointments.isEmpty) { return LocaleKeys.noUpcomingAppointments.tr().toText(fontSize: 16, color: MyColors.lightTextColor).paddingAll(21); } else { - return getCarouselSliderWidget(AppState().currentAppType, model); + return getCarouselSliderWidget(AppState().currentAppType, model).toWhiteContainer(width: double.infinity, margin: EdgeInsets.symmetric(horizontal: 21)); } } else { if (model.myUpComingAppointments.isEmpty) { @@ -74,7 +74,7 @@ class CommonAppointmentSliderWidget extends StatelessWidget { ), ).padding(const EdgeInsets.symmetric(vertical: 10, horizontal: 21)); } else { - return getCarouselSliderWidget(AppState().currentAppType, model); + return getCarouselSliderWidget(AppState().currentAppType, model).toWhiteContainer(width: double.infinity, margin: EdgeInsets.symmetric(horizontal: 21)); } } } @@ -215,10 +215,8 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - (AppState().currentAppType == AppType.provider ? appointmentListModel!.branchName ?? "" : appointmentListModel!.branchName ?? "") - .toText(color: MyColors.greyColor, fontSize: 12, letterSpacing: 0, height: 19 / 12), - (AppState().currentAppType == AppType.provider ? appointmentListModel!.customerName ?? "" : appointmentListModel!.branchName ?? "") - .toText(color: MyColors.black, fontSize: 16, letterSpacing: -0.64, height: 24 / 16), + (AppState().currentAppType == AppType.provider ? appointmentListModel!.branchName ?? "" : appointmentListModel!.branchName ?? "").toText(color: MyColors.greyColor, fontSize: 12, letterSpacing: 0, height: 19 / 12), + (AppState().currentAppType == AppType.provider ? appointmentListModel!.customerName ?? "" : appointmentListModel!.branchName ?? "").toText(color: MyColors.black, fontSize: 16, letterSpacing: -0.64, height: 24 / 16), Row( children: [ !isForProvider ? MyAssets.miniClock.buildSvg(height: 12) : SizedBox(), diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index c32245b..c9f6f10 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -1,25 +1,17 @@ -import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; -import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/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/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/utils.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; +import 'package:mc_common_app/views/chat/widgets/chat_message_widget.dart'; import 'package:mc_common_app/views/requests/request_bottomsheets.dart'; -import 'package:mc_common_app/widgets/button/show_fill_button.dart'; -import 'package:mc_common_app/widgets/checkbox_with_title_desc.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'; @@ -65,70 +57,6 @@ class _ChatViewState extends State { super.dispose(); } - // Future buildSendOfferBottomSheet(BuildContext context) { - // RequestModel requestDetail = chatViewArgumentsForRequest!.requestModel!; - // return showModalBottomSheet( - // context: context, - // isScrollControlled: true, - // enableDrag: true, - // builder: (BuildContext context) { - // return Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) { - // return InfoBottomSheet( - // title: LocaleKeys.makeAnOffer.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, - // TxtField( - // value: requestsVM.offerPrice, - // errorValue: requestsVM.offerPriceError, - // keyboardType: TextInputType.number, - // hint: LocaleKeys.enterAmount.tr(), - // numbersOnly: true, - // onChanged: (v) => requestsVM.updateOfferPrice(v), - // ), - // 12.height, - // TxtField( - // maxLines: 5, - // value: requestsVM.offerDescription, - // errorValue: requestsVM.offerDescriptionError, - // keyboardType: TextInputType.text, - // hint: LocaleKeys.description.tr(), - // onChanged: (v) => requestsVM.updateOfferDescription(v), - // ), - // ], - // ), - // 25.height, - // ShowFillButton( - // title: LocaleKeys.submit.tr(), - // onPressed: () { - // requestsVM.onSendOfferPressed( - // context: context, - // receiverId: requestDetail.customerID, - // message: requestsVM.offerDescription, - // requestId: requestDetail.id, - // offerPrice: requestsVM.offerPrice, - // requestModel: requestDetail, - // requestIndex: chatViewArgumentsForRequest!.requestIndex, - // isFromChatScreen: true, - // ); - // }, - // maxWidth: double.infinity, - // ), - // 19.height, - // ], - // ), - // )); - // }); - // }, - // ); - // } - Future onTextMessageSend() async { bool status = false; if (chatTypeEnum == ChatTypeEnum.requestOffer) { @@ -205,7 +133,7 @@ class _ChatViewState extends State { size: 30, ).onPress( () { - RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(requestIndex: chatViewArgumentsForRequest!.requestIndex , requestModel: chatViewArgumentsForRequest!.requestModel!); + RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(requestIndex: chatViewArgumentsForRequest!.requestIndex, requestModel: chatViewArgumentsForRequest!.requestModel!); buildSendOfferBottomSheet(context, requestDetailArguments); }, ), @@ -247,336 +175,3 @@ class _ChatViewState extends State { ); } } - -class ChatMessageCustomWidget extends StatefulWidget { - final ChatMessageModel chatMessageModel; - - const ChatMessageCustomWidget({super.key, required this.chatMessageModel}); - - @override - State createState() => _ChatMessageCustomWidgetState(); -} - -class _ChatMessageCustomWidgetState extends State { - Future buildRejectOfferBottomSheet({required ChatMessageModel chatMessageModel}) { - return showModalBottomSheet( - context: context, - isScrollControlled: true, - enableDrag: true, - builder: (BuildContext context) { - return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { - return InfoBottomSheet( - title: LocaleKeys.makeAnOffer.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), - description: Padding( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 12.height, - ListView.separated( - shrinkWrap: true, - itemCount: chatVM.offerRejectModelList.length, - separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5), - itemBuilder: (BuildContext context, int index) { - OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index]; - return CircleCheckBoxWithTitle( - isChecked: offerRequestCommentModel.isSelected ?? false, - title: '${offerRequestCommentModel.title}', - onSelected: () { - chatVM.updateSelectionInOfferRejectModelList(index); - }, - selectedColor: MyColors.darkPrimaryColor, - ); - }, - ), - if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) ...[ - // 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 - { - comments = chatVM.rejectOfferDescription; - } else { - comments = chatVM.selectedOfferRequestCommentModel.title ?? ""; - } - if (!chatVM.isRejectOfferButtonValidated()) { - return; - } - Navigator.pop(context); - bool status = await context.read().onSendMessageForActionOnRequestOffer( - receiverId: chatMessageModel.receiverUserID ?? "", - chatMessageType: ChatMessageTypeEnum.offer, - comments: comments, - requestId: chatMessageModel.reqOffer!.requestID ?? -1, - serviceProviderID: chatMessageModel.serviceProviderID ?? 0, - requestOfferID: chatMessageModel.reqOffer!.id ?? -1, - offerPrice: chatMessageModel.reqOffer!.price.toString(), - requestOfferStatusEnum: RequestOfferStatusEnum.rejected, - context: context, - ); - - if (status) { - chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected; - setState(() {}); - // Navigator.pop(context); - Utils.showToast("Offer Rejected"); - // navigateReplaceWithName(context, AppRoutes.dashboard); - } - }, - maxWidth: double.infinity, - ), - 19.height, - ], - ), - )); - }); - }, - ); - } - - void offerAcceptConfirmationBottomSheet({required ChatMessageModel chatMessageModel}) { - return actionConfirmationBottomSheet( - context: context, - title: LocaleKeys.acceptOfferConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), - subtitle: LocaleKeys.acceptOfferConfirmationMessage.tr(), - actionButtonYes: Expanded( - child: ShowFillButton( - maxHeight: 55, - title: LocaleKeys.yes.tr(), - fontSize: 15, - onPressed: () async { - log("ServiceProviderID: ${chatMessageModel.toString()}"); - Navigator.pop(context); - // int status = await context.read().onActionOfferTapped( - // context: context, - // requestOfferStatusEnum: RequestOfferStatusEnum.accepted, - // reqOfferId: chatMessageModel.reqOfferID ?? -1, - // comments: GlobalConsts.acceptingThisOffer, - // ); - - bool status = await context.read().onSendMessageForActionOnRequestOffer( - receiverId: chatMessageModel.receiverUserID ?? "", - 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(), - requestOfferStatusEnum: RequestOfferStatusEnum.accepted, - context: context, - ); - - if (status) { - chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted; - setState(() {}); - // Navigator.pop(context); - Utils.showToast("Offer Accepted"); - - // navigateReplaceWithName(context, AppRoutes.dashboard); - } - }, - ), - ), - actionButtonNo: Expanded( - child: ShowFillButton( - maxHeight: 55, - isFilled: false, - borderColor: MyColors.darkPrimaryColor, - title: LocaleKeys.no.tr(), - txtColor: MyColors.darkPrimaryColor, - fontSize: 15, - onPressed: () { - Navigator.pop(context); - }, - ), - ), - ); - } - - Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) { - final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer; - - switch (requestOfferStatusEnum) { - case RequestOfferStatusEnum.offer: - return Column( - children: [ - 5.height, - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - "${chatMessageModel.reqOffer!.price}".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), - ], - ), - if (widget.chatMessageModel.isMyMessage == false) ...[ - 10.height, - Row( - children: [ - Expanded( - child: ShowFillButton( - maxHeight: 27, - title: LocaleKeys.accept.tr(), - fontSize: 9, - borderColor: MyColors.greenColor, - isFilled: false, - onPressed: () { - offerAcceptConfirmationBottomSheet(chatMessageModel: chatMessageModel); - }, - backgroundColor: MyColors.white, - txtColor: MyColors.greenColor, - ), - ), - 20.width, - Expanded( - child: ShowFillButton( - maxHeight: 27, - title: LocaleKeys.reject.tr(), - borderColor: MyColors.redColor, - isFilled: false, - backgroundColor: MyColors.white, - txtColor: MyColors.redColor, - fontSize: 9, - onPressed: () async { - buildRejectOfferBottomSheet(chatMessageModel: chatMessageModel); - }, - ), - ) - ], - ), - ], - ], - ); - case RequestOfferStatusEnum.negotiate: - return Column( - children: [ - Center( - child: LocaleKeys.newOfferRequired.tr().toText( - color: MyColors.adPendingStatusColor, - fontSize: 12, - isItalic: true, - ), - ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), - ], - ); - case RequestOfferStatusEnum.accepted: - return Column( - children: [ - Center( - child: LocaleKeys.offerHasBeenAccepted.tr().toText( - color: MyColors.adPendingStatusColor, - fontSize: 12, - isItalic: true, - ), - ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), - ], - ); - case RequestOfferStatusEnum.rejected: - return Column( - children: [ - Center( - child: LocaleKeys.offerHasBeenRejected.tr().toText( - color: MyColors.adPendingStatusColor, - fontSize: 12, - isItalic: true, - ), - ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), - ], - ); - case RequestOfferStatusEnum.cancel: - return Column( - children: [ - Center( - child: LocaleKeys.offerHasBeenCancelled.tr().toText( - color: MyColors.adPendingStatusColor, - fontSize: 12, - isItalic: true, - ), - ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), - ], - ); - } - } - - @override - Widget build(BuildContext context) { - return Directionality( - textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 1, - child: Image.asset( - MyAssets.bnCar, - width: 34, - height: 34, - fit: BoxFit.fill, - ).toCircle(borderRadius: 100), - ), - 10.width, - Expanded( - flex: 10, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ((widget.chatMessageModel.isMyMessage ?? false) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true), - ], - ), - 5.height, - Column( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Directionality( - textDirection: TextDirection.ltr, - child: (widget.chatMessageModel.chatText ?? "").toText( - color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, - fontSize: 12, - // isBold: true, - ), - ), - ), - ], - ), - if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[ - buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context), - ], - ], - ).toContainer( - isShadowEnabled: !(widget.chatMessageModel.isMyMessage ?? false), - backgroundColor: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.darkIconColor : MyColors.white, - borderRadius: 0, - margin: EdgeInsets.fromLTRB((widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0, !(widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0), - ), - ], - ), - ) - ], - ), - ); - } -} diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart new file mode 100644 index 0000000..05c8d4b --- /dev/null +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -0,0 +1,389 @@ +import 'dart:developer'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +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/chat_models/chat_message_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/utils.dart'; +import 'package:mc_common_app/view_models/chat_view_model.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart'; +import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; +import 'package:mc_common_app/widgets/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; + + const ChatMessageCustomWidget({super.key, required this.chatMessageModel}); + + @override + State createState() => _ChatMessageCustomWidgetState(); +} + +class _ChatMessageCustomWidgetState extends State { + Future buildRejectOfferBottomSheet({required ChatMessageModel chatMessageModel}) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { + return InfoBottomSheet( + title: LocaleKeys.makeAnOffer.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + description: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 12.height, + ListView.separated( + shrinkWrap: true, + itemCount: chatVM.offerRejectModelList.length, + separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5), + itemBuilder: (BuildContext context, int index) { + OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index]; + return CircleCheckBoxWithTitle( + isChecked: offerRequestCommentModel.isSelected ?? false, + title: '${offerRequestCommentModel.title}', + onSelected: () { + chatVM.updateSelectionInOfferRejectModelList(index); + }, + selectedColor: MyColors.darkPrimaryColor, + ); + }, + ), + if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) ...[ + // 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 + { + comments = chatVM.rejectOfferDescription; + } else { + comments = chatVM.selectedOfferRequestCommentModel.title ?? ""; + } + if (!chatVM.isRejectOfferButtonValidated()) { + return; + } + Navigator.pop(context); + bool status = await context.read().onSendMessageForActionOnRequestOffer( + receiverId: chatMessageModel.senderUserID ?? "", + chatMessageType: ChatMessageTypeEnum.offer, + comments: comments, + requestId: chatMessageModel.reqOffer!.requestID ?? -1, + serviceProviderID: chatMessageModel.serviceProviderID ?? 0, + requestOfferID: chatMessageModel.reqOffer!.id ?? -1, + offerPrice: chatMessageModel.reqOffer!.price.toString(), + serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "", + manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "", + manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0, + requestOfferStatusEnum: RequestOfferStatusEnum.rejected, + context: context, + ); + + if (status) { + chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected; + setState(() {}); + // Navigator.pop(context); + Utils.showToast("Offer Rejected"); + // navigateReplaceWithName(context, AppRoutes.dashboard); + } + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + )); + }); + }, + ); + } + + void offerAcceptConfirmationBottomSheet({required ChatMessageModel chatMessageModel}) { + return actionConfirmationBottomSheet( + context: context, + title: LocaleKeys.acceptOfferConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: LocaleKeys.acceptOfferConfirmationMessage.tr(), + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.yes.tr(), + fontSize: 15, + onPressed: () async { + Navigator.pop(context); + bool status = await context.read().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; + setState(() {}); + // Navigator.pop(context); + Utils.showToast("Offer Accepted"); + + // navigateReplaceWithName(context, AppRoutes.dashboard); + } + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: LocaleKeys.no.tr(), + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } + + Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) { + final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer; + + switch (requestOfferStatusEnum) { + case RequestOfferStatusEnum.offer: + return Column( + children: [ + 5.height, + 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), + 5.width, + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), + ], + ), + if (widget.chatMessageModel.isMyMessage == false) ...[ + 10.height, + Row( + children: [ + Expanded( + child: ShowFillButton( + maxHeight: 27, + title: LocaleKeys.accept.tr(), + fontSize: 9, + borderColor: MyColors.greenColor, + isFilled: false, + onPressed: () { + offerAcceptConfirmationBottomSheet(chatMessageModel: chatMessageModel); + }, + backgroundColor: MyColors.white, + txtColor: MyColors.greenColor, + ), + ), + 20.width, + Expanded( + child: ShowFillButton( + maxHeight: 27, + title: LocaleKeys.reject.tr(), + borderColor: MyColors.redColor, + isFilled: false, + backgroundColor: MyColors.white, + txtColor: MyColors.redColor, + fontSize: 9, + onPressed: () async { + buildRejectOfferBottomSheet(chatMessageModel: chatMessageModel); + }, + ), + ) + ], + ), + ], + ], + ); + case RequestOfferStatusEnum.negotiate: + return Column( + children: [ + Center( + child: LocaleKeys.newOfferRequired.tr().toText( + color: MyColors.adPendingStatusColor, + fontSize: 12, + isItalic: true, + ), + ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), + ], + ); + case RequestOfferStatusEnum.accepted: + return Column( + children: [ + Center( + child: LocaleKeys.offerHasBeenAccepted.tr().toText( + color: MyColors.adPendingStatusColor, + fontSize: 12, + isItalic: true, + ), + ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), + ], + ); + case RequestOfferStatusEnum.rejected: + return Column( + children: [ + Center( + child: LocaleKeys.offerHasBeenRejected.tr().toText( + color: MyColors.adPendingStatusColor, + fontSize: 12, + isItalic: true, + ), + ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), + ], + ); + case RequestOfferStatusEnum.cancel: + return Column( + children: [ + Center( + child: LocaleKeys.offerHasBeenCancelled.tr().toText( + color: MyColors.adPendingStatusColor, + fontSize: 12, + isItalic: true, + ), + ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), + ], + ); + } + } + + Widget getProfilePicture({String profileImageUrl = ""}) { + Widget widget = const SizedBox(); + + if (profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userLocalImage != null) { + widget = Image.file( + AppState().getUser.data!.userInfo!.userLocalImage!, + width: 34, + height: 34, + fit: BoxFit.fill, + ); + } else if (profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userImageUrl != null) { + widget = CachedNetworkImage( + imageUrl: AppState().getUser.data!.userInfo!.userImageUrl, + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, + fit: BoxFit.cover, + ), + ), + ), + placeholder: (context, url) => const Center(child: CircularProgressIndicator()), + errorWidget: (context, url, error) => const Icon(Icons.supervised_user_circle_outlined), + fadeInCurve: Curves.easeIn, + width: 34, + height: 34, + fit: BoxFit.fill, + fadeInDuration: const Duration(milliseconds: 1000), + useOldImageOnUrlChange: false); + } + + return widget.toCircle(borderRadius: 100); + } + + @override + Widget build(BuildContext context) { + return Directionality( + textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 1, + child: (widget.chatMessageModel.isMyMessage ?? false) + ? getProfilePicture() + : Container( + width: 34, + height: 34, + alignment: Alignment.center, + color: MyColors.darkTextColor, + child: ((widget.chatMessageModel.senderName ?? "").getInitials()).toText(color: MyColors.white, isBold: true, fontSize: 12), + ).toCircle(borderRadius: 100), + ), + 10.width, + Expanded( + flex: 10, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ((widget.chatMessageModel.isMyMessage ?? false) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true), + ], + ), + 5.height, + Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Directionality( + textDirection: TextDirection.ltr, + child: (widget.chatMessageModel.chatText ?? "").toText( + color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, + fontSize: 12, + // isBold: true, + ), + ), + ), + ], + ), + if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[ + buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context), + ], + ], + ).toContainer( + isShadowEnabled: !(widget.chatMessageModel.isMyMessage ?? false), + backgroundColor: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.darkIconColor : MyColors.white, + borderRadius: 0, + margin: EdgeInsets.fromLTRB((widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0, !(widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0), + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/views/requests/request_bottomsheets.dart b/lib/views/requests/request_bottomsheets.dart index f6a4276..9480fce 100644 --- a/lib/views/requests/request_bottomsheets.dart +++ b/lib/views/requests/request_bottomsheets.dart @@ -88,15 +88,18 @@ Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArgument maxHeight: 55, onPressed: () { requestsVM.onSendOfferPressed( - context: context, - receiverId: requestDetail.customerID, - message: requestsVM.offerDescription, - requestId: requestDetail.id, - offerPrice: requestsVM.offerPrice, - requestModel: requestDetail, - requestIndex: requestDetailPageArguments.requestIndex, - isFromChatScreen: false, - ); + context: context, + receiverId: requestDetail.customerID, + message: requestsVM.offerDescription, + requestId: requestDetail.id, + offerPrice: requestsVM.offerPrice, + requestModel: requestDetail, + requestIndex: requestDetailPageArguments.requestIndex, + isFromChatScreen: false, + manufacturedById: 1, + // Todo: It should be the ID of the manufacturer + manufacturedOn: requestsVM.serviceItemCreatedOn, + serviceItemName: requestsVM.serviceItem); }, maxWidth: double.infinity, ), diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart new file mode 100644 index 0000000..a1c9063 --- /dev/null +++ b/lib/views/requests/request_detail_page.dart @@ -0,0 +1,222 @@ +import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/config/routes.dart'; +import 'package:mc_common_app/extensions/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/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/views/advertisement/components/ads_images_corousel_widget.dart'; +import 'package:mc_common_app/views/requests/request_bottomsheets.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; +import 'package:easy_localization/easy_localization.dart'; + +class RequestDetailPage extends StatelessWidget { + final RequestDetailPageArguments requestDetailPageArguments; + + const RequestDetailPage({Key? key, required this.requestDetailPageArguments}) : super(key: key); + + Widget buildBottomButton({required RequestStatus requestStatus, required String statusText, required BuildContext context, required Function() onViewChatTapped}) { + switch (requestStatus) { + case RequestStatus.submitted: + case RequestStatus.inProgress: + case RequestStatus.paid: + case RequestStatus.shipping: + return ShowFillButton( + maxWidth: double.infinity, + margin: const EdgeInsets.all(15), + maxHeight: 55, + title: LocaleKeys.viewChat.tr(), + isBold: false, + onPressed: onViewChatTapped, + ); + case RequestStatus.completed: + case RequestStatus.cancelled: + case RequestStatus.expired: + case RequestStatus.pending: + return buildDisabledButton(statusText); + } + } + + Widget buildDisabledButton(String text) { + return ShowFillButton( + backgroundColor: MyColors.grey98Color.withOpacity(0.3), + txtColor: MyColors.lightTextColor, + maxWidth: double.infinity, + margin: const EdgeInsets.all(15), + maxHeight: 55, + title: text, + isBold: false, + onPressed: () {}, + ); + } + + Future onViewChatTapped(BuildContext context) async { + ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest( + chatTypeEnum: ChatTypeEnum.requestOffer, + receiverId: requestDetailPageArguments.requestModel.customerID, + senderId: AppState().getUser.data!.userInfo!.userId.toString(), + requestId: requestDetailPageArguments.requestModel.id, + providerIndex: -1, + // This will be only sent in case of customer + requestModel: requestDetailPageArguments.requestModel, + requestIndex: requestDetailPageArguments.requestIndex, // This will be only sent in case of provider + ); + ChatViewArguments chatViewArguments = ChatViewArguments( + chatTypeEnum: ChatTypeEnum.requestOffer, + chatViewArgumentsForRequest: chatViewArgumentsForRequest, + ); + final chatVM = context.read(); + await chatVM + .getUsersChatMessagesForProvider( + customerId: requestDetailPageArguments.requestModel.customerId, + context: context, + requestOfferId: 0, + requestId: requestDetailPageArguments.requestModel.id, + customerRequestIndex: requestDetailPageArguments.requestIndex, + ) + .whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: LocaleKeys.requestDetail.tr(), + ), + body: SizedBox( + width: double.infinity, + height: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + ImagesCorouselWidget(imagesList: requestDetailPageArguments.requestModel.requestImages ?? []), + 24.height, + buildRequestContainer(context), + ], + ).toWhiteContainer( + width: double.infinity, + allPading: 12, + margin: const EdgeInsets.only(top: 21, right: 21, left: 21), + ), + if (!requestDetailPageArguments.requestModel.isChatted) ...[ + ShowFillButton( + maxWidth: double.infinity, + margin: const EdgeInsets.all(15), + maxHeight: 55, + title: LocaleKeys.sendOffer.tr(), + isBold: false, + fontSize: 18, + onPressed: () => buildSendOfferBottomSheet(context, requestDetailPageArguments), + ), + ] else ...[ + buildBottomButton( + requestStatus: requestDetailPageArguments.requestModel.requestStatus, + statusText: "Offer ${requestDetailPageArguments.requestModel.requestStatusName}", + context: context, + onViewChatTapped: () async => onViewChatTapped(context), + ), + ], + ], + ), + ), + ); + } + + Widget buildRequestContainer(BuildContext context) { + final requestDetail = requestDetailPageArguments.requestModel; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + requestDetail.vehicleTypeName.toText(fontSize: 16, isBold: true), + showItem("Manufacturer:", requestDetail.brand), + showItem("Model:", "${requestDetail.year}"), + ], + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + "${requestDetail.city ?? ""} ${requestDetail.countryName}".toText( + color: MyColors.lightTextColor, + ), + if (requestDetail.createdOn != null) ...[ + DateTime.parse(requestDetail.createdOn!).getTimeAgo().toText(color: MyColors.lightTextColor), + ], + ], + ), + ], + ), + showItem("Customer Name: ", requestDetail.customerName), + showItem("Description: ", requestDetail.description), + showItem("Price Range:", ""), + Row( + children: [ + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "${requestDetail.price.toInt()}".toText(fontSize: 25, isBold: true), + 2.width, + LocaleKeys.sar.tr().toText( + color: MyColors.lightTextColor, + fontSize: 16, + height: 2.3, + ), + ], + ), + Row( + children: [ + Utils.statusContainerChip( + text: (requestDetail.requestStatusName), + chipColor: MyColors.grey98Color.withOpacity(0.3), + textColor: MyColors.lightTextColor, + ), + ], + ), + ], + ), + ), + // const Icon(Icons.arrow_forward) + ], + ), + ], + ); + } + + Widget showItem(String title, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (title.isNotEmpty) + title.toText( + color: MyColors.lightTextColor, + ), + if (title.isNotEmpty) 2.width, + if (value.isNotEmpty) Expanded(child: value.toText(isBold: true)), + ], + ); + } +} diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index 0d7847f..11f2d09 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -4,6 +4,7 @@ import 'dart:developer'; import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:flutter/material.dart'; @@ -126,7 +127,7 @@ class RequestItem extends StatelessWidget { children: [ title.toText(color: MyColors.lightTextColor), 3.width, - Flexible(child: value.toText(isBold: true, overflow: TextOverflow.ellipsis )), + Flexible(child: value.toText(isBold: true, overflow: TextOverflow.ellipsis)), ], ); } diff --git a/lib/widgets/button/show_fill_button.dart b/lib/widgets/button/show_fill_button.dart index 6be9970..b59e56a 100644 --- a/lib/widgets/button/show_fill_button.dart +++ b/lib/widgets/button/show_fill_button.dart @@ -4,18 +4,19 @@ import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; class ShowFillButton extends StatelessWidget { - String title; - Color? backgroundColor; - VoidCallback onPressed; - Color txtColor; - double elevation, radius, maxWidth, maxHeight, fontSize, horizontalPadding, horizontalMargin, verticalMargin; - bool isFlatButton, isBold; - EdgeInsets? margin; - bool isFilled; - Color borderColor; - Widget? iconWidget; + final String title; + final Color? backgroundColor; + final VoidCallback onPressed; + final Color txtColor; + final double elevation, radius, maxWidth, maxHeight, fontSize, horizontalPadding, horizontalMargin, verticalMargin; + final bool isFlatButton, isBold; + final EdgeInsets? margin; + final bool isFilled; + final bool isDisabled; + final Color borderColor; + final Widget? iconWidget; - ShowFillButton({ + const ShowFillButton({ super.key, required this.title, required this.onPressed, @@ -23,6 +24,7 @@ class ShowFillButton extends StatelessWidget { this.backgroundColor = MyColors.darkPrimaryColor, this.elevation = 0, this.isFilled = true, + this.isDisabled = false, this.radius = 0, this.maxWidth = 88, this.maxHeight = 45, @@ -65,11 +67,13 @@ class ShowFillButton extends StatelessWidget { Widget showButton() { return Container( // decoration: isFlatButton ? null : MyColors.gradientButton, - color: isFlatButton - ? null - : isFilled - ? backgroundColor - : null, + color: isDisabled + ? MyColors.grey98Color.withOpacity(0.3) + : (isFlatButton + ? null + : isFilled + ? backgroundColor + : null), margin: EdgeInsets.symmetric(horizontal: horizontalMargin, vertical: verticalMargin), child: MaterialButton( onPressed: onPressed, @@ -85,7 +89,7 @@ class ShowFillButton extends StatelessWidget { title.toText( fontSize: fontSize, isBold: isBold, - color: txtColor, + color: isDisabled ? MyColors.lightTextColor : txtColor, maxLines: 1, ), ], @@ -93,7 +97,7 @@ class ShowFillButton extends StatelessWidget { : title.toText( fontSize: fontSize, isBold: isBold, - color: txtColor, + color: isDisabled ? MyColors.lightTextColor : txtColor, maxLines: 1, ), ), diff --git a/lib/widgets/common_widgets/branch_details_card.dart b/lib/widgets/common_widgets/branch_details_card.dart index ffeecee..e376714 100644 --- a/lib/widgets/common_widgets/branch_details_card.dart +++ b/lib/widgets/common_widgets/branch_details_card.dart @@ -30,8 +30,6 @@ class BranchDetailCard extends StatelessWidget { @override Widget build(BuildContext context) { - print("Provider Image Title" + title); - print("Provider Image URL" + providerImageUrl); return Padding( padding: EdgeInsets.zero, // padding: const EdgeInsets.only( diff --git a/lib/widgets/extensions/extensions_widget.dart b/lib/widgets/extensions/extensions_widget.dart index fec9705..a896cb4 100644 --- a/lib/widgets/extensions/extensions_widget.dart +++ b/lib/widgets/extensions/extensions_widget.dart @@ -24,8 +24,7 @@ extension ExtendedText on Widget { } extension ContainerExt on Widget { - Widget toWhiteContainer( - {required double width, double? allPading, EdgeInsets? pading, EdgeInsets? margin, VoidCallback? onTap, Color? backgroundColor, bool isBorderRequired = false, double borderRadius = 0}) { + Widget toWhiteContainer({required double width, double? allPading, EdgeInsets? pading, EdgeInsets? margin, VoidCallback? onTap, Color? backgroundColor, bool isBorderRequired = false, double borderRadius = 0}) { return InkWell( onTap: onTap, child: Container( @@ -84,12 +83,12 @@ extension ContainerExt on Widget { boxShadow: !isShadowEnabled ? null : [ - BoxShadow( - color: const Color(0xff000000).withOpacity(.05), - blurRadius: 26, - offset: const Offset(0, -3), - ), - ], + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], ), padding: padding ?? EdgeInsets.all(paddingAll), margin: margin ?? EdgeInsets.all(marginAll), @@ -234,8 +233,7 @@ extension WidgetExt on Widget { ); } - Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => - Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this); + Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this); // Widget onTap(VoidCallback onTap, {double corners = 0}) { // return Clickable.widget(child: this, corners: corners, onTap: onTap); @@ -365,10 +363,14 @@ extension BuildSVG on String? { if (imageChunk == null) { return child!; } - return const Center( - child: CircularProgressIndicator( - strokeWidth: 0.5, - color: MyColors.darkPrimaryColor, + return Center( + child: SizedBox( + height: height, + width: width, + child: const CircularProgressIndicator( + strokeWidth: 0.6, + color: MyColors.darkPrimaryColor, + ), ), ); }, @@ -382,12 +384,7 @@ extension BuildSVG on String? { extension LocaleSetup on MultiProvider { Widget setupLocale() { - return EasyLocalization(supportedLocales: MyLocales.supportedLocales, - fallbackLocale: MyLocales.fallBackLocale, - startLocale: MyLocales.startLocale, - assetLoader: MyLocales.assetLoader, - path: MyLocales.localesAssetPath, - child: this); + return EasyLocalization(supportedLocales: MyLocales.supportedLocales, fallbackLocale: MyLocales.fallBackLocale, startLocale: MyLocales.startLocale, assetLoader: MyLocales.assetLoader, path: MyLocales.localesAssetPath, child: this); } }