From 4949a141b9461464316628427ae9301ca5cab648 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Thu, 10 Oct 2024 14:30:12 +0300 Subject: [PATCH 1/8] Added Media Viewer --- assets/images/broken_image.png | Bin 0 -> 3098 bytes assets/langs/ar-SA.json | 5 +- assets/langs/en-US.json | 6 +- lib/classes/consts.dart | 3 + lib/config/routes.dart | 10 +- lib/extensions/string_extensions.dart | 6 +- lib/generated/codegen_loader.g.dart | 8 +- lib/generated/locale_keys.g.dart | 2 + .../chat_models/chat_message_model.dart | 5 + .../provider_offers_model.dart | 7 +- lib/repositories/payments_repo.dart | 16 ++- lib/services/payments_service.dart | 29 +++-- lib/utils/enums.dart | 10 +- lib/utils/utils.dart | 36 ++++++ lib/view_models/chat_view_model.dart | 48 +++++--- lib/view_models/payment_view_model.dart | 4 +- .../advertisement/ads_buyer_chats_view.dart | 2 +- .../ads_detail_view/ads_detail_view.dart | 35 ++++++ .../picked_images_container_widget.dart | 26 ++-- lib/views/chat/chat_view.dart | 31 +++-- .../chat/widgets/chat_message_widget.dart | 38 +++--- lib/views/requests/offer_list_page.dart | 5 +- lib/views/requests/request_detail_page.dart | 2 +- lib/widgets/extensions/extensions_widget.dart | 2 +- .../image_viewer/image_viewer_screen.dart | 114 ++++++++++++++++++ 25 files changed, 360 insertions(+), 90 deletions(-) create mode 100644 assets/images/broken_image.png create mode 100644 lib/widgets/image_viewer/image_viewer_screen.dart diff --git a/assets/images/broken_image.png b/assets/images/broken_image.png new file mode 100644 index 0000000000000000000000000000000000000000..dbf6a48e3a45be5f3b89d3eed28849d3c1b70eed GIT binary patch literal 3098 zcmZ`*c{o(<8$QN1BB5;gLQDuLk##I-jBQBBmaJn)W2u>JL)MBYOBf8HvSm+}X>5_D z##Uo6WkNzm2r07tPS^MQ_dD0~p7*@Zb*}e0&$;jSex5{YE0coKE&gu zHxLpMqH-D)T!fcHaXKKYT$t=FDx_-8$)VsZpwQ1Mb#T8^xXIGxnSpq``1P44cNEerax7$}H z+ok+YMA0Cru!VY*+&V1N{K1%CgZ(?0me<*wsbETqp5)L|?n9DT&CI5pH|em*#`gC1 zw4wP^1DDeTV!EcwCfXtiWO2c4(n;w@Z{bD6Ko=DimCCp1H8Zs^bzZ~tXG1pSBcbbb z8AQ5yaR3&JouKEs)&pp#+o;I4)pELDnVsUKpJgCD*E(x9#c_HvYQJ^VL_p_9*C7M- z_)MxdadgM>0b9Z%H_sINJ&qqS7a*G*@8M9Gvq_dWOo-u@ugrse*VtI z4Nu=PT*U$mIChdqO+d(uGnvLk*48LZ9i97x-^)^6za`J-x_O4nTkF>)~eR@?Chb4bS%W_N$TgNbt+b>?<~B zQVfoEwz{6~>FGJLo`aI>9~x3?|M~Q(Z1pYldC1fo216X}6aa0%G1HS7Mj#M!WIS#i zY&p&rBvUY?)|GEnTC!7#Hy`@S!xMcNqqgn#)=&856LpKJzw2O1zWCm+r7Mw^*&+y*D2SQ&=|Z9Gqt9s z=8%5D#im!IMVW>VQ7Du<7R#52_+rznOdX$`?0x;3jfnDJv^0{`&zN z-j1BpZJQ7c(gSga3VP}QChaZS&aG{1vAeS~*7b4=2T!ybJ3D*m<_{05+e%*_R8l`e z0et|P3gA7!{oSH=8|I7)b@U4kxkYXWhZ6|5o;%5^a}6hgz`}3uh*2mM=)JDz72zsG z-rvj2!)ICh@*wf*9F>1;?>f4;AawGPGHHhT1txWl@dKEy;b8#~k-j70%&q;c$hpSA z(jwIk?!g9MHqSb-hYuf41!>jR)=J|A=u7xkz^F*TsHnZBH?iAubB+|4y|{*uy~^YS zN#7E_!Lk`&SUB0(M0zDRA6IhyO0-c?bVI|rl1Lq)#3_*D&+RLNdF~j2?B@hP^k+au zj8-Y@EqoMlkBmGfDk|FF>|Ax%P`c1}D1aEGm29mL?^%}s8u$>TRcYZAPRoRV$H!s0 ze*UI5*5Cie>Fw{|lNsIEz`&WGKQjnn;j~*2EnVHNB+=9A#mrBiXz0FS^9JW`7R#@z zaryEMWJv{wb-7*bix-_c@=BJwv9G1?cgqmhni?B}f`d!M3I;Hg0N~?g$oR*PMj9l4 z=*rSk_uAT75J!nGpooCP`W(zKF)`WP7;~#ZiSpmOv$+-a{v0Hs+cR{1&hkR@TXgK? z%Dn+h+~kVSE1XxI}z+b zKEW(KJ1pNyo{Nj?Xy^JC{INmQXB(#{sx5$D!%>)-=j}^YrH7jWC)ey}i8$r$v%DEi5b?ot-@!b-1~?6Od0WiSkFgc&9cN$50ObyPUOoYP`I> z{q@1Cp)RX{Ja6$ysUL#?D@bd2a25rm4w&uj_U4 z;^Ly04xY^&hqI+wTq2Ec^jr>+}~oi5{E+ zd|Ww$P84P}L$%_kjZJw5>qFItj2itJcRgD(u?|W=@~yRAsF)4#-uFoR{vP2%GXr~f za%C*yI`ZPhlrI)xTWcrQc^yb=TM_yZKLytZvB1hJE_*7BdUkYl6#t3j%iYBzO{99@ zaF4__&z+;cj2fK1sT#srL05#C`F-<*#6*9|SSe8B7s26h{c%tHq8#K^Gr-Ew!(u6} zTZl_a_EcSIat>eK*@3UuBIO>DM+}szzLXDW<(f%6K;CiL{=j!vsDny8oiqOxRE{~( zj!wUZ=f8#)@XoZ1i%M~EaiFq#_UJ|Jx!jPT92P?YtD*ns>gx74cXu0i&)7SQi;H{w zS@w29N()#|pl?!`xz%A?@=XCu&tL~oQezAY7v>l49OtlBI3fV$$hNIdOymg&W-%B} znEku~Ob!X9L8_Wn3*TT65&rK=g(=L3 zWU@9U?-C#Em}qo5r5CH=b?sUrg%aZHYf2+;SCm9Ji%Us4{+r8m+(CSN&ZiJphg70P9@8R`F7RARYlYkO_!mOCW|Q38!76 zeyEz7+EZWYg^>~KW?*frMJ^--%DT!%9ZoYZPDRO)iKS?1Z8-dULJXqp?^ywnR;LgU zg&5f+gv|5y4cY;uS}Ii7P-$6GP=V2>n0a62YD^j*tdnE-ImP6NxyB2WT-Q&IHH9H4 zm(X#D?-)}Sa!!Il(hO`4oGLq|mZY8v6$B1}Q8Tb;x}WfLQ1zgKKp^<`2o~MG`|Ap=oikK8UnBLK9MC$UClf?=`4QfFaC(E( z8WzHtul|4klWY>nD_|1jpaJ(zVFqYtcqmjn5( z38!%h1wg)KP)s<$yg{+e&p)>8HhY;%=&@#MkYKd)y1I_e^NN3ei&0U>PmPyJxiRtc z2VpK8wPf;J>;bSa3ujLFk5E+Y9fBev`t#Nrsr~(Yws`nKm_Kia0vL{uk5A!aUvNkW zxrRv^z)Z9f930rqK0(gh|94-{y3H|q9sH77>W^_H-zxy}=I;c<0b~}4<)=Qto1G^` z&3Thzus81dpqeMm6hX`~}yu&ow`sCt7jhiS+?l4y4Q`F0nG3DsL zO(caGu(x#dwQx!=uDPY9l3_nFBGxgp3csoKPv%^OaY7;3Tz=UImpb>TbU{N;jPG_X zO0M9UO}Ru%)g{FBY?NFm{PBgFvF&eryW2$-xE5j)-AGeIW7%FYGmtv_qJr)tump1b sQ6mGp{|9VN7$VBC($N1fed#^!r8LVjg(L-Wu#X4KjI9vWM((ly1KwB13jhEB literal 0 HcmV?d00001 diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 1f4c471..0658fe9 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -654,5 +654,8 @@ "date": "التاريخ", "selectAll": "اختر الكل", "unselectAll": "إلغاء اختيار الكل", - "copySelectedServices": "نسخ الخدمات المحددة" + "copySelectedServices": "نسخ الخدمات المحددة", + "pictures": "الصور", + "noChatMessage": "لا توجد رسائل بعد. أرسل رسالة لبدء المحادثة!" + } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 4ceed5a..cf67266 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -655,5 +655,9 @@ "date": "Date", "selectAll": "Select All", "unselectAll": "Unselect All", - "copySelectedServices": "Copy Selected Services" + "copySelectedServices": "Copy Selected Services", + "pictures": "Pictures", + "noChatMessage": "No messages yet. Send a message to start the conversation!" + + } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 79e5d87..7d19e56 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -292,6 +292,9 @@ class MyAssets { static String whatsAppIcon = "${assetPath}icons/whatsapp_icon.svg"; static const String arrowRight = "${assetPath}icons/ic_arrow_right.svg"; + static const String brokenImage = "${assetPath}images/broken_image.png"; + + } RegExp numReg = RegExp(r".*[0-9].*"); diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 4c59feb..49df5ae 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,5 +1,6 @@ import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; import 'package:mc_common_app/models/chat_models/buyers_chat_for_ads_model.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'; import 'package:mc_common_app/models/requests_models/request_model.dart'; import 'package:mc_common_app/models/user_models/register_user.dart'; @@ -40,6 +41,7 @@ import 'package:mc_common_app/views/user/register_selection_page.dart'; import 'package:mc_common_app/views/splash/splash_page.dart'; import 'package:mc_common_app/views/user/vertify_password_page.dart'; import 'package:flutter/material.dart'; +import 'package:mc_common_app/widgets/image_viewer/image_viewer_screen.dart'; class AppRoutes { //User @@ -110,6 +112,9 @@ class AppRoutes { static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends"; static const String settingOptionsHelp = "/settingOptionsHelp"; + //MediaViewer + static const String mediaViewerScreen = "/mediaViewer"; + //Profile Screen static const String profileView = "/profileView"; @@ -151,7 +156,7 @@ class AppRoutes { AppRoutes.adsDetailView: (context) => AdsDetailView(adDetails: ModalRoute.of(context)!.settings.arguments as AdDetailsModel), AppRoutes.createAdView: (context) => const CreateAdView(), AppRoutes.adsFilterView: (context) => const AdsFilterView(), - AppRoutes.selectAdTypeView: (context) => SelectAdTypeView(arguments: ModalRoute.of(context)!.settings.arguments as List), + 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.adsBuyerChatsListView: (context) => AdsBuyerChatsView(buyersListViewArguments: ModalRoute.of(context)!.settings.arguments as List), AppRoutes.settingOptionsFaqs: (context) => const SettingOptionsFAQs(), @@ -163,6 +168,9 @@ class AppRoutes { AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments), AppRoutes.reviewRequestOffer: (context) => const ReviewRequestOffer(), AppRoutes.requestsFilterView: (context) => const RequestsFilterView(), + + //MediaViewer + AppRoutes.mediaViewerScreen: (context) => MediaViewerScreen(images: ModalRoute.of(context)!.settings.arguments as List), }; } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 58208c7..e71607c 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -455,16 +455,12 @@ extension PaymentTypesToInt on PaymentTypes { switch (this) { case PaymentTypes.subscription: return 1; - case PaymentTypes.appointment: return 2; - case PaymentTypes.adReserve: return 4; - case PaymentTypes.ads: return 3; - case PaymentTypes.request: return 5; case PaymentTypes.extendAds: @@ -577,7 +573,7 @@ extension DateTimeConversions on DateTime { final date2 = DateTime.now(); final difference = date2.difference(this); if ((difference.inDays / 7).floor() >= 2) { - return DateHelper.formatAsDayMonthYear(date2); + return DateHelper.formatAsDayMonthYear(this); } else if ((difference.inDays / 7).floor() >= 1) { return (numericDates) ? '1 week ago' : 'Last week'; } else if (difference.inDays >= 2) { diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 2f0664a..1f339eb 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -670,7 +670,9 @@ class CodegenLoader extends AssetLoader{ "date": "التاريخ", "selectAll": "اختر الكل", "unselectAll": "إلغاء اختيار الكل", - "copySelectedServices": "نسخ الخدمات المحددة" + "copySelectedServices": "نسخ الخدمات المحددة", + "pictures": "الصور", + "noChatMessage": "لا توجد رسائل بعد. أرسل رسالة لبدء المحادثة!" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1329,7 +1331,9 @@ static const Map en_US = { "date": "Date", "selectAll": "Select All", "unselectAll": "Unselect All", - "copySelectedServices": "Copy Selected Services" + "copySelectedServices": "Copy Selected Services", + "pictures": "Pictures", + "noChatMessage": "No messages yet. Send a message to start the conversation!" }; 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 37af609..e430e49 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -634,5 +634,7 @@ abstract class LocaleKeys { static const selectAll = 'selectAll'; static const unselectAll = 'unselectAll'; static const copySelectedServices = 'copySelectedServices'; + static const pictures = 'pictures'; + static const noChatMessage = 'noChatMessage'; } diff --git a/lib/models/chat_models/chat_message_model.dart b/lib/models/chat_models/chat_message_model.dart index 8a5c728..4ad318d 100644 --- a/lib/models/chat_models/chat_message_model.dart +++ b/lib/models/chat_models/chat_message_model.dart @@ -163,4 +163,9 @@ class MessageImageModel { 'reqOfferID': reqOfferID, // We don't include 'isFromNetwork' since it's set manually when parsing JSON }; } + + @override + String toString() { + return 'MessageImageModel{id: $id, imageUrl: $imageUrl, imageStr: $imageStr, imagePath: $imagePath, isFromNetwork: $isFromNetwork, reqOfferID: $reqOfferID}'; + } } diff --git a/lib/models/requests_models/provider_offers_model.dart b/lib/models/requests_models/provider_offers_model.dart index a101485..c829a8c 100644 --- a/lib/models/requests_models/provider_offers_model.dart +++ b/lib/models/requests_models/provider_offers_model.dart @@ -1,7 +1,9 @@ import 'dart:developer'; +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/utils/enums.dart'; class ProviderOffersModel { int? id; @@ -61,6 +63,7 @@ class ServiceProvidersOffers { String? email; String? companyName; String? createdOn; + RequestOfferStatusEnum? requestOfferStatusEnum; int? offerCount; List? chatMessages; @@ -74,6 +77,7 @@ class ServiceProvidersOffers { this.chatMessages, this.providerUserId, this.createdOn, + this.requestOfferStatusEnum, }); ServiceProvidersOffers.fromJson(Map json) { @@ -85,11 +89,12 @@ class ServiceProvidersOffers { companyName = json['companyName']; offerCount = json['offerCount']; createdOn = json['createdOn']; + requestOfferStatusEnum = ((json['offerStatusLast']) as int).toRequestOfferStatusEnum(); chatMessages = []; } @override String toString() { - return 'ServiceProvidersOffers{providerUserId: $providerUserId, providerId: $providerId, name: $name, mobileNo: $mobileNo, email: $email, companyName: $companyName,offeredItemCreatedOn: $createdOn, offerCount: $offerCount, chatMessages: $chatMessages}'; + return 'ServiceProvidersOffers{providerUserId: $providerUserId, providerId: $providerId, name: $name, mobileNo: $mobileNo, email: $email, companyName: $companyName,offeredItemCreatedOn: $createdOn, requestOfferStatusEnum: $requestOfferStatusEnum, offerCount: $offerCount, chatMessages: $chatMessages}'; } } diff --git a/lib/repositories/payments_repo.dart b/lib/repositories/payments_repo.dart index 4ffa1fe..579f999 100644 --- a/lib/repositories/payments_repo.dart +++ b/lib/repositories/payments_repo.dart @@ -6,7 +6,7 @@ import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/payment_models/pay_order_detail_resp_model.dart'; abstract class PaymentsRepo { - Future getPayOrderDetails({required int paymentId, required int adId}); + Future getPayOrderDetails({required int paymentTypeId, required int adId}); } class PaymentsRepoImp implements PaymentsRepo { @@ -14,8 +14,18 @@ class PaymentsRepoImp implements PaymentsRepo { AppState appState = injector.get(); @override - Future getPayOrderDetails({required int paymentId, required int adId}) async { - GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.payForOrderDetailGet); + Future getPayOrderDetails({required int paymentTypeId, required int adId}) async { + var queryParameters = { + "AdsID": adId.toString(), + "PaymentType": paymentTypeId.toString(), + }; + + GenericRespModel adsGenericModel = await apiClient.getJsonForObject( + token: appState.getUser.data!.accessToken, + queryParameters: queryParameters, + (json) => GenericRespModel.fromJson(json), + ApiConsts.payForOrderDetailGet, + ); PayOrderDetailRespModel payOrderDetailRespModel = PayOrderDetailRespModel.fromJson(adsGenericModel.data); return payOrderDetailRespModel; } diff --git a/lib/services/payments_service.dart b/lib/services/payments_service.dart index aed50b8..36f5c91 100644 --- a/lib/services/payments_service.dart +++ b/lib/services/payments_service.dart @@ -27,23 +27,15 @@ abstract class PaymentService { class PaymentServiceImp implements PaymentService { MyInAppBrowser? myInAppBrowser; var inAppBrowserOptions = InAppBrowserClassOptions( - inAppWebViewGroupOptions: - InAppWebViewGroupOptions(crossPlatform: InAppWebViewOptions(useShouldOverrideUrlLoading: true, transparentBackground: false), ios: IOSInAppWebViewOptions(applePayAPIEnabled: true)), + inAppWebViewGroupOptions: InAppWebViewGroupOptions(crossPlatform: InAppWebViewOptions(useShouldOverrideUrlLoading: true, transparentBackground: false), ios: IOSInAppWebViewOptions(applePayAPIEnabled: true)), crossPlatform: InAppBrowserOptions(hideUrlBar: true, toolbarTopBackgroundColor: Colors.black), android: AndroidInAppBrowserOptions(), - ios: - IOSInAppBrowserOptions(hideToolbarBottom: true, toolbarBottomBackgroundColor: Colors.white, closeButtonColor: Colors.white, presentationStyle: IOSUIModalPresentationStyle.OVER_FULL_SCREEN)); + ios: IOSInAppBrowserOptions(hideToolbarBottom: true, toolbarBottomBackgroundColor: Colors.white, closeButtonColor: Colors.white, presentationStyle: IOSUIModalPresentationStyle.OVER_FULL_SCREEN)); - @override - Future placePayment({ - required int id, - List? appointmentIds, - required PaymentTypes paymentType, - required Function() onSuccess, - required Function() onFailure, - }) async { + String getUrlRequestByPaymentId({required int id, List? appointmentIds, required PaymentTypes paymentType}) { String urlRequest = ""; int customerId = AppState().getUser.data!.userInfo!.customerId ?? 0; + switch (paymentType) { case PaymentTypes.subscription: urlRequest = "${ApiConsts.paymentWebViewUrl}?PaymentType=${paymentType.getIdFromPaymentTypesEnum()}&OrderProviderSubscriptionID=$id"; @@ -74,6 +66,19 @@ class PaymentServiceImp implements PaymentService { urlRequest = "${ApiConsts.paymentWebViewUrl}?PaymentType=${paymentType.getIdFromPaymentTypesEnum()}&CustomerID=$customerId&RequestID=$id"; break; } + + return urlRequest; + } + + @override + Future placePayment({ + required int id, + List? appointmentIds, + required PaymentTypes paymentType, + required Function() onSuccess, + required Function() onFailure, + }) async { + String urlRequest = getUrlRequestByPaymentId(id: id, paymentType: paymentType, appointmentIds: appointmentIds); log("PaymentUrl: $urlRequest"); myInAppBrowser = MyInAppBrowser(onExitCallback: () { log("Browser Exited"); diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index b214b10..f948197 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -78,7 +78,15 @@ enum PaymentMethods { tamara, } -enum PaymentTypes { subscription, appointment, adReserve, ads, request, extendAds, partialAppointment } +enum PaymentTypes { + subscription, + appointment, + adReserve, + ads, + request, + extendAds, + partialAppointment, +} enum AdCreationSteps { vehicleDetails, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 0d7464b..6470fd9 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -249,6 +249,42 @@ class Utils { } } + static Color getChipColorByRequestOfferStatusEnum(RequestOfferStatusEnum requestOfferStatusEnum) { + switch (requestOfferStatusEnum) { + case RequestOfferStatusEnum.offer: + return MyColors.pendingColor; + + case RequestOfferStatusEnum.negotiate: + return MyColors.submittedColor; + + case RequestOfferStatusEnum.accepted: + return MyColors.completedColor; + case RequestOfferStatusEnum.rejected: + return MyColors.cancelledColor; + + case RequestOfferStatusEnum.cancel: + return MyColors.redColor; + } + } + + static String getNameByRequestOfferStatusEnum(RequestOfferStatusEnum requestOfferStatusEnum) { + switch (requestOfferStatusEnum) { + case RequestOfferStatusEnum.offer: + return "Pending"; + + case RequestOfferStatusEnum.negotiate: + return "Negotiate"; + + case RequestOfferStatusEnum.accepted: + return "Accepted"; + case RequestOfferStatusEnum.rejected: + return "Rejected"; + + case RequestOfferStatusEnum.cancel: + return "Cancelled"; + } + } + static Color getChipColorByBranchStatus(BranchStatusEnum branchStatusEnum) { switch (branchStatusEnum) { case BranchStatusEnum.pending: diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 34b308b..5de3d93 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -303,11 +303,7 @@ class ChatVM extends ChangeNotifier { MessageImageModel convertFileToMessageImageModel({required File file}) { List imageBytes = file.readAsBytesSync(); String image = base64Encode(imageBytes); - MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image, - id: 0, - isFromNetwork: false, - reqOfferID: latestOfferId, - imagePath: file.path); + MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image, id: 0, isFromNetwork: false, reqOfferID: latestOfferId, imagePath: file.path); return vehiclePostingImages; } @@ -376,7 +372,7 @@ class ChatVM extends ChangeNotifier { "ReceiverUserID": receiverId, "SenderUserID": userId, "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), - "ChatText": message, + "ChatText": chatMessageType == ChatMessageTypeEnum.image ? "${messageImages.length} Image(s)" : message, "RequestID": requestId, "ReqOfferID": latestOfferId, }; @@ -413,10 +409,7 @@ class ChatVM extends ChangeNotifier { serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel); } } else { - int providerIndex = context - .read() - .myFilteredRequests - .indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId)); + int providerIndex = context.read().myFilteredRequests.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId)); log("providerIndex2:$providerIndex"); if (providerIndex != -1) { context.read().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex); @@ -444,7 +437,7 @@ class ChatVM extends ChangeNotifier { } } - Future getUsersChatMessagesForCustomer({ + Future getRequestsChatMessagesForCustomer({ required BuildContext context, required int providerId, required int requestOfferId, @@ -468,6 +461,17 @@ class ChatVM extends ChangeNotifier { } } } + List unreadMessageIds = []; + + for (var msg in chatMessages) { + if (!msg.isRead! && !msg.isMyMessage!) { + unreadMessageIds.add(msg.id!); + } + } + + if (unreadMessageIds.isNotEmpty) { + await markMessagesAsRead(context, unreadMessageIds, ChatTypeEnum.requestOffer); + } Utils.hideLoading(context); notifyListeners(); } catch (e) { @@ -477,7 +481,7 @@ class ChatVM extends ChangeNotifier { } } - Future getUsersChatMessagesForProvider({ + Future getRequestsChatMessagesForProvider({ required BuildContext context, required int customerId, required int requestOfferId, @@ -489,6 +493,15 @@ class ChatVM extends ChangeNotifier { Utils.showLoading(context); List chatMessages = await chatRepo.getUsersChatMessagesForRequests(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId); context.read().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex, context: context); + List unreadMessageIds = []; + for (var msg in chatMessages) { + if (!msg.isRead! && !msg.isMyMessage!) { + unreadMessageIds.add(msg.id!); + } + } + if (unreadMessageIds.isNotEmpty) { + await markMessagesAsRead(context, unreadMessageIds, ChatTypeEnum.requestOffer); + } Utils.hideLoading(context); } catch (e) { logger.i(e.toString()); @@ -578,7 +591,7 @@ class ChatVM extends ChangeNotifier { for (var msg in currentMessagesForAds) { msg.senderName = senderName ?? ""; - if (!msg.isRead!) { + if (!msg.isRead! && !msg.isMyMessage!) { unreadMessageIds.add(msg.id!); } } @@ -598,13 +611,13 @@ class ChatVM extends ChangeNotifier { Future markMessagesAsRead(BuildContext context, List messageIds, ChatTypeEnum chatTypeEnum) async { bool status = false; try { - Utils.showLoading(context); + // Utils.showLoading(context); status = await chatRepo.markMessageAsRead(messageIds: messageIds, chatTypeEnum: chatTypeEnum); - Utils.hideLoading(context); + // Utils.hideLoading(context); } catch (e) { logger.i(e.toString()); Utils.showToast(e.toString()); - Utils.hideLoading(context); + // Utils.hideLoading(context); status = false; } return status; @@ -659,7 +672,8 @@ class ChatVM extends ChangeNotifier { notifyListeners(); - if (AppState().currentAppType == AppType.customer) {} else {} + if (AppState().currentAppType == AppType.customer) { + } else {} return true; } return false; diff --git a/lib/view_models/payment_view_model.dart b/lib/view_models/payment_view_model.dart index 89fba48..e60d2f3 100644 --- a/lib/view_models/payment_view_model.dart +++ b/lib/view_models/payment_view_model.dart @@ -78,7 +78,7 @@ class PaymentVM extends ChangeNotifier { Future onAdsPaymentSuccess({required BuildContext context, required int currentAdId, required int paymentTypeId}) async { Utils.showLoading(context); - PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentId: paymentTypeId, adId: currentAdId); + PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentTypeId: paymentTypeId, adId: currentAdId); await Future.delayed(const Duration(seconds: 2)); Utils.hideLoading(context); @@ -98,7 +98,7 @@ class PaymentVM extends ChangeNotifier { Future onAppointmentPaymentSuccess({required BuildContext context, required int currentAdId, required int paymentTypeId}) async { Utils.showLoading(context); //TODO: CONFIRM FROM ZAHOOR THAT WILL THIS METHOD WORK FOR APPOINTMENT - PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentId: paymentTypeId, adId: currentAdId); + PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentTypeId: paymentTypeId, adId: currentAdId); await Future.delayed(const Duration(seconds: 2)); Utils.hideLoading(context); diff --git a/lib/views/advertisement/ads_buyer_chats_view.dart b/lib/views/advertisement/ads_buyer_chats_view.dart index 520896c..3645d77 100644 --- a/lib/views/advertisement/ads_buyer_chats_view.dart +++ b/lib/views/advertisement/ads_buyer_chats_view.dart @@ -57,7 +57,7 @@ class AdsBuyerChatsView extends StatelessWidget { (chatForAdsModel.buyerName ?? "").toText(fontSize: 16, isBold: true), if (chatForAdsModel.unReadMessagesCount != null && chatForAdsModel.unReadMessagesCount! > 0) ...[ Center( - child: "${chatForAdsModel.unReadMessagesCount ?? "1"}".toText( + child: "${chatForAdsModel.unReadMessagesCount}".toText( color: Colors.white, isBold: true, fontSize: 10, diff --git a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart index 28516fd..e9a0e90 100644 --- a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart @@ -7,6 +7,7 @@ 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/date_helper.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'; @@ -271,6 +272,36 @@ class _AdsDetailViewState extends State { }).toWhiteContainer(width: double.infinity, allPading: 12); } + Widget buildAdStartEndDates() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + ("${LocaleKeys.startDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), + widget.adDetails.startdate != null + ? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.startdate!)).toText( + fontSize: 10, + ) + : const SizedBox(), + ], + ), + Row( + children: [ + ("${LocaleKeys.endDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), + widget.adDetails.enddate != null + ? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.enddate!)).toText( + fontSize: 10, + ) + : const SizedBox(), + ], + ), + ], + ).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) { Widget actionWidget = const SizedBox(); @@ -318,6 +349,10 @@ class _AdsDetailViewState extends State { if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) ...[ buildDamagePartDetails(), ], + if (widget.adDetails.isMyAd ?? false) ...[ + 12.height, + buildAdStartEndDates(), + ], 12.height, if (widget.adDetails.adPostStatus == AdPostStatus.rejected) ...[ buildAdRejectionDetails(), diff --git a/lib/views/advertisement/components/picked_images_container_widget.dart b/lib/views/advertisement/components/picked_images_container_widget.dart index 6089489..6809fa6 100644 --- a/lib/views/advertisement/components/picked_images_container_widget.dart +++ b/lib/views/advertisement/components/picked_images_container_widget.dart @@ -107,18 +107,20 @@ class BuildFilesContainer extends StatelessWidget { child: const Icon(Icons.picture_as_pdf).paddingAll(8), ) : image.isFromNetwork! - ? Image.network( - image.filePath!, - fit: BoxFit.fill, - height: 75, - width: 73, - ).paddingAll(8) - : Image.file( - File(image.filePath!), - fit: BoxFit.fill, - height: 72, - width: 70, - ).paddingAll(8), + ? image.filePath! + .buildNetworkImage( + fit: BoxFit.fill, + height: 75, + width: 73, + ) + .paddingAll(8) + : image.filePath! + .buildFileImage( + fit: BoxFit.fill, + height: 72, + width: 70, + ) + .paddingAll(8), !isReview ? Align( alignment: Alignment.topRight, diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index f95b1aa..3284ca0 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -110,14 +110,18 @@ class _ChatViewState extends State { children: [ Expanded( child: chatMessages.isEmpty - ? Center(child: LocaleKeys.noRequestsShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor)) + ? Center(child: LocaleKeys.noChatMessage.tr().toText(fontSize: 16, color: MyColors.lightTextColor)) : ListView.separated( controller: chatVM.scrollController, itemCount: chatMessages.length, separatorBuilder: (BuildContext context, int index) => 20.height, itemBuilder: (BuildContext context, int index) { ChatMessageModel chatMessageModel = chatMessages[index]; - return ChatMessageCustomWidget(chatMessageModel: chatMessageModel, requestStatusEnum: requestVM.currentSelectedRequest!.requestStatus); + return ChatMessageCustomWidget( + chatMessageModel: chatMessageModel, + requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus, + chatTypeEnum: chatTypeEnum, + ); }, ).horPaddingMain(), ), @@ -190,7 +194,7 @@ class _ChatViewState extends State { onAddFilePressed: () => chatVM.pickMultipleImages(), ), ), - ] else ...[ + ] else if (chatTypeEnum == ChatTypeEnum.requestOffer) ...[ Expanded( flex: 1, child: const Icon( @@ -199,21 +203,22 @@ class _ChatViewState extends State { size: 30, ).onPress(() => chatVM.pickMultipleImages()), ), - Expanded( - flex: 8, - child: TxtField( - value: chatVM.chatMessageText, - hint: LocaleKeys.typeMessageHere.tr(), - keyboardType: TextInputType.text, - isNeedBorder: false, - onChanged: (v) => chatVM.updateChatMessageText(v), - ), - ), ], + Expanded( + flex: 8, + child: TxtField( + value: chatVM.chatMessageText, + hint: LocaleKeys.typeMessageHere.tr(), + keyboardType: TextInputType.text, + isNeedBorder: false, + onChanged: (v) => chatVM.updateChatMessageText(v), + ), + ), Expanded( flex: 1, child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress( () async { + debugPrint("AppState().getUser.data.userInfo.userId: ${AppState().getUser.data!.userInfo!.userId}"); ChatMessageTypeEnum chatMessageTypeEnum = ChatMessageTypeEnum.freeText; if (chatVM.pickedImagesForMessage.isNotEmpty) { diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 39ac551..cdadefb 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -1,9 +1,8 @@ -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/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'; @@ -11,6 +10,7 @@ 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/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; @@ -24,9 +24,10 @@ import 'package:easy_localization/easy_localization.dart' as lcl; class ChatMessageCustomWidget extends StatefulWidget { final ChatMessageModel chatMessageModel; - final RequestStatusEnum requestStatusEnum; + final RequestStatusEnum? requestStatusEnum; + final ChatTypeEnum chatTypeEnum; - const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum}); + const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum, required this.chatTypeEnum}); @override State createState() => _ChatMessageCustomWidgetState(); @@ -424,12 +425,13 @@ class _ChatMessageCustomWidgetState extends State { ); } - Widget buildImageGridWidget( - {required List messagesImages, - double gridItemSize = 50.0, // Default fixed size for each grid item - double spacing = 8.0, - double borderWidth = 1, - Color borderColor = MyColors.textColor}) { + Widget buildImageGridWidget({ + required List messagesImages, + double gridItemSize = 50.0, + double spacing = 8.0, + double borderWidth = 1, + Color borderColor = MyColors.textColor, + }) { int imageCount = messagesImages.length; if (imageCount == 1) { return Container( @@ -447,7 +449,9 @@ class _ChatMessageCustomWidgetState extends State { ) : messagesImages[0].imagePath.buildFileImage(fit: BoxFit.cover), ), - ); + ).onPress(() { + navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages); + }); } return SizedBox( // height: (gridItemSize * 2) + (spacing * 2), // Fixed height for 2 rows including spacing @@ -468,7 +472,7 @@ class _ChatMessageCustomWidgetState extends State { return Stack( fit: StackFit.expand, children: [ - (messagesImages[0].isFromNetwork ?? false) + (messagesImages[index].isFromNetwork ?? false) ? messagesImages[index].imageUrl.buildNetworkImage( fit: BoxFit.cover, width: gridItemSize, @@ -490,7 +494,9 @@ class _ChatMessageCustomWidgetState extends State { ), ), ], - ); + ).onPress(() { + navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages); + }); } else { return Container( width: gridItemSize, @@ -507,7 +513,9 @@ class _ChatMessageCustomWidgetState extends State { height: gridItemSize, ), ), - ); + ).onPress(() { + navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages); + }); } }, ), @@ -534,7 +542,7 @@ class _ChatMessageCustomWidgetState extends State { children: [ buildFreeTextDetailsInMessage(), 10.height, - buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum, chatMessageModel: widget.chatMessageModel, context: context), + buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum!, chatMessageModel: widget.chatMessageModel, context: context), ], ); break; diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart index 031de54..ea1dac7 100644 --- a/lib/views/requests/offer_list_page.dart +++ b/lib/views/requests/offer_list_page.dart @@ -10,6 +10,7 @@ import 'package:mc_common_app/models/requests_models/provider_offers_model.dart' import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; @@ -34,7 +35,9 @@ class OfferListPage extends StatelessWidget { itemBuilder: (context, index) { ServiceProvidersOffers offersModel = serviceProviderOffers[index]; return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ + Utils.statusContainerChip(text: Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!), chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, @@ -91,7 +94,7 @@ class OfferListPage extends StatelessWidget { final chatVM = context.read(); await chatVM - .getUsersChatMessagesForCustomer( + .getRequestsChatMessagesForCustomer( context: context, providerId: offersModel.providerId ?? 0, requestOfferId: 0, diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index d85b491..5b3e1c0 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -115,7 +115,7 @@ class RequestDetailPage extends StatelessWidget { ); final chatVM = context.read(); await chatVM - .getUsersChatMessagesForProvider( + .getRequestsChatMessagesForProvider( customerId: requestDetailPageArguments.requestModel.customerId, context: context, requestOfferId: 0, diff --git a/lib/widgets/extensions/extensions_widget.dart b/lib/widgets/extensions/extensions_widget.dart index b8c95a0..a29c170 100644 --- a/lib/widgets/extensions/extensions_widget.dart +++ b/lib/widgets/extensions/extensions_widget.dart @@ -369,7 +369,7 @@ extension BuildSVG on String? { return SizedBox( height: height, width: width, - child: SvgPicture.asset(MyAssets.providersIcon, color: MyColors.darkPrimaryColor).paddingAll(15), + child: Image.asset(MyAssets.brokenImage).paddingAll(10), ); } diff --git a/lib/widgets/image_viewer/image_viewer_screen.dart b/lib/widgets/image_viewer/image_viewer_screen.dart new file mode 100644 index 0000000..e4c84d7 --- /dev/null +++ b/lib/widgets/image_viewer/image_viewer_screen.dart @@ -0,0 +1,114 @@ +import 'dart:developer'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/int_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/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; + +class MediaViewerScreen extends StatefulWidget { + final List images; // List of image URLs or file paths + + const MediaViewerScreen({Key? key, required this.images}) : super(key: key); + + @override + MediaViewerScreenState createState() => MediaViewerScreenState(); +} + +class MediaViewerScreenState extends State { + int selectedIndex = 0; // Track selected image index + + @override + Widget build(BuildContext context) { + debugPrint("loaded"); + return Scaffold( + appBar: CustomAppBar(title: LocaleKeys.pictures.tr()), + body: Column( + children: [ + // Main Image Display (selected image) + Expanded( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: InteractiveViewer( + + panEnabled: true, + // Allow panning + scaleEnabled: true, + // Allow zooming + minScale: 1.0, + // Minimum zoom level + maxScale: 4.0, + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: (widget.images[selectedIndex].isFromNetwork ?? false) + ? widget.images[selectedIndex].imageUrl.buildNetworkImage( + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ) + : widget.images[selectedIndex].imagePath.buildFileImage( + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + ), + ), + ), + ), + ), + // Thumbnail List at the bottom + Container( + height: 100, // Set height for thumbnail list + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: widget.images.length, + itemBuilder: (context, index) { + return GestureDetector( + onTap: () { + setState(() { + selectedIndex = index; // Change the selected image + }); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Container( + width: 80, // Thumbnail size + height: 80, + decoration: BoxDecoration( + border: Border.all( + color: selectedIndex == index ? MyColors.darkPrimaryColor : MyColors.greyShadowColor, // Highlight selected image + width: selectedIndex == index ? 2 : 1, + ), + borderRadius: BorderRadius.circular(10), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: (widget.images[index].isFromNetwork ?? false) + ? widget.images[index].imageUrl.buildNetworkImage( + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ) + : widget.images[index].imagePath.buildFileImage( + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + ), + ), + ), + ); + }, + ), + ), + 10.height, + ], + ), + ); + } +} From e2f10aba52a0d9aa9d6252602a3afe904380e16b Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Wed, 16 Oct 2024 15:10:39 +0300 Subject: [PATCH 2/8] FixedThings Before QA --- assets/langs/ar-SA.json | 14 +- assets/langs/en-US.json | 15 +- lib/classes/app_state.dart | 2 +- lib/classes/consts.dart | 1 + lib/config/routes.dart | 2 +- lib/extensions/string_extensions.dart | 24 ++ lib/generated/codegen_loader.g.dart | 26 +- lib/generated/locale_keys.g.dart | 11 + .../profile/document.dart | 37 ++- lib/repositories/appointment_repo.dart | 12 +- lib/repositories/items_repo.dart | 3 - lib/repositories/payments_repo.dart | 52 +++ lib/utils/utils.dart | 40 ++- lib/view_models/appointments_view_model.dart | 4 +- lib/view_models/base_view_model.dart | 3 +- lib/view_models/chat_view_model.dart | 14 +- .../dashboard_view_model_customer.dart | 5 - lib/view_models/payment_view_model.dart | 148 ++++----- lib/view_models/service_view_model.dart | 42 ++- lib/view_models/subscriptions_view_model.dart | 11 +- .../vehicle_details_container.dart | 12 +- .../ads_detail_view/ads_detail_view.dart | 2 +- .../ads_detail_view/components.dart | 4 +- .../components/ads_list_widget.dart | 6 +- .../advertisement/select_ad_type_view.dart | 11 +- .../common_appointment_slider_widget.dart | 1 + lib/views/chat/chat_view.dart | 22 +- .../chat/widgets/chat_bottom_sheets.dart | 1 + lib/views/profile/profile_view.dart | 44 ++- lib/views/requests/create_request_page.dart | 3 +- lib/views/requests/request_detail_page.dart | 26 +- .../provider_license_page.dart | 302 +++++++++--------- .../setting_options_language.dart | 22 +- .../widgets/custom_setting_options_tile.dart | 8 +- lib/views/user/change_email_page.dart | 10 +- lib/views/user/change_mobile_page.dart | 10 +- lib/views/user/change_password_page.dart | 8 +- lib/views/user/edit_account_page.dart | 1 - lib/views/user/login_with_password_page.dart | 11 +- lib/widgets/extensions/extensions_widget.dart | 1 - 40 files changed, 582 insertions(+), 389 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 0658fe9..e342417 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -656,6 +656,16 @@ "unselectAll": "إلغاء اختيار الكل", "copySelectedServices": "نسخ الخدمات المحددة", "pictures": "الصور", - "noChatMessage": "لا توجد رسائل بعد. أرسل رسالة لبدء المحادثة!" - + "noChatMessage": "لا توجد رسائل بعد. أرسل رسالة لبدء المحادثة!", + "shippingManagement": "إدارة الشحن", + "appointmentCancelled": "تم إلغاء الموعد", + "appointmentsPerSlot": "المواعيد لكل فترة زمنية", + "createSchedule": "إنشاء الجدول الزمني", + "addItems": "إضافة عناصر", + "itemName": "اسم العنصر", + "itemDescription": "وصف العنصر", + "itemPrice": "سعر العنصر", + "appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.", + "workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.", + "appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره." } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index cf67266..68a0110 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -657,7 +657,16 @@ "unselectAll": "Unselect All", "copySelectedServices": "Copy Selected Services", "pictures": "Pictures", - "noChatMessage": "No messages yet. Send a message to start the conversation!" - - + "noChatMessage": "No messages yet. Send a message to start the conversation!", + "shippingManagement": "Shipping Management", + "appointmentCancelled": "Appointment Cancelled", + "appointmentsPerSlot": "Appointments Per Slot", + "createSchedule": "Create Schedule", + "addItems": "Add Items", + "itemName": "Item Name", + "itemDescription": "Item Description", + "itemPrice": "Item Price", + "bookAppointmentForServices": "This option will allow customer to book appointment for these services.", + "showServiceAvailability": "This option will show to customer that you can avail this service on workshop or not.", + "bookAppointmentAtLocation": "This option will allow customer to book appointment at their desired location." } \ No newline at end of file diff --git a/lib/classes/app_state.dart b/lib/classes/app_state.dart index d8e4dce..873d0ae 100644 --- a/lib/classes/app_state.dart +++ b/lib/classes/app_state.dart @@ -65,7 +65,7 @@ class AppState { List? _providerSubscription; - List get getproviderSubscription => _providerSubscription!; + List get getproviderSubscription => _providerSubscription ?? [Subscription(name: "")]; set setproviderSubscription(List value) { _providerSubscription = value; diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 7d19e56..872f394 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -136,6 +136,7 @@ class ApiConsts { // Payment static String paymentWebViewUrl = "https://ms.hmg.com/pay/PaymentHome"; static String payForOrderDetailGet = "${baseUrlServices}api/Payment/PayFortOrderDetail_Get"; + static String payForOrderIsPaidGet = "${baseUrlServices}api/Payment/PayFortOrderIsPaid_Get"; //Duplicate Services static String getMatchedServices = "${baseUrlServices}api/ServiceProviders/ServiceProviderBranchServicesMapping_Get"; diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 49df5ae..333a2a4 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -142,10 +142,10 @@ class AppRoutes { completeProfile: (context) => CompleteProfilePage(ModalRoute.of(context)!.settings.arguments as RegisterUserRespModel), verifyPassword: (context) => VerifyPasswordPage(), confirmNewPasswordPage: (context) => ConfirmNewPasswordPage(ModalRoute.of(context)!.settings.arguments as String), - changePassword: (context) => const ChangePasswordPage(), forgetPasswordMethodPage: (context) => ForgetPasswordMethodPage(ModalRoute.of(context)!.settings.arguments as String), changeMobilePage: (context) => ChangeMobilePage(), changeEmailPage: (context) => const ChangeEmailPage(), + changePassword: (context) => const ChangePasswordPage(), editAccountPage: (context) => const EditAccountPage(), profileView: (context) => const ProfileScreen(), settingOptionsLanguages: (context) => const SettingOptionsLanguage(), diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index e71607c..8055de3 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -474,6 +474,30 @@ extension PaymentTypesToInt on PaymentTypes { } } +extension PaymentIdToPaymentTypes on int { + PaymentTypes getPaymentTypeFromId() { + switch (this) { + case 1: + return PaymentTypes.subscription; + case 2: + return PaymentTypes.appointment; + case 4: + return PaymentTypes.adReserve; + case 3: + return PaymentTypes.ads; + case 5: + return PaymentTypes.request; + case 6: + return PaymentTypes.extendAds; + case 7: + return PaymentTypes.partialAppointment; + + default: + return PaymentTypes.ads; + } + } +} + extension CreatedByRoleEnumToInt on CreatedByRoleEnum { int getIdFromCreatedByRoleEnum() { switch (this) { diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 1f339eb..aa3b212 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -672,7 +672,18 @@ class CodegenLoader extends AssetLoader{ "unselectAll": "إلغاء اختيار الكل", "copySelectedServices": "نسخ الخدمات المحددة", "pictures": "الصور", - "noChatMessage": "لا توجد رسائل بعد. أرسل رسالة لبدء المحادثة!" + "noChatMessage": "لا توجد رسائل بعد. أرسل رسالة لبدء المحادثة!", + "shippingManagement": "إدارة الشحن", + "appointmentCancelled": "تم إلغاء الموعد", + "appointmentsPerSlot": "المواعيد لكل فترة زمنية", + "createSchedule": "إنشاء الجدول الزمني", + "addItems": "إضافة عناصر", + "itemName": "اسم العنصر", + "itemDescription": "وصف العنصر", + "itemPrice": "سعر العنصر", + "appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.", + "workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.", + "appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره." }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1333,7 +1344,18 @@ static const Map en_US = { "unselectAll": "Unselect All", "copySelectedServices": "Copy Selected Services", "pictures": "Pictures", - "noChatMessage": "No messages yet. Send a message to start the conversation!" + "noChatMessage": "No messages yet. Send a message to start the conversation!", + "shippingManagement": "Shipping Management", + "appointmentCancelled": "Appointment Cancelled", + "appointmentsPerSlot": "Appointments Per Slot", + "createSchedule": "Create Schedule", + "addItems": "Add Items", + "itemName": "Item Name", + "itemDescription": "Item Description", + "itemPrice": "Item Price", + "bookAppointmentForServices": "This option will allow customer to book appointment for these services.", + "showServiceAvailability": "This option will show to customer that you can avail this service on workshop or not.", + "bookAppointmentAtLocation": "This option will allow customer to book appointment at their desired location." }; static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index e430e49..eea31b9 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -636,5 +636,16 @@ abstract class LocaleKeys { static const copySelectedServices = 'copySelectedServices'; static const pictures = 'pictures'; static const noChatMessage = 'noChatMessage'; + static const shippingManagement = 'shippingManagement'; + static const appointmentCancelled = 'appointmentCancelled'; + static const appointmentsPerSlot = 'appointmentsPerSlot'; + static const createSchedule = 'createSchedule'; + static const addItems = 'addItems'; + static const itemName = 'itemName'; + static const itemDescription = 'itemDescription'; + static const itemPrice = 'itemPrice'; + static const appointmentBookingOption = 'appointmentBookingOption'; + static const workshopAvailabilityOption = 'workshopAvailabilityOption'; + static const appointmentLocationOption = 'appointmentLocationOption'; } diff --git a/lib/models/provider_branches_models/profile/document.dart b/lib/models/provider_branches_models/profile/document.dart index 8f2f87c..497e93b 100644 --- a/lib/models/provider_branches_models/profile/document.dart +++ b/lib/models/provider_branches_models/profile/document.dart @@ -22,17 +22,17 @@ class Document { String? message; factory Document.fromJson(Map json) => Document( - totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"], + totalItemsCount: json["totalItemsCount"], data: json["data"] == null ? null : List.from(json["data"].map((x) => DocumentData.fromJson(x))), - messageStatus: json["messageStatus"] == null ? null : json["messageStatus"], - message: json["message"] == null ? null : json["message"], + messageStatus: json["messageStatus"], + message: json["message"], ); Map toJson() => { - "totalItemsCount": totalItemsCount == null ? null : totalItemsCount, + "totalItemsCount": totalItemsCount, "data": data == null ? null : List.from(data!.map((x) => x.toJson())), - "messageStatus": messageStatus == null ? null : messageStatus, - "message": message == null ? null : message, + "messageStatus": messageStatus, + "message": message, }; } @@ -43,6 +43,7 @@ class DocumentData { this.documentId, this.documentUrl, this.status, + this.statusText, this.comment, this.isActive, this.document, @@ -60,29 +61,31 @@ class DocumentData { bool? isActive; String? document; String? fileExt; + String? statusText; String? documentName; bool? isLocalFile; factory DocumentData.fromJson(Map json) => DocumentData( - id: json["id"] == null ? null : json["id"], - serviceProviderId: json["serviceProviderID"] == null ? null : json["serviceProviderID"], - documentId: json["documentID"] == null ? null : json["documentID"], + id: json["id"], + serviceProviderId: json["serviceProviderID"], + documentId: json["documentID"], documentUrl: json["documentURL"], - status: json["status"] == null ? null : json["status"], + status: json["status"], + statusText: json["statusText"], comment: json["comment"], - isActive: json["isActive"] == null ? null : json["isActive"], + isActive: json["isActive"], document: null, fileExt: null, - documentName: json["documentName"] == null ? null : json["documentName"], + documentName: json["documentName"], isLocalFile: false); Map toJson() => { - "id": id == null ? null : id, - "serviceProviderID": serviceProviderId == null ? null : serviceProviderId, - "documentID": documentId == null ? null : documentId, + "id": id, + "serviceProviderID": serviceProviderId, + "documentID": documentId, "documentURL": documentUrl, - "status": status == null ? null : status, + "status": status, "comment": comment, - "isActive": isActive == null ? null : isActive, + "isActive": isActive, }; } diff --git a/lib/repositories/appointment_repo.dart b/lib/repositories/appointment_repo.dart index 09b8293..bfb47e3 100644 --- a/lib/repositories/appointment_repo.dart +++ b/lib/repositories/appointment_repo.dart @@ -4,11 +4,13 @@ import 'package:mc_common_app/api/api_client.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/dependency_injection.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; import 'package:mc_common_app/models/appointments_models/schedule_model.dart'; import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/provider_branches_models/profile/services.dart'; +import 'package:mc_common_app/utils/enums.dart'; abstract class AppointmentRepo { Future> getMyAppointmentsForProvider(Map map); @@ -20,7 +22,7 @@ abstract class AppointmentRepo { List? branchIdsList, }); - Future updateAppointmentStatus(Map map); + Future updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum}); Future updateAppointmentPaymentStatus(Map map); @@ -240,9 +242,13 @@ class AppointmentRepoImp implements AppointmentRepo { } @override - Future updateAppointmentStatus(Map map) async { + Future updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum}) async { String t = appState.getUser.data!.accessToken ?? ""; - return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.updateAppointmentStatus, map, token: t); + var params = { + "appointmentID": appointmentId.toString(), + "appointmentStatusID": appointmentStatusEnum.getIdFromAppointmentStatusEnum().toString(), + }; + return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.updateAppointmentStatus, params, token: t); } @override diff --git a/lib/repositories/items_repo.dart b/lib/repositories/items_repo.dart index 3387ff2..3a90629 100644 --- a/lib/repositories/items_repo.dart +++ b/lib/repositories/items_repo.dart @@ -20,7 +20,6 @@ class ItemsRepoImp implements ItemsRepo { @override Future createServiceItems(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; - debugPrint(t); return await injector.get().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.createItems, map, token: t); } @@ -30,7 +29,6 @@ class ItemsRepoImp implements ItemsRepo { "ServiceProviderServiceID": serviceId.toString(), }; String? token = AppState().getUser.data?.accessToken; - debugPrint(token); return await injector .get() .getJsonForObject((json) => ItemModel.fromJson(json), ApiConsts.getServiceItems, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? ""); @@ -39,7 +37,6 @@ class ItemsRepoImp implements ItemsRepo { @override Future updateServiceItem(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; - debugPrint(t); return await injector.get().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.updateServiceItem, map, token: t); } diff --git a/lib/repositories/payments_repo.dart b/lib/repositories/payments_repo.dart index 579f999..020eeae 100644 --- a/lib/repositories/payments_repo.dart +++ b/lib/repositories/payments_repo.dart @@ -2,10 +2,14 @@ import 'package:mc_common_app/api/api_client.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/dependency_injection.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/payment_models/pay_order_detail_resp_model.dart'; +import 'package:mc_common_app/utils/enums.dart'; abstract class PaymentsRepo { + Future verifyPayment({required int paymentTypeId, required int id, bool isForAppointments = false, List? appointmentIds}); + Future getPayOrderDetails({required int paymentTypeId, required int adId}); } @@ -29,4 +33,52 @@ class PaymentsRepoImp implements PaymentsRepo { PayOrderDetailRespModel payOrderDetailRespModel = PayOrderDetailRespModel.fromJson(adsGenericModel.data); return payOrderDetailRespModel; } + + String getKeyByPaymentType({required int paymentType}) { + PaymentTypes paymentTypeEnum = paymentType.getPaymentTypeFromId(); + switch (paymentTypeEnum) { + case PaymentTypes.subscription: + return "SubscriptionAppliedID"; + case PaymentTypes.appointment: + case PaymentTypes.partialAppointment: + return "appointmentIds"; + case PaymentTypes.ads: + case PaymentTypes.adReserve: + case PaymentTypes.extendAds: + return "AdsID"; + case PaymentTypes.request: + return "RequestID"; + } + } + + @override + Future verifyPayment({required int paymentTypeId, required int id, bool isForAppointments = false, List? appointmentIds}) async { + Map queryParameters = { + "ID": id.toString(), + "PaymentType": paymentTypeId.toString(), + }; + + if (isForAppointments) { + List ids = []; + if (appointmentIds != null) { + for (var element in appointmentIds) { + ids.add(element.toString()); + } + } + queryParameters.removeWhere((key, value) => key == "ID"); + queryParameters.addAll({"IDs": ids}); + } + + GenericRespModel adsGenericModel = await apiClient.getJsonForObject( + token: appState.getUser.data!.accessToken, + queryParameters: queryParameters, + (json) => GenericRespModel.fromJson(json), + ApiConsts.payForOrderIsPaidGet, + ); + if (adsGenericModel.data == null) { + return false; + } + bool isPaid = adsGenericModel.data['isPaid'] ?? false; + return isPaid; + } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 6470fd9..4bc0b09 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -8,6 +8,7 @@ import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/utils/date_helper.dart'; import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:path/path.dart' as p; import 'package:fluttertoast/fluttertoast.dart'; @@ -26,7 +27,13 @@ class Utils { static bool get isLoading => _isLoadingVisible; static void showToast(String message) { - Fluttertoast.showToast(msg: message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.black54, textColor: Colors.white, fontSize: 16.0); + Fluttertoast.showToast(msg: message, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + timeInSecForIosWeb: 2, + backgroundColor: Colors.black54, + textColor: Colors.white, + fontSize: 16.0); } static Future openNumberViaCaller({required String phoneNumber}) async { @@ -69,7 +76,11 @@ class Utils { return ""; } - return ("${timeOfDay.hour.toString().length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute.toString().length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); + return ("${timeOfDay.hour + .toString() + .length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute + .toString() + .length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); } static dynamic getNotNullValue(List list, int index) { @@ -196,7 +207,7 @@ class Utils { return MyColors.adPendingStatusColor; case AdPostStatus.pendingForPayment: - return MyColors.adPendingStatusColor; + return MyColors.submittedColor; case AdPostStatus.rejected: return MyColors.adCancelledStatusColor; @@ -205,7 +216,7 @@ class Utils { return MyColors.adCancelledStatusColor; case AdPostStatus.pendingForPost: - return MyColors.adPendingStatusColor; + return MyColors.inProgressColor; case AdPostStatus.active: return MyColors.adActiveStatusColor; @@ -472,7 +483,7 @@ class Utils { border: Border.all( width: w, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -495,7 +506,7 @@ class Utils { border: Border.all( width: 1, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -513,7 +524,8 @@ class Utils { } static String getAdsPaymentBrowserForm({required int paymentId, required int adId}) { - return '
'; + return '
'; } // BOTTOM SHEETS @@ -534,4 +546,18 @@ class Utils { actionButtonNo: const SizedBox(), ); } + + static Widget buildStatusContainer(String text) { + return Center( + child: text.toText(color: MyColors.lightTextColor, fontSize: 14 + // isItalic: true, + ), + ).toContainer( + marginAll: 8, + paddingAll: 15, + borderRadius: 8, + width: double.infinity, + backgroundColor: MyColors.grey98Color.withOpacity(0.1), + ); + } } diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index c38d0cd..d9e004d 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -382,10 +382,10 @@ class AppointmentsVM extends BaseVM { setState(ViewState.idle); } - updateAppointmentStatus(Map map, {bool isNeedToRebuild = false}) async { + updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum, bool isNeedToRebuild = false}) async { if (isNeedToRebuild) setState(ViewState.busy); try { - GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentStatus(map); + GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentStatus(appointmentId: appointmentId, appointmentStatusEnum: appointmentStatusEnum); if (genericRespModel.messageStatus == 1) { Utils.showToast(LocaleKeys.appointmentStatusUpdated.tr()); diff --git a/lib/view_models/base_view_model.dart b/lib/view_models/base_view_model.dart index 12833c5..8386b7a 100644 --- a/lib/view_models/base_view_model.dart +++ b/lib/view_models/base_view_model.dart @@ -1,7 +1,8 @@ import 'package:flutter/cupertino.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/utils/enums.dart'; - class BaseVM extends ChangeNotifier { ViewState _state = ViewState.idle; bool isInternetConnection = true; diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 5de3d93..d360063 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -303,7 +303,11 @@ class ChatVM extends ChangeNotifier { MessageImageModel convertFileToMessageImageModel({required File file}) { List imageBytes = file.readAsBytesSync(); String image = base64Encode(imageBytes); - MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image, id: 0, isFromNetwork: false, reqOfferID: latestOfferId, imagePath: file.path); + MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image, + id: 0, + isFromNetwork: false, + reqOfferID: latestOfferId, + imagePath: file.path); return vehiclePostingImages; } @@ -409,7 +413,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); @@ -672,8 +679,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/dashboard_view_model_customer.dart b/lib/view_models/dashboard_view_model_customer.dart index 819b425..b17f536 100644 --- a/lib/view_models/dashboard_view_model_customer.dart +++ b/lib/view_models/dashboard_view_model_customer.dart @@ -1,15 +1,10 @@ -import 'dart:developer'; import 'dart:io'; import 'package:mc_common_app/classes/app_state.dart'; -import 'package:mc_common_app/config/dependency_injection.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/main.dart'; -import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/models/user_models/image_response.dart'; import 'package:mc_common_app/repositories/user_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; -import 'package:mc_common_app/services/firebase_service.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/appointments_view_model.dart'; diff --git a/lib/view_models/payment_view_model.dart b/lib/view_models/payment_view_model.dart index e60d2f3..96a9af7 100644 --- a/lib/view_models/payment_view_model.dart +++ b/lib/view_models/payment_view_model.dart @@ -1,20 +1,20 @@ import 'dart:developer'; import 'package:flutter/cupertino.dart'; +import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; -import 'package:mc_common_app/models/payment_models/pay_order_detail_resp_model.dart'; import 'package:mc_common_app/repositories/payments_repo.dart'; import 'package:mc_common_app/services/payments_service.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/subscriptions_view_model.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; -import '../classes/app_state.dart'; class PaymentVM extends ChangeNotifier { final PaymentService paymentService; @@ -47,6 +47,7 @@ class PaymentVM extends ChangeNotifier { void updateAppointmentIdsForPayment({required List ids}) { appointmentIdsForPayment = ids; + log("appointmentIdsForPayment: ${appointmentIdsForPayment}"); } updateSelectedPaymentMethod(PaymentMethods selectedMethod) { @@ -57,62 +58,43 @@ class PaymentVM extends ChangeNotifier { Future onContinuePressed(BuildContext context, {required PaymentTypes paymentType}) async { switch (selectedPaymentMethod) { case PaymentMethods.mada: - // TODO: Handle this case. break; case PaymentMethods.visa: await onVisaCardSelected(context, paymentType); break; case PaymentMethods.applePay: - // TODO: Handle this case. break; case PaymentMethods.masterCard: - // TODO: Handle this case. break; case PaymentMethods.tamara: - // TODO: Handle this case. break; } return; } - Future onAdsPaymentSuccess({required BuildContext context, required int currentAdId, required int paymentTypeId}) async { - Utils.showLoading(context); - PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentTypeId: paymentTypeId, adId: currentAdId); - await Future.delayed(const Duration(seconds: 2)); - Utils.hideLoading(context); - - log("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}"); - - if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) { - Utils.showToast(LocaleKeys.paymentFailed.tr()); - return; - } - - if (payOrderDetailRespModel.isPaid != null && payOrderDetailRespModel.isPaid!) { + Future verifyPayments({required BuildContext context, required int id, List? appointmentIds, required int paymentTypeId, required Function() onVerified, bool isForAppointment = false}) async { + try { + Utils.showLoading(context); + bool isPaid = await paymentRepo.verifyPayment( + paymentTypeId: paymentTypeId, + id: id, + isForAppointments: isForAppointment, + appointmentIds: appointmentIds, + ); + Utils.hideLoading(context); + + if (!isPaid) { + Utils.showToast(LocaleKeys.paymentFailed.tr()); + pop(context); + return; + } Utils.showToast(LocaleKeys.paymentSuccessful.tr()); - navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); - } - } - - Future onAppointmentPaymentSuccess({required BuildContext context, required int currentAdId, required int paymentTypeId}) async { - Utils.showLoading(context); - //TODO: CONFIRM FROM ZAHOOR THAT WILL THIS METHOD WORK FOR APPOINTMENT - PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentTypeId: paymentTypeId, adId: currentAdId); - await Future.delayed(const Duration(seconds: 2)); - Utils.hideLoading(context); - - log("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}"); - - if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) { - Utils.showToast(LocaleKeys.paymentFailed.tr()); + onVerified(); + } catch (e) { + Utils.showToast(e.toString()); return; } - - if (payOrderDetailRespModel.isPaid != null && payOrderDetailRespModel.isPaid!) { - Utils.showToast(LocaleKeys.paymentSuccessful.tr()); - navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); - } } int getIdTypeByPaymentType(PaymentTypes paymentTypes) { @@ -141,66 +123,58 @@ class PaymentVM extends ChangeNotifier { Utils.showToast(LocaleKeys.paymentFailed.tr()); switch (paymentTypeEnum) { case PaymentTypes.subscription: - // TODO: Handle this case. - Utils.showToast("Appointment Payment has been Failed!!"); - break; - - case PaymentTypes.request: - // TODO: Handle this case. - Utils.showToast("Request Payment has been Failed!!"); - break; - + case PaymentTypes.appointment: case PaymentTypes.adReserve: - // TODO: Handle this case. - Utils.showToast("adReserve Payment has been Failed!!"); - break; - case PaymentTypes.ads: - // TODO: Handle this case. - Utils.showToast("ads Payment has been Failed!!"); - break; - + case PaymentTypes.request: case PaymentTypes.extendAds: - // TODO: Handle this case. - Utils.showToast("extendAds Payment has been Failed!!"); - - break; - - case PaymentTypes.appointment: - // TODO: Handle this case. - Utils.showToast("Appointment Payment has been Failed!!"); - break; - case PaymentTypes.partialAppointment: - Utils.showToast("partialAppointment Payment has been Failed!!"); break; } }, onSuccess: () async { - // TOD0: we have to take payment confirmation methods from Backend team and make success callbacks like onAdsPaymentSuccess + Utils.showToast("Verifying Payment.."); + switch (paymentTypeEnum) { case PaymentTypes.subscription: - onSubscriptionPaymentSuccess(context); + await verifyPayments( + context: context, + paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), + id: orderProviderSubscriptionId, + onVerified: () => onSubscriptionPaymentSuccess(context), + ); break; case PaymentTypes.appointment: - Utils.showToast("Appointment Payment has been Succeeded"); - break; - case PaymentTypes.partialAppointment: - Utils.showToast("Partial Appointment Payment has been Succeeded"); + await verifyPayments( + id: 0, + isForAppointment: true, + appointmentIds: appointmentIdsForPayment, + context: context, + paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), + onVerified: () => onAppointmentSuccess(context), + ); break; case PaymentTypes.request: - Utils.showToast("Request Payment has been Succeeded"); - onRequestPaymentSuccess(context); + await verifyPayments( + context: context, + paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), + id: requestId, + onVerified: () => onRequestPaymentSuccess(context), + ); break; case PaymentTypes.adReserve: case PaymentTypes.ads: case PaymentTypes.extendAds: - await onAdsPaymentSuccess(context: context, paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), currentAdId: currentAdId); - break; + await verifyPayments( + context: context, + paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), + id: currentAdId, + onVerified: () => onAdsPaymentSuccess(context), + ); } }, ); @@ -208,16 +182,22 @@ class PaymentVM extends ChangeNotifier { void onRequestPaymentSuccess(BuildContext context) { context.read().onNavbarTapped(4); - navigateReplaceWithName(context, AppRoutes.dashboard); + navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); } - onSubscriptionPaymentSuccess(BuildContext context) { + void onAdsPaymentSuccess(BuildContext context) { + context.read().onNavbarTapped(3); + navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); + } + + void onSubscriptionPaymentSuccess(BuildContext context) { pop(context); + // context.read().getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true); + navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); + } - // context.read().getMySubscriptions(AppState().getUser.data?.userInfo?.providerId.toString() ?? ""); - context.read().getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true); - // context.read().mySubscriptionsBySp = []; - // context.read().getMySubscriptionsBySP(); + void onAppointmentSuccess(BuildContext context) { + context.read().onNavbarTapped(1); navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); } diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index a7e9715..ca488a1 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; @@ -218,8 +219,8 @@ class ServiceVM extends BaseVM { documentID == 1 ? commerceCertificates.addAll(imageModels) : documentID == 2 - ? commercialCertificates.addAll(imageModels) - : vatCertificates.addAll(imageModels); + ? commercialCertificates.addAll(imageModels) + : vatCertificates.addAll(imageModels); document!.data![index].document = Utils.convertFileToBase64(files.first); document!.data![index].fileExt = Utils.checkFileExt(files.first.path); document!.data![index].documentUrl = files.first.path; @@ -228,6 +229,7 @@ class ServiceVM extends BaseVM { } Future commerceRemove(String filePath) async { + log("commerceRemove with: $filePath"); int index = commerceCertificates.indexWhere((element) => element.filePath == filePath); if (index == -1) { return; @@ -237,6 +239,8 @@ class ServiceVM extends BaseVM { } Future removeNetworkImag(String filePath) async { + log("removeNetworkImag with: $filePath"); + int index = document!.data!.indexWhere((element) => element.documentUrl == filePath); if (index == -1) { return; @@ -247,6 +251,8 @@ class ServiceVM extends BaseVM { } Future commercialRemove(String filePath) async { + log("commercialRemove with: $filePath"); + int index = commercialCertificates.indexWhere((element) => element.filePath == filePath); if (index == -1) { return; @@ -256,6 +262,8 @@ class ServiceVM extends BaseVM { } Future vatRemove(String filePath) async { + log("vatRemove with: $filePath"); + int index = vatCertificates.indexWhere((element) => element.filePath == filePath); if (index == -1) { return; @@ -272,7 +280,11 @@ class ServiceVM extends BaseVM { Future getBranchAndServices() async { setState(ViewState.busy); branches = await branchRepo.getBranchAndServices(); - homePageBranches = branches!.data!.serviceProviderBranch!.where((element) => element.branchStatus == BranchStatusEnum.approvedOrActive).toList(); + if (branches!.data == null) { + homePageBranches = []; + } else { + homePageBranches = branches!.data!.serviceProviderBranch!.where((element) => element.branchStatus == BranchStatusEnum.approvedOrActive).toList(); + } setState(ViewState.idle); } @@ -389,10 +401,10 @@ class ServiceVM extends BaseVM { DropValue( element.id ?? 0, ((element.categoryName!.isEmpty - ? "N/A" - : countryCode == "SA" - ? element.categoryNameN - : element.categoryName) ?? + ? "N/A" + : countryCode == "SA" + ? element.categoryNameN + : element.categoryName) ?? "N/A"), "", ), @@ -574,9 +586,7 @@ class ServiceVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path - .split('/') - .last; + String fileName = file.path.split('/').last; branchPostingImages = BranchPostingImages( imageName: fileName, imageStr: image, @@ -621,8 +631,8 @@ class ServiceVM extends BaseVM { GenericRespModel res = await branchRepo.createBranch( branchName: branchName, branchDescription: branchDesc, - openTime: branchDesc, - closeTime: branchDesc, + openTime: openTime, + closeTime: closeTime, cityId: cityID, address: address, latitude: latitude, @@ -656,6 +666,14 @@ class ServiceVM extends BaseVM { required String latitude, required String longitude, }) async { + // log(branchName); + // log(branchDesc); + // log(openTime); + // log(closedTime); + // log(cityID.toString()); + // log(address); + // log(latitude); + // log(longitude); if (branchName.isEmpty || branchDesc.isEmpty || address.isEmpty) { branchErrorScreen = GlobalConsts.fillAllFields; notifyListeners(); diff --git a/lib/view_models/subscriptions_view_model.dart b/lib/view_models/subscriptions_view_model.dart index 2bae993..deefeef 100644 --- a/lib/view_models/subscriptions_view_model.dart +++ b/lib/view_models/subscriptions_view_model.dart @@ -1,10 +1,6 @@ -import 'dart:convert'; - import 'package:mc_common_app/classes/app_state.dart'; -import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/subscriptions_models/branch_user_selection_model.dart'; -import 'package:mc_common_app/models/subscriptions_models/provider_subscription_model.dart'; import 'package:mc_common_app/models/subscriptions_models/subscription_model.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; @@ -118,12 +114,7 @@ class SubscriptionsVM extends BaseVM { } Future createSubscriptionOrder(int subscriptionId, bool isStartNow, bool isRenew, {bool isDegrade = false, List? listOfBranches, List? listOfUsers}) async { - Map map = { - "providerID": AppState().getUser.data?.userInfo?.providerId.toString() ?? "", - "subscriptionID": subscriptionId.toString(), - "isStartNow": isStartNow.toString(), - "isRenew": isRenew.toString() - }; + Map map = {"providerID": AppState().getUser.data?.userInfo?.providerId.toString() ?? "", "subscriptionID": subscriptionId.toString(), "isStartNow": isStartNow.toString(), "isRenew": isRenew.toString()}; GenericRespModel genericRespModel = await subscriptionRepo.payForProviderSubscription(map); return genericRespModel; } diff --git a/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart b/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart index d82fa97..ce14254 100644 --- a/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart +++ b/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart @@ -264,11 +264,13 @@ class VehicleDetails extends StatelessWidget { 28.height, LocaleKeys.vehiclePictures.tr().toText(fontSize: 18, isBold: true), 8.height, - DottedRectContainer( - onTap: () => context.read().pickMultipleImages(), - text: LocaleKeys.attachImage.tr(), - icon: MyAssets.attachmentIcon.buildSvg(), - ), + if (adVM.pickedPostingImages.isEmpty) ...[ + DottedRectContainer( + onTap: () => context.read().pickMultipleImages(), + text: LocaleKeys.attachImage.tr(), + icon: MyAssets.attachmentIcon.buildSvg(), + ), + ], if (adVM.vehicleImageError != "") ...[ 10.height, Row( diff --git a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart index e9a0e90..2ea8546 100644 --- a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart @@ -305,7 +305,7 @@ class _AdsDetailViewState extends State { @override Widget build(BuildContext context) { Widget actionWidget = const SizedBox(); - if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active)) { + if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active) && (widget.adDetails.adPostStatus != AdPostStatus.pendingForPost)) { actionWidget = IconButton( icon: const Icon(Icons.delete_outline, color: MyColors.redColor), onPressed: () { diff --git a/lib/views/advertisement/ads_detail_view/components.dart b/lib/views/advertisement/ads_detail_view/components.dart index 22022cc..dc21cfe 100644 --- a/lib/views/advertisement/ads_detail_view/components.dart +++ b/lib/views/advertisement/ads_detail_view/components.dart @@ -496,11 +496,11 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { } Widget pendingForReviewAction({required String pendingText}) { - bool isForShippingOrDelivery = false; + bool isForShippingOrDelivery = true; return Center( child: pendingText.toText( color: isForShippingOrDelivery ? MyColors.lightTextColor : MyColors.adPendingStatusColor, - fontSize: isForShippingOrDelivery ? 14 : 16, + fontSize: isForShippingOrDelivery ? 15 : 16, // isItalic: true, ), ).toContainer( diff --git a/lib/views/advertisement/components/ads_list_widget.dart b/lib/views/advertisement/components/ads_list_widget.dart index 60740bb..386552e 100644 --- a/lib/views/advertisement/components/ads_list_widget.dart +++ b/lib/views/advertisement/components/ads_list_widget.dart @@ -137,9 +137,7 @@ class AdCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.start, children: [ - (adDetails.vehicle!.cityName ?? "").toText( - color: MyColors.lightTextColor, - ), + (adDetails.vehicle!.cityName ?? "").toText(color: MyColors.lightTextColor), adDetails.createdOn != null ? DateTime.parse(adDetails.createdOn!).getTimeAgo().toText( color: MyColors.lightTextColor, @@ -159,7 +157,7 @@ class AdCard extends StatelessWidget { children: [ (adDetails.vehicle!.demandAmount!.toInt().toString()).toText(fontSize: 19, isBold: true, letterSpacing: -1.16, height: 29 / 19), 2.width, - LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, letterSpacing: -0.4, height: 16 / 10), + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, letterSpacing: -0.4, height: 16 / 10).paddingOnly(bottom: 3), ], ), ), diff --git a/lib/views/advertisement/select_ad_type_view.dart b/lib/views/advertisement/select_ad_type_view.dart index 7188ba4..9936850 100644 --- a/lib/views/advertisement/select_ad_type_view.dart +++ b/lib/views/advertisement/select_ad_type_view.dart @@ -1,6 +1,7 @@ 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/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; @@ -46,6 +47,14 @@ class SelectAdTypeView extends StatelessWidget { if (arguments.length > 2) { adsId = arguments[2] ?? -1; } + + int totalAds = 0; + int remainingAds = 0; + + if (AppState().getproviderSubscription.isNotEmpty && AppState().getproviderSubscription.first.adsRemaining != null) { + totalAds = AppState().getproviderSubscription.first.totalAds ?? 0; + remainingAds = AppState().getproviderSubscription.first.adsRemaining ?? 0; + } return Scaffold( appBar: CustomAppBar( title: LocaleKeys.selectAdType.tr(), @@ -138,7 +147,7 @@ class SelectAdTypeView extends StatelessWidget { children: [ Row( children: [ - "5 of 10 ".toText(fontSize: 29, isBold: true, letterSpacing: 0, height: 1), + "$remainingAds of $totalAds ".toText(fontSize: 29, isBold: true, letterSpacing: 0, height: 1), LocaleKeys.adsRemainingVar.tr().toText(fontSize: 17, color: MyColors.lightTextColor, isBold: true), ], ), diff --git a/lib/views/appointments/widgets/common_appointment_slider_widget.dart b/lib/views/appointments/widgets/common_appointment_slider_widget.dart index 1fc4f9e..ce7cb80 100644 --- a/lib/views/appointments/widgets/common_appointment_slider_widget.dart +++ b/lib/views/appointments/widgets/common_appointment_slider_widget.dart @@ -14,6 +14,7 @@ import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/widgets/common_widgets/custom_add_button_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index 3284ca0..25288f5 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -110,7 +110,7 @@ class _ChatViewState extends State { children: [ Expanded( child: chatMessages.isEmpty - ? Center(child: LocaleKeys.noChatMessage.tr().toText(fontSize: 16, color: MyColors.lightTextColor)) + ? Center(child: LocaleKeys.noChatMessage.tr().toText(fontSize: 16, color: MyColors.lightTextColor, textAlign: TextAlign.center)).paddingAll(22) : ListView.separated( controller: chatVM.scrollController, itemCount: chatMessages.length, @@ -204,16 +204,18 @@ class _ChatViewState extends State { ).onPress(() => chatVM.pickMultipleImages()), ), ], - Expanded( - flex: 8, - child: TxtField( - value: chatVM.chatMessageText, - hint: LocaleKeys.typeMessageHere.tr(), - keyboardType: TextInputType.text, - isNeedBorder: false, - onChanged: (v) => chatVM.updateChatMessageText(v), + if (chatVM.pickedImagesForMessage.isEmpty) ...[ + Expanded( + flex: 8, + child: TxtField( + value: chatVM.chatMessageText, + hint: LocaleKeys.typeMessageHere.tr(), + keyboardType: TextInputType.text, + isNeedBorder: false, + onChanged: (v) => chatVM.updateChatMessageText(v), + ), ), - ), + ], Expanded( flex: 1, child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress( diff --git a/lib/views/chat/widgets/chat_bottom_sheets.dart b/lib/views/chat/widgets/chat_bottom_sheets.dart index 2ee31af..2c9bc16 100644 --- a/lib/views/chat/widgets/chat_bottom_sheets.dart +++ b/lib/views/chat/widgets/chat_bottom_sheets.dart @@ -9,6 +9,7 @@ import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; diff --git a/lib/views/profile/profile_view.dart b/lib/views/profile/profile_view.dart index 80ab4c2..be4495c 100644 --- a/lib/views/profile/profile_view.dart +++ b/lib/views/profile/profile_view.dart @@ -8,6 +8,7 @@ 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/navigator.dart'; import 'package:mc_common_app/view_models/user_view_model.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; @@ -79,7 +80,7 @@ class _ProfileScreenState extends State { color: Colors.white, child: ListView( children: [ - 20.height, + 60.height, "${AppState().getUser.data!.userInfo!.firstName} ${AppState().getUser.data!.userInfo!.lastName ?? ""}".toText(fontSize: 20).paddingOnly(left: 25), Column( children: [ @@ -87,6 +88,7 @@ class _ProfileScreenState extends State { titleText: LocaleKeys.country.tr(), subtitleText: "Saudi Arabia", needBorderBelow: true, + needEditButton: false, onTap: () {}, ), CustomProfileOptionsTile( @@ -98,7 +100,7 @@ class _ProfileScreenState extends State { }, ), CustomProfileOptionsTile( - titleText: LocaleKeys.phoneNumber.tr(), + titleText: LocaleKeys.phone.tr(), subtitleText: "${AppState().getUser.data!.userInfo!.email}", needBorderBelow: true, onTap: () { @@ -121,16 +123,16 @@ class _ProfileScreenState extends State { ], ), Positioned( - top: MediaQuery.of(context).size.height * 0.25, + top: MediaQuery.of(context).size.height * 0.24, child: Container( - height: 90, + height: 100, alignment: Alignment.centerLeft, child: ClipOval( child: AppState().getUser.data!.userInfo!.userLocalImage != null ? Image.file( AppState().getUser.data!.userInfo!.userLocalImage!, - width: 90, - height: 90, + width: 100, + height: 100, fit: BoxFit.fill, ) : CachedNetworkImage( @@ -145,11 +147,11 @@ class _ProfileScreenState extends State { ), placeholder: (context, url) => const Center(child: CircularProgressIndicator()), errorWidget: (context, url, error) => const Icon(Icons.error), - width: 90, - height: 90, + width: 100, + height: 100, fit: BoxFit.fill, fadeInCurve: Curves.easeIn, - fadeInDuration: Duration(milliseconds: 1000), + fadeInDuration: const Duration(milliseconds: 1000), useOldImageOnUrlChange: false, ), ), @@ -157,18 +159,32 @@ class _ProfileScreenState extends State { ), Positioned( top: MediaQuery.of(context).size.height * 0.30, - right: MediaQuery.of(context).size.height * 0.036, + right: MediaQuery.of(context).size.height * 0.03, child: Container( - height: 35, - width: 35, + height: 40, + width: 40, + padding: EdgeInsets.all(8), decoration: BoxDecoration(color: MyColors.white, shape: BoxShape.circle, border: Border.all(color: MyColors.darkTextColor, width: 0.1)), - child: const Icon(Icons.edit_note, color: MyColors.darkIconColor, size: 27).paddingOnly(left: 5), + child: MyAssets.icEdit.buildSvg(), ).onPress( () async { - model.updateUserImage(context); + await model.updateUserImage(context).whenComplete(() => setState(() {})); }, ), ), + Positioned( + top: MediaQuery.of(context).size.height * 0.05, + left: MediaQuery.of(context).size.height * 0.03, + child: const Icon(Icons.arrow_back_ios, color: Colors.black, size: 18) + .toContainer( + padding: const EdgeInsets.only(left: 5), + borderRadius: 100, + borderColor: MyColors.lightGreyEFColor, + isEnabledBorder: true, + height: 40, + width: 40, + ) + .onPress(() => pop(context))), ], ); }), diff --git a/lib/views/requests/create_request_page.dart b/lib/views/requests/create_request_page.dart index 74a614f..a79819a 100644 --- a/lib/views/requests/create_request_page.dart +++ b/lib/views/requests/create_request_page.dart @@ -7,7 +7,6 @@ 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/models/general_models/widgets_models.dart'; -import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart'; import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; import 'package:mc_common_app/views/location_views/pick_location_page.dart'; @@ -121,7 +120,7 @@ class CreateRequestPage extends StatelessWidget { vehicleOwnerDrop.add(DropValue(element.id.toInt(), element.enumValueStr, "")); } return DropdownField( - (DropValue value) => requestsVM.updateSelectionVehicleConditionId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true), + (DropValue value) => requestsVM.updateSelectionVehicleConditionId(SelectionModel(selectedId: value.id, selectedOption: value.value)), list: vehicleOwnerDrop, dropdownValue: requestsVM.vehicleConditionId.selectedId != -1 ? DropValue(requestsVM.vehicleConditionId.selectedId, requestsVM.vehicleConditionId.selectedOption, "") : null, hint: LocaleKeys.condition.tr(), diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index 5b3e1c0..598859b 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -38,9 +38,9 @@ class RequestDetailPage extends StatelessWidget { case RequestStatusEnum.inProgress: if (requestTypeEnum == RequestsTypeEnum.serviceRequest) { - return buildStatusContainer(LocaleKeys.awaitingPaymentFromCustomer.tr()); + return Utils.buildStatusContainer(LocaleKeys.awaitingPaymentFromCustomer.tr()); } else { - return buildStatusContainer(LocaleKeys.awaitingResponseFromCustomer.tr()); + return Utils.buildStatusContainer(LocaleKeys.awaitingResponseFromCustomer.tr()); } case RequestStatusEnum.paid: @@ -56,13 +56,13 @@ class RequestDetailPage extends StatelessWidget { }, ); } else { - return buildStatusContainer(LocaleKeys.awaitingResponseFromProvider.tr()); + return Utils.buildStatusContainer(LocaleKeys.awaitingResponseFromProvider.tr()); } case RequestStatusEnum.shipping: case RequestStatusEnum.delivery: if (AppState().currentAppType == AppType.provider) { - return buildStatusContainer(LocaleKeys.shippingManagementInstruction.tr(), isForShippingOrDelivery: true); + return Utils.buildStatusContainer(LocaleKeys.shippingManagementInstruction.tr()); } else { return ShowFillButton( maxWidth: double.infinity, @@ -78,26 +78,10 @@ class RequestDetailPage extends StatelessWidget { case RequestStatusEnum.cancelled: case RequestStatusEnum.expired: case RequestStatusEnum.pending: - return buildStatusContainer(statusText); + return Utils.buildStatusContainer(statusText); } } - Widget buildStatusContainer(String text, {bool isForShippingOrDelivery = true}) { - return Center( - child: text.toText( - color: isForShippingOrDelivery ? MyColors.lightTextColor : MyColors.adPendingStatusColor, - fontSize: isForShippingOrDelivery ? 14 : 16, - // isItalic: true, - ), - ).toContainer( - marginAll: 12, - paddingAll: 12, - borderRadius: 8, - width: double.infinity, - backgroundColor: isForShippingOrDelivery ? MyColors.grey98Color.withOpacity(0.1) : MyColors.adPendingStatusColor.withOpacity(0.16), - ); - } - Future onViewChatTapped(BuildContext context) async { ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest( chatTypeEnum: ChatTypeEnum.requestOffer, diff --git a/lib/views/setting_options/provider_license_page.dart b/lib/views/setting_options/provider_license_page.dart index 534196a..afad8bb 100644 --- a/lib/views/setting_options/provider_license_page.dart +++ b/lib/views/setting_options/provider_license_page.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; @@ -14,9 +15,11 @@ import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/provider_branches_models/profile/document.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/ad_view_model.dart'; import 'package:mc_common_app/view_models/service_view_model.dart'; +import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart'; import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -24,6 +27,7 @@ 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:sizer/sizer.dart'; class ProviderLicensePage extends StatefulWidget { const ProviderLicensePage({super.key}); @@ -36,59 +40,72 @@ class _ProviderLicensePageState extends State { late ServiceVM branchVM; bool showAttachment = false; String? attachedFile; + bool isAllApproved = true; @override void initState() { super.initState(); scheduleMicrotask(() { - // branchVM = Provider.of(context, listen: false); + // branchVM = Provider.of(context, listen: false); context.read().getServiceProviderDocument(AppState().getUser.data!.userInfo!.providerId ?? 0); }); } @override Widget build(BuildContext context) { + final serviceVm = context.read(); + if (serviceVm.document != null && serviceVm.document!.data != null && serviceVm.document!.data!.isNotEmpty) { + for (var doc in serviceVm.document!.data!) { + if (doc.status != 3) { + isAllApproved = false; + } + } + setState(() {}); + } return Scaffold( appBar: CustomAppBar( title: LocaleKeys.defineLicences.tr(), isRemoveBackButton: false, ), body: Consumer(builder: (_, model, __) { + if (model.state == ViewState.busy) { + return const Center( + child: CircularProgressIndicator(), + ); + } return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(0.0), - child: Column( - children: [ - showWidget(model), - ], - ), - ), - ), + child: SingleChildScrollView(child: buildContent(model)), ), - Padding( - padding: const EdgeInsets.all(12.0), - child: ShowFillButton( - title: LocaleKeys.continu.tr(), - maxWidth: double.infinity, - onPressed: () { - if (AppState().getUser.data!.userInfo!.roleId == 5) { - if (validation(model)) { - updateDocument(model); + if (!isAllApproved) ...[ + Padding( + padding: const EdgeInsets.all(12.0), + child: ShowFillButton( + title: LocaleKeys.continu.tr(), + maxWidth: double.infinity, + onPressed: () async { + bool status = false; + if (AppState().getUser.data!.userInfo!.roleId == 5) { + if (validation(model)) { + status = await updateDocument(model); + } else { + Utils.showToast(LocaleKeys.allDocumentMandatoryDealershipProvider.tr()); + return; + } } else { - Utils.showToast(LocaleKeys.allDocumentMandatoryDealershipProvider.tr()); + status = await updateDocument(model); } - } else { - updateDocument(model); - } - Future.delayed(const Duration(seconds: 1), () { - Navigator.of(context).pop(); - }); - }, + Future.delayed(const Duration(seconds: 1), () { + if (status) { + pop(context); + } + }); + }, + ), ), - ), + ], ], ); }), @@ -105,120 +122,93 @@ class _ProviderLicensePageState extends State { return valid; } - updateDocument(ServiceVM model) async { - Utils.showLoading(context); - GenericRespModel res = await model.updateDocument(model.document!.data); - Utils.hideLoading(context); - if (res.messageStatus == 1) { - Utils.showToast(LocaleKeys.documentsUploadedSuccessfully.tr()); - Navigator.of(context).pop(); - } else { - Utils.showToast(res.message ?? ""); + Future updateDocument(ServiceVM model) async { + try { + Utils.showLoading(context); + GenericRespModel res = await model.updateDocument(model.document!.data); + Utils.hideLoading(context); + if (res.messageStatus == 1) { + Utils.showToast(LocaleKeys.documentsUploadedSuccessfully.tr()); + Navigator.of(context).pop(); + return true; + } else { + Utils.showToast(res.message ?? ""); + return false; + } + } catch (e, s) { + return false; } } - Widget showWidget(ServiceVM model) { - if (model.state == ViewState.idle) { - return model.document!.data!.isEmpty - ? Text(LocaleKeys.somethingWrong.tr()) - : ListView.separated( - itemBuilder: (context, index) { - DocumentData? document = model.document?.data![index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - 10.height, - (document!.documentName! ?? "").toText(fontSize: 16, letterSpacing: -0.56, fontWeight: MyFonts.SemiBold), - Padding( - padding: const EdgeInsets.only(left: 20, right: 20, top: 4, bottom: 8), - child: LocaleKeys.enter_licence_detail.tr().toText(fontSize: 14, color: MyColors.lightTextColor, textAlign: TextAlign.center), - ), - TxtField( - hint: LocaleKeys.description.tr(), - maxLines: 3, - isBackgroundEnabled: true, - ), - 10.height, - if (isNeedToShow(model: model, document: document)) ...[ - PickedFilesContainer( - // isReview: document.status == 3 ? true : false, - pickedFiles: isLocalOrNetworkFiles(model: model, document: document), - onCrossPressedPrimary: chkIsLocalOrNetwork(document: document) - ? model.removeNetworkImag - : document.documentId == 1 - ? model.commerceRemove - : document.documentId == 2 - ? model.commercialRemove - : model.vatRemove, - isPdf: model.document!.data![index].fileExt == "pdf", - isFromNetwork: document.isLocalFile! ? false : true, - onAddFilePressed: () { - model.pickPdfReceiptFile(context, document.documentId!, index); - }, - ), - if (document.comment != null && document.comment.isNotEmpty && document.status == 4) ...[buildStatusContainer(document: document)] - ] else - ...[ + Widget buildContent(ServiceVM model) { + return model.document!.data!.isEmpty + ? Text(LocaleKeys.somethingWrong.tr()) + : ListView.separated( + separatorBuilder: (context, index) { + return 20.height; + }, + itemCount: model.document!.data!.length, + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: const EdgeInsets.symmetric(horizontal: 20), + itemBuilder: (context, index) { + DocumentData? document = model.document?.data![index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ 10.height, - InkWell( - onTap: () async { - model.pickPdfReceiptFile(context, document.documentId!, index) ?? ""; - }, - child: Container( - width: double.infinity, - height: 45, - decoration: BoxDecoration( - color: Colors.transparent, - border: Border.all(color: MyColors.greyACColor, width: 2), - borderRadius: const BorderRadius.all(Radius.circular(0)), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Icon( - Icons.attach_file, - size: 18, - color: MyColors.darkPrimaryColor, - ), - 8.width, - Text( - LocaleKeys.attachFile.tr(), - style: const TextStyle( - color: MyColors.darkPrimaryColor, - ), - ), - const Icon( - Icons.attach_file, - size: 18, - color: Colors.transparent, - ), - ], - ), - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + (document!.documentName!).toText(fontSize: 16, letterSpacing: -0.56, fontWeight: MyFonts.SemiBold), + if (document.statusText != null && document.statusText!.isNotEmpty) ...[ + 10.width, + Utils.statusContainerChip(text: document.statusText!.replaceFirst('OrActive', ''), chipColor: getColorByStatus(document.status ?? 1)), + ], + ], ), + if (document.status != 1 && document.status != 3) ...[ + Padding( + padding: const EdgeInsets.only(top: 4, bottom: 8), + child: LocaleKeys.enter_licence_detail.tr().toText(fontSize: 14, color: MyColors.lightTextColor), + ), + TxtField( + hint: LocaleKeys.description.tr(), + maxLines: 3, + isBackgroundEnabled: true, + ), + ], + 10.height, + if (isNeedToShow(model: model, document: document)) ...[ + PickedFilesContainer( + isReview: document.status != 4, + pickedFiles: isLocalOrNetworkFiles(model: model, document: document), + onCrossPressedPrimary: chkIsLocalOrNetwork(document: document) + ? model.removeNetworkImag + : document.documentId == 1 + ? model.commerceRemove + : document.documentId == 2 + ? model.commercialRemove + : model.vatRemove, + isPdf: model.document!.data![index].fileExt == "pdf", + isFromNetwork: !(document.isLocalFile ?? false), + onAddFilePressed: () { + model.pickPdfReceiptFile(context, document.documentId!, index); + }, + ), + buildCommentContainer(document: document), + ] else ...[ + 10.height, + DottedRectContainer( + onTap: () => model.pickPdfReceiptFile(context, document.documentId!, index) ?? "", + text: LocaleKeys.attachImage.tr(), + icon: MyAssets.attachmentIcon.buildSvg(), + ), + ], ], - ], + ); + }, ); - }, - separatorBuilder: (context, index) { - return 20.height; - }, - itemCount: model.document!.data!.length, - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - padding: EdgeInsets.symmetric(horizontal: 20), - ); - } else { - return Column( - children: [ - 20.height, - const Center( - child: CircularProgressIndicator(), - ), - ], - ); - } } List isLocalOrNetworkFiles({required ServiceVM model, required DocumentData document}) { @@ -281,19 +271,39 @@ class _ProviderLicensePageState extends State { return isNetworkImage; } - Widget buildStatusContainer({required DocumentData document}) { - return Center( - child: document.comment.toString().toText( - color: MyColors.adCancelledStatusColor, - fontSize: 14, - // isItalic: true, - ), - ).toContainer( - marginAll: 10, - paddingAll: 10, + Widget buildCommentContainer({required DocumentData document}) { + String comment = ""; + if (document.status == 4 && document.comment != null) { + comment = document.comment; + } + + if (comment.isEmpty) { + return const SizedBox(); + } + return Center(child: comment.toString().toText(color: MyColors.adCancelledStatusColor, fontSize: 14)).toContainer( borderRadius: 8, + margin: const EdgeInsets.only(top: 10), width: double.infinity, backgroundColor: MyColors.adCancelledStatusColor.withOpacity(0.16), ); } + + Color getColorByStatus(int docStatus) { + switch (docStatus) { + case 1: + return MyColors.adPendingStatusColor; + + case 2: + return MyColors.adActiveStatusColor; + + case 3: + return MyColors.greenColor; + + case 4: + return MyColors.adCancelledStatusColor; + + default: + return MyColors.adPendingStatusColor; + } + } } diff --git a/lib/views/setting_options/setting_options_language.dart b/lib/views/setting_options/setting_options_language.dart index d58628a..7f6ca57 100644 --- a/lib/views/setting_options/setting_options_language.dart +++ b/lib/views/setting_options/setting_options_language.dart @@ -9,6 +9,7 @@ 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/view_models/base_view_model.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/user_view_model.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; @@ -45,14 +46,6 @@ class _SettingOptionsLanguageState extends State { children: [ Column( children: [ - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.quickreply_outlined, size: 20), - titleText: LocaleKeys.requests.tr(), - needBorderBelow: true, - onTap: () { - context.read().onNavbarTapped(4); - Navigator.pop(context); - }), if (AppState().currentAppType == AppType.customer) ...[ CustomSettingOptionsTile( leadingWidget: const Icon(Icons.favorite, size: 20), @@ -61,6 +54,14 @@ class _SettingOptionsLanguageState extends State { onTap: () => navigateWithName(context, AppRoutes.favoriteListView), ), ], + // else ...[ + // CustomSettingOptionsTile( + // leadingWidget: const Icon(Icons.local_shipping, size: 20), + // titleText: LocaleKeys.shippingManagement.tr(), + // needBorderBelow: true, + // onTap: () {}, + // ), + // ], CustomSettingOptionsTile( leadingWidget: const Icon(Icons.settings, size: 20), titleText: LocaleKeys.settings.tr(), @@ -111,7 +112,7 @@ class _SettingOptionsLanguageState extends State { ), ), titleText: LocaleKeys.mySubscription.tr(), - subTitle: AppState().getproviderSubscription.first.name ?? "Silver", + subTitle: AppState().getproviderSubscription != null ? AppState().getproviderSubscription.first.name ?? "Silver" : "", isForLanguage: false, needBorderBelow: true, onTap: () { @@ -185,7 +186,7 @@ class _SettingOptionsLanguageState extends State { // titleText: "My Requests", // needBorderBelow: true, // onTap: () { - // context.read().onNavbarTapped(4); + // context.read().onNavbarTapped(4); // Navigator.pop(context); // }), // CustomSettingOptionsTile(leadingWidget: const Icon(Icons.favorite, size: 20), titleText: "Favorite list", needBorderBelow: true, onTap: () {}), @@ -198,7 +199,6 @@ class _SettingOptionsLanguageState extends State { // ], // ), // ), - (AppState().currentAppType == AppType.provider) ? Text(LocaleKeys.provider.tr()) : Text(LocaleKeys.customer.tr()), Row( children: [ Expanded( diff --git a/lib/views/setting_options/widgets/custom_setting_options_tile.dart b/lib/views/setting_options/widgets/custom_setting_options_tile.dart index 2a80cd8..748a7d4 100644 --- a/lib/views/setting_options/widgets/custom_setting_options_tile.dart +++ b/lib/views/setting_options/widgets/custom_setting_options_tile.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; @@ -76,10 +77,11 @@ class CustomSettingOptionsTile extends StatelessWidget { class CustomProfileOptionsTile extends StatelessWidget { final String titleText; final bool needBorderBelow; + final bool needEditButton; final Function() onTap; final String subtitleText; - const CustomProfileOptionsTile({super.key, required this.onTap, required this.titleText, required this.subtitleText, this.needBorderBelow = false}); + const CustomProfileOptionsTile({super.key, required this.onTap, required this.titleText, required this.subtitleText, this.needBorderBelow = false, this.needEditButton = true}); @override Widget build(BuildContext context) { @@ -95,7 +97,9 @@ class CustomProfileOptionsTile extends StatelessWidget { subtitleText.toText(fontSize: 12, color: MyColors.darkTextColor.withOpacity(0.7)), ], ), - const Icon(Icons.edit_note, size: 25), + if (needEditButton) ...[ + MyAssets.icEdit.buildSvg() + ], ], ).onPress(onTap), 5.height, diff --git a/lib/views/user/change_email_page.dart b/lib/views/user/change_email_page.dart index 7eee14a..1e2685a 100644 --- a/lib/views/user/change_email_page.dart +++ b/lib/views/user/change_email_page.dart @@ -31,7 +31,9 @@ class _ChangeEmailPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: CustomAppBar( isRemoveBackButton: true, title: LocaleKeys.changeEmail.tr()), + appBar: CustomAppBar( + title: LocaleKeys.changeEmail.tr(), + ), body: SingleChildScrollView( child: Container( // width: double.infinity, @@ -39,7 +41,11 @@ class _ChangeEmailPageState extends State { padding: const EdgeInsets.all(20), child: Column( children: [ - LocaleKeys.enterEmail.tr().toText(height: 23 / 24, fontSize: 24, letterSpacing: -1.44,), + LocaleKeys.enterEmail.tr().toText( + height: 23 / 24, + fontSize: 24, + letterSpacing: -1.44, + ), 12.height, TxtField( hint: LocaleKeys.enterNewEmail.tr(), diff --git a/lib/views/user/change_mobile_page.dart b/lib/views/user/change_mobile_page.dart index 06cd26a..f74cef0 100644 --- a/lib/views/user/change_mobile_page.dart +++ b/lib/views/user/change_mobile_page.dart @@ -31,15 +31,19 @@ class _ChangeMobilePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: CustomAppBar( isRemoveBackButton: true, title: LocaleKeys.changeMobile.tr()), + appBar: CustomAppBar(title: LocaleKeys.changeMobile.tr()), body: SingleChildScrollView( child: Container( // width: double.infinity, // height: double.infinity, - padding: EdgeInsets.all(20), + padding: const EdgeInsets.all(20), child: Column( children: [ - LocaleKeys.enterNewPhoneNumber.tr().toText(height: 23 / 24, fontSize: 24, letterSpacing: -1.44,), + LocaleKeys.enterNewPhoneNumber.tr().toText( + height: 23 / 24, + fontSize: 24, + letterSpacing: -1.44, + ), 12.height, TxtField( hint: LocaleKeys.enterNewPhoneNumber.tr(), diff --git a/lib/views/user/change_password_page.dart b/lib/views/user/change_password_page.dart index 10c1a20..cea9f1e 100644 --- a/lib/views/user/change_password_page.dart +++ b/lib/views/user/change_password_page.dart @@ -31,7 +31,7 @@ class _ChangePasswordPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: CustomAppBar( isRemoveBackButton: true, title: LocaleKeys.changePassword.tr()), + appBar: CustomAppBar(title: LocaleKeys.changePassword.tr()), body: SingleChildScrollView( child: Container( // width: double.infinity, @@ -39,7 +39,11 @@ class _ChangePasswordPageState extends State { padding: const EdgeInsets.all(20), child: Column( children: [ - LocaleKeys.enterNewPassword.tr().toText(height: 23 / 24, fontSize: 24, letterSpacing: -1.44,), + LocaleKeys.enterNewPassword.tr().toText( + height: 23 / 24, + fontSize: 24, + letterSpacing: -1.44, + ), 12.height, TxtField( hint: LocaleKeys.enterOldPassword.tr(), diff --git a/lib/views/user/edit_account_page.dart b/lib/views/user/edit_account_page.dart index a2231b8..bf9e6c9 100644 --- a/lib/views/user/edit_account_page.dart +++ b/lib/views/user/edit_account_page.dart @@ -73,7 +73,6 @@ class _EditAccountPageState extends State { ), ], ).toWhiteContainer(width: double.infinity, pading: EdgeInsets.symmetric(horizontal: 12)), - Column( children: [ CustomSettingOptionsTile( diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index 4f25a70..6cabdb9 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -34,9 +34,9 @@ class _LoginWithPasswordState extends State { //TODO: ONLY FOR DEVELOPMENT PURPOSE // String phoneNum = "966504278212", password = "Fa@1234"; - String phoneNum = "966504278213", password = "Fa@1234"; + // String phoneNum = "966504278213", password = "Fa@1234"; - // String phoneNum = "", password = ""; + String phoneNum = "", password = ""; String email = ""; String countryCode = ""; Country? _country; @@ -52,8 +52,11 @@ class _LoginWithPasswordState extends State { userVM = Provider.of(context, listen: false); context.read().getAvailBio(); if (AppState().currentAppType == AppType.provider) { - phoneNum = "966530896018"; - password = "Amir@1234"; + // phoneNum = "966530896018"; + // password = "Amir@1234"; + // + // phoneNum = "966530896020"; // JAMES BOND + // password = "Amir@1234"; // // // phoneNum = "966569755630"; // // password = "Amir12345@"; //AAmir Sal diff --git a/lib/widgets/extensions/extensions_widget.dart b/lib/widgets/extensions/extensions_widget.dart index a29c170..c966632 100644 --- a/lib/widgets/extensions/extensions_widget.dart +++ b/lib/widgets/extensions/extensions_widget.dart @@ -337,7 +337,6 @@ extension xDouble on int { extension BuildSVG on String? { Widget buildFileImage({double? height, double? width, BoxFit fit = BoxFit.contain, Color? color}) { - logger.i("!File(this!).existsSync(): ${!File(this!).existsSync()}"); if (this == null || this!.isEmpty || !File(this!).existsSync()) { return SizedBox( height: height, From fea3c806ece43c38e870fd474c062f8c7afb1faa Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Thu, 17 Oct 2024 11:34:54 +0300 Subject: [PATCH 3/8] FixedThings Before QA --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 6 +- lib/generated/codegen_loader.g.dart | 12 +++- lib/generated/locale_keys.g.dart | 11 ++- lib/services/common_services.dart | 4 +- lib/view_models/appointments_view_model.dart | 2 + lib/view_models/service_view_model.dart | 2 +- .../picked_images_container_widget.dart | 42 ++++++----- ...appointment_service_pick_bottom_sheet.dart | 19 +++-- lib/widgets/dropdown/dropdow_field.dart | 71 ++++++++++--------- 10 files changed, 107 insertions(+), 68 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index e342417..be8b18f 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -667,5 +667,9 @@ "itemPrice": "سعر العنصر", "appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.", "workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.", - "appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره." + "appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره.", + "deleteScheduleConfirmation": "هل أنت متأكد أنك تريد حذف هذا الجدول؟", + "deleteScheduleAdConfirmationMessage": "سيتم إزالة جميع فترات المواعيد ولن يتمكن العملاء من حجز مواعيد لهذا الجدول.", + "branchSchedules": "جداول الفرع", + "noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع." } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 68a0110..08a3a39 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -668,5 +668,9 @@ "itemPrice": "Item Price", "bookAppointmentForServices": "This option will allow customer to book appointment for these services.", "showServiceAvailability": "This option will show to customer that you can avail this service on workshop or not.", - "bookAppointmentAtLocation": "This option will allow customer to book appointment at their desired location." + "bookAppointmentAtLocation": "This option will allow customer to book appointment at their desired location.", + "deleteScheduleConfirmation": " Are you sure you want to delete this Schedule?", + "deleteScheduleAdConfirmationMessage": "All the appointment slots will be removed and customers will no longer be able to book appointments for this schedule.", + "branchSchedules": "Branch Schedules", + "noSchedulesFound": "You have not added any schedule for this branch." } \ No newline at end of file diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index aa3b212..bc65789 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -683,7 +683,11 @@ class CodegenLoader extends AssetLoader{ "itemPrice": "سعر العنصر", "appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.", "workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.", - "appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره." + "appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره.", + "deleteScheduleConfirmation": "هل أنت متأكد أنك تريد حذف هذا الجدول؟", + "deleteScheduleAdConfirmationMessage": "سيتم إزالة جميع فترات المواعيد ولن يتمكن العملاء من حجز مواعيد لهذا الجدول.", + "branchSchedules": "جداول الفرع", + "noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع." }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1355,7 +1359,11 @@ static const Map en_US = { "itemPrice": "Item Price", "bookAppointmentForServices": "This option will allow customer to book appointment for these services.", "showServiceAvailability": "This option will show to customer that you can avail this service on workshop or not.", - "bookAppointmentAtLocation": "This option will allow customer to book appointment at their desired location." + "bookAppointmentAtLocation": "This option will allow customer to book appointment at their desired location.", + "deleteScheduleConfirmation": " Are you sure you want to delete this Schedule?", + "deleteScheduleAdConfirmationMessage": "All the appointment slots will be removed and customers will no longer be able to book appointments for this schedule.", + "branchSchedules": "Branch Schedules", + "noSchedulesFound": "You have not added any schedule for this branch." }; 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 eea31b9..d87c01b 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1,6 +1,6 @@ // DO NOT EDIT. This is code generated via package:easy_localization/generate.dart -abstract class LocaleKeys { +abstract class LocaleKeys { static const firstTimeLogIn = 'firstTimeLogIn'; static const signUp = 'signUp'; static const changeMobile = 'changeMobile'; @@ -47,7 +47,7 @@ abstract class LocaleKeys { static const emailChangedSuccessfully = 'emailChangedSuccessfully'; static const passwordIsUpdated = 'passwordIsUpdated'; static const passwordShouldContains = 'passwordShouldContains'; - static const successfullyRegistered = ' successfullyRegistered'; + static const successfullyRegistered = ' successfullyRegistered'; static const pleaseEnterSamePassword = 'pleaseEnterSamePassword'; static const firstNameMandatory = 'firstNameMandatory'; static const surnameNameMandatory = 'surnameNameMandatory'; @@ -647,5 +647,12 @@ abstract class LocaleKeys { static const appointmentBookingOption = 'appointmentBookingOption'; static const workshopAvailabilityOption = 'workshopAvailabilityOption'; static const appointmentLocationOption = 'appointmentLocationOption'; + static const deleteScheduleConfirmation = 'deleteScheduleConfirmation'; + static const deleteScheduleAdConfirmationMessage = 'deleteScheduleAdConfirmationMessage'; + static const branchSchedules = 'branchSchedules'; + static const noSchedulesFound = 'noSchedulesFound'; + static const bookAppointmentAtLocation = 'bookAppointmentAtLocation'; + static const showServiceAvailability = 'showServiceAvailability'; + static const bookAppointmentForServices = 'bookAppointmentForServices'; } diff --git a/lib/services/common_services.dart b/lib/services/common_services.dart index ab50cc8..53ffe31 100644 --- a/lib/services/common_services.dart +++ b/lib/services/common_services.dart @@ -79,7 +79,7 @@ class CommonServicesImp implements CommonAppServices { final status = await AppPermissions.checkStoragePermissions(context); if (status) { - result = await FilePicker.platform.pickFiles(allowMultiple: true, type: fileType, allowedExtensions: allowedExtensions); + result = await FilePicker.platform.pickFiles(allowMultiple: false, type: fileType, allowedExtensions: allowedExtensions); } List pickedFiles = []; @@ -109,6 +109,4 @@ class CommonServicesImp implements CommonAppServices { return 0.0; } } - - } diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index d9e004d..9b3a3ed 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -225,6 +225,8 @@ class AppointmentsVM extends BaseVM { branchSelectedServiceId = id; currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId); + log("currentServiceSelection isAllowAppointmentHome: ${currentServiceSelection!.isAllowAppointmentHome}"); + log("currentServiceSelection serviceDescription: ${currentServiceSelection!.serviceDescription}"); notifyListeners(); } diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index ca488a1..ad800a4 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -122,7 +122,6 @@ class ServiceVM extends BaseVM { filterUserBranchCategories() { categories = []; - print("Filter Branch Executed"); List? localbranches; if (branches!.data != null) { if (selectedBranchStatus == 3) { @@ -416,6 +415,7 @@ class ServiceVM extends BaseVM { Future fetchServicesByCategoryId(int categoryId) async { servicesDropList = []; setState(ViewState.busy); + //TODO: here services = await branchRepo.fetchServicesByCategoryId(serviceCategoryId: categoryId); // to get all the services for (var element in services!.data!) { servicesDropList.add( diff --git a/lib/views/advertisement/components/picked_images_container_widget.dart b/lib/views/advertisement/components/picked_images_container_widget.dart index 6809fa6..ff41080 100644 --- a/lib/views/advertisement/components/picked_images_container_widget.dart +++ b/lib/views/advertisement/components/picked_images_container_widget.dart @@ -14,6 +14,7 @@ class PickedFilesContainer extends StatelessWidget { final bool isReview; final bool isPdf; final bool isFromNetwork; + final bool allowAdButton; final Function() onAddFilePressed; const PickedFilesContainer({ @@ -26,6 +27,7 @@ class PickedFilesContainer extends StatelessWidget { this.isReview = false, this.isPdf = false, this.isFromNetwork = false, + this.allowAdButton = true, }) : super(key: key); @override @@ -37,9 +39,13 @@ class PickedFilesContainer extends StatelessWidget { crossAxisSpacing: 4.0, mainAxisSpacing: 8.0, children: List.generate( - isReview ? pickedFiles.length : pickedFiles.length + 1, + !allowAdButton + ? pickedFiles.length + : (isReview) + ? pickedFiles.length + : pickedFiles.length + 1, (index) { - if (index == pickedFiles.length && !isReview) { + if (index == pickedFiles.length && !isReview && allowAdButton) { return Container( decoration: BoxDecoration(color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)), margin: const EdgeInsets.all(8), @@ -121,22 +127,22 @@ class BuildFilesContainer extends StatelessWidget { width: 70, ) .paddingAll(8), - !isReview - ? Align( - alignment: Alignment.topRight, - child: MyAssets.closeWithOrangeBg.buildSvg( - fit: BoxFit.fill, - height: 30, - width: 30, - ), - ).onPress(() { - if (onCrossPressedPrimary == null) { - onCrossPressedSecondary!(index!, image.filePath!); - return; - } - onCrossPressedPrimary!(image.filePath!); - }) - : const SizedBox() + if (!isReview) ...[ + Align( + alignment: Alignment.topRight, + child: MyAssets.closeWithOrangeBg.buildSvg( + fit: BoxFit.fill, + height: 30, + width: 30, + ), + ).onPress(() { + if (onCrossPressedPrimary == null) { + onCrossPressedSecondary!(index!, image.filePath!); + return; + } + onCrossPressedPrimary!(image.filePath!); + }) + ] ], )), ], diff --git a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart index 8381dcd..288b681 100644 --- a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -8,6 +8,7 @@ import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/views/location_views/pick_location_page.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; @@ -94,7 +95,13 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { maxHeight: 48, title: LocaleKeys.home.tr(), txtColor: appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor, - onPressed: () => appointmentsVM.updateIsHomeTapped(true), + onPressed: () { + if (appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) { + appointmentsVM.updateIsHomeTapped(true); + } else { + Utils.showToast("The Selected Service is not available at Home Location."); + } + }, ), ), 12.width, @@ -116,8 +123,8 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { errorValue: appointmentsVM.pickHomeLocationError, value: appointmentsVM.pickedHomeLocation, isNeedClickAll: true, - postfixData: Icons.location_on, - postFixDataColor: MyColors.darkTextColor, + postfixData: Icons.add_location_outlined, + postFixDataColor: MyColors.darkPrimaryColor, onTap: () { navigateTo( context, @@ -148,14 +155,14 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { width: double.infinity, child: Column( children: [ - if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices) ...[ + if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices && appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) ...[ const Divider(thickness: 1, height: 1), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ // TODO: This Price will be decided according to the service selected, We will calculate the KMs and multiple it with price per KMs - 150.toString().toText(fontSize: 30, isBold: true), - LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + appointmentsVM.currentServiceSelection!.rangePricePerKm.toString().toText(fontSize: 30, isBold: true), + "${LocaleKeys.sar.tr()}/km".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), ], ), LocaleKeys.additionalChargesNotice.tr().toText(fontSize: 12, color: MyColors.lightTextColor), diff --git a/lib/widgets/dropdown/dropdow_field.dart b/lib/widgets/dropdown/dropdow_field.dart index d0e40c7..2926cbf 100644 --- a/lib/widgets/dropdown/dropdow_field.dart +++ b/lib/widgets/dropdown/dropdow_field.dart @@ -1,10 +1,10 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; -import 'package:sizer/sizer.dart'; class DropValue { int id; @@ -55,40 +55,43 @@ class _DropdownFieldState extends State { dropdownValue = widget.dropdownValue; return Column( children: [ - Container( - decoration: widget.showAppointmentPickerVariant ? null : Utils.containerColorRadiusBorderWidth(MyColors.white, 0, MyColors.darkPrimaryColor, 2), - margin: const EdgeInsets.all(0), - padding: const EdgeInsets.only(left: 8, right: 8), - width: widget.showAppointmentPickerVariant ? 170 : null, - child: DropdownButton( - value: dropdownValue, - icon: const Icon(Icons.keyboard_arrow_down_sharp), - elevation: 16, - iconSize: widget.showAppointmentPickerVariant ? 26 : 16, - iconEnabledColor: borderColor, - iconDisabledColor: borderColor, - isExpanded: true, - style: const TextStyle( - color: borderColor, - fontSize: 15, - ), - hint: (widget.hint ?? "").toText(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium), - underline: Container(height: 0), - onChanged: (DropValue? newValue) { - setState(() { - dropdownValue = newValue!; - widget.onSelect(newValue); - }); - }, - items: (widget.list ?? defaultV).map>( - (DropValue value) { - return DropdownMenuItem( - value: value, - enabled: value.isEnabled ?? true, - child: value.value.toText(fontSize: 15, color: value.isEnabled == false ? MyColors.darkTextColor : null, fontWeight: MyFonts.Medium), - ); + IgnorePointer( + ignoring: !widget.isSelectAble, + child: Container( + decoration: widget.showAppointmentPickerVariant ? null : Utils.containerColorRadiusBorderWidth(MyColors.white, 0, widget.isSelectAble ? MyColors.darkPrimaryColor : MyColors.greyACColor, 2), + margin: const EdgeInsets.all(0), + padding: const EdgeInsets.only(left: 8, right: 8), + width: widget.showAppointmentPickerVariant ? 170 : null, + child: DropdownButton( + value: dropdownValue, + icon: Icon(Icons.keyboard_arrow_down_sharp, color: !widget.isSelectAble ? Colors.transparent : null), + elevation: 16, + iconSize: widget.showAppointmentPickerVariant ? 26 : 16, + iconEnabledColor: borderColor, + iconDisabledColor: borderColor, + isExpanded: true, + style: const TextStyle( + color: borderColor, + fontSize: 15, + ), + hint: (widget.hint ?? "").toText(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium), + underline: Container(height: 0), + onChanged: (DropValue? newValue) { + setState(() { + dropdownValue = newValue!; + widget.onSelect(newValue); + }); }, - ).toList(), + items: (widget.list ?? defaultV).map>( + (DropValue value) { + return DropdownMenuItem( + value: value, + enabled: value.isEnabled ?? true, + child: value.value.toText(fontSize: 15, color: value.isEnabled == false ? MyColors.darkTextColor : null, fontWeight: MyFonts.Medium), + ); + }, + ).toList(), + ), ), ), if (widget.errorValue != "") From fb41cd0770a201fe64afa4f415d0c48361e470de Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Thu, 17 Oct 2024 16:12:05 +0300 Subject: [PATCH 4/8] FixedThings Before QA --- assets/langs/ar-SA.json | 2 +- assets/langs/en-US.json | 2 +- lib/extensions/string_extensions.dart | 26 +++++- lib/generated/codegen_loader.g.dart | 4 +- lib/generated/locale_keys.g.dart | 2 +- lib/main.dart | 3 + .../appointment_list_model.dart | 4 + .../service_schedule_model.dart | 4 + lib/repositories/common_repo.dart | 7 ++ lib/utils/enums.dart | 5 ++ lib/utils/utils.dart | 25 ++---- .../appointments/appointment_detail_view.dart | 85 ++++++++----------- .../book_appointment_schedules_view.dart | 3 +- .../book_appointment_services_view.dart | 1 + .../book_appointments_item_view.dart | 19 +++-- .../appointments/review_appointment_view.dart | 5 +- .../setting_options/setting_option_help.dart | 25 ++---- .../setting_options_language.dart | 31 +++---- lib/views/user/login_with_password_page.dart | 13 ++- 19 files changed, 141 insertions(+), 125 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index be8b18f..6a96725 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -579,7 +579,7 @@ "requestAlreadyInProgress": "هذا الطلب قيد التقدم بالفعل.", "acceptOfferConfirmation": "هل تريد قبول هذا العرض؟", "acceptOfferConfirmationMessage": "سيتم قبول هذا العرض وستدفع المبلغ المطلوب للمشتري في وقت معين وإلا سيتم إلغاء هذا العرض.", - "noUpcomingAppointments": "لا يوجد موعد قادم متاح", + "noUpcomingAppointments": "لا مواعيد قادم..", "addNewAppointment": "إضافة موعد جديد", "myNearbyBranches": "فروعي القريبة", "myRecentBranches": "فروعي الأخيرة", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 08a3a39..3d26af4 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -580,7 +580,7 @@ "upgradeVar": "Upgrade", "whendoyouWanttoRenew": "When do you want to renew", "renewVar": "Renew", - "noUpcomingAppointments": "No Upcoming Appointment Available.", + "noUpcomingAppointments": "No Upcoming Appointments.", "addNewAppointment": "Add New Appointment", "myNearbyBranches": "My Nearby Branches", "myRecentBranches": "My Recent Branches", diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 8055de3..7dc07d5 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -3,7 +3,6 @@ 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/date_helper.dart'; import 'package:mc_common_app/utils/enums.dart'; @@ -283,6 +282,31 @@ extension AppointmentStatusToInt on AppointmentStatusEnum { } } +extension AppointmentTypeEnumExt on int { + AppointmentTypeEnum toAppointmentTypeEnum() { + if (this == 1) { + return AppointmentTypeEnum.workshop; + } else if (this == 2) { + return AppointmentTypeEnum.home; + } else { + return AppointmentTypeEnum.workshop; + } + } +} + +extension AppointmentTypeEnumExtString on AppointmentTypeEnum { + int getIdFromAppointmentTypeEnum() { + switch (this) { + case AppointmentTypeEnum.workshop: + return 1; + case AppointmentTypeEnum.home: + return 2; + default: + return 1; + } + } +} + extension AppointmentStatusToString on AppointmentStatusEnum { String getAppointmentNameFromEnum() { switch (this) { diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index bc65789..591c656 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -595,7 +595,7 @@ class CodegenLoader extends AssetLoader{ "requestAlreadyInProgress": "هذا الطلب قيد التقدم بالفعل.", "acceptOfferConfirmation": "هل تريد قبول هذا العرض؟", "acceptOfferConfirmationMessage": "سيتم قبول هذا العرض وستدفع المبلغ المطلوب للمشتري في وقت معين وإلا سيتم إلغاء هذا العرض.", - "noUpcomingAppointments": "لا يوجد موعد قادم متاح", + "noUpcomingAppointments": "لا مواعيد قادم..", "addNewAppointment": "إضافة موعد جديد", "myNearbyBranches": "فروعي القريبة", "myRecentBranches": "فروعي الأخيرة", @@ -1271,7 +1271,7 @@ static const Map en_US = { "upgradeVar": "Upgrade", "whendoyouWanttoRenew": "When do you want to renew", "renewVar": "Renew", - "noUpcomingAppointments": "No Upcoming Appointment Available.", + "noUpcomingAppointments": "No Upcoming Appointments.", "addNewAppointment": "Add New Appointment", "myNearbyBranches": "My Nearby Branches", "myRecentBranches": "My Recent Branches", diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index d87c01b..2e0162c 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -651,8 +651,8 @@ abstract class LocaleKeys { static const deleteScheduleAdConfirmationMessage = 'deleteScheduleAdConfirmationMessage'; static const branchSchedules = 'branchSchedules'; static const noSchedulesFound = 'noSchedulesFound'; - static const bookAppointmentAtLocation = 'bookAppointmentAtLocation'; static const showServiceAvailability = 'showServiceAvailability'; static const bookAppointmentForServices = 'bookAppointmentForServices'; + static const bookAppointmentAtLocation = 'bookAppointmentAtLocation'; } diff --git a/lib/main.dart b/lib/main.dart index 7d16923..235274f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,9 @@ import 'package:logger/logger.dart'; Logger logger = Logger(printer: PrettyPrinter(printEmojis: false, colors: true, printTime: false)); +bool disableThingsForQA = true; + +// Language Tile in Settings // todo terminal command to genertate translation files // flutter pub run easy_localization:generate --source-dir ./assets/langs diff --git a/lib/models/appointments_models/appointment_list_model.dart b/lib/models/appointments_models/appointment_list_model.dart index 5f4f026..bb8353f 100644 --- a/lib/models/appointments_models/appointment_list_model.dart +++ b/lib/models/appointments_models/appointment_list_model.dart @@ -16,6 +16,8 @@ class AppointmentListModel { String? customerName; String? customerMobileNum; int? appointmentType; + AppointmentTypeEnum? appointmentTypeEnum; + String? providerName; String? duration; String? appointmentDate; @@ -45,6 +47,7 @@ class AppointmentListModel { this.customerName, this.customerMobileNum, this.appointmentType, + this.appointmentTypeEnum, this.providerName, this.duration, this.branchName, @@ -76,6 +79,7 @@ class AppointmentListModel { customerName = json['customerName']; customerMobileNum = json['customerMobile']; appointmentType = json['appointmentType']; + appointmentTypeEnum = json['appointmentType'] != null ? (json['appointmentType'] as int).toAppointmentTypeEnum() : AppointmentTypeEnum.workshop; providerName = json['providerName']; duration = json['duration']; appointmentDate = json['appointmentDate']; diff --git a/lib/models/appointments_models/service_schedule_model.dart b/lib/models/appointments_models/service_schedule_model.dart index 7d9444f..a515097 100644 --- a/lib/models/appointments_models/service_schedule_model.dart +++ b/lib/models/appointments_models/service_schedule_model.dart @@ -1,6 +1,7 @@ import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/services_models/service_model.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; +import 'package:mc_common_app/utils/enums.dart'; class CustomTimeDateSlotModel { TimeSlotModel? date; @@ -19,6 +20,7 @@ class ServiceAppointmentScheduleModel { double? amountTotal; double? amountRem; int? appointmentType; + AppointmentTypeEnum? appointmentTypeEnum; ServiceAppointmentScheduleModel({ this.serviceSlotList, @@ -30,6 +32,7 @@ class ServiceAppointmentScheduleModel { this.amountTotal, this.amountRem, this.appointmentType, + this.appointmentTypeEnum, }); List getFormattedDateTimeSlotPackage() { @@ -105,6 +108,7 @@ class ServiceAppointmentScheduleModel { amountTotal = json['amountTotal']; amountRem = json['amountRem']; appointmentType = json['appointmentType']; + appointmentTypeEnum = json['appointmentType'] != null ? (json['appointmentType'] as int).toAppointmentTypeEnum() : AppointmentTypeEnum.workshop; } } diff --git a/lib/repositories/common_repo.dart b/lib/repositories/common_repo.dart index bd83be8..79358fe 100644 --- a/lib/repositories/common_repo.dart +++ b/lib/repositories/common_repo.dart @@ -11,6 +11,7 @@ import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/user_models/cities.dart'; import 'package:mc_common_app/models/user_models/country.dart'; import 'package:mc_common_app/models/user_models/role.dart'; +import 'package:mc_common_app/utils/utils.dart'; abstract class CommonRepo { Future getAllCountries(); @@ -129,6 +130,12 @@ class CommonRepoImp implements CommonRepo { {"id": 2, "enumTypeID": 0, "enumValueStr": "Used", "enumValue": 2, "isActive": true}, ], }; + + if (json.containsKey('StatusMessage')) { + if ((json['StatusMessage'] as String).contains('Internal server error')) { + Utils.showToast("${json['StatusMessage']}"); + } + } List enums = List.generate((json['data'] as List).length, (index) => EnumsModel.fromJson(json['data'][index])); return enums; } diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index f948197..2549b6e 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -146,6 +146,11 @@ enum BranchStatusEnum { deactivated // 6 } +enum AppointmentTypeEnum { + workshop, + home, +} + enum AppointmentStatusEnum { booked, confirmed, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 4bc0b09..c6a7ca3 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -27,13 +27,7 @@ class Utils { static bool get isLoading => _isLoadingVisible; static void showToast(String message) { - Fluttertoast.showToast(msg: message, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - timeInSecForIosWeb: 2, - backgroundColor: Colors.black54, - textColor: Colors.white, - fontSize: 16.0); + Fluttertoast.showToast(msg: message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.black54, textColor: Colors.white, fontSize: 16.0); } static Future openNumberViaCaller({required String phoneNumber}) async { @@ -76,11 +70,7 @@ class Utils { return ""; } - return ("${timeOfDay.hour - .toString() - .length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute - .toString() - .length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); + return ("${timeOfDay.hour.toString().length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute.toString().length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); } static dynamic getNotNullValue(List list, int index) { @@ -483,7 +473,7 @@ class Utils { border: Border.all( width: w, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -506,7 +496,7 @@ class Utils { border: Border.all( width: 1, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -524,8 +514,7 @@ class Utils { } static String getAdsPaymentBrowserForm({required int paymentId, required int adId}) { - return '
'; + return '
'; } // BOTTOM SHEETS @@ -550,8 +539,8 @@ class Utils { static Widget buildStatusContainer(String text) { return Center( child: text.toText(color: MyColors.lightTextColor, fontSize: 14 - // isItalic: true, - ), + // isItalic: true, + ), ).toContainer( marginAll: 8, paddingAll: 15, diff --git a/lib/views/appointments/appointment_detail_view.dart b/lib/views/appointments/appointment_detail_view.dart index 1f0046e..52a16bf 100644 --- a/lib/views/appointments/appointment_detail_view.dart +++ b/lib/views/appointments/appointment_detail_view.dart @@ -8,6 +8,7 @@ import 'package:mc_common_app/models/services_models/service_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/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/appointments_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -15,6 +16,7 @@ import 'package:mc_common_app/widgets/common_widgets/card_button_with_icon.dart' import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:sizer/sizer.dart'; class AppointmentDetailView extends StatelessWidget { final AppointmentListModel appointmentListModel; @@ -43,15 +45,9 @@ class AppointmentDetailView extends StatelessWidget { case AppointmentPaymentStatusEnum.payPartial: return Align( alignment: Alignment.bottomCenter, - child: Row( - children: [ - getBaseActionButtonWidget( - color: MyColors.grey98Color.withOpacity(0.1), - textColor: MyColors.lightTextColor, - onPressed: () {}, - text: LocaleKeys.workInProgress.tr(), - ), - ], + child: SizedBox( + height: 8.h, + child: Utils.buildStatusContainer(LocaleKeys.workInProgress.tr()), ), ); case AppointmentPaymentStatusEnum.payNow: @@ -85,7 +81,7 @@ class AppointmentDetailView extends StatelessWidget { alignment: Alignment.bottomCenter, child: Row( children: [ - getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"), + getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: LocaleKeys.cancel.tr()), 12.width, getBaseActionButtonWidget( color: MyColors.greenColor, @@ -108,15 +104,9 @@ class AppointmentDetailView extends StatelessWidget { case AppointmentStatusEnum.visitCompleted: return Align( alignment: Alignment.bottomCenter, - child: Row( - children: [ - getBaseActionButtonWidget( - color: MyColors.grey98Color.withOpacity(0.3), - textColor: MyColors.lightTextColor, - onPressed: () {}, - text: LocaleKeys.visitCompleted.tr(), - ), - ], + child: SizedBox( + height: 8.h, + child: Utils.buildStatusContainer(LocaleKeys.visitCompleted.tr()), ), ); @@ -126,20 +116,19 @@ class AppointmentDetailView extends StatelessWidget { case AppointmentStatusEnum.cancelled: return Align( alignment: Alignment.bottomCenter, - child: Row( - children: [ - getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "Cancelled"), - ], + child: SizedBox( + height: 8.h, + child: Utils.buildStatusContainer(LocaleKeys.appointmentCancelled.tr()), ), ); case AppointmentStatusEnum.allAppointments: - return SizedBox(); + return const SizedBox(); case AppointmentStatusEnum.rescheduled: return Align( alignment: Alignment.bottomCenter, child: Row( children: [ - getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"), + getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: LocaleKeys.cancel.tr()), 12.width, getBaseActionButtonWidget( color: MyColors.greenColor, @@ -151,7 +140,7 @@ class AppointmentDetailView extends StatelessWidget { ), ); default: - return SizedBox(); + return const SizedBox(); } } @@ -224,44 +213,40 @@ class AppointmentDetailView extends StatelessWidget { Column( children: List.generate(appointmentListModel.appointmentServicesList!.length, (index) { ServiceModel service = appointmentListModel.appointmentServicesList![index]; - return Column( + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - // MyAssets.maintenanceIcon.buildSvg( - // height: 10, - // width: 10, - // fit: BoxFit.fill, - // ), - // 10.width, "${index + 1}. ${service.providerServiceDescription}".toText(fontSize: 14, isBold: true), + if (service.serviceItems != null && service.serviceItems!.isNotEmpty) ...[ + Column( + children: List.generate( + service.serviceItems!.length, + (index) => "${service.serviceItems![index].name}".toText( + textAlign: TextAlign.start, + fontSize: 12, + color: MyColors.lightTextColor, + ), + ), + ).paddingOnly(left: 15), + ], ], ), - if (service.serviceItems != null && service.serviceItems!.isNotEmpty) ...[ - Column( - children: List.generate( - service.serviceItems!.length, - (index) => "${service.serviceItems![index].name}".toText( - textAlign: TextAlign.start, - fontSize: 12, - color: MyColors.lightTextColor, - ), - ), - ).paddingOnly(left: 15), - ], 5.height, Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - ((service.currentTotalServicePrice).toString()).toText(fontSize: 25, isBold: true), + ((service.currentTotalServicePrice).toString()).toText(fontSize: 19, isBold: true), 2.width, - LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), - const Icon(Icons.arrow_drop_down, size: 30) + LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 5), + const Icon(Icons.arrow_drop_down, size: 25) ], ).onPress(() => appointmentsVM.priceBreakDownClicked(context, service)), ], - ); + ).paddingOnly(bottom: 15); }), ), ], diff --git a/lib/views/appointments/book_appointment_schedules_view.dart b/lib/views/appointments/book_appointment_schedules_view.dart index ab527ea..5752f34 100644 --- a/lib/views/appointments/book_appointment_schedules_view.dart +++ b/lib/views/appointments/book_appointment_schedules_view.dart @@ -5,6 +5,7 @@ import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart'; import 'package:mc_common_app/models/services_models/service_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; @@ -62,7 +63,7 @@ class BookAppointmentSchedulesView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ (LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - (scheduleData.appointmentType == 2 ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(), + (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(), ], ), Column( diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart index 99c91c2..cb0d265 100644 --- a/lib/views/appointments/book_appointment_services_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -65,6 +65,7 @@ class BookAppointmentServicesView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ (LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + 4.width, (serviceData.isHomeSelected ? serviceData.homeLocation : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(), ], ), diff --git a/lib/views/appointments/book_appointments_item_view.dart b/lib/views/appointments/book_appointments_item_view.dart index 046a152..284d348 100644 --- a/lib/views/appointments/book_appointments_item_view.dart +++ b/lib/views/appointments/book_appointments_item_view.dart @@ -49,15 +49,16 @@ class BookAppointmentsItemView extends StatelessWidget { children: [ LocaleKeys.workshopFullAccessServices.tr().toText(fontSize: 12, isItalic: true, color: MyColors.lightTextColor), 8.height, - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.changeLocationService.tr().toText(fontSize: 14, isBold: true), - LocaleKeys.edit.tr().toText(fontSize: 14, isBold: true, isUnderLine: true, color: MyColors.adPendingStatusColor), - 5.width, - MyAssets.icEdit.buildSvg(width: 17), - ], - ).onPress(() {}), + // Row( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // LocaleKeys.changeLocationService.tr().toText(fontSize: 14, isBold: true), + // 5.width, + // LocaleKeys.edit.tr().toText(fontSize: 14, isBold: true, isUnderLine: true, color: MyColors.adPendingStatusColor), + // 5.width, + // MyAssets.icEdit.buildSvg(width: 17), + // ], + // ).onPress(() {}), 16.height, const Divider(), ], diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart index c0af252..7082f4e 100644 --- a/lib/views/appointments/review_appointment_view.dart +++ b/lib/views/appointments/review_appointment_view.dart @@ -8,6 +8,7 @@ import 'package:mc_common_app/models/appointments_models/service_schedule_model. import 'package:mc_common_app/models/services_models/item_model.dart'; import 'package:mc_common_app/models/services_models/service_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -160,7 +161,7 @@ class ReviewAppointment extends StatelessWidget { ]), Row(children: [ ("${LocaleKeys.location.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - (scheduleData.appointmentType == 2 ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true), + (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true), ]), ], ), @@ -184,7 +185,7 @@ class ReviewAppointment extends StatelessWidget { "${scheduleData.amountTotal.toString()} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true), ], ), - if (scheduleData.appointmentType == 1) ...[ + if (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/views/setting_options/setting_option_help.dart b/lib/views/setting_options/setting_option_help.dart index 9768496..6ffb72f 100644 --- a/lib/views/setting_options/setting_option_help.dart +++ b/lib/views/setting_options/setting_option_help.dart @@ -37,22 +37,15 @@ class SettingOptionsHelp extends StatelessWidget { leadingWidget: const Icon(Icons.mail, size: 20), titleText: LocaleKeys.contactUs.tr(), needBorderBelow: true, - onTap: () => navigateWithName( - context, AppRoutes.settingOptionsFaqs), + onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs), ), CustomSettingOptionsTile( leadingWidget: const Icon(Icons.lock, size: 20), titleText: LocaleKeys.termPrivacy.tr(), - onTap: () => - navigateWithName(context, AppRoutes.profileView), + onTap: () {}, ), ], - ).toContainer( - width: double.infinity, - isShadowEnabled: true, - paddingAll: 10, - margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), - borderRadius: 0), + ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), ], ), ), @@ -69,13 +62,7 @@ class CustomSettingOptionsTile extends StatelessWidget { final bool isForLanguage; final Function() onTap; - const CustomSettingOptionsTile( - {super.key, - required this.leadingWidget, - required this.onTap, - required this.titleText, - this.needBorderBelow = false, - this.isForLanguage = false}); + const CustomSettingOptionsTile({super.key, required this.leadingWidget, required this.onTap, required this.titleText, this.needBorderBelow = false, this.isForLanguage = false}); @override Widget build(BuildContext context) { @@ -91,9 +78,7 @@ class CustomSettingOptionsTile extends StatelessWidget { titleText.toText(fontSize: 16), ], ), - isForLanguage - ? const Icon(Icons.language, size: 18) - : const Icon(Icons.arrow_forward, size: 18) + isForLanguage ? const Icon(Icons.language, size: 18) : const Icon(Icons.arrow_forward, size: 18) ], ).onPress(onTap), 5.height, diff --git a/lib/views/setting_options/setting_options_language.dart b/lib/views/setting_options/setting_options_language.dart index 7f6ca57..e501d1d 100644 --- a/lib/views/setting_options/setting_options_language.dart +++ b/lib/views/setting_options/setting_options_language.dart @@ -6,11 +6,10 @@ 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/generated/locale_keys.g.dart'; +import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; -import 'package:mc_common_app/view_models/base_view_model.dart'; -import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/user_view_model.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; @@ -87,18 +86,20 @@ class _SettingOptionsLanguageState extends State { ], ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), 10.height, - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.translate, size: 20), - titleText: LocaleKeys.language.tr(), - isForLanguage: true, - onTap: () { - context.read().changeLanguage(context); - Future.delayed(Duration(seconds: 1), () { - setState(() {}); - }); - }, - ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), - 10.height, + if (!disableThingsForQA) ...[ + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.translate, size: 20), + titleText: LocaleKeys.language.tr(), + isForLanguage: true, + onTap: () { + context.read().changeLanguage(context); + Future.delayed(const Duration(seconds: 1), () { + setState(() {}); + }); + }, + ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), + 10.height, + ], (AppState().currentAppType == AppType.provider) ? Column( children: [ @@ -112,7 +113,7 @@ class _SettingOptionsLanguageState extends State { ), ), titleText: LocaleKeys.mySubscription.tr(), - subTitle: AppState().getproviderSubscription != null ? AppState().getproviderSubscription.first.name ?? "Silver" : "", + subTitle: AppState().getproviderSubscription.first.name ?? "", isForLanguage: false, needBorderBelow: true, onTap: () { diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index 6cabdb9..f159b4b 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -93,10 +93,15 @@ class _LoginWithPasswordState extends State { child: Column( children: [ 12.height, - LocaleKeys.login.tr().toText( - fontSize: 20, - letterSpacing: -1.44, - ), + (AppState().currentAppType == AppType.provider ? "Provider" : "Customer").toText( + fontSize: 25, + letterSpacing: -1.44, + ), + 8.height, + (LocaleKeys.login.tr()).toText( + fontSize: 20, + letterSpacing: -1.44, + ), // 20.height, // (type == ClassType.NUMBER ? LocaleKeys.\enterPhoneNumber.tr() : LocaleKeys.enterEmail.tr()).toText14( // color: MyColors.lightTextColor, From 450dfff6e8425f880230f41a873708f7e0efd0d2 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Mon, 21 Oct 2024 16:20:24 +0300 Subject: [PATCH 5/8] Added Shipping Management --- lib/classes/consts.dart | 8 +- lib/config/dependency_injection.dart | 2 + lib/config/routes.dart | 10 +- lib/extensions/string_extensions.dart | 35 +++ .../general_models/generic_resp_model.dart | 21 +- lib/models/requests_models/request_model.dart | 6 + lib/models/services_models/item_model.dart | 2 +- .../shipping_status_model.dart | 27 +++ lib/repositories/ads_repo.dart | 6 +- lib/repositories/common_repo.dart | 20 +- lib/repositories/shipping_repo.dart | 73 +++++++ lib/utils/enums.dart | 9 + lib/utils/utils.dart | 44 +++- lib/view_models/ad_view_model.dart | 77 ++++++- lib/view_models/service_view_model.dart | 16 +- .../shipping_management_view_model.dart | 110 ++++++++++ .../ads_detail_view/ads_detail_view.dart | 1 + .../ads_detail_view/components.dart | 160 ++++++++++---- lib/views/chat/chat_view.dart | 34 ++- .../common_fragments/requests_fragment.dart | 2 +- lib/views/requests/offer_list_page.dart | 2 +- lib/views/requests/widget/request_item.dart | 2 +- .../setting_options_language.dart | 17 +- .../shipping_management_view.dart | 199 ++++++++++++++++++ 24 files changed, 792 insertions(+), 91 deletions(-) create mode 100644 lib/models/shipping_models/shipping_status_model.dart create mode 100644 lib/repositories/shipping_repo.dart create mode 100644 lib/view_models/shipping_management_view_model.dart create mode 100644 lib/views/shipping_management/shipping_management_view.dart diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 872f394..9e3e565 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -145,8 +145,6 @@ class ApiConsts { //Branch Users static String getAllProviderDealers = "${baseUrlServices}api/ServiceProviders/DealershipUserBranchWise_Get"; - // /api/ServiceProviders/BranchUser_Get - //DealershipUserBranchWise_Get static String getBranchUser = "${baseUrlServices}api/ServiceProviders/BranchUser_Get"; static String assignDealerToBranch = "${baseUrlServices}api/ServiceProviders/BranchUser_Create"; static String removeDealerFromBranch = "${baseUrlServices}api/ServiceProviders/BranchUser_Update"; @@ -164,6 +162,10 @@ class ApiConsts { static String requestOffersSpsGet = "${baseUrlServices}api/RequestManagement/Request_OfferSPs_Get"; static String getServiceRequestsForProvider = "${baseUrlServices}api/RequestManagement/Request_ServiceProvider"; + //Shipping + static String shippingRequestStatusUpdate = "${baseUrlServices}api/RequestManagement/ShippingRequestStatus_Update"; + static String shippingRequestStatusGet = "${baseUrlServices}api/RequestManagement/ShippingRequestStatus_Get"; + //Chat static String chatHubUrl = "$baseUrlServices/McHub"; static String messageIsReadUpdateForRequests = "${baseUrlServices}api/RequestManagement/ReqOfferChatIsRead_Update"; @@ -294,8 +296,6 @@ class MyAssets { static String whatsAppIcon = "${assetPath}icons/whatsapp_icon.svg"; static const String arrowRight = "${assetPath}icons/ic_arrow_right.svg"; static const String brokenImage = "${assetPath}images/broken_image.png"; - - } RegExp numReg = RegExp(r".*[0-9].*"); diff --git a/lib/config/dependency_injection.dart b/lib/config/dependency_injection.dart index aea3bcc..f3c8745 100644 --- a/lib/config/dependency_injection.dart +++ b/lib/config/dependency_injection.dart @@ -12,6 +12,7 @@ import 'package:mc_common_app/repositories/branch_repo.dart'; import 'package:mc_common_app/repositories/chat_repo.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/repositories/payments_repo.dart'; +import 'package:mc_common_app/repositories/shipping_repo.dart'; import 'package:mc_common_app/repositories/user_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; import 'package:mc_common_app/services/firebase_service.dart'; @@ -42,6 +43,7 @@ class AppDependencies { injector.registerSingleton(() => AppointmentRepoImp()); injector.registerSingleton(() => ChatRepoImp()); injector.registerSingleton(() => BranchRepoImp()); + injector.registerSingleton(() => ShippingRepoImp()); // } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 333a2a4..515974b 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -23,6 +23,7 @@ import 'package:mc_common_app/views/setting_options/setting_option_help.dart'; import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart'; import 'package:mc_common_app/views/setting_options/setting_options_invite_friends.dart'; import 'package:mc_common_app/views/setting_options/setting_options_language.dart'; +import 'package:mc_common_app/views/shipping_management/shipping_management_view.dart'; import 'package:mc_common_app/views/user/change_email_page.dart'; import 'package:mc_common_app/views/user/change_mobile_page.dart'; import 'package:mc_common_app/views/user/change_password_page.dart'; @@ -69,7 +70,7 @@ class AppRoutes { static const String dashboard = "/dashboard"; static const String bookProviderAppView = "/bookProviderAppView"; - // Appoinments + // Appointments static const String appointmentDetailView = "/appointmentDetailView"; static const String bookAppointmenServicesView = "/bookAppointmenServicesView"; static const String bookAppointmenSchedulesView = "/bookAppointmenSchedulesView"; @@ -87,6 +88,10 @@ class AppRoutes { // Payments static const String paymentMethodsView = "/paymentMethodsView"; + //Shipping + + static const shippingManagementView = "/shippingManagementView"; + //Customer APP: Provider & Services static const String branchDetailView = "/branchDetailPage"; static const String branchSearchFilterPage = "/branchSearchFilterPage"; @@ -171,6 +176,9 @@ class AppRoutes { //MediaViewer AppRoutes.mediaViewerScreen: (context) => MediaViewerScreen(images: ModalRoute.of(context)!.settings.arguments as List), + + //Shipping + AppRoutes.shippingManagementView: (context) => const ShippingManagementView(), }; } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 7dc07d5..71c00f0 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -907,3 +907,38 @@ extension SubscriptionTypeEnumToString on SubscriptionTypeEnum { } } } + +extension ShippingStatusEnumExt on int { + ShippingRequestStatusEnum toShippingStatusEnum() { + if (this == 0) { + return ShippingRequestStatusEnum.allRequests; + } else if (this == 1) { + return ShippingRequestStatusEnum.initiated; + } else if (this == 2) { + return ShippingRequestStatusEnum.inTransit; + } else if (this == 3) { + return ShippingRequestStatusEnum.outForDelivery; + } else if (this == 4) { + return ShippingRequestStatusEnum.delivered; + } + return ShippingRequestStatusEnum.initiated; + } +} + +extension ShippingStatusEnumToInt on ShippingRequestStatusEnum { + int getIdFromShippingStatusEnum() { + switch (this) { + case ShippingRequestStatusEnum.initiated: + return 1; + case ShippingRequestStatusEnum.inTransit: + return 2; + case ShippingRequestStatusEnum.outForDelivery: + return 3; + case ShippingRequestStatusEnum.delivered: + return 4; + + default: + return 0; + } + } +} diff --git a/lib/models/general_models/generic_resp_model.dart b/lib/models/general_models/generic_resp_model.dart index 557d725..0f036e3 100644 --- a/lib/models/general_models/generic_resp_model.dart +++ b/lib/models/general_models/generic_resp_model.dart @@ -1,5 +1,7 @@ import 'dart:io'; +import 'package:mc_common_app/utils/utils.dart'; + class GenericRespModel { GenericRespModel({ this.data, @@ -13,12 +15,19 @@ class GenericRespModel { int? totalItemsCount; String? message; - factory GenericRespModel.fromJson(Map json) => GenericRespModel( - data: json["data"], - messageStatus: json["messageStatus"], - totalItemsCount: json["totalItemsCount"], - message: json["message"], - ); + factory GenericRespModel.fromJson(Map json) { + if (json.containsKey('StatusMessage')) { + if ((json['StatusMessage'] as String).contains('Internal server error')) { + Utils.showToast("${json['StatusMessage']}"); + } + } + return GenericRespModel( + data: json["data"], + messageStatus: json["messageStatus"], + totalItemsCount: json["totalItemsCount"], + message: json["message"], + ); + } Map toJson() => { "data": data, diff --git a/lib/models/requests_models/request_model.dart b/lib/models/requests_models/request_model.dart index 95eb502..8f4bd01 100644 --- a/lib/models/requests_models/request_model.dart +++ b/lib/models/requests_models/request_model.dart @@ -8,6 +8,8 @@ class RequestModel { String requestTypeName; String requestStatusName; RequestStatusEnum requestStatus; + int? shippingStatus; + ShippingRequestStatusEnum? shippingStatusEnum; String cityName; String vehicleTypeName; String countryName; @@ -46,6 +48,8 @@ class RequestModel { required this.requestTypeName, required this.requestStatusName, required this.requestStatus, + required this.shippingStatus, + required this.shippingStatusEnum, required this.cityName, required this.vehicleTypeName, required this.countryName, @@ -96,6 +100,8 @@ class RequestModel { requestTypeName: json["requestTypeName"], requestStatusName: json["requestStatusName"], requestStatus: (json['requestStatus'] as int).toRequestStatusEnum(), + shippingStatus: json['shippingRequestStatus'], + shippingStatusEnum: json['shippingRequestStatus'] != null ? (json['shippingRequestStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.initiated, cityName: json["cityName"], vehicleTypeName: json["vehicleTypeName"], countryName: json["countryName"], diff --git a/lib/models/services_models/item_model.dart b/lib/models/services_models/item_model.dart index 3141a89..322e077 100644 --- a/lib/models/services_models/item_model.dart +++ b/lib/models/services_models/item_model.dart @@ -42,7 +42,7 @@ class ItemData { final String? price; final String? manufactureDate; final String? description; - final dynamic pictureUrl; + final String? pictureUrl; final int? companyId; final int? serviceProviderServiceId; final String? serviceProviderServiceDescription; diff --git a/lib/models/shipping_models/shipping_status_model.dart b/lib/models/shipping_models/shipping_status_model.dart new file mode 100644 index 0000000..c68f73c --- /dev/null +++ b/lib/models/shipping_models/shipping_status_model.dart @@ -0,0 +1,27 @@ +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/models/requests_models/request_model.dart'; +import 'package:mc_common_app/utils/enums.dart'; + +class ShippingRequestModel { + int? id; + int? requestID; + RequestModel? request; + int? shippingStatus; + ShippingRequestStatusEnum? shippingStatusEnum; + String? deliveredOn; + String? comment; + String? createdOn; + + ShippingRequestModel({this.id, this.requestID, this.request, this.shippingStatus, this.deliveredOn, this.comment, this.createdOn}); + + ShippingRequestModel.fromJson(Map json) { + id = json['id']; + requestID = json['requestID']; + request = json['request']; + shippingStatus = json['shippingStatus']; + shippingStatusEnum = json['shippingStatus'] != null ? (json['shippingStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.initiated; + deliveredOn = json['deliveredOn']; + comment = json['comment']; + createdOn = json['createdOn']; + } +} diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index 37a20e6..5cd5769 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -30,7 +30,7 @@ abstract class AdsRepo { Future getAdBankingAccountInfo({required int adId}); - Future updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate}); + Future updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate, String? comment}); Future createAppointmentForAdSpecialService({required int adId, required int photoOfficeID, required int photoOfficeSlotID, required int adsSpecialServiceID}); @@ -263,11 +263,11 @@ class AdsRepoImp implements AdsRepo { } @override - Future updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate}) async { + Future updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate, String? comment}) async { var postParams = { "id": adId, "status": adStatusToUpdate.getIdFromAdPostStatusEnum().toString(), - "comment": "", + "comment": comment ?? "", }; String token = appState.getUser.data!.accessToken ?? ""; diff --git a/lib/repositories/common_repo.dart b/lib/repositories/common_repo.dart index 79358fe..df6275b 100644 --- a/lib/repositories/common_repo.dart +++ b/lib/repositories/common_repo.dart @@ -131,11 +131,21 @@ class CommonRepoImp implements CommonRepo { ], }; - if (json.containsKey('StatusMessage')) { - if ((json['StatusMessage'] as String).contains('Internal server error')) { - Utils.showToast("${json['StatusMessage']}"); - } - } + List enums = List.generate((json['data'] as List).length, (index) => EnumsModel.fromJson(json['data'][index])); + return enums; + } + + if (enumTypeID == -2) { + // This is for the ShippingRequestStatus which will be added to API later + final Map json = { + "data": [ + {"id": 1, "enumTypeID": 0, "enumValueStr": "Initiated", "enumValue": 1, "isActive": true}, + {"id": 2, "enumTypeID": 0, "enumValueStr": "In Transit", "enumValue": 2, "isActive": true}, + {"id": 3, "enumTypeID": 0, "enumValueStr": "Out for Delivery", "enumValue": 3, "isActive": true}, + {"id": 4, "enumTypeID": 0, "enumValueStr": "Delivered", "enumValue": 4, "isActive": true}, + ], + }; + List enums = List.generate((json['data'] as List).length, (index) => EnumsModel.fromJson(json['data'][index])); return enums; } diff --git a/lib/repositories/shipping_repo.dart b/lib/repositories/shipping_repo.dart new file mode 100644 index 0000000..6b99c6f --- /dev/null +++ b/lib/repositories/shipping_repo.dart @@ -0,0 +1,73 @@ +import 'dart:developer'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:mc_common_app/api/api_client.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/dependency_injection.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; +import 'package:mc_common_app/models/shipping_models/shipping_status_model.dart'; + +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; + +abstract class ShippingRepo { + Future> getShippingRequestListByStatus({ShippingRequestStatusEnum? shippingStatusEnum, int? requestId}); + + Future updateShippingRequestStatus({required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId, String? comment}); +} + +class ShippingRepoImp extends ShippingRepo { + ApiClient apiClient = injector.get(); + AppState appState = injector.get(); + + @override + Future> getShippingRequestListByStatus({ShippingRequestStatusEnum? shippingStatusEnum, int? requestId}) async { + String token = appState.getUser.data!.accessToken ?? ""; + Map queryParameters = { + "requestID": "${requestId ?? 0}", + "shippingStatus": "${shippingStatusEnum != null ? shippingStatusEnum.getIdFromShippingStatusEnum() : 0}", + }; + + GenericRespModel genericRespModel = await injector.get().postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.shippingRequestStatusGet, + queryParameters, + token: token, + ); + + if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) { + Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } + + List list = List.generate(genericRespModel.data.length, (index) => ShippingRequestModel.fromJson(genericRespModel.data[index])); + + return list; + } + + @override + Future updateShippingRequestStatus({required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId, String? comment}) async { + String token = appState.getUser.data!.accessToken ?? ""; + Map queryParameters = { + "id": "$shippingRequestId", + "shippingStatus": "${shippingStatusEnum.getIdFromShippingStatusEnum()}", + "comment": comment ?? "", + }; + + GenericRespModel genericRespModel = await injector.get().postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.shippingRequestStatusUpdate, + queryParameters, + token: token, + ); + + if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) { + Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr()); + } + + return genericRespModel; + } +} diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index 2549b6e..8717e22 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -16,6 +16,7 @@ class AppEnums { static const int appointmentsFilterEnumId = 13; // Appointments Filter Enums static const int requestStatusesFilterEnumId = 15; // Appointments Filter Enums static const int conditionEnumId = -1; // to get the Condition Filter Enums + static const int shippingStatusEnumId = -2; // to get the Shipping Filter Enums } enum VehicleType { @@ -210,3 +211,11 @@ enum SubscriptionActionTypeEnum { users, branches, } + +enum ShippingRequestStatusEnum { + allRequests, + initiated, + inTransit, + outForDelivery, + delivered, +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index c6a7ca3..3412f22 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -209,7 +209,7 @@ class Utils { return MyColors.inProgressColor; case AdPostStatus.active: - return MyColors.adActiveStatusColor; + return MyColors.greenColor; case AdPostStatus.expired: return MyColors.adCancelledStatusColor; @@ -286,6 +286,44 @@ class Utils { } } + static String getNameByShippingRequestStatusEnum(ShippingRequestStatusEnum shippingRequestStatusEnum) { + switch (shippingRequestStatusEnum) { + case ShippingRequestStatusEnum.allRequests: + return "All Requests"; + + case ShippingRequestStatusEnum.initiated: + return "Initiated"; + + case ShippingRequestStatusEnum.inTransit: + return "In Transit"; + + case ShippingRequestStatusEnum.outForDelivery: + return "Out For Delivery"; + + case ShippingRequestStatusEnum.delivered: + return "Delivered"; + } + } + + static Color getChipColorByShippingRequestStatusEnum(ShippingRequestStatusEnum shippingRequestStatusEnum) { + switch (shippingRequestStatusEnum) { + case ShippingRequestStatusEnum.allRequests: + return MyColors.submittedColor; + + case ShippingRequestStatusEnum.initiated: + return MyColors.submittedColor; + + case ShippingRequestStatusEnum.inTransit: + return MyColors.shippingColor; + + case ShippingRequestStatusEnum.outForDelivery: + return MyColors.deliveryColor; + + case ShippingRequestStatusEnum.delivered: + return MyColors.greenColor; + } + } + static Color getChipColorByBranchStatus(BranchStatusEnum branchStatusEnum) { switch (branchStatusEnum) { case BranchStatusEnum.pending: @@ -538,9 +576,7 @@ class Utils { static Widget buildStatusContainer(String text) { return Center( - child: text.toText(color: MyColors.lightTextColor, fontSize: 14 - // isItalic: true, - ), + child: text.toText(color: MyColors.lightTextColor, fontSize: 14), ).toContainer( marginAll: 8, paddingAll: 15, diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 10382f2..7efff83 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -361,9 +361,9 @@ class AdVM extends BaseVM { navigateReplaceWithName(context, AppRoutes.dashboard); } - Future deactivateTheAd(BuildContext context, {required int adId}) async { + Future deactivateTheAd(BuildContext context, {required int adId, String? comment}) async { Utils.showLoading(context); - GenericRespModel respModel = await adsRepo.updateAdStatus(adId: adId, adStatusToUpdate: AdPostStatus.cancelled); + GenericRespModel respModel = await adsRepo.updateAdStatus(adId: adId, adStatusToUpdate: AdPostStatus.cancelled, comment: comment); if (respModel.messageStatus != 1) { Utils.hideLoading(context); @@ -372,8 +372,9 @@ class AdVM extends BaseVM { } Utils.hideLoading(context); Utils.showToast(LocaleKeys.adDeactivatedSuccessfully.tr()); + updateDeactivateAdReasonDescription(''); updateIsExploreAds(false); - applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.cancelled); //pending for review + applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.cancelled); //Cacncelled navigateReplaceWithName(context, AppRoutes.dashboard); } @@ -580,6 +581,76 @@ class AdVM extends BaseVM { reservationCancelReason = value; } + List deActivateAdModelList = [ + OfferRequestCommentModel( + index: 0, + isSelected: true, + title: LocaleKeys.changedMind.tr(), + ), + OfferRequestCommentModel( + index: 1, + isSelected: false, + title: LocaleKeys.veryHighPrice.tr(), + ), + OfferRequestCommentModel( + index: 2, + isSelected: false, + title: LocaleKeys.alreadySold.tr(), + ), + OfferRequestCommentModel( + index: 3, + isSelected: false, + title: LocaleKeys.customerNotResponding.tr(), + ), + OfferRequestCommentModel( + index: 4, + isSelected: false, + title: LocaleKeys.otherVar.tr(), + ), + ]; + + OfferRequestCommentModel selectedDeActivateAdCommentModel = OfferRequestCommentModel( + index: 0, + isSelected: true, + title: LocaleKeys.changedMind.tr(), + ); + + void updateSelectionInDeActivateAdModelList(int index) { + for (var value in deActivateAdModelList) { + value.isSelected = false; + } + selectedDeActivateAdCommentModel = deActivateAdModelList[index]; + deActivateAdModelList[index].isSelected = true; + notifyListeners(); + } + + String deactivateAdReasonDescription = ""; + String deactivateAdReasonDescriptionError = ""; + + void updateDeactivateAdReasonDescription(String value) { + deactivateAdReasonDescription = value; + if (value.isNotEmpty) { + deactivateAdReasonDescriptionError = ""; + } + } + + bool isDeActivateReasonSubmitValidated() { + if (selectedDeActivateAdCommentModel.index != deActivateAdModelList.length - 1) { + return true; + } + bool isValidated = true; + if (deactivateAdReasonDescription.isEmpty) { + deactivateAdReasonDescriptionError = GlobalConsts.descriptionError; + isValidated = false; + } else { + deactivateAdReasonDescriptionError = ""; + } + + notifyListeners(); + + return isValidated; + } + String completeDealNotesForAdmin = ""; void updateCompleteDealNotesForAdmin(String value) { diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index ad800a4..eef76a4 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -218,8 +218,8 @@ class ServiceVM extends BaseVM { documentID == 1 ? commerceCertificates.addAll(imageModels) : documentID == 2 - ? commercialCertificates.addAll(imageModels) - : vatCertificates.addAll(imageModels); + ? commercialCertificates.addAll(imageModels) + : vatCertificates.addAll(imageModels); document!.data![index].document = Utils.convertFileToBase64(files.first); document!.data![index].fileExt = Utils.checkFileExt(files.first.path); document!.data![index].documentUrl = files.first.path; @@ -400,10 +400,10 @@ class ServiceVM extends BaseVM { DropValue( element.id ?? 0, ((element.categoryName!.isEmpty - ? "N/A" - : countryCode == "SA" - ? element.categoryNameN - : element.categoryName) ?? + ? "N/A" + : countryCode == "SA" + ? element.categoryNameN + : element.categoryName) ?? "N/A"), "", ), @@ -586,7 +586,9 @@ class ServiceVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path.split('/').last; + String fileName = file.path + .split('/') + .last; branchPostingImages = BranchPostingImages( imageName: fileName, imageStr: image, diff --git a/lib/view_models/shipping_management_view_model.dart b/lib/view_models/shipping_management_view_model.dart new file mode 100644 index 0000000..ca5a672 --- /dev/null +++ b/lib/view_models/shipping_management_view_model.dart @@ -0,0 +1,110 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/main.dart'; +import 'package:mc_common_app/models/general_models/enums_model.dart'; +import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; +import 'package:mc_common_app/models/general_models/widgets_models.dart'; +import 'package:mc_common_app/models/shipping_models/shipping_status_model.dart'; +import 'package:mc_common_app/repositories/common_repo.dart'; +import 'package:mc_common_app/repositories/shipping_repo.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; + +class ShippingManagementVM extends BaseVM { + final ShippingRepo shippingRepo; + final CommonRepo commonRepo; + + ShippingManagementVM({required this.shippingRepo, required this.commonRepo}); + + List shippingRequestsList = []; + + List shippingStatusEnums = []; + + List shippingRequestFilterOptions = []; + List shippingRequestStatusesList = []; + + String requestStatusComments = ""; + + void updateRequestStatusComments(var value) { + requestStatusComments = value; + } + + resetFilters() { + if (shippingRequestFilterOptions.isEmpty) return; + for (var value in shippingRequestFilterOptions) { + value.isSelected = false; + } + shippingRequestFilterOptions[0].isSelected = true; + requestStatusComments = ""; + } + + Future populateShippingRequestFilterList() async { + if (shippingRequestFilterOptions.isNotEmpty && shippingRequestStatusesList.isNotEmpty) return; + + shippingStatusEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.shippingStatusEnumId); + shippingRequestFilterOptions.clear(); + shippingRequestStatusesList.clear(); + for (int i = 0; i < shippingStatusEnums.length; i++) { + shippingRequestFilterOptions.add(FilterListModel(title: shippingStatusEnums[i].enumValueStr, isSelected: false, id: shippingStatusEnums[i].enumValue)); + shippingRequestStatusesList.add(FilterListModel(title: shippingStatusEnums[i].enumValueStr, isSelected: false, id: shippingStatusEnums[i].enumValue)); + } + + shippingRequestFilterOptions.insert(0, FilterListModel(title: Utils.getNameByShippingRequestStatusEnum(0.toShippingStatusEnum()), isSelected: true, id: 0)); + notifyListeners(); + } + + Future applyFiltersOnShippingRequests({required ShippingRequestStatusEnum shippingRequestStatusEnum}) async { + for (var value in shippingRequestFilterOptions) { + value.isSelected = false; + } + if (shippingRequestStatusEnum == ShippingRequestStatusEnum.allRequests) { + shippingRequestFilterOptions[0].isSelected = true; + await getShippingRequestsListByFilters(); + notifyListeners(); + return; + } + shippingRequestFilterOptions[shippingRequestStatusEnum.getIdFromShippingStatusEnum()].isSelected = true; // -1 to match with the index + await getShippingRequestsListByFilters(shippingStatusEnum: shippingRequestStatusEnum); + notifyListeners(); + } + + void updateSelectionInShippingRequestStatuses(int index) { + for (var value in shippingRequestStatusesList) { + value.isSelected = false; + } + shippingRequestStatusesList[index].isSelected = true; + notifyListeners(); + } + + Future getShippingRequestsListByFilters({ShippingRequestStatusEnum? shippingStatusEnum}) async { + setState(ViewState.busy); + try { + shippingRequestsList = await shippingRepo.getShippingRequestListByStatus(shippingStatusEnum: shippingStatusEnum); + setState(ViewState.idle); + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + setState(ViewState.idle); + } + } + + Future onUpdateShippingStatusTapped({required BuildContext context, required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId}) async { + + Utils.showLoading(context); + try { + GenericRespModel? genericRespModel = await shippingRepo.updateShippingRequestStatus(shippingStatusEnum: shippingStatusEnum, shippingRequestId: shippingRequestId); + Utils.showToast(genericRespModel.message.toString()); + Utils.hideLoading(context); + return genericRespModel.messageStatus == 1; + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + return false; + } + } +} diff --git a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart index 2ea8546..5b1564c 100644 --- a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart @@ -364,6 +364,7 @@ class _AdsDetailViewState extends State { child: Column( children: [ if (!(widget.adDetails.isMyAd ?? false)) ...[ + const Divider(thickness: 1, height: 1), const Divider(thickness: 1, height: 1), 18.height, Row( diff --git a/lib/views/advertisement/ads_detail_view/components.dart b/lib/views/advertisement/ads_detail_view/components.dart index dc21cfe..913e345 100644 --- a/lib/views/advertisement/ads_detail_view/components.dart +++ b/lib/views/advertisement/ads_detail_view/components.dart @@ -7,6 +7,7 @@ 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/advertisment_models/special_service_model.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_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'; @@ -19,6 +20,7 @@ import 'package:mc_common_app/views/advertisement/components/picked_images_conta 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/checkbox_with_title_desc.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'; @@ -512,22 +514,6 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { ); } - Widget buildStatusContainer(String text, {bool isForShippingOrDelivery = true}) { - return Center( - child: text.toText( - color: isForShippingOrDelivery ? MyColors.lightTextColor : MyColors.adPendingStatusColor, - fontSize: isForShippingOrDelivery ? 14 : 16, - // isItalic: true, - ), - ).toContainer( - marginAll: 12, - paddingAll: 12, - borderRadius: 8, - width: double.infinity, - backgroundColor: isForShippingOrDelivery ? MyColors.grey98Color.withOpacity(0.1) : MyColors.adPendingStatusColor.withOpacity(0.16), - ); - } - Widget pendingForPaymentAction(BuildContext context, {required AdDetailsModel ad}) { SpecialServiceModelForAds? photoSpecialServiceModel; for (var element in ad.specialservice!) { @@ -610,35 +596,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { title: LocaleKeys.deactivateAd.tr(), txtColor: MyColors.redColor, onPressed: () { - return actionConfirmationBottomSheet( - context: context, - title: LocaleKeys.doWantDeactivateAd.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), - subtitle: LocaleKeys.stoptheBuyers.tr(), - actionButtonYes: Expanded( - child: ShowFillButton( - maxHeight: 55, - title: LocaleKeys.yes.tr(), - fontSize: 15, - onPressed: () { - Navigator.pop(context); - adVM.deactivateTheAd(context, adId: adDetailsModel.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); - }, - ), - ), - ); + buildAdDeactivateReasonsBottomSheet(context, adDetails: adDetailsModel); }, ), ), @@ -648,6 +606,118 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { ); } + Future buildAdDeactivateReasonsBottomSheet(BuildContext context, {required AdDetailsModel adDetails}) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return Consumer(builder: (BuildContext context, AdVM adVM, 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.separated( + shrinkWrap: true, + itemCount: adVM.deActivateAdModelList.length, + separatorBuilder: (BuildContext context, int index) { + if (adVM.deActivateAdModelList[index].index == 3) { + return const SizedBox(); + } + return const Divider(thickness: 0.5); + }, + itemBuilder: (BuildContext context, int index) { + if (adVM.deActivateAdModelList[index].index == 3) { + return const SizedBox(); + } + OfferRequestCommentModel offerRequestCommentModel = adVM.deActivateAdModelList[index]; + return CircleCheckBoxWithTitle( + isChecked: offerRequestCommentModel.isSelected ?? false, + title: '${offerRequestCommentModel.title}', + onSelected: () { + adVM.updateSelectionInDeActivateAdModelList(index); + }, + selectedColor: MyColors.darkPrimaryColor, + ); + }, + ), + if (adVM.selectedDeActivateAdCommentModel.index == adVM.deActivateAdModelList.length - 1) ...[ + // comparing if the "other" is selected + 12.height, + TxtField( + maxLines: 5, + value: adVM.deactivateAdReasonDescription, + errorValue: adVM.deactivateAdReasonDescriptionError, + keyboardType: TextInputType.text, + hint: LocaleKeys.reason.tr(), + onChanged: (v) => adVM.updateDeactivateAdReasonDescription(v), + ), + ], + ], + ), + 25.height, + ShowFillButton( + title: LocaleKeys.submit.tr(), + onPressed: () { + String comments = ""; + if (adVM.selectedDeActivateAdCommentModel.index == adVM.deActivateAdModelList.length - 1) //Other + { + comments = adVM.deactivateAdReasonDescription; + } else { + comments = adVM.selectedDeActivateAdCommentModel.title ?? ""; + } + + if (!adVM.isDeActivateReasonSubmitValidated()) { + return; + } + + return actionConfirmationBottomSheet( + context: context, + title: LocaleKeys.doWantDeactivateAd.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: LocaleKeys.stoptheBuyers.tr(), + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.yes.tr(), + fontSize: 15, + onPressed: () { + adVM.deactivateTheAd(context, adId: adDetailsModel.id!, comment: comments); + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: LocaleKeys.no.tr(), + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + )); + }); + }, + ); + } + Future buildCancelReservationBottomSheet(BuildContext context, {required AdDetailsModel adDetails}) { return showModalBottomSheet( context: context, diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index 25288f5..f50967b 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -23,6 +23,7 @@ import 'package:mc_common_app/views/requests/request_bottomsheets.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:mc_common_app/widgets/row_with_arrow.dart'; import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart' as lcl; @@ -93,8 +94,35 @@ class _ChatViewState extends State { @override Widget build(BuildContext context) { + Widget? appBarHeadlines; + final requestVM = context.read(); + if (chatTypeEnum == ChatTypeEnum.requestOffer && + requestVM.currentSelectedRequest != null && + (requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.shipping || requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.delivery)) { + appBarHeadlines = Container( + width: double.infinity, + color: MyColors.darkIconColor, + padding: const EdgeInsets.symmetric(horizontal: 21, vertical: 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + "Your Delivery / Shipping Status is: ".toText( + fontSize: 12, + color: Colors.white, + decorationColor: MyColors.white, + ), + Utils.getNameByShippingRequestStatusEnum(requestVM.currentSelectedRequest?.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated).toText( + fontSize: 16, + color: Colors.white, + ), + ], + ), + ); + } return Scaffold( - appBar: CustomAppBar(title: LocaleKeys.chat.tr()), + appBar: CustomAppBar( + title: LocaleKeys.chat.tr(), + ), body: Consumer2(builder: (BuildContext context, ChatVM chatVM, RequestsVM requestVM, Widget? child) { List chatMessages = []; if (chatTypeEnum == ChatTypeEnum.ads) { @@ -108,6 +136,10 @@ class _ChatViewState extends State { } return Column( children: [ + if (appBarHeadlines != null) ...[ + appBarHeadlines, + 5.height, + ], Expanded( child: chatMessages.isEmpty ? Center(child: LocaleKeys.noChatMessage.tr().toText(fontSize: 16, color: MyColors.lightTextColor, textAlign: TextAlign.center)).paddingAll(22) diff --git a/lib/views/common_fragments/requests_fragment.dart b/lib/views/common_fragments/requests_fragment.dart index 608d373..0c0f924 100644 --- a/lib/views/common_fragments/requests_fragment.dart +++ b/lib/views/common_fragments/requests_fragment.dart @@ -110,7 +110,7 @@ class MyRequestsFragment extends StatelessWidget { 8.height, Expanded( child: RefreshIndicator( - onRefresh: () async => await requestsVM.getRequests(isNeedToRebuild: true), + onRefresh: () async => await requestsVM.getRequestsBasedOnFilters(), child: requestsVM.state == ViewState.busy ? const Center(child: CircularProgressIndicator()) : requestsVM.myFilteredRequests.isEmpty diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart index ea1dac7..0cb44d0 100644 --- a/lib/views/requests/offer_list_page.dart +++ b/lib/views/requests/offer_list_page.dart @@ -37,7 +37,7 @@ class OfferListPage extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.statusContainerChip(text: Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!), chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)), + Utils.statusContainerChip(text: "Offer ${Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)}", chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index 5c88a12..e09ef63 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -43,7 +43,7 @@ class RequestItem extends StatelessWidget { children: [ Utils.statusContainerChip(text: request.requestStatusName, chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)), 6.height, - "${request.brand} ${request.model}".toText(fontSize: 16, letterSpacing: -0.64), + "${request.brand} ${request.model} | ${request.id}".toText(fontSize: 16, letterSpacing: -0.64), showItem("${LocaleKeys.model.tr()}:", "${request.year}"), if (request.customerName.isNotEmpty) ...[ showItem("${LocaleKeys.customerName.tr()}:", request.customerName), diff --git a/lib/views/setting_options/setting_options_language.dart b/lib/views/setting_options/setting_options_language.dart index e501d1d..ea3da5a 100644 --- a/lib/views/setting_options/setting_options_language.dart +++ b/lib/views/setting_options/setting_options_language.dart @@ -52,15 +52,16 @@ class _SettingOptionsLanguageState extends State { needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.favoriteListView), ), + ] else ...[ + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.local_shipping, size: 20), + titleText: LocaleKeys.shippingManagement.tr(), + needBorderBelow: true, + onTap: () { + navigateWithName(context, AppRoutes.shippingManagementView); + }, + ), ], - // else ...[ - // CustomSettingOptionsTile( - // leadingWidget: const Icon(Icons.local_shipping, size: 20), - // titleText: LocaleKeys.shippingManagement.tr(), - // needBorderBelow: true, - // onTap: () {}, - // ), - // ], CustomSettingOptionsTile( leadingWidget: const Icon(Icons.settings, size: 20), titleText: LocaleKeys.settings.tr(), diff --git a/lib/views/shipping_management/shipping_management_view.dart b/lib/views/shipping_management/shipping_management_view.dart new file mode 100644 index 0000000..8f54e8e --- /dev/null +++ b/lib/views/shipping_management/shipping_management_view.dart @@ -0,0 +1,199 @@ +import 'dart:async'; +import 'dart:developer'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/general_models/widgets_models.dart'; +import 'package:mc_common_app/models/shipping_models/shipping_status_model.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/shipping_management_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/app_bar.dart'; +import 'package:mc_common_app/widgets/common_widgets/categories_list.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'; + +class ShippingManagementView extends StatefulWidget { + const ShippingManagementView({super.key}); + + @override + State createState() => _ShippingManagementViewState(); +} + +class _ShippingManagementViewState extends State { + late ShippingManagementVM shippingViewModel; + + @override + void initState() { + shippingViewModel = context.read(); + scheduleMicrotask(() async { + if (shippingViewModel.shippingRequestFilterOptions.isNotEmpty) { + await shippingViewModel.populateShippingRequestFilterList(); + } + await shippingViewModel.getShippingRequestsListByFilters(); + }); + super.initState(); + } + + Future buildUpdateShippingStatusBottomSheet({required int shippingRequestId}) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return Consumer(builder: (BuildContext context, ShippingManagementVM shippingManagementVM, 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.separated( + shrinkWrap: true, + itemCount: shippingManagementVM.shippingRequestStatusesList.length, + separatorBuilder: (BuildContext context, int index) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 3.0), + child: Divider(thickness: 0.5), + ); + }, + itemBuilder: (BuildContext context, int index) { + List list = shippingManagementVM.shippingRequestStatusesList; + FilterListModel shippingFilterListModel = list[index]; + return CircleCheckBoxWithTitle( + isChecked: shippingFilterListModel.isSelected, + title: shippingFilterListModel.title, + onSelected: () { + shippingManagementVM.updateSelectionInShippingRequestStatuses(index); + }, + selectedColor: MyColors.darkPrimaryColor, + ); + }, + ), + 12.height, + TxtField( + maxLines: 4, + value: shippingManagementVM.requestStatusComments, + errorValue: "", + keyboardType: TextInputType.text, + hint: LocaleKeys.comment.tr(), + onChanged: (v) => shippingManagementVM.updateRequestStatusComments(v), + ), + ], + ), + 25.height, + ShowFillButton( + title: LocaleKeys.submit.tr(), + onPressed: () async { + FilterListModel value = shippingManagementVM.shippingRequestStatusesList.firstWhere((element) => element.isSelected); + ShippingRequestStatusEnum shippingStatusEnum = value.id.toShippingStatusEnum(); + bool status = await shippingManagementVM.onUpdateShippingStatusTapped(shippingStatusEnum: shippingStatusEnum, shippingRequestId: shippingRequestId, context: context); + if (status) { + pop(context); + await shippingManagementVM.getShippingRequestsListByFilters(); + } + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + )); + }); + }, + ); + } + + @override + void dispose() { + shippingViewModel.resetFilters(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Consumer(builder: (BuildContext context, ShippingManagementVM shippingVm, Widget? child) { + return Scaffold( + appBar: CustomAppBar(title: LocaleKeys.shippingManagement.tr()), + body: Container( + color: MyColors.backgroundColor, + width: double.infinity, + height: double.infinity, + child: Column( + children: [ + 16.height, + FiltersList( + filterList: shippingVm.shippingRequestFilterOptions, + onFilterTapped: (index, selectedFilterId) { + shippingVm.applyFiltersOnShippingRequests(shippingRequestStatusEnum: selectedFilterId.toShippingStatusEnum()); + }, + ), + 8.height, + Expanded( + child: RefreshIndicator( + onRefresh: () async { + int index = shippingVm.shippingRequestFilterOptions.indexWhere((element) => element.isSelected); + await shippingVm.getShippingRequestsListByFilters(shippingStatusEnum: index.toShippingStatusEnum()); + }, + child: (shippingVm.state == ViewState.busy) + ? const Center( + child: CircularProgressIndicator(), + ) + : shippingVm.shippingRequestsList.isEmpty + ? Center(child: LocaleKeys.noRequeststoShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor)) + : ListView.separated( + itemCount: shippingVm.shippingRequestsList.length, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + ShippingRequestModel shippingRequest = shippingVm.shippingRequestsList[index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.statusContainerChip( + text: Utils.getNameByShippingRequestStatusEnum(shippingRequest.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated), + chipColor: Utils.getChipColorByShippingRequestStatusEnum(shippingRequest.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated), + ), + ("Request Name | ${shippingRequest.id.toString()}").toText(fontSize: 16), + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + "customer name".toText(color: MyColors.lightTextColor, fontSize: 14), + if (shippingRequest.createdOn != null && shippingRequest.createdOn!.isNotEmpty) ...[ + " | ${DateTime.parse(shippingRequest.createdOn!).getTimeAgo()}".toText(color: MyColors.lightTextColor, fontSize: 14), + ], + ], + ), + const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18), + ], + ), + ], + ).onPress(() { + buildUpdateShippingStatusBottomSheet(shippingRequestId: shippingRequest.id!); + }).toContainer(isShadowEnabled: true); + }, + separatorBuilder: (context, index) => 16.height, + ), + )), + ], + ), + )); + }); + } +} From 0230afc5c78678b970b10062894af838a7c4a8ff Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Mon, 21 Oct 2024 17:06:53 +0300 Subject: [PATCH 6/8] Fixed Ads Filter --- lib/repositories/ads_repo.dart | 16 +++++++++++++--- lib/view_models/ad_view_model.dart | 15 ++++++++++----- .../components/ads_list_widget.dart | 1 + lib/views/common_fragments/ads_fragment.dart | 6 +++++- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index 5cd5769..4a8b868 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -20,7 +20,7 @@ abstract class AdsRepo { Future createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew}); - Future> getAllAds({required bool isMyAds}); + Future> getAllAds({required bool isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum}); Future> getExploreAdsBasedOnFilters({List? cityIdsList, List? vehicleModelYearIdsList, List? vehicleBrandIdsList, List? createdByRolesIdsList}); @@ -140,16 +140,26 @@ class AdsRepoImp implements AdsRepo { } @override - Future> getAllAds({required isMyAds}) async { - var onlyMyAdsParams = { + Future> getAllAds({required isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum}) async { + Map onlyMyAdsParams = { "userID": appState.getUser.data!.userInfo!.userId ?? "", }; + if (isMyAds && adPostStatus != null) { + onlyMyAdsParams.addAll({ + "AdsStatuses": ["${adPostStatus.getIdFromAdPostStatusEnum()}"] + }); + } var allAdsParams = { "AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS "isActive": "true", //only Active ADS "isExplore": "true" }; + if (!isMyAds && createdByRoleEnum != null) { + allAdsParams.addAll({ + "CreatedByRoles": ["${createdByRoleEnum.getIdFromCreatedByRoleEnum()}"], + }); + } GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 7efff83..8483fe2 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -198,7 +198,7 @@ class AdVM extends BaseVM { notifyListeners(); } - applyFilterOnExploreAds({required CreatedByRoleEnum createdByRoleFilter}) { + applyFilterOnExploreAds({required CreatedByRoleEnum createdByRoleFilter}) async { if (exploreAdsFilterOptions.isEmpty) return; int index = exploreAdsFilterOptions.indexWhere((element) => element.id.toCreatedByRoleEnum() == createdByRoleFilter); @@ -212,12 +212,16 @@ class AdVM extends BaseVM { return; } - exploreAdsFilteredList = exploreAds.where((element) => element.createdByRoleEnum == createdByRoleFilter).toList(); + exploreAdsFilteredList = await getAdsByFilter(createdByRoleEnum: createdByRoleFilter, isMyAds: false); notifyListeners(); } - applyFilterOnMyAds({required AdPostStatus adPostStatusEnum}) { + Future> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum}) async { + return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum); + } + + applyFilterOnMyAds({required AdPostStatus adPostStatusEnum}) async { if (myAdsFilterOptions.isEmpty) return; int index = myAdsFilterOptions.indexWhere((element) => element.id.toAdPostEnum() == adPostStatusEnum); @@ -240,8 +244,9 @@ class AdVM extends BaseVM { notifyListeners(); return; } - - myAdsFilteredList = myAds.where((element) => element.statusID! == adPostStatusEnum.getIdFromAdPostStatusEnum()).toList(); + setState(ViewState.busy); + myAdsFilteredList = await getAdsByFilter(adPostStatus: adPostStatusEnum, isMyAds: true); + setState(ViewState.idle); notifyListeners(); } diff --git a/lib/views/advertisement/components/ads_list_widget.dart b/lib/views/advertisement/components/ads_list_widget.dart index 386552e..b2d2c9e 100644 --- a/lib/views/advertisement/components/ads_list_widget.dart +++ b/lib/views/advertisement/components/ads_list_widget.dart @@ -30,6 +30,7 @@ class AdsListWidget extends StatelessWidget { @override Widget build(BuildContext context) { + if (isAdsFragment && adsList.isEmpty) { return Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/views/common_fragments/ads_fragment.dart b/lib/views/common_fragments/ads_fragment.dart index 23ac72e..4125729 100644 --- a/lib/views/common_fragments/ads_fragment.dart +++ b/lib/views/common_fragments/ads_fragment.dart @@ -128,7 +128,11 @@ class AdsFragment extends StatelessWidget { await adVM.getExploreAds(); await adVM.getMyAds(); }, - child: AdsListWidget(isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM)), + child: adVM.state == ViewState.busy + ? const Center( + child: CircularProgressIndicator(), + ) + : AdsListWidget(isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM)), ), ) ], From 6e7480185240ffee7f3d911da01ce817e88938e5 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Thu, 24 Oct 2024 09:59:41 +0300 Subject: [PATCH 7/8] Added Settings Modules --- assets/langs/ar-SA.json | 10 +- assets/langs/en-US.json | 7 +- lib/classes/consts.dart | 6 + lib/config/dependency_injection.dart | 2 + lib/config/routes.dart | 20 +- lib/generated/codegen_loader.g.dart | 17 +- lib/generated/locale_keys.g.dart | 15 +- .../profile/document.dart | 4 +- .../setting_utils_models/app_info_model.dart | 26 +++ .../contact_infos_model.dart | 36 ++++ .../setting_utils_models/faqs_model.dart | 32 +++ lib/repositories/chat_repo.dart | 45 +++++ lib/repositories/setting_options_repo.dart | 84 ++++++++ lib/repositories/shipping_repo.dart | 1 - lib/view_models/ad_view_model.dart | 11 +- lib/view_models/appointments_view_model.dart | 41 ++++ lib/view_models/chat_view_model.dart | 184 ++++++++++++++---- lib/view_models/service_view_model.dart | 55 +++--- .../setting_options_view_model.dart | 59 ++++++ .../ads_detail_view/components.dart | 36 +++- .../picked_images_container_widget.dart | 2 +- .../appointments/review_appointment_view.dart | 2 +- lib/views/chat/chat_view.dart | 19 +- lib/views/chat/general_chats_list_page.dart | 113 +++++++++++ lib/views/common_fragments/ads_fragment.dart | 9 +- .../provider_license_page.dart | 78 ++++---- .../setting_options/setting_option_help.dart | 71 +++---- .../setting_options_app_info.dart | 117 +++++++++++ .../setting_options_contact_us.dart | 123 ++++++++++++ .../setting_options/setting_options_faqs.dart | 137 ++++++++----- .../setting_options_invite_friends.dart | 135 +++++++------ ...anguage.dart => setting_options_more.dart} | 29 +-- .../widgets/custom_setting_options_tile.dart | 11 +- lib/widgets/extensions/extensions_widget.dart | 7 +- 34 files changed, 1229 insertions(+), 315 deletions(-) create mode 100644 lib/models/setting_utils_models/app_info_model.dart create mode 100644 lib/models/setting_utils_models/contact_infos_model.dart create mode 100644 lib/models/setting_utils_models/faqs_model.dart create mode 100644 lib/repositories/setting_options_repo.dart create mode 100644 lib/view_models/setting_options_view_model.dart create mode 100644 lib/views/chat/general_chats_list_page.dart create mode 100644 lib/views/setting_options/setting_options_app_info.dart create mode 100644 lib/views/setting_options/setting_options_contact_us.dart rename lib/views/setting_options/{setting_options_language.dart => setting_options_more.dart} (88%) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 6a96725..0e1df59 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -665,11 +665,19 @@ "itemName": "اسم العنصر", "itemDescription": "وصف العنصر", "itemPrice": "سعر العنصر", + "bookAppointmentForServices": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.", + "showServiceAvailability": "سيظهر هذا الخيار للعميل ما إذا كان بإمكانه الحصول على هذه الخدمة في الورشة أم لا.", + "bookAppointmentAtLocation": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يرغب فيه.", "appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.", "workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.", "appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره.", "deleteScheduleConfirmation": "هل أنت متأكد أنك تريد حذف هذا الجدول؟", "deleteScheduleAdConfirmationMessage": "سيتم إزالة جميع فترات المواعيد ولن يتمكن العملاء من حجز مواعيد لهذا الجدول.", "branchSchedules": "جداول الفرع", - "noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع." + "noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع.", + "inviteFriendsBySMS": "دعوة الأصدقاء عبر الرسائل القصيرة", + "inviteFriendsByWhatsApp": "دعوة الأصدقاء عبر واتساب", + "inviteFriendsByEmail": "دعوة الأصدقاء عبر البريد الإلكتروني", + "noFAQsToShow": "لا توجد أسئلة شائعة في الوقت الحالي. يرجى الاتصال بنا إذا كان لديك أي استفسار.", + "appInfo": "معلومات التطبيق" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 3d26af4..a4cf2bd 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -672,5 +672,10 @@ "deleteScheduleConfirmation": " Are you sure you want to delete this Schedule?", "deleteScheduleAdConfirmationMessage": "All the appointment slots will be removed and customers will no longer be able to book appointments for this schedule.", "branchSchedules": "Branch Schedules", - "noSchedulesFound": "You have not added any schedule for this branch." + "noSchedulesFound": "You have not added any schedule for this branch.", + "inviteFriendsBySMS": "Invite Friends By SMS", + "inviteFriendsByWhatsApp": "Invite Friends By WhatsApp", + "inviteFriendsByEmail": "Invite Friends By Email", + "noFAQsToShow": "There are no Frequently asked Questions Right now. Please contact us if you have any query.", + "appInfo": "App Info" } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 9e3e565..59fd5a2 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -174,6 +174,12 @@ class ApiConsts { static String getChatMessagesForAds = "${baseUrlServices}api/Advertisement/AdsChat_Get"; static String getChatBuyersForAds = "${baseUrlServices}api/Advertisement/AdsChatBuyer_Get"; + //Settings Options + static String getAllFAQs = "${baseUrlServices}api/Common/FAQ_Get"; + static String getAppInvitationLink = "${baseUrlServices}api/Common/AppInvitation_Get"; + static String getContactInfo = "${baseUrlServices}api/Master/ContactInfo_Get"; + static String getAppInfo = "${baseUrlServices}api/Master/AppInfo_Get"; + static List closingUrls = ["PayFortResponse"]; } diff --git a/lib/config/dependency_injection.dart b/lib/config/dependency_injection.dart index f3c8745..8a42acd 100644 --- a/lib/config/dependency_injection.dart +++ b/lib/config/dependency_injection.dart @@ -12,6 +12,7 @@ import 'package:mc_common_app/repositories/branch_repo.dart'; import 'package:mc_common_app/repositories/chat_repo.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/repositories/payments_repo.dart'; +import 'package:mc_common_app/repositories/setting_options_repo.dart'; import 'package:mc_common_app/repositories/shipping_repo.dart'; import 'package:mc_common_app/repositories/user_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; @@ -44,6 +45,7 @@ class AppDependencies { injector.registerSingleton(() => ChatRepoImp()); injector.registerSingleton(() => BranchRepoImp()); injector.registerSingleton(() => ShippingRepoImp()); + injector.registerSingleton(() => SettingOptionsRepoImp()); // } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 515974b..ea4978e 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -20,9 +20,11 @@ import 'package:mc_common_app/views/requests/requests_filter_view.dart'; import 'package:mc_common_app/views/requests/review_request_offer.dart'; import 'package:mc_common_app/views/setting_options/provider_license_page.dart'; import 'package:mc_common_app/views/setting_options/setting_option_help.dart'; +import 'package:mc_common_app/views/setting_options/setting_options_app_info.dart'; +import 'package:mc_common_app/views/setting_options/setting_options_contact_us.dart'; import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart'; import 'package:mc_common_app/views/setting_options/setting_options_invite_friends.dart'; -import 'package:mc_common_app/views/setting_options/setting_options_language.dart'; +import 'package:mc_common_app/views/setting_options/setting_options_more.dart'; import 'package:mc_common_app/views/shipping_management/shipping_management_view.dart'; import 'package:mc_common_app/views/user/change_email_page.dart'; import 'package:mc_common_app/views/user/change_mobile_page.dart'; @@ -113,7 +115,9 @@ class AppRoutes { //Setting Options static const String settingOptionsFaqs = "/settingOptionsFaqs"; - static const String settingOptionsLanguages = "/settingOptionsLanguages"; + static const String settingOptionsContactUs = "/settingOptionsContactUs"; + static const String settingOptionsAppInfo = "/settingOptionsAppInfo"; + static const String settingOptionsMore = "/settingOptionsLanguages"; static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends"; static const String settingOptionsHelp = "/settingOptionsHelp"; @@ -128,9 +132,12 @@ class AppRoutes { //Provider User Page static const dealerUser = "/dealerUser"; + // HomePage Provider + + static const String generalChatsListForProvider = "/generalChatsListForProvider"; + //Chat static const String chatView = "/chatView"; - static const String initialRoute = splash; static final Map routes = { @@ -153,7 +160,7 @@ class AppRoutes { changePassword: (context) => const ChangePasswordPage(), editAccountPage: (context) => const EditAccountPage(), profileView: (context) => const ProfileScreen(), - settingOptionsLanguages: (context) => const SettingOptionsLanguage(), + settingOptionsMore: (context) => const SettingOptionsMore(), settingOptionsHelp: (context) => const SettingOptionsHelp(), providerLicensePage: (context) => const ProviderLicensePage(), @@ -165,6 +172,8 @@ class AppRoutes { AppRoutes.chatView: (context) => ChatView(chatViewArguments: ModalRoute.of(context)!.settings.arguments as ChatViewArguments), AppRoutes.adsBuyerChatsListView: (context) => AdsBuyerChatsView(buyersListViewArguments: ModalRoute.of(context)!.settings.arguments as List), AppRoutes.settingOptionsFaqs: (context) => const SettingOptionsFAQs(), + AppRoutes.settingOptionsContactUs: (context) => const SettingOptionsContactUs(), + AppRoutes.settingOptionsAppInfo: (context) => const SettingOptionsAppInfo(), AppRoutes.settingOptionsInviteFriends: (context) => const SettingOptionsInviteFriends(), AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypes), //Requests @@ -177,6 +186,9 @@ class AppRoutes { //MediaViewer AppRoutes.mediaViewerScreen: (context) => MediaViewerScreen(images: ModalRoute.of(context)!.settings.arguments as List), + // ChatsList Provider + AppRoutes.generalChatsListForProvider: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments), + //Shipping AppRoutes.shippingManagementView: (context) => const ShippingManagementView(), }; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 591c656..5e42c42 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -681,13 +681,21 @@ class CodegenLoader extends AssetLoader{ "itemName": "اسم العنصر", "itemDescription": "وصف العنصر", "itemPrice": "سعر العنصر", + "bookAppointmentForServices": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.", + "showServiceAvailability": "سيظهر هذا الخيار للعميل ما إذا كان بإمكانه الحصول على هذه الخدمة في الورشة أم لا.", + "bookAppointmentAtLocation": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يرغب فيه.", "appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.", "workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.", "appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره.", "deleteScheduleConfirmation": "هل أنت متأكد أنك تريد حذف هذا الجدول؟", "deleteScheduleAdConfirmationMessage": "سيتم إزالة جميع فترات المواعيد ولن يتمكن العملاء من حجز مواعيد لهذا الجدول.", "branchSchedules": "جداول الفرع", - "noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع." + "noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع.", + "inviteFriendsBySMS": "دعوة الأصدقاء عبر الرسائل القصيرة", + "inviteFriendsByWhatsApp": "دعوة الأصدقاء عبر واتساب", + "inviteFriendsByEmail": "دعوة الأصدقاء عبر البريد الإلكتروني", + "noFAQsToShow": "لا توجد أسئلة شائعة في الوقت الحالي. يرجى الاتصال بنا إذا كان لديك أي استفسار.", + "appInfo": "معلومات التطبيق" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1363,7 +1371,12 @@ static const Map en_US = { "deleteScheduleConfirmation": " Are you sure you want to delete this Schedule?", "deleteScheduleAdConfirmationMessage": "All the appointment slots will be removed and customers will no longer be able to book appointments for this schedule.", "branchSchedules": "Branch Schedules", - "noSchedulesFound": "You have not added any schedule for this branch." + "noSchedulesFound": "You have not added any schedule for this branch.", + "inviteFriendsBySMS": "Invite Friends By SMS", + "inviteFriendsByWhatsApp": "Invite Friends By WhatsApp", + "inviteFriendsByEmail": "Invite Friends By Email", + "noFAQsToShow": "There are no Frequently asked Questions Right now. Please contact us if you have any query.", + "appInfo": "App Info" }; 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 2e0162c..9734eae 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1,6 +1,6 @@ // DO NOT EDIT. This is code generated via package:easy_localization/generate.dart -abstract class LocaleKeys { +abstract class LocaleKeys { static const firstTimeLogIn = 'firstTimeLogIn'; static const signUp = 'signUp'; static const changeMobile = 'changeMobile'; @@ -47,7 +47,7 @@ abstract class LocaleKeys { static const emailChangedSuccessfully = 'emailChangedSuccessfully'; static const passwordIsUpdated = 'passwordIsUpdated'; static const passwordShouldContains = 'passwordShouldContains'; - static const successfullyRegistered = ' successfullyRegistered'; + static const successfullyRegistered = ' successfullyRegistered'; static const pleaseEnterSamePassword = 'pleaseEnterSamePassword'; static const firstNameMandatory = 'firstNameMandatory'; static const surnameNameMandatory = 'surnameNameMandatory'; @@ -644,6 +644,9 @@ abstract class LocaleKeys { static const itemName = 'itemName'; static const itemDescription = 'itemDescription'; static const itemPrice = 'itemPrice'; + static const bookAppointmentForServices = 'bookAppointmentForServices'; + static const showServiceAvailability = 'showServiceAvailability'; + static const bookAppointmentAtLocation = 'bookAppointmentAtLocation'; static const appointmentBookingOption = 'appointmentBookingOption'; static const workshopAvailabilityOption = 'workshopAvailabilityOption'; static const appointmentLocationOption = 'appointmentLocationOption'; @@ -651,8 +654,10 @@ abstract class LocaleKeys { static const deleteScheduleAdConfirmationMessage = 'deleteScheduleAdConfirmationMessage'; static const branchSchedules = 'branchSchedules'; static const noSchedulesFound = 'noSchedulesFound'; - static const showServiceAvailability = 'showServiceAvailability'; - static const bookAppointmentForServices = 'bookAppointmentForServices'; - static const bookAppointmentAtLocation = 'bookAppointmentAtLocation'; + static const inviteFriendsBySMS = 'inviteFriendsBySMS'; + static const inviteFriendsByWhatsApp = 'inviteFriendsByWhatsApp'; + static const inviteFriendsByEmail = 'inviteFriendsByEmail'; + static const noFAQsToShow = 'noFAQsToShow'; + static const appInfo = 'appInfo'; } diff --git a/lib/models/provider_branches_models/profile/document.dart b/lib/models/provider_branches_models/profile/document.dart index 497e93b..eb6d2f8 100644 --- a/lib/models/provider_branches_models/profile/document.dart +++ b/lib/models/provider_branches_models/profile/document.dart @@ -55,9 +55,9 @@ class DocumentData { int? id; int? serviceProviderId; int? documentId; - dynamic? documentUrl; + String? documentUrl; int? status; - dynamic? comment; + String? comment; bool? isActive; String? document; String? fileExt; diff --git a/lib/models/setting_utils_models/app_info_model.dart b/lib/models/setting_utils_models/app_info_model.dart new file mode 100644 index 0000000..1bb141f --- /dev/null +++ b/lib/models/setting_utils_models/app_info_model.dart @@ -0,0 +1,26 @@ +import 'package:mc_common_app/view_models/ad_view_model.dart'; + +class AppInfoModel { + int? id; + String? header; + String? content; + bool? isActive; + int? channel; + List? appInfoImages; + + AppInfoModel({this.id, this.header, this.content, this.isActive, this.channel, this.appInfoImages}); + + AppInfoModel.fromJson(Map json) { + id = json['id']; + header = json['header']; + content = json['content']; + isActive = json['isActive']; + channel = json['channel']; + if (json['appInfoImages'] != null) { + appInfoImages = []; + json['appInfoImages'].forEach((v) { + appInfoImages!.add(ImageModel.fromJson(v)); + }); + } + } +} diff --git a/lib/models/setting_utils_models/contact_infos_model.dart b/lib/models/setting_utils_models/contact_infos_model.dart new file mode 100644 index 0000000..d7e6ea6 --- /dev/null +++ b/lib/models/setting_utils_models/contact_infos_model.dart @@ -0,0 +1,36 @@ +import 'package:mc_common_app/view_models/ad_view_model.dart'; + +class ContactInfoModel { + int? id; + String? companyName; + String? mobileNo; + String? phoneNo; + String? email; + String? address; + String? latitude; + String? longitude; + int? channel; + bool? isActive; + List? contactInfoImages; + + ContactInfoModel({this.id, this.companyName, this.mobileNo, this.phoneNo, this.email, this.address, this.latitude, this.longitude, this.channel, this.isActive, this.contactInfoImages}); + + ContactInfoModel.fromJson(Map json) { + id = json['id']; + companyName = json['companyName']; + mobileNo = json['mobileNo']; + phoneNo = json['phoneNo']; + email = json['email']; + address = json['address']; + latitude = json['latitude']; + longitude = json['longitude']; + channel = json['channel']; + isActive = json['isActive']; + if (json['contactInfoImages'] != null) { + contactInfoImages = []; + json['contactInfoImages'].forEach((v) { + contactInfoImages!.add(ImageModel.fromJson(v)); + }); + } + } +} diff --git a/lib/models/setting_utils_models/faqs_model.dart b/lib/models/setting_utils_models/faqs_model.dart new file mode 100644 index 0000000..6ee0944 --- /dev/null +++ b/lib/models/setting_utils_models/faqs_model.dart @@ -0,0 +1,32 @@ +class FAQsModel { + int? id; + String? question; + String? answer; + int? sequenceNo; + bool? isActive; + int? channel; + bool? isCollapsed; + + FAQsModel({this.id, this.question, this.answer, this.sequenceNo, this.isActive, this.channel, this.isCollapsed}); + + FAQsModel.fromJson(Map json) { + id = json['id']; + question = json['question']; + answer = json['answer']; + sequenceNo = json['sequenceNo']; + isActive = json['isActive']; + channel = json['channel']; + isCollapsed = false; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['question'] = question; + data['answer'] = answer; + data['sequenceNo'] = sequenceNo; + data['isActive'] = isActive; + data['channel'] = channel; + return data; + } +} diff --git a/lib/repositories/chat_repo.dart b/lib/repositories/chat_repo.dart index b23999f..f099937 100644 --- a/lib/repositories/chat_repo.dart +++ b/lib/repositories/chat_repo.dart @@ -33,6 +33,15 @@ abstract class ChatRepo { int pageIndex = 0, int pageSize = 0, }); + + Future> getUsersChatMessagesForGeneralChat({ + String? userID, + int? adID, + int? adsChatBuyerId, + required bool isForBuyer, + int pageIndex = 0, + int pageSize = 0, + }); } class ChatRepoImp implements ChatRepo { @@ -66,6 +75,8 @@ class ChatRepoImp implements ChatRepo { url = ApiConsts.messageIsReadUpdateForAds; } else if (chatTypeEnum == ChatTypeEnum.requestOffer) { url = ApiConsts.messageIsReadUpdateForRequests; + } else if (chatTypeEnum == ChatTypeEnum.general) { + url = ApiConsts.messageIsReadUpdateForRequests; } var queryParameters = {"iDs": messageIds}; GenericRespModel genericRespModel = await apiClient.postJsonForObject( @@ -144,4 +155,38 @@ class ChatRepoImp implements ChatRepo { } return []; } + + @override + Future> getUsersChatMessagesForGeneralChat({String? userID, int? adID, int? adsChatBuyerId, required bool isForBuyer, int pageIndex = 0, int pageSize = 0}) async { + var parameterForBuyer = { + "UserID": userID.toString(), + "AdsID": adID.toString(), + "IsAdsBuyer": isForBuyer.toString(), + "PageSize": pageSize.toString(), + "PageIndex": pageIndex.toString(), + }; + + var parameterForSeller = { + "AdsChatBuyerID": adsChatBuyerId.toString(), + "IsAdsBuyer": isForBuyer.toString(), + "PageSize": pageSize.toString(), + "PageIndex": pageIndex.toString(), + }; + + var queryParameters = isForBuyer ? parameterForBuyer : parameterForSeller; + GenericRespModel genericRespModel = await apiClient.getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getChatMessagesForAds, + queryParameters: queryParameters, + token: appState.getUser.data!.accessToken, + ); + if (genericRespModel.messageStatus == 1 && genericRespModel.data != null) { + List chatMessages = List.generate( + genericRespModel.data.length, + (index) => ChatMessageModel.fromJson(genericRespModel.data[index]), + ); + return chatMessages; + } + return []; + } } diff --git a/lib/repositories/setting_options_repo.dart b/lib/repositories/setting_options_repo.dart new file mode 100644 index 0000000..f4122ca --- /dev/null +++ b/lib/repositories/setting_options_repo.dart @@ -0,0 +1,84 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:mc_common_app/api/api_client.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/dependency_injection.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart'; +import 'package:mc_common_app/utils/utils.dart'; + +abstract class SettingOptionsRepo { + Future> getAllFaqs(); + + Future> getAllContactInfos(); + + Future> getAppInfoList(); +} + +class SettingOptionsRepoImp extends SettingOptionsRepo { + ApiClient apiClient = injector.get(); + AppState appState = injector.get(); + + @override + Future> getAllFaqs() async { + String token = appState.getUser.data!.accessToken ?? ""; + + GenericRespModel genericRespModel = await injector.get().getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getAllFAQs, + token: token, + ); + + if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) { + Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } + + List list = List.generate(genericRespModel.data.length, (index) => FAQsModel.fromJson(genericRespModel.data[index])); + + return list; + } + + @override + Future> getAllContactInfos() async { + String token = appState.getUser.data!.accessToken ?? ""; + + GenericRespModel genericRespModel = await injector.get().getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getContactInfo, + token: token, + ); + + if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) { + Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } + + List list = List.generate(genericRespModel.data.length, (index) => ContactInfoModel.fromJson(genericRespModel.data[index])); + + return list; + } + + @override + Future> getAppInfoList() async { + String token = appState.getUser.data!.accessToken ?? ""; + + GenericRespModel genericRespModel = await injector.get().getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getAppInfo, + token: token, + ); + + if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) { + Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } + + List list = List.generate(genericRespModel.data.length, (index) => AppInfoModel.fromJson(genericRespModel.data[index])); + + return list; + } +} diff --git a/lib/repositories/shipping_repo.dart b/lib/repositories/shipping_repo.dart index 6b99c6f..e1b0312 100644 --- a/lib/repositories/shipping_repo.dart +++ b/lib/repositories/shipping_repo.dart @@ -1,4 +1,3 @@ -import 'dart:developer'; import 'package:easy_localization/easy_localization.dart'; import 'package:mc_common_app/api/api_client.dart'; diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 8483fe2..1c33200 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -211,8 +211,9 @@ class AdVM extends BaseVM { notifyListeners(); return; } - + setState(ViewState.busy); exploreAdsFilteredList = await getAdsByFilter(createdByRoleEnum: createdByRoleFilter, isMyAds: false); + setState(ViewState.idle); notifyListeners(); } @@ -328,7 +329,7 @@ class AdVM extends BaseVM { return; } Utils.hideLoading(context); - Utils.showToast(respModel.message ?? "A has been marked as sold successfully!"); + Utils.showToast(respModel.message ?? "Ad has been marked as sold successfully!"); updateIsExploreAds(false); applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.sold); //pending for review navigateReplaceWithName(context, AppRoutes.dashboard); @@ -1985,6 +1986,12 @@ class ImageModel { ImageModel({this.id, this.filePath, this.isFromNetwork}); + ImageModel.fromJson(Map json) { + id = json['id']; + filePath = json['image']; + isFromNetwork = true; + } + @override String toString() { return 'ImageModel{id: $id, isFromNetwork: $isFromNetwork, filePath: $filePath}'; diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 9b3a3ed..cf7ee78 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -2,6 +2,7 @@ import 'dart:developer'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; @@ -1498,4 +1499,44 @@ class AppointmentsVM extends BaseVM { } setState(ViewState.idle); } + + // Chat Related + + Future getGeneralChatsListForProvider({required BuildContext context}) async { + try { + // Utils.showLoading(context); + // List buyersChatList = await appointmentRepo.getChatBuyersForAds(adsID: adsID); + // Utils.hideLoading(context); + // buyersChatListForAds.clear(); + // buyersChatListForAds = buyersChatList; + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + } + } + + void onGeneralChatMessagesButtonPressed({required BuildContext context}) async { + final myUserID = AppState().getUser.data!.userInfo!.userId; + // if (adDetailsModel.isMyAd != null && adDetailsModel.isMyAd!) { + // await getGeneralChatsListForProvider(context: context); + // // navigateWithName(context, AppRoutes.generalChatsListForProvider, arguments: buyersChatListForAds); + // } else { + // ChatViewArgumentsForAd chatViewArgumentsForAd = ChatViewArgumentsForAd(receiverUserID: adDetailsModel.userID, adsID: adDetailsModel.id); + // ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.ads, chatViewArgumentsForAd: chatViewArgumentsForAd); + // final chatVM = context.read(); + // await chatVM + // .getUsersChatMessagesForAd( + // 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 d360063..2825c3a 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -193,14 +193,6 @@ class ChatVM extends ChangeNotifier { notifyListeners(); } - Future onNewMessageReceivedForAds({required List messages}) async { - for (var msg in messages) { - currentMessagesForAds.add(msg); - } - scrollChatDown(); - notifyListeners(); - } - void subscribeToReceiveRequestOfferMessages(BuildContext context) { hubConnection!.on(SignalrConsts.receiveMessageRequestOffer, (List? arguments) { if (arguments == null || arguments.isEmpty) return; @@ -215,18 +207,19 @@ class ChatVM extends ChangeNotifier { }); } - void subscribeToReceiveAdMessages(BuildContext context) { - hubConnection!.on(SignalrConsts.receiveMessageAds, (List? arguments) { - if (arguments == null || arguments.isEmpty) return; - List chat = []; - for (var message in arguments) { - final chatMessage = ChatMessageModel.fromJson(message as Map); - chat.add(chatMessage); - } - onNewMessageReceivedForAds(messages: chat); - logger.i(arguments); - // Utils.showToast(arguments.toString()); - }); + Future markMessagesAsRead(BuildContext context, List messageIds, ChatTypeEnum chatTypeEnum) async { + bool status = false; + try { + // Utils.showLoading(context); + status = await chatRepo.markMessageAsRead(messageIds: messageIds, chatTypeEnum: chatTypeEnum); + // Utils.hideLoading(context); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + // Utils.hideLoading(context); + status = false; + } + return status; } Future buildHubConnection(BuildContext context) async { @@ -236,6 +229,7 @@ class ChatVM extends ChangeNotifier { await hubConnection!.start(); subscribeToReceiveRequestOfferMessages(context); subscribeToReceiveAdMessages(context); + subscribeToReceiveGeneralMessages(context); hubConnection!.onclose((exception) { logger.i("onClose: ${exception.toString()}"); buildHubConnection(context); @@ -303,11 +297,7 @@ class ChatVM extends ChangeNotifier { MessageImageModel convertFileToMessageImageModel({required File file}) { List imageBytes = file.readAsBytesSync(); String image = base64Encode(imageBytes); - MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image, - id: 0, - isFromNetwork: false, - reqOfferID: latestOfferId, - imagePath: file.path); + MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image, id: 0, isFromNetwork: false, reqOfferID: latestOfferId, imagePath: file.path); return vehiclePostingImages; } @@ -413,10 +403,7 @@ class ChatVM extends ChangeNotifier { serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel); } } else { - int providerIndex = context - .read() - .myFilteredRequests - .indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId)); + int providerIndex = context.read().myFilteredRequests.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId)); log("providerIndex2:$providerIndex"); if (providerIndex != -1) { context.read().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex); @@ -615,19 +602,26 @@ class ChatVM extends ChangeNotifier { } } - Future markMessagesAsRead(BuildContext context, List messageIds, ChatTypeEnum chatTypeEnum) async { - bool status = false; - try { - // Utils.showLoading(context); - status = await chatRepo.markMessageAsRead(messageIds: messageIds, chatTypeEnum: chatTypeEnum); - // Utils.hideLoading(context); - } catch (e) { - logger.i(e.toString()); - Utils.showToast(e.toString()); - // Utils.hideLoading(context); - status = false; + void subscribeToReceiveAdMessages(BuildContext context) { + hubConnection!.on(SignalrConsts.receiveMessageAds, (List? arguments) { + if (arguments == null || arguments.isEmpty) return; + List chat = []; + for (var message in arguments) { + final chatMessage = ChatMessageModel.fromJson(message as Map); + chat.add(chatMessage); + } + onNewMessageReceivedForAds(messages: chat); + logger.i(arguments); + // Utils.showToast(arguments.toString()); + }); + } + + Future onNewMessageReceivedForAds({required List messages}) async { + for (var msg in messages) { + currentMessagesForAds.add(msg); } - return status; + scrollChatDown(); + notifyListeners(); } Future onTextMessageSendForAds({ @@ -679,7 +673,113 @@ class ChatVM extends ChangeNotifier { notifyListeners(); - if (AppState().currentAppType == AppType.customer) {} else {} + if (AppState().currentAppType == AppType.customer) { + } else {} + return true; + } + return false; + } + + // ========================General ================== + + List currentMessagesForGeneralChat = []; + + Future getUsersChatMessagesForGeneralChat({required BuildContext context, int? adID, int? adsChatBuyerId, String? userID, String? senderName, required bool isForBuyer}) async { + try { + Utils.showLoading(context); + currentMessagesForGeneralChat = await chatRepo.getUsersChatMessagesForGeneralChat(userID: userID, adID: adID, isForBuyer: isForBuyer, adsChatBuyerId: adsChatBuyerId); + Utils.hideLoading(context); + + List unreadMessageIds = []; + + for (var msg in currentMessagesForGeneralChat) { + msg.senderName = senderName ?? ""; + if (!msg.isRead! && !msg.isMyMessage!) { + unreadMessageIds.add(msg.id!); + } + } + + if (unreadMessageIds.isNotEmpty) { + await markMessagesAsRead(context, unreadMessageIds, ChatTypeEnum.general); + } + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + } + } + + Future onNewMessageReceivedForGeneral({required List messages}) async { + for (var msg in messages) { + currentMessagesForAds.add(msg); + } + scrollChatDown(); + notifyListeners(); + } + + void subscribeToReceiveGeneralMessages(BuildContext context) { + hubConnection!.on(SignalrConsts.receiveMessageGeneral, (List? arguments) { + if (arguments == null || arguments.isEmpty) return; + List chat = []; + for (var message in arguments) { + final chatMessage = ChatMessageModel.fromJson(message as Map); + chat.add(chatMessage); + } + onNewMessageReceivedForGeneral(messages: chat); + logger.i(arguments); + // Utils.showToast(arguments.toString()); + }); + } + + Future onTextMessageSendForGeneralChat({ + required String receiverId, + required ChatMessageTypeEnum chatMessageType, + required String message, + required int adId, + required BuildContext context, + }) async { + if (message.isEmpty) return false; + + if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) { + logger.i("Hub Not Connected!!"); + await buildHubConnection(context); + } + + if (hubConnection!.state == HubConnectionState.connected) { + final userId = AppState().getUser.data!.userInfo!.userId.toString(); + final name = AppState().getUser.data!.userInfo!.firstName.toString(); + final obj = { + "ReceiverUserID": receiverId, + "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), + "ChatText": message, + }; + logger.i("$obj"); + + hubConnection!.invoke( + SignalrConsts.sendMessageGeneral, + args: [obj], + ).catchError((e) { + logger.i("error in invoking SendMessage: ${e.toString()}"); + Utils.showToast(e.toString()); + return false; + }); + + ChatMessageModel chatMessageModel = ChatMessageModel( + messageType: chatMessageType.getIdFromChatMessageTypeEnum(), + chatText: message, + isMyMessage: true, + senderName: name, + senderUserID: userId, + chatMessageTypeEnum: ChatMessageTypeEnum.freeText, + receiverUserID: receiverId, + ); + + currentMessagesForGeneralChat.add(chatMessageModel); + notifyListeners(); + + if (AppState().currentAppType == AppType.customer) { + } else {} return true; } return false; diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index eef76a4..ce58286 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -94,6 +94,14 @@ class ServiceVM extends BaseVM { Future getServiceProviderDocument(int providerId) async { setState(ViewState.busy); document = await branchRepo.getServiceProviderDocument(providerId); + if (document != null && document!.data != null && document!.data!.isNotEmpty) { + for (var doc in document!.data!) { + log("doc: ${doc.status}"); + if (doc.status != 3) { + updateIsAllDocsApproved(false); + } + } + } setState(ViewState.idle); } @@ -202,13 +210,20 @@ class ServiceVM extends BaseVM { // return null; // } + bool isAllDocsApproved = false; + + updateIsAllDocsApproved(var value) { + isAllDocsApproved = value; + notifyListeners(); + } + Future pickPdfReceiptFile(BuildContext context, int documentID, int index) async { List imageModels = []; List? files = await commonServices.pickMultipleFiles( context, allowMultiple: false, ); - if (files == null) return null; + if (files == null) return; for (var element in files) { imageModels.add(ImageModel( filePath: element.path, @@ -218,8 +233,8 @@ class ServiceVM extends BaseVM { documentID == 1 ? commerceCertificates.addAll(imageModels) : documentID == 2 - ? commercialCertificates.addAll(imageModels) - : vatCertificates.addAll(imageModels); + ? commercialCertificates.addAll(imageModels) + : vatCertificates.addAll(imageModels); document!.data![index].document = Utils.convertFileToBase64(files.first); document!.data![index].fileExt = Utils.checkFileExt(files.first.path); document!.data![index].documentUrl = files.first.path; @@ -237,18 +252,6 @@ class ServiceVM extends BaseVM { notifyListeners(); } - Future removeNetworkImag(String filePath) async { - log("removeNetworkImag with: $filePath"); - - int index = document!.data!.indexWhere((element) => element.documentUrl == filePath); - if (index == -1) { - return; - } - document!.data![index].documentUrl = null; - document!.data![index].isLocalFile = true; - notifyListeners(); - } - Future commercialRemove(String filePath) async { log("commercialRemove with: $filePath"); @@ -271,6 +274,16 @@ class ServiceVM extends BaseVM { notifyListeners(); } + Future removeNetworkImage(String filePath) async { + int index = document!.data!.indexWhere((element) => element.documentUrl == filePath); + if (index == -1) { + return; + } + document!.data![index].documentUrl = null; + document!.data![index].isLocalFile = true; + notifyListeners(); + } + Future updateDocument(List? data) async { return await branchRepo.serviceProviderDocumentsUpdate(data); } @@ -400,10 +413,10 @@ class ServiceVM extends BaseVM { DropValue( element.id ?? 0, ((element.categoryName!.isEmpty - ? "N/A" - : countryCode == "SA" - ? element.categoryNameN - : element.categoryName) ?? + ? "N/A" + : countryCode == "SA" + ? element.categoryNameN + : element.categoryName) ?? "N/A"), "", ), @@ -586,9 +599,7 @@ class ServiceVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path - .split('/') - .last; + String fileName = file.path.split('/').last; branchPostingImages = BranchPostingImages( imageName: fileName, imageStr: image, diff --git a/lib/view_models/setting_options_view_model.dart b/lib/view_models/setting_options_view_model.dart new file mode 100644 index 0000000..d48d294 --- /dev/null +++ b/lib/view_models/setting_options_view_model.dart @@ -0,0 +1,59 @@ +import 'package:mc_common_app/main.dart'; +import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart'; +import 'package:mc_common_app/repositories/setting_options_repo.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; + +class SettingOptionsVM extends BaseVM { + final SettingOptionsRepo settingOptionsRepo; + + SettingOptionsVM({required this.settingOptionsRepo}); + + List faqsList = []; + + Future getAllFaqs() async { + setState(ViewState.busy); + try { + faqsList = await settingOptionsRepo.getAllFaqs(); + setState(ViewState.idle); + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + setState(ViewState.idle); + } + } + + List contactInfosList = []; + + Future getAllContactInfosList() async { + setState(ViewState.busy); + try { + contactInfosList = await settingOptionsRepo.getAllContactInfos(); + setState(ViewState.idle); + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + setState(ViewState.idle); + } + } + + List appInfoList = []; + + Future getAppInfoList() async { + setState(ViewState.busy); + try { + appInfoList = await settingOptionsRepo.getAppInfoList(); + setState(ViewState.idle); + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + setState(ViewState.idle); + } + } +} diff --git a/lib/views/advertisement/ads_detail_view/components.dart b/lib/views/advertisement/ads_detail_view/components.dart index 913e345..9216b4f 100644 --- a/lib/views/advertisement/ads_detail_view/components.dart +++ b/lib/views/advertisement/ads_detail_view/components.dart @@ -279,11 +279,11 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { builder: (BuildContext context) { return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) { return InfoBottomSheet( - title: LocaleKeys.setDateandTime.tr().toText(fontSize: 16, isBold: true, letterSpacing: -1.44, height: 1.2), + title: LocaleKeys.setDateandTime.tr().toText(fontSize: 16, height: 1.2), description: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - 25.height, + 20.height, adVM.state == ViewState.busy ? const Center(child: CircularProgressIndicator()) : Builder( @@ -345,7 +345,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { ], ), ], - 19.height, + 30.height, ], )); }); @@ -579,7 +579,34 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { title: LocaleKeys.markAsSold.tr(), isBold: false, onPressed: () { - adVM.markAdAsSold(context, adId: adDetailsModel.id!); + return actionConfirmationBottomSheet( + context: context, + title: LocaleKeys.markAsSold.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: LocaleKeys.markAsSold.tr(), + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.yes.tr(), + fontSize: 15, + onPressed: () { + adVM.markAdAsSold(context, adId: adDetailsModel.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); + }, + ), + ), + ); }, ), ), @@ -1017,6 +1044,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { case AdPostStatus.rejected: return pendingForReviewAction(pendingText: LocaleKeys.rejectedFormAdmin.tr()); case AdPostStatus.cancelled: + return pendingForReviewAction(pendingText: LocaleKeys.cancelledByOwner.tr()); case AdPostStatus.pendingForPost: return pendingForReviewAction(pendingText: LocaleKeys.waitingAdminPost.tr()); diff --git a/lib/views/advertisement/components/picked_images_container_widget.dart b/lib/views/advertisement/components/picked_images_container_widget.dart index ff41080..1e134bc 100644 --- a/lib/views/advertisement/components/picked_images_container_widget.dart +++ b/lib/views/advertisement/components/picked_images_container_widget.dart @@ -11,11 +11,11 @@ class PickedFilesContainer extends StatelessWidget { final Function(String filePath)? onCrossPressedPrimary; final Function(int index, String filePath)? onCrossPressedSecondary; final int? index; + final Function() onAddFilePressed; final bool isReview; final bool isPdf; final bool isFromNetwork; final bool allowAdButton; - final Function() onAddFilePressed; const PickedFilesContainer({ Key? key, diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart index 7082f4e..1ecfa41 100644 --- a/lib/views/appointments/review_appointment_view.dart +++ b/lib/views/appointments/review_appointment_view.dart @@ -218,7 +218,7 @@ class ReviewAppointment extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Divider(thickness: 0.7, height: 3), + const Divider(thickness: 0.7, height: 3), 8.height, if (appointmentsVM.amountToPayForAppointment > 0) ...[ Row( diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index f50967b..fb7eb9c 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -23,7 +23,6 @@ import 'package:mc_common_app/views/requests/request_bottomsheets.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; -import 'package:mc_common_app/widgets/row_with_arrow.dart'; import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart' as lcl; @@ -51,6 +50,9 @@ class _ChatViewState extends State { chatViewArgumentsForAd = widget.chatViewArguments.chatViewArgumentsForAd; } else if (chatTypeEnum == ChatTypeEnum.requestOffer) { chatViewArgumentsForRequest = widget.chatViewArguments.chatViewArgumentsForRequest; + } else if (chatTypeEnum == ChatTypeEnum.general) { + // For General Chat + chatViewArgumentsForAd = widget.chatViewArguments.chatViewArgumentsForAd; } chatVM = context.read(); chatVM.isUserOnChatScreen = true; @@ -86,7 +88,15 @@ class _ChatViewState extends State { adId: chatViewArgumentsForAd!.adsID!, context: context, ); - } else if (chatTypeEnum == ChatTypeEnum.general) {} + } else if (chatTypeEnum == ChatTypeEnum.general) { + status = await chatVM.onTextMessageSendForGeneralChat( + receiverId: chatViewArgumentsForAd!.receiverUserID ?? "", + chatMessageType: ChatMessageTypeEnum.freeText, + message: chatVM.chatMessageText, + adId: chatViewArgumentsForAd!.adsID!, + context: context, + ); + } Utils.hideLoading(context); return status; @@ -125,7 +135,9 @@ class _ChatViewState extends State { ), body: Consumer2(builder: (BuildContext context, ChatVM chatVM, RequestsVM requestVM, Widget? child) { List chatMessages = []; - if (chatTypeEnum == ChatTypeEnum.ads) { + if (chatTypeEnum == ChatTypeEnum.general) { + chatMessages = chatVM.currentMessagesForGeneralChat; + } else if (chatTypeEnum == ChatTypeEnum.ads) { chatMessages = chatVM.currentMessagesForAds; } else if (chatTypeEnum == ChatTypeEnum.requestOffer) { if (AppState().currentAppType == AppType.customer && chatVM.serviceProviderOffersList.isNotEmpty) { @@ -252,7 +264,6 @@ class _ChatViewState extends State { flex: 1, child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress( () async { - debugPrint("AppState().getUser.data.userInfo.userId: ${AppState().getUser.data!.userInfo!.userId}"); ChatMessageTypeEnum chatMessageTypeEnum = ChatMessageTypeEnum.freeText; if (chatVM.pickedImagesForMessage.isNotEmpty) { diff --git a/lib/views/chat/general_chats_list_page.dart b/lib/views/chat/general_chats_list_page.dart new file mode 100644 index 0000000..ab7eafe --- /dev/null +++ b/lib/views/chat/general_chats_list_page.dart @@ -0,0 +1,113 @@ +import 'dart:developer'; + +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/models/requests_models/provider_offers_model.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/chat_view_model.dart'; +import 'package:mc_common_app/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 GeneralChatListPage extends StatelessWidget { + final OfferListPageArguments generalChatListPageArguments; + + const GeneralChatListPage({super.key, required this.generalChatListPageArguments}); + + @override + Widget build(BuildContext context) { + final List serviceProviderOffers = generalChatListPageArguments.serviceProviderOffers; + return Scaffold( + appBar: CustomAppBar(title: LocaleKeys.offers.tr()), + body: serviceProviderOffers.isEmpty + ? Center(child: LocaleKeys.noOffersShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor)) + : ListView.separated( + itemCount: serviceProviderOffers.length, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + ServiceProvidersOffers offersModel = serviceProviderOffers[index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.statusContainerChip(text: "Offer ${Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)}", chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (offersModel.name ?? "").toText(fontSize: 16, isBold: true), + Center( + child: "${offersModel.offerCount}".toText( + color: Colors.white, + isBold: true, + fontSize: 10, + ), + ).toContainer( + backgroundColor: MyColors.redColor, + borderRadius: 100, + paddingAll: 1, + width: 22, + height: 22, + ), + ], + ), + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + "${offersModel.companyName}".toText(color: MyColors.lightTextColor, fontSize: 14), + if (offersModel.createdOn != null && offersModel.createdOn!.isNotEmpty) ...[ + " | ${DateTime.parse(offersModel.createdOn!).getTimeAgo()}".toText(color: MyColors.lightTextColor, fontSize: 14), + ], + // " | 1 hour ago".toText( + // color: MyColors.lightTextColor, + // fontSize: 12, + // ), + ], + ), + const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18), + ], + ), + ], + ).onPress(() async { + ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest( + chatTypeEnum: ChatTypeEnum.requestOffer, + receiverId: "${offersModel.providerUserId}", + senderId: AppState().getUser.data!.userInfo!.userId.toString(), + requestId: generalChatListPageArguments.requestId, + providerIndex: index, + requestIndex: -1, // This will be only send in case of provider + ); + + ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest); + + final chatVM = context.read(); + + await chatVM + .getRequestsChatMessagesForCustomer( + context: context, + providerId: offersModel.providerId ?? 0, + requestOfferId: 0, + requestId: generalChatListPageArguments.requestId ?? 0, + providerOfferIndex: index, + ) + .whenComplete( + () => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments), + ); + }).toContainer(isShadowEnabled: true); + }, + separatorBuilder: (context, index) => 16.height, + ), + ); + } +} diff --git a/lib/views/common_fragments/ads_fragment.dart b/lib/views/common_fragments/ads_fragment.dart index 4125729..9b08eb8 100644 --- a/lib/views/common_fragments/ads_fragment.dart +++ b/lib/views/common_fragments/ads_fragment.dart @@ -125,8 +125,13 @@ class AdsFragment extends StatelessWidget { Expanded( child: RefreshIndicator( onRefresh: () async { - await adVM.getExploreAds(); - await adVM.getMyAds(); + if (adVM.isExploreAdsTapped) { + CreatedByRoleEnum createdByRoleEnum = adVM.exploreAdsFilterOptions.firstWhere((element) => element.isSelected).id.toCreatedByRoleEnum(); + adVM.applyFilterOnExploreAds(createdByRoleFilter: createdByRoleEnum); + } else { + AdPostStatus adPostStatusEnum = adVM.myAdsFilterOptions.firstWhere((element) => element.isSelected).id.toAdPostEnum(); + adVM.applyFilterOnMyAds(adPostStatusEnum: adPostStatusEnum); + } }, child: adVM.state == ViewState.busy ? const Center( diff --git a/lib/views/setting_options/provider_license_page.dart b/lib/views/setting_options/provider_license_page.dart index afad8bb..3718669 100644 --- a/lib/views/setting_options/provider_license_page.dart +++ b/lib/views/setting_options/provider_license_page.dart @@ -1,8 +1,5 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:developer'; -import 'dart:io'; - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; @@ -10,7 +7,6 @@ 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/main.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/provider_branches_models/profile/document.dart'; import 'package:mc_common_app/theme/colors.dart'; @@ -27,7 +23,6 @@ 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:sizer/sizer.dart'; class ProviderLicensePage extends StatefulWidget { const ProviderLicensePage({super.key}); @@ -40,35 +35,25 @@ class _ProviderLicensePageState extends State { late ServiceVM branchVM; bool showAttachment = false; String? attachedFile; - bool isAllApproved = true; @override void initState() { super.initState(); - scheduleMicrotask(() { - // branchVM = Provider.of(context, listen: false); - context.read().getServiceProviderDocument(AppState().getUser.data!.userInfo!.providerId ?? 0); + scheduleMicrotask(() async { + final serviceVm = context.read(); + await serviceVm.getServiceProviderDocument(AppState().getUser.data!.userInfo!.providerId ?? 0); }); } @override Widget build(BuildContext context) { - final serviceVm = context.read(); - if (serviceVm.document != null && serviceVm.document!.data != null && serviceVm.document!.data!.isNotEmpty) { - for (var doc in serviceVm.document!.data!) { - if (doc.status != 3) { - isAllApproved = false; - } - } - setState(() {}); - } return Scaffold( appBar: CustomAppBar( title: LocaleKeys.defineLicences.tr(), isRemoveBackButton: false, ), - body: Consumer(builder: (_, model, __) { - if (model.state == ViewState.busy) { + body: Consumer(builder: (_, serviceVM, __) { + if (serviceVM.state == ViewState.busy) { return const Center( child: CircularProgressIndicator(), ); @@ -77,9 +62,9 @@ class _ProviderLicensePageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: SingleChildScrollView(child: buildContent(model)), + child: SingleChildScrollView(child: buildContent(serviceVM)), ), - if (!isAllApproved) ...[ + if (!serviceVM.isAllDocsApproved) ...[ Padding( padding: const EdgeInsets.all(12.0), child: ShowFillButton( @@ -88,14 +73,14 @@ class _ProviderLicensePageState extends State { onPressed: () async { bool status = false; if (AppState().getUser.data!.userInfo!.roleId == 5) { - if (validation(model)) { - status = await updateDocument(model); + if (validateDocuments(serviceVM)) { + status = await updateDocument(serviceVM); } else { Utils.showToast(LocaleKeys.allDocumentMandatoryDealershipProvider.tr()); return; } } else { - status = await updateDocument(model); + status = await updateDocument(serviceVM); } Future.delayed(const Duration(seconds: 1), () { if (status) { @@ -112,9 +97,10 @@ class _ProviderLicensePageState extends State { ); } - validation(ServiceVM model) { + validateDocuments(ServiceVM model) { bool valid = true; for (var element in model.document!.data!) { + log("documentUrl: ${element.documentUrl}"); if (element.documentUrl == null) { valid = false; } @@ -140,19 +126,19 @@ class _ProviderLicensePageState extends State { } } - Widget buildContent(ServiceVM model) { - return model.document!.data!.isEmpty + Widget buildContent(ServiceVM serviceVM) { + return serviceVM.document!.data!.isEmpty ? Text(LocaleKeys.somethingWrong.tr()) : ListView.separated( separatorBuilder: (context, index) { return 20.height; }, - itemCount: model.document!.data!.length, + itemCount: serviceVM.document!.data!.length, physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.symmetric(horizontal: 20), itemBuilder: (context, index) { - DocumentData? document = model.document?.data![index]; + DocumentData? document = serviceVM.document?.data![index]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -179,28 +165,29 @@ class _ProviderLicensePageState extends State { ), ], 10.height, - if (isNeedToShow(model: model, document: document)) ...[ + if (isNeedToShow(model: serviceVM, document: document)) ...[ PickedFilesContainer( - isReview: document.status != 4, - pickedFiles: isLocalOrNetworkFiles(model: model, document: document), + isReview: document.status == 4, + allowAdButton: false, + pickedFiles: isLocalOrNetworkFiles(model: serviceVM, document: document), onCrossPressedPrimary: chkIsLocalOrNetwork(document: document) - ? model.removeNetworkImag + ? serviceVM.removeNetworkImage : document.documentId == 1 - ? model.commerceRemove + ? serviceVM.commerceRemove : document.documentId == 2 - ? model.commercialRemove - : model.vatRemove, - isPdf: model.document!.data![index].fileExt == "pdf", + ? serviceVM.commercialRemove + : serviceVM.vatRemove, + isPdf: serviceVM.document!.data![index].fileExt == "pdf", isFromNetwork: !(document.isLocalFile ?? false), onAddFilePressed: () { - model.pickPdfReceiptFile(context, document.documentId!, index); + serviceVM.pickPdfReceiptFile(context, document.documentId!, index); }, ), buildCommentContainer(document: document), ] else ...[ 10.height, DottedRectContainer( - onTap: () => model.pickPdfReceiptFile(context, document.documentId!, index) ?? "", + onTap: () => serviceVM.pickPdfReceiptFile(context, document.documentId!, index) ?? "", text: LocaleKeys.attachImage.tr(), icon: MyAssets.attachmentIcon.buildSvg(), ), @@ -214,7 +201,7 @@ class _ProviderLicensePageState extends State { List isLocalOrNetworkFiles({required ServiceVM model, required DocumentData document}) { bool isNetworkImage = false; if (!document.isLocalFile!) { - isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false; + isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false; } if (isNetworkImage) { return [ImageModel(id: document.id, isFromNetwork: isNetworkImage, filePath: document.documentUrl)]; @@ -229,7 +216,8 @@ class _ProviderLicensePageState extends State { bool isNeedToShow({required ServiceVM model, required DocumentData document}) { bool allow = false; - bool isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false; + bool isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty && !(document.isLocalFile ?? true); + log("document.documentUrl: ${document.documentUrl}"); if (isNetworkImage) { allow = true; } else { @@ -247,7 +235,7 @@ class _ProviderLicensePageState extends State { } dynamic checkOnCrossPress({required ServiceVM model, required DocumentData document}) async { - bool isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false; + bool isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false; if (isNetworkImage) { return document.documentUrl; } else { @@ -266,7 +254,7 @@ class _ProviderLicensePageState extends State { bool chkIsLocalOrNetwork({required DocumentData document}) { bool isNetworkImage = false; if (!document.isLocalFile!) { - isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false; + isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false; } return isNetworkImage; } @@ -274,7 +262,7 @@ class _ProviderLicensePageState extends State { Widget buildCommentContainer({required DocumentData document}) { String comment = ""; if (document.status == 4 && document.comment != null) { - comment = document.comment; + comment = document.comment ?? ""; } if (comment.isEmpty) { diff --git a/lib/views/setting_options/setting_option_help.dart b/lib/views/setting_options/setting_option_help.dart index 6ffb72f..38fc246 100644 --- a/lib/views/setting_options/setting_option_help.dart +++ b/lib/views/setting_options/setting_option_help.dart @@ -3,7 +3,9 @@ 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/navigator.dart'; +import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -28,24 +30,46 @@ class SettingOptionsHelp extends StatelessWidget { Column( children: [ CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.info_rounded, size: 20), + leadingWidget: const Icon( + Icons.info_rounded, + size: 20, + color: MyColors.greyColor, + ), titleText: LocaleKeys.faqs.tr(), needBorderBelow: true, - onTap: () {}, + onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs), ), CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.mail, size: 20), + leadingWidget: const Icon( + Icons.mail, + size: 20, + color: MyColors.greyColor, + ), titleText: LocaleKeys.contactUs.tr(), needBorderBelow: true, - onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs), + onTap: () => navigateWithName(context, AppRoutes.settingOptionsContactUs), ), CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.lock, size: 20), + leadingWidget: const Icon( + Icons.lock, + size: 20, + color: MyColors.greyColor, + ), titleText: LocaleKeys.termPrivacy.tr(), + needBorderBelow: true, onTap: () {}, ), + CustomSettingOptionsTile( + leadingWidget: const Icon( + Icons.info_rounded, + size: 20, + color: MyColors.greyColor, + ), + titleText: LocaleKeys.appInfo.tr(), + onTap: () => navigateWithName(context, AppRoutes.settingOptionsAppInfo), + ), ], - ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), + ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(21, 24, 21, 0), borderRadius: 0), ], ), ), @@ -54,38 +78,3 @@ class SettingOptionsHelp extends StatelessWidget { ); } } - -class CustomSettingOptionsTile extends StatelessWidget { - final Widget leadingWidget; - final String titleText; - final bool needBorderBelow; - final bool isForLanguage; - final Function() onTap; - - const CustomSettingOptionsTile({super.key, required this.leadingWidget, required this.onTap, required this.titleText, this.needBorderBelow = false, this.isForLanguage = false}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - leadingWidget, - 12.width, - titleText.toText(fontSize: 16), - ], - ), - isForLanguage ? const Icon(Icons.language, size: 18) : const Icon(Icons.arrow_forward, size: 18) - ], - ).onPress(onTap), - 5.height, - if (needBorderBelow) ...[ - const Divider(thickness: 1), - ], - ], - ); - } -} diff --git a/lib/views/setting_options/setting_options_app_info.dart b/lib/views/setting_options/setting_options_app_info.dart new file mode 100644 index 0000000..998a514 --- /dev/null +++ b/lib/views/setting_options/setting_options_app_info.dart @@ -0,0 +1,117 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/view_models/ad_view_model.dart'; +import 'package:mc_common_app/view_models/setting_options_view_model.dart'; +import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:provider/provider.dart'; + +class SettingOptionsAppInfo extends StatefulWidget { + const SettingOptionsAppInfo({super.key}); + + @override + State createState() => _SettingOptionsAppInfoState(); +} + +class _SettingOptionsAppInfoState extends State { + late SettingOptionsVM settingsOptionsVM; + + @override + void initState() { + settingsOptionsVM = context.read(); + scheduleMicrotask(() async { + await settingsOptionsVM.getAppInfoList(); + }); + super.initState(); + } + + Widget showData(String title, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.toText( + fontSize: 13, + color: MyColors.darkTextColor, + ), + if (title.isNotEmpty) 5.width, + Flexible( + child: value.toText( + fontSize: 13, + color: MyColors.lightTextColor, + ), + ) + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: LocaleKeys.appInfo.tr(), + isRemoveBackButton: false, + isDrawerEnabled: false, + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Consumer( + builder: (BuildContext context, SettingOptionsVM settingsOptionsVM, Widget? child) { + return Container( + color: MyColors.backgroundColor, + width: double.infinity, + height: double.infinity, + child: Column( + children: [ + 16.height, + Expanded( + child: RefreshIndicator( + onRefresh: () async => await settingsOptionsVM.getAllFaqs(), + child: (settingsOptionsVM.state == ViewState.busy) + ? const Center(child: CircularProgressIndicator()) + : settingsOptionsVM.appInfoList.isEmpty + ? Padding( + padding: const EdgeInsets.all(21), + child: Center(child: LocaleKeys.somethingWrong.tr().toText(textAlign: TextAlign.center, fontSize: 16, color: MyColors.lightTextColor)), + ) + : ListView.separated( + itemCount: settingsOptionsVM.appInfoList.length, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + AppInfoModel appInfoModel = settingsOptionsVM.appInfoList[index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (appInfoModel.header ?? "").toString().toText(fontSize: 16), + 5.height, + (appInfoModel.content ?? "").toString().toText(fontSize: 14, color: MyColors.lightTextColor), + if (appInfoModel.appInfoImages != null && appInfoModel.appInfoImages!.isNotEmpty) ...[ + PickedFilesContainer( + pickedFiles: appInfoModel.appInfoImages ?? [], + isReview: true, + onAddFilePressed: () {}, + ), + ] + ], + ).toContainer(isShadowEnabled: true); + }, + separatorBuilder: (context, index) => 16.height, + ), + )), + ], + ), + ); + }, + )); + } +} diff --git a/lib/views/setting_options/setting_options_contact_us.dart b/lib/views/setting_options/setting_options_contact_us.dart new file mode 100644 index 0000000..05bf650 --- /dev/null +++ b/lib/views/setting_options/setting_options_contact_us.dart @@ -0,0 +1,123 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/view_models/ad_view_model.dart'; +import 'package:mc_common_app/view_models/setting_options_view_model.dart'; +import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:provider/provider.dart'; + +class SettingOptionsContactUs extends StatefulWidget { + const SettingOptionsContactUs({super.key}); + + @override + State createState() => _SettingOptionsContactUsState(); +} + +class _SettingOptionsContactUsState extends State { + late SettingOptionsVM settingsOptionsVM; + + @override + void initState() { + settingsOptionsVM = context.read(); + scheduleMicrotask(() async { + await settingsOptionsVM.getAllContactInfosList(); + }); + super.initState(); + } + + Widget showData(String title, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.toText( + fontSize: 13, + color: MyColors.darkTextColor, + ), + if (title.isNotEmpty) 5.width, + Flexible( + child: value.toText( + fontSize: 13, + color: MyColors.lightTextColor, + ), + ) + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: LocaleKeys.contactUs.tr(), + isRemoveBackButton: false, + isDrawerEnabled: false, + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Consumer( + builder: (BuildContext context, SettingOptionsVM settingsOptionsVM, Widget? child) { + return Container( + color: MyColors.backgroundColor, + width: double.infinity, + height: double.infinity, + child: Column( + children: [ + 16.height, + Expanded( + child: RefreshIndicator( + onRefresh: () async => await settingsOptionsVM.getAllFaqs(), + child: (settingsOptionsVM.state == ViewState.busy) + ? const Center(child: CircularProgressIndicator()) + : settingsOptionsVM.contactInfosList.isEmpty + ? Padding( + padding: const EdgeInsets.all(21), + child: Center(child: LocaleKeys.somethingWrong.tr().toText(textAlign: TextAlign.center, fontSize: 16, color: MyColors.lightTextColor)), + ) + : ListView.separated( + itemCount: settingsOptionsVM.contactInfosList.length, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + ContactInfoModel contactInfoModel = settingsOptionsVM.contactInfosList[index]; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + contactInfoModel.companyName.toString().toText(fontSize: 16), + 5.height, + LocaleKeys.openMapLocation.tr().toText(isUnderLine: true, color: MyColors.darkPrimaryColor, fontSize: 10).onPress(() {}), + showData("${LocaleKeys.phoneNumber.tr()}:", "${contactInfoModel.phoneNo}"), + showData("${LocaleKeys.email.tr()}:", "${contactInfoModel.email}"), + if (contactInfoModel.address != null && contactInfoModel.address!.isNotEmpty) ...[ + showData("${LocaleKeys.address.tr()}:", ''), + "${contactInfoModel.address}".toText(height: 1.5, fontSize: 13, color: MyColors.lightTextColor), + ], + if (contactInfoModel.contactInfoImages != null && contactInfoModel.contactInfoImages!.isNotEmpty) ...[ + PickedFilesContainer( + pickedFiles: contactInfoModel.contactInfoImages ?? [], + isReview: true, + onAddFilePressed: () {}, + ), + ] + ], + ).toContainer(isShadowEnabled: true); + }, + separatorBuilder: (context, index) => 16.height, + ), + )), + ], + ), + ); + }, + )); + } +} diff --git a/lib/views/setting_options/setting_options_faqs.dart b/lib/views/setting_options/setting_options_faqs.dart index fb3abde..d6e7bfa 100644 --- a/lib/views/setting_options/setting_options_faqs.dart +++ b/lib/views/setting_options/setting_options_faqs.dart @@ -1,61 +1,104 @@ +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/views/setting_options/widgets/custom_setting_options_tile.dart'; +import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/view_models/setting_options_view_model.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:provider/provider.dart'; -class SettingOptionsFAQs extends StatelessWidget { +class SettingOptionsFAQs extends StatefulWidget { const SettingOptionsFAQs({super.key}); + @override + State createState() => _SettingOptionsFAQsState(); +} + +class _SettingOptionsFAQsState extends State { + late SettingOptionsVM settingsOptionsVM; + + @override + void initState() { + settingsOptionsVM = context.read(); + scheduleMicrotask(() async { + await settingsOptionsVM.getAllFaqs(); + }); + super.initState(); + } + @override Widget build(BuildContext context) { return Scaffold( - appBar: CustomAppBar( - title: LocaleKeys.more.tr(), - isRemoveBackButton: false, - isDrawerEnabled: false, - onBackButtonTapped: () => Navigator.pop(context), - ), - body: Column( - children: [ - Expanded( - child: ListView( - children: [ - Column( - children: [ - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.info, size: 20), - titleText: LocaleKeys.faqs.tr(), - needBorderBelow: true, - onTap: () {}), - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.mail, size: 20), - titleText: LocaleKeys.contactUs.tr(), - needBorderBelow: true, - onTap: () {}), - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.settings, size: 20), - titleText: LocaleKeys.termPrivacy.tr(), - onTap: () {}), - ], - ).toContainer( - width: double.infinity, - isShadowEnabled: true, - paddingAll: 10, - margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), - borderRadius: 0), - ], - ), - ), - Row( - children: [ - "Version: 1.0.0".toText(fontSize: 14), - ], - ).paddingAll(21), - ], - ), - ); + appBar: CustomAppBar( + title: LocaleKeys.faqs.tr(), + isRemoveBackButton: false, + isDrawerEnabled: false, + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Consumer( + builder: (BuildContext context, SettingOptionsVM settingsOptionsVM, Widget? child) { + return Container( + color: MyColors.backgroundColor, + width: double.infinity, + height: double.infinity, + child: Column( + children: [ + 16.height, + Expanded( + child: RefreshIndicator( + onRefresh: () async => await settingsOptionsVM.getAllFaqs(), + child: (settingsOptionsVM.state == ViewState.busy) + ? const Center(child: CircularProgressIndicator()) + : settingsOptionsVM.faqsList.isEmpty + ? Padding( + padding: const EdgeInsets.all(21), + child: Center(child: LocaleKeys.noFAQsToShow.tr().toText(textAlign: TextAlign.center, fontSize: 16, color: MyColors.lightTextColor)), + ) + : ListView.separated( + itemCount: settingsOptionsVM.faqsList.length, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + FAQsModel faqModel = settingsOptionsVM.faqsList[index]; + return ExpansionTile( + tilePadding: EdgeInsets.zero, + shape: const Border(), + title: (faqModel.question ?? "").toText(fontSize: 16).paddingOnly(left: 8, right: 8), + onExpansionChanged: (value) { + setState(() { + settingsOptionsVM.faqsList[index].isCollapsed = value; + }); + }, + backgroundColor: Colors.transparent, + collapsedBackgroundColor: Colors.transparent, + initiallyExpanded: settingsOptionsVM.faqsList[index].isCollapsed ?? false, + trailing: (settingsOptionsVM.faqsList[index].isCollapsed ?? false) ? const Icon(Icons.keyboard_arrow_up) : const Icon(Icons.keyboard_arrow_down), + subtitle: ("Channel: ${faqModel.channel.toString()}").toText(color: MyColors.lightTextColor).paddingOnly(left: 8, right: 8), + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 5.height, + (faqModel.answer ?? "").toText(color: MyColors.lightTextColor, fontSize: 14, fontWeight: MyFonts.Medium), + 10.height, + ], + ).paddingOnly(left: 8, right: 8) + ], + ).toContainer(isShadowEnabled: true); + }, + separatorBuilder: (context, index) => 12.height, + ), + )), + ], + ), + ); + }, + )); } } diff --git a/lib/views/setting_options/setting_options_invite_friends.dart b/lib/views/setting_options/setting_options_invite_friends.dart index 2d48e3f..d09c9ab 100644 --- a/lib/views/setting_options/setting_options_invite_friends.dart +++ b/lib/views/setting_options/setting_options_invite_friends.dart @@ -1,21 +1,76 @@ import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; -import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.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:easy_localization/easy_localization.dart'; class SettingOptionsInviteFriends extends StatelessWidget { const SettingOptionsInviteFriends({super.key}); + buildInviteFriendsBottomSheet(BuildContext context) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return InfoBottomSheet( + description: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 20.height, + CustomSettingOptionsTile( + leadingWidget: MyAssets.whatsAppIcon.buildSvg( + height: 20, + width: 20, + color: MyColors.greyColor, + ), + titleText: LocaleKeys.inviteFriendsByWhatsApp.tr(), + needBorderBelow: true, + showTrailingArrow: false, + onTap: () {}, + ), + CustomSettingOptionsTile( + leadingWidget: const Icon( + Icons.sms_outlined, + size: 20, + color: MyColors.greyColor, + ), + titleText: LocaleKeys.inviteFriendsBySMS.tr(), + needBorderBelow: true, + showTrailingArrow: false, + onTap: () {}, + ), + CustomSettingOptionsTile( + leadingWidget: const Icon( + Icons.email_outlined, + size: 20, + color: MyColors.greyColor, + ), + titleText: LocaleKeys.inviteFriendsByEmail.tr(), + needBorderBelow: false, + showTrailingArrow: false, + onTap: () {}, + ), + 30.height, + ], + ), + title: const SizedBox()); + }, + ); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar( - title: LocaleKeys.more.tr(), + title: LocaleKeys.settings.tr(), isRemoveBackButton: false, isDrawerEnabled: false, onBackButtonTapped: () => Navigator.pop(context), @@ -28,31 +83,34 @@ class SettingOptionsInviteFriends extends StatelessWidget { Column( children: [ CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.person, size: 20), + leadingWidget: const Icon(Icons.group, size: 20), titleText: LocaleKeys.inviteFriends.tr(), needBorderBelow: true, - onTap: () {}, + onTap: () { + return buildInviteFriendsBottomSheet(context); + }, ), CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.help, size: 20), + leadingWidget: const Icon(Icons.question_mark_outlined, size: 20), titleText: LocaleKeys.help.tr(), needBorderBelow: true, - onTap: () => navigateWithName( - context, AppRoutes.settingOptionsFaqs), + onTap: () => navigateWithName(context, AppRoutes.settingOptionsHelp), ), CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.person, size: 20), - titleText: LocaleKeys.account.tr(), - onTap: () => - navigateWithName(context, AppRoutes.profileView), - ), + leadingWidget: const Icon(Icons.person, size: 20), + titleText: LocaleKeys.account.tr(), + needBorderBelow: false, + onTap: () { + navigateWithName(context, AppRoutes.profileView); + }), ], ).toContainer( - width: double.infinity, - isShadowEnabled: true, - paddingAll: 10, - margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), - borderRadius: 0), + width: double.infinity, + isShadowEnabled: true, + paddingAll: 10, + margin: const EdgeInsets.fromLTRB(21, 21, 21, 0), + borderRadius: 0, + ), ], ), ), @@ -61,46 +119,3 @@ class SettingOptionsInviteFriends extends StatelessWidget { ); } } - -class CustomSettingOptionsTile extends StatelessWidget { - final Widget leadingWidget; - final String titleText; - final bool needBorderBelow; - final bool isForLanguage; - final Function() onTap; - - const CustomSettingOptionsTile( - {super.key, - required this.leadingWidget, - required this.onTap, - required this.titleText, - this.needBorderBelow = false, - this.isForLanguage = false}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - leadingWidget, - 12.width, - titleText.toText(fontSize: 16), - ], - ), - isForLanguage - ? const Icon(Icons.language, size: 18) - : const Icon(Icons.arrow_forward, size: 18) - ], - ).onPress(onTap), - 5.height, - if (needBorderBelow) ...[ - const Divider(thickness: 1), - ], - ], - ); - } -} diff --git a/lib/views/setting_options/setting_options_language.dart b/lib/views/setting_options/setting_options_more.dart similarity index 88% rename from lib/views/setting_options/setting_options_language.dart rename to lib/views/setting_options/setting_options_more.dart index ea3da5a..39aa21b 100644 --- a/lib/views/setting_options/setting_options_language.dart +++ b/lib/views/setting_options/setting_options_more.dart @@ -17,14 +17,14 @@ import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; -class SettingOptionsLanguage extends StatefulWidget { - const SettingOptionsLanguage({super.key}); +class SettingOptionsMore extends StatefulWidget { + const SettingOptionsMore({super.key}); @override - State createState() => _SettingOptionsLanguageState(); + State createState() => _SettingOptionsMoreState(); } -class _SettingOptionsLanguageState extends State { +class _SettingOptionsMoreState extends State { @override Widget build(BuildContext context) { return Scaffold( @@ -65,25 +65,10 @@ class _SettingOptionsLanguageState extends State { CustomSettingOptionsTile( leadingWidget: const Icon(Icons.settings, size: 20), titleText: LocaleKeys.settings.tr(), - needBorderBelow: true, - onTap: () {}, - //navigateWithName(context, AppRoutes.settingOptionsInviteFriends), + needBorderBelow: false, + onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends), ), - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.question_mark_outlined, size: 20), - titleText: LocaleKeys.help.tr(), - needBorderBelow: true, - onTap: () => navigateWithName(context, AppRoutes.settingOptionsHelp), - ), - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.person, size: 20), - titleText: LocaleKeys.account.tr(), - needBorderBelow: false, - onTap: () { - navigateWithName(context, AppRoutes.profileView); - // context.read().onNavbarTapped(4); - // Navigator.pop(context); - }), + ], ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0), 10.height, diff --git a/lib/views/setting_options/widgets/custom_setting_options_tile.dart b/lib/views/setting_options/widgets/custom_setting_options_tile.dart index 748a7d4..2ecd66a 100644 --- a/lib/views/setting_options/widgets/custom_setting_options_tile.dart +++ b/lib/views/setting_options/widgets/custom_setting_options_tile.dart @@ -6,13 +6,13 @@ 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/widgets/extensions/extensions_widget.dart'; -import 'package:sizer/sizer.dart'; class CustomSettingOptionsTile extends StatelessWidget { final Widget leadingWidget; final String titleText; final bool needBorderBelow; final bool isForLanguage; + final bool showTrailingArrow; final Function() onTap; final String? subTitle; @@ -23,6 +23,7 @@ class CustomSettingOptionsTile extends StatelessWidget { required this.titleText, this.needBorderBelow = false, this.isForLanguage = false, + this.showTrailingArrow = true, this.subTitle, }); @@ -62,7 +63,9 @@ class CustomSettingOptionsTile extends StatelessWidget { // ) // - : const Icon(Icons.arrow_forward, size: 18) + : showTrailingArrow + ? const Icon(Icons.arrow_forward, size: 18) + : const SizedBox() ], ).onPress(onTap), subTitle != null ? 3.height : 5.height, @@ -97,9 +100,7 @@ class CustomProfileOptionsTile extends StatelessWidget { subtitleText.toText(fontSize: 12, color: MyColors.darkTextColor.withOpacity(0.7)), ], ), - if (needEditButton) ...[ - MyAssets.icEdit.buildSvg() - ], + if (needEditButton) ...[MyAssets.icEdit.buildSvg()], ], ).onPress(onTap), 5.height, diff --git a/lib/widgets/extensions/extensions_widget.dart b/lib/widgets/extensions/extensions_widget.dart index c966632..4bdc8b5 100644 --- a/lib/widgets/extensions/extensions_widget.dart +++ b/lib/widgets/extensions/extensions_widget.dart @@ -375,7 +375,12 @@ extension BuildSVG on String? { return Image.network( this!, errorBuilder: (BuildContext context, Object obj, StackTrace? s) { - return SizedBox(height: height, width: width, child: const Icon(Icons.signal_wifi_connected_no_internet_4_outlined)); + return Container( + height: height, + width: width, + decoration: BoxDecoration(border: Border.all(color: MyColors.roundedCrossBgColor)), + child: const Icon(Icons.image_not_supported_outlined, color: MyColors.roundedCrossBgColor), + ); }, loadingBuilder: (BuildContext context, Widget? child, ImageChunkEvent? imageChunk) { if (imageChunk == null) { From b2494d00ab4ce6a1648aebb63ab811ed74c82b87 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Thu, 24 Oct 2024 12:05:17 +0300 Subject: [PATCH 8/8] Subs Push --- lib/repositories/branch_repo.dart | 9 ++++++++- lib/view_models/service_view_model.dart | 5 ----- lib/views/setting_options/provider_license_page.dart | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/repositories/branch_repo.dart b/lib/repositories/branch_repo.dart index 520e630..ebfaaed 100644 --- a/lib/repositories/branch_repo.dart +++ b/lib/repositories/branch_repo.dart @@ -196,7 +196,14 @@ class BranchRepoImp implements BranchRepo { List> map = []; for (int i = 0; i < documents!.length; i++) { if (documents[i].document != null) { - var postParams = {"id": documents[i].id, "serviceProviderID": documents[i].serviceProviderId, "documentID": documents[i].documentId, "documentExt": documents[i].fileExt, "documentImage": documents[i].document, "isActive": true}; + var postParams = { + "id": documents[i].id, + "serviceProviderID": documents[i].serviceProviderId, + "documentID": documents[i].documentId, + "documentExt": documents[i].fileExt, + "documentImage": documents[i].document, + "isActive": true, + }; map.add(postParams); } } diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index ce58286..90ad442 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -243,7 +243,6 @@ class ServiceVM extends BaseVM { } Future commerceRemove(String filePath) async { - log("commerceRemove with: $filePath"); int index = commerceCertificates.indexWhere((element) => element.filePath == filePath); if (index == -1) { return; @@ -253,8 +252,6 @@ class ServiceVM extends BaseVM { } Future commercialRemove(String filePath) async { - log("commercialRemove with: $filePath"); - int index = commercialCertificates.indexWhere((element) => element.filePath == filePath); if (index == -1) { return; @@ -264,8 +261,6 @@ class ServiceVM extends BaseVM { } Future vatRemove(String filePath) async { - log("vatRemove with: $filePath"); - int index = vatCertificates.indexWhere((element) => element.filePath == filePath); if (index == -1) { return; diff --git a/lib/views/setting_options/provider_license_page.dart b/lib/views/setting_options/provider_license_page.dart index 3718669..9d17e99 100644 --- a/lib/views/setting_options/provider_license_page.dart +++ b/lib/views/setting_options/provider_license_page.dart @@ -170,7 +170,7 @@ class _ProviderLicensePageState extends State { isReview: document.status == 4, allowAdButton: false, pickedFiles: isLocalOrNetworkFiles(model: serviceVM, document: document), - onCrossPressedPrimary: chkIsLocalOrNetwork(document: document) + onCrossPressedPrimary: isNetworkImage(document: document) ? serviceVM.removeNetworkImage : document.documentId == 1 ? serviceVM.commerceRemove @@ -200,6 +200,7 @@ class _ProviderLicensePageState extends State { List isLocalOrNetworkFiles({required ServiceVM model, required DocumentData document}) { bool isNetworkImage = false; + if (!document.isLocalFile!) { isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false; } @@ -217,7 +218,6 @@ class _ProviderLicensePageState extends State { bool isNeedToShow({required ServiceVM model, required DocumentData document}) { bool allow = false; bool isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty && !(document.isLocalFile ?? true); - log("document.documentUrl: ${document.documentUrl}"); if (isNetworkImage) { allow = true; } else { @@ -251,7 +251,7 @@ class _ProviderLicensePageState extends State { } } - bool chkIsLocalOrNetwork({required DocumentData document}) { + bool isNetworkImage({required DocumentData document}) { bool isNetworkImage = false; if (!document.isLocalFile!) { isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false;