diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index c844f1c..60cdaaa 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -53,7 +53,9 @@ class ApiConsts { //Appointment APIs static String serviceProvidersAppointmentGet = "${baseUrlServices}api/ServiceProviders/ServiceProvidersAppointment_Get"; static String serviceCategoryGet = "${baseUrlServices}api/Master/ServiceCategory_Get"; - static String servicesGet = "${baseUrlServices}api/ServiceProviders/Services_Get"; + static String serviceItemsGet = "${baseUrlServices}api/ServiceProviders/ServiceItem_Get"; + //ServiceProvidersServiceID as params + // static String servicesGet = "${baseUrlServices}api/ServiceProviders/Services_Get"; //Advertisement APIs static String vehicleTypeGet = "${baseUrlServices}api/ServiceProviders/VehicleType_Get"; @@ -75,6 +77,12 @@ class ApiConsts { static String vehicleAdsGet = "${baseUrlServices}api/Advertisement/Ads_Get"; static String adsCarCheckupSPBranchScheduleSlotGet = "${baseUrlServices}api/Advertisement/AdsCarCheckupSPBranchScheduleSlot_Get"; static String adsPhotoOfficeAppointmentScheduleSlotGet = "${baseUrlServices}api/Advertisement/PhotoOfficeAppointmentScheduleSlot_Get"; + + // Payment + static String paymentWebViewUrl = "https://ms.hmg.com/pay/PaymentHome"; + static String payForOrderDetailGet = "${baseUrlServices}api/Payment/PayFortOrderDetail_Get"; + + static List closingUrls = ["PayFortResponse"]; } class GlobalConsts { diff --git a/lib/config/dependencies.dart b/lib/config/dependencies.dart index 6357210..ec7fa03 100644 --- a/lib/config/dependencies.dart +++ b/lib/config/dependencies.dart @@ -6,8 +6,10 @@ import 'package:mc_common_app/api/api_client.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/repositories/ads_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/user_repo.dart'; -import 'package:mc_common_app/services/services.dart'; +import 'package:mc_common_app/services/common_services.dart'; +import 'package:mc_common_app/services/payments_service.dart'; Injector injector = Injector.appInstance; @@ -16,11 +18,13 @@ class AppDependencies { //services injector.registerSingleton(() => AppState()); injector.registerSingleton(() => ApiClientImp()); - injector.registerSingleton(() => CommonServicesImp()); + injector.registerSingleton(() => CommonServicesImp()); + injector.registerSingleton(() => PaymentServiceImp()); //repos injector.registerSingleton(() => UserRepoImp()); injector.registerSingleton(() => CommonRepoImp()); injector.registerSingleton(() => AdsRepoImp()); + injector.registerSingleton(() => PaymentsRepoImp()); } } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index f26ea7e..f9b9f66 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -5,22 +5,23 @@ import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; extension EmailValidator on String { - Widget toText( - {Color? color, - bool isBold = false, - double? fontSize, - bool isUnderLine = false, - TextDecoration? textDecoration, - double letterSpacing = -0.4, - TextAlign? textAlign, - double? height, - int? maxLines}) => + Widget toText({Color? color, + bool isBold = false, + double? fontSize, + bool isUnderLine = false, + bool isItalic = false, + TextDecoration? textDecoration, + double letterSpacing = -0.4, + TextAlign? textAlign, + double? height, + int? maxLines}) => AutoSizeText( this, textAlign: textAlign, maxLines: maxLines, style: TextStyle( + fontStyle: isItalic ? FontStyle.italic : null, height: height, decoration: isUnderLine ? TextDecoration.underline : textDecoration ?? TextDecoration.none, fontSize: fontSize ?? 10, diff --git a/lib/models/payment_models/pay_order_detail_resp_model.dart b/lib/models/payment_models/pay_order_detail_resp_model.dart new file mode 100644 index 0000000..ca54aeb --- /dev/null +++ b/lib/models/payment_models/pay_order_detail_resp_model.dart @@ -0,0 +1,68 @@ +class PayOrderDetailRespModel { + int? id; + String? userFullName; + String? userMobile; + String? userEmail; + String? userID; + double? amount; + String? currency; + int? payFortProjectID; + int? payFortServiceID; + String? description; + bool? isPaid; + String? language; + int? serviceAppointmentID; + int? providerSubscriptionID; + + PayOrderDetailRespModel( + {this.id, + this.userFullName, + this.userMobile, + this.userEmail, + this.userID, + this.amount, + this.currency, + this.payFortProjectID, + this.payFortServiceID, + this.description, + this.isPaid, + this.language, + this.serviceAppointmentID, + this.providerSubscriptionID}); + + PayOrderDetailRespModel.fromJson(Map json) { + id = json['id']; + userFullName = json['userFullName']; + userMobile = json['userMobile']; + userEmail = json['userEmail']; + userID = json['userID']; + amount = json['amount']; + currency = json['currency']; + payFortProjectID = json['payFort_ProjectID']; + payFortServiceID = json['payFort_ServiceID']; + description = json['description']; + isPaid = json['isPaid']; + language = json['language']; + serviceAppointmentID = json['serviceAppointmentID']; + providerSubscriptionID = json['providerSubscriptionID']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['userFullName'] = userFullName; + data['userMobile'] = userMobile; + data['userEmail'] = userEmail; + data['userID'] = userID; + data['amount'] = amount; + data['currency'] = currency; + data['payFort_ProjectID'] = payFortProjectID; + data['payFort_ServiceID'] = payFortServiceID; + data['description'] = description; + data['isPaid'] = isPaid; + data['language'] = language; + data['serviceAppointmentID'] = serviceAppointmentID; + data['providerSubscriptionID'] = providerSubscriptionID; + return data; + } +} diff --git a/lib/repositories/payments_repo.dart b/lib/repositories/payments_repo.dart new file mode 100644 index 0000000..5fc6407 --- /dev/null +++ b/lib/repositories/payments_repo.dart @@ -0,0 +1,22 @@ +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/dependencies.dart'; +import 'package:mc_common_app/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}); +} + +class PaymentsRepoImp implements PaymentsRepo { + ApiClient apiClient = injector.get(); + 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); + PayOrderDetailRespModel payOrderDetailRespModel = PayOrderDetailRespModel.fromJson(adsGenericModel.data); + return payOrderDetailRespModel; + } +} diff --git a/lib/services/services.dart b/lib/services/common_services.dart similarity index 95% rename from lib/services/services.dart rename to lib/services/common_services.dart index b46ffb2..482037a 100644 --- a/lib/services/services.dart +++ b/lib/services/common_services.dart @@ -4,7 +4,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; import 'package:mc_common_app/utils/utils.dart'; -abstract class CommonServices { +abstract class CommonAppServices { Future> pickMultipleImages(); Future pickImageFromPhone(int sourceFlag); @@ -12,7 +12,7 @@ abstract class CommonServices { Future pickFile({FileType fileType = FileType.custom, List? allowedExtensions}); } -class CommonServicesImp implements CommonServices { +class CommonServicesImp implements CommonAppServices { @override Future pickImageFromPhone(int sourceFlag) async { final picker = ImagePicker(); diff --git a/lib/services/my_in_app_browser.dart b/lib/services/my_in_app_browser.dart new file mode 100644 index 0000000..ccb24fd --- /dev/null +++ b/lib/services/my_in_app_browser.dart @@ -0,0 +1,33 @@ +import 'dart:developer'; + +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; + +class MyInAppBrowser extends InAppBrowser { + final Function onExitCallback; + final Function(String) onLoadStartCallback; + + MyInAppBrowser({ + required this.onExitCallback, + required this.onLoadStartCallback, + }); + + @override + Future onBrowserCreated() async { + log("\n\nBrowser Created!\n\n"); + } + + @override + Future onLoadStart(Uri? url) async { + onLoadStartCallback(url.toString()); + } + + @override + Future onLoadStop(Uri? url) async { + log("\n\nStopped $url\n\n"); + } + + @override + void onLoadError(Uri? url, int code, String message) { + log("Can't load $url.. Error: $message"); + } +} diff --git a/lib/services/payments_service.dart b/lib/services/payments_service.dart new file mode 100644 index 0000000..7ff2949 --- /dev/null +++ b/lib/services/payments_service.dart @@ -0,0 +1,80 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/services/my_in_app_browser.dart'; +import 'package:mc_common_app/utils/utils.dart'; + +abstract class PaymentService { + Future placeAdPayment({ + required int id, + required int paymentType, + required Function() onSuccess, + required Function() onFailure, + }); + + Future onBrowserLoadStart({ + required String url, + required Function() onSuccess, + required Function() onFailure, + }); +} + +class PaymentServiceImp implements PaymentService { + MyInAppBrowser? myInAppBrowser; + var inAppBrowserOptions = InAppBrowserClassOptions( + 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)); + + String generateBrowserUrl(int id) { + String form = Utils.getAdsPaymentBrowserForm(paymentId: 3, adId: id); + // form.replaceFirst("SERVICE_URL_VALUE", ApiConsts.paymentWebViewUrl); + // form.replaceFirst("PaymentType_Value", 4.toString()); + // form.replaceFirst("AdID_Value", id.toString()); + + var bytes = utf8.encode(form); + var base64Str = base64.encode(bytes); + return 'data:text/html;base64,$base64Str'; + } + + @override + Future placeAdPayment({ + required int id, + required int paymentType, + required Function() onSuccess, + required Function() onFailure, + }) async { + myInAppBrowser = MyInAppBrowser(onExitCallback: () { + log("Browser Exited"); + }, onLoadStartCallback: (String url) { + log("Browser LoadStart"); + onBrowserLoadStart(onFailure: onFailure, onSuccess: onSuccess, url: url); + }); + await myInAppBrowser!.openUrlRequest( + urlRequest: URLRequest(url: Uri.parse("${ApiConsts.paymentWebViewUrl}?PaymentType=$paymentType&AdsID=$id")), + options: inAppBrowserOptions, + ); + } + + @override + Future onBrowserLoadStart({ + required String url, + required Function() onSuccess, + required Function() onFailure, + }) async { + for (var element in ApiConsts.closingUrls) { + if (url.contains(element)) { + if (myInAppBrowser!.isOpened()) myInAppBrowser!.close(); + onSuccess(); + return; + } + // onFailure(); + } + } +} diff --git a/lib/utils/navigator.dart b/lib/utils/navigator.dart index 58cd370..f8bc636 100644 --- a/lib/utils/navigator.dart +++ b/lib/utils/navigator.dart @@ -4,15 +4,18 @@ navigateWithName(BuildContext context, String routeName, {Object? arguments}) { Navigator.pushNamed(context, routeName, arguments: arguments); } - navigateReplaceWithName(BuildContext context, String routeName, {Object? arguments}) { Navigator.pushReplacementNamed(context, routeName, arguments: arguments); } +navigateReplaceWithNameUntilRoute(BuildContext context, String routeName, {Object? arguments}) { + Navigator.pushNamedAndRemoveUntil(context, routeName, (route) => false); +} + pop(BuildContext context) { Navigator.of(context).pop(); } navigateTo(context, page) { Navigator.push(context, MaterialPageRoute(builder: (context) => page)); -} \ No newline at end of file +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 00f3394..55a65eb 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/exceptions/api_exception.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; @@ -372,4 +373,17 @@ class Utils { RegExp regExp = RegExp(p); return regExp.hasMatch(email); } + + static String getAdsPaymentBrowserForm({required int paymentId, required int adId}) { + return ' ' + + '' + + '' + + '
' + + '' + + '' + + '
' + + '' + + '' + + ''; + } } diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index faf4a97..c15070e 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -17,7 +17,7 @@ import 'package:mc_common_app/models/generic_resp_model.dart'; import 'package:mc_common_app/models/widgets_models.dart'; import 'package:mc_common_app/repositories/ads_repo.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; -import 'package:mc_common_app/services/services.dart'; +import 'package:mc_common_app/services/common_services.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'; @@ -28,7 +28,7 @@ class AdVM extends BaseVM { final CommonRepo commonRepo; final AdsRepo adsRepo; - final CommonServices commonServices; + final CommonAppServices commonServices; AdVM({required this.commonServices, required this.commonRepo, required this.adsRepo}); diff --git a/lib/view_models/payment_view_model.dart b/lib/view_models/payment_view_model.dart new file mode 100644 index 0000000..f41f9b8 --- /dev/null +++ b/lib/view_models/payment_view_model.dart @@ -0,0 +1,95 @@ +import 'package:flutter/cupertino.dart'; +import 'package:mc_common_app/config/routes.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'; + +class PaymentVM extends ChangeNotifier { + final PaymentService paymentService; + final PaymentsRepo paymentRepo; + + PaymentVM({required this.paymentService, required this.paymentRepo}); + + PaymentMethodsEnum selectedPaymentMethod = PaymentMethodsEnum.mada; + PaymentTypesEnum currentPaymentType = PaymentTypesEnum.ads; + + int currentAdId = -1; + + void updateCurrentAdId({required int id}) { + currentAdId = id; + } + + updateSelectedPaymentMethod(PaymentMethodsEnum selectedMethod) { + selectedPaymentMethod = selectedMethod; + notifyListeners(); + } + + Future onContinuePressed(BuildContext context) async { + switch (selectedPaymentMethod) { + case PaymentMethodsEnum.mada: + // TODO: Handle this case. + break; + case PaymentMethodsEnum.visa: + await onVisaCardSelected(context); + break; + case PaymentMethodsEnum.applePay: + // TODO: Handle this case. + break; + case PaymentMethodsEnum.masterCard: + // TODO: Handle this case. + break; + case PaymentMethodsEnum.tamara: + // TODO: Handle this case. + break; + } + + return; + } + + Future onVisaCardSelected(BuildContext context) async { + switch (currentPaymentType) { + case PaymentTypesEnum.subscription: + // TODO: Handle this case. + break; + case PaymentTypesEnum.appointment: + // TODO: Handle this case. + break; + case PaymentTypesEnum.ads: + if (currentAdId == -1) return; + int paymentType = 3; + await paymentService.placeAdPayment( + id: currentAdId, + paymentType: paymentType, + onFailure: () { + Utils.showToast("Payment Failed!"); + }, + onSuccess: () async { + Utils.showLoading(context); + PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentId: paymentType, adId: 3); + await Future.delayed(Duration(seconds: 2)); + Utils.hideLoading(context); + + if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) { + Utils.showToast("Payment Failed!"); + return; + } + + if (payOrderDetailRespModel.isPaid != null && payOrderDetailRespModel.isPaid!) { + Utils.showToast("Payment Successfully Completed!"); + navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); + } + }, + ); + break; + case PaymentTypesEnum.adReserve: + // TODO: Handle this case. + break; + case PaymentTypesEnum.request: + // TODO: Handle this case. + break; + } + } +} diff --git a/lib/views/advertisement/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view.dart index d749971..614fab3 100644 --- a/lib/views/advertisement/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view.dart @@ -7,10 +7,12 @@ import 'package:mc_common_app/models/advertisment_models/ad_details_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/view_models/payment_view_model.dart'; import 'package:mc_common_app/views/advertisement/ads_images_slider.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; class AdsDetailView extends StatelessWidget { final AdDetailsModel adDetails; @@ -28,87 +30,90 @@ class AdsDetailView extends StatelessWidget { ), body: Container( padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), - child: Stack( + child: Column( children: [ - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CarouselWithIndicatorDemo(vehicleImages: adDetails.vehicle!.image!), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "${adDetails.vehicle!.vehicleTitle} | ${adDetails.vehicle!.color!.label}".toText(fontSize: 18, isBold: true), - (adDetails.vehicle!.cityName ?? "").toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - "Model: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "${adDetails.vehicle!.modelyear!.label}".toText( - fontSize: 14, - isBold: true, - ), - ], - ), - "${adDetails.vehicle!.countryID}".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - "Mileage: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "${adDetails.vehicle!.mileage!.mileageEnd}Km".toText( - fontSize: 14, - isBold: true, - ), - ], - ), - adDetails.createdOn != null ? DateTime.parse(adDetails.createdOn!).getTimeAgo().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor) : SizedBox(), - ], - ), - Row( - children: [ - "Transmission: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "${adDetails.vehicle!.transmission!.label}".toText( - fontSize: 14, - isBold: true, - ), - ], - ), - 8.height, - "Description: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "${adDetails.vehicle!.vehicleDescription}".toText( - fontSize: 14, - isBold: true, - ), - ], - ).toWhiteContainer(width: double.infinity, allPading: 12), - Align( - alignment: Alignment.bottomCenter, - child: SizedBox( - height: 150, - child: Column( + Expanded( + child: ListView( + children: [ + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Divider(thickness: 1, height: 1), - 18.height, + CarouselWithIndicatorDemo(vehicleImages: adDetails.vehicle!.image!), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "${adDetails.vehicle!.vehicleTitle} | ${adDetails.vehicle!.color!.label}".toText(fontSize: 18, isBold: true), + (adDetails.vehicle!.cityName ?? "").toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + "Model: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + "${adDetails.vehicle!.modelyear!.label}".toText( + fontSize: 14, + isBold: true, + ), + ], + ), + "${adDetails.vehicle!.countryID}".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), + ], + ), Row( - crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true), - "SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor), + Row( + children: [ + "Mileage: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + "${adDetails.vehicle!.mileage!.mileageEnd}Km".toText( + fontSize: 14, + isBold: true, + ), + ], + ), + adDetails.createdOn != null ? DateTime.parse(adDetails.createdOn!).getTimeAgo().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor) : const SizedBox(), ], ), - 14.height, - BuildAdDetailsActionButton(adPostStatus: adDetails.adPostStatus!), + Row( + children: [ + "Transmission: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + "${adDetails.vehicle!.transmission!.label}".toText( + fontSize: 14, + isBold: true, + ), + ], + ), + 8.height, + "Description: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + "${adDetails.vehicle!.vehicleDescription}".toText( + fontSize: 14, + isBold: true, + ), + ], + ).toWhiteContainer(width: double.infinity, allPading: 12), + ], + ), + ), + SizedBox( + child: Column( + children: [ + const Divider(thickness: 1, height: 1), + 18.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true), + "SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor), ], ), - )), + 14.height, + BuildAdDetailsActionButton(adPostStatus: adDetails.adPostStatus!, adId: adDetails.id!), + ], + ), + ) ], ), ), @@ -118,18 +123,20 @@ class AdsDetailView extends StatelessWidget { class BuildAdDetailsActionButton extends StatelessWidget { final AdPostStatus adPostStatus; + final int adId; - const BuildAdDetailsActionButton({Key? key, required this.adPostStatus}) : super(key: key); + const BuildAdDetailsActionButton({Key? key, required this.adPostStatus, required this.adId}) : super(key: key); - Widget pendingForPaymentAction(BuildContext context) { + Widget pendingForPaymentAction(BuildContext context, {required int adID}) { return Column( + crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( children: [ Expanded( child: ShowFillButton( maxHeight: 55, - title: "Reserve Ad", + title: "Pay Now", onPressed: () { navigateWithName(context, AppRoutes.paymentMethodsView); }, @@ -138,14 +145,13 @@ class BuildAdDetailsActionButton extends StatelessWidget { ], ), Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Expanded( - child: ShowFillButton( - maxHeight: 55, - title: "Reserve Ad", - onPressed: () { - navigateWithName(context, AppRoutes.paymentMethodsView); - }, + InkWell( + onTap: () {}, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: "Delete Ad".toText(fontSize: 15, isBold: true, color: MyColors.redColor), ), ), ], @@ -155,15 +161,32 @@ class BuildAdDetailsActionButton extends StatelessWidget { } Widget markAsSoldAction(BuildContext context) { - return Row( + return Column( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded( - child: ShowFillButton( - maxHeight: 55, - title: "Mark As Sold", - onPressed: () {}, - ), + Row( + children: [ + Expanded( + child: ShowFillButton( + maxHeight: 55, + title: "Mark As Sold", + onPressed: () {}, + ), + ), + ], ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InkWell( + onTap: () {}, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: "Delete Ad".toText(fontSize: 15, isBold: true, color: MyColors.redColor), + ), + ), + ], + ) ], ); } @@ -173,6 +196,10 @@ class BuildAdDetailsActionButton extends StatelessWidget { children: [ Expanded( child: ShowFillButton( + borderColor: MyColors.redColor, + txtColor: MyColors.redColor, + isFilled: false, + fontSize: 16, maxHeight: 55, title: "Cancel Reservation", onPressed: () {}, @@ -208,9 +235,10 @@ class BuildAdDetailsActionButton extends StatelessWidget { @override Widget build(BuildContext context) { + context.read().updateCurrentAdId(id: adId); switch (adPostStatus) { case AdPostStatus.pendingForPayment: - return pendingForPaymentAction(context); + return pendingForPaymentAction(context, adID: adId); case AdPostStatus.active: return markAsSoldAction(context); case AdPostStatus.reserved: @@ -222,6 +250,7 @@ class BuildAdDetailsActionButton extends StatelessWidget { case AdPostStatus.cancelled: case AdPostStatus.pendingForPost: case AdPostStatus.pendingForReview: + return pendingForPaymentAction(context, adID: adId); case AdPostStatus.sold: case AdPostStatus.expired: break; diff --git a/lib/views/advertisement/ads_list.dart b/lib/views/advertisement/ads_list.dart index 73af6a9..4bfc121 100644 --- a/lib/views/advertisement/ads_list.dart +++ b/lib/views/advertisement/ads_list.dart @@ -95,9 +95,6 @@ class AdCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - if (isAdsFragment && context.read().isExploreAdsTapped) ...[ - Utils.statusContainerChip(text: adDetails.statuslabel!, chipColor: Utils.getChipColorByAdStatus(adDetails.adPostStatus!)), - ], if (shouldShowAdStatus) ...[ Utils.statusContainerChip(text: adDetails.statuslabel!, chipColor: Utils.getChipColorByAdStatus(adDetails.adPostStatus!)), ], diff --git a/lib/views/payments/payment_methods_view.dart b/lib/views/payments/payment_methods_view.dart new file mode 100644 index 0000000..c96b521 --- /dev/null +++ b/lib/views/payments/payment_methods_view.dart @@ -0,0 +1,127 @@ +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/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/payment_view_model.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:provider/provider.dart'; + +class PaymentMethodsView extends StatelessWidget { + const PaymentMethodsView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: "Payment Method", + profileImageUrl: MyAssets.bnCar, + isRemoveBackButton: false, + isDrawerEnabled: false, + ), + body: Container( + padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 20.height, + "Select Payment Method".toText(fontSize: 18, isBold: true), + 15.height, + Consumer( + builder: (BuildContext context, PaymentVM paymentVm, Widget? child) { + return Expanded( + child: ListView( + shrinkWrap: true, + children: [ + PaymentMethodContainer( + isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.mada, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.mada), + cardAsset: MyAssets.madaPng), + PaymentMethodContainer( + isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.visa, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.visa), + cardAsset: MyAssets.visaPng), + PaymentMethodContainer( + isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.applePay, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.applePay), + cardAsset: MyAssets.applePayPng), + PaymentMethodContainer( + isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.masterCard, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.masterCard), + cardAsset: MyAssets.mastercardPng), + PaymentMethodContainer( + isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.tamara, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.tamara), + cardAsset: MyAssets.tamaraEngPng), + ], + ), + ); + }, + ), + SizedBox( + width: double.infinity, + child: ShowFillButton( + maxHeight: 55, + title: "Continue", + onPressed: () { + context.read().onContinuePressed(context); + }, + backgroundColor: MyColors.darkPrimaryColor, + ), + ), + ], + ), + ), + ); + } +} + +class PaymentMethodContainer extends StatelessWidget { + final bool isSelected; + final String cardAsset; + final Function() onTap; + + const PaymentMethodContainer({Key? key, required this.isSelected, required this.cardAsset, required this.onTap}) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Container( + margin: EdgeInsets.symmetric(vertical: 10), + width: double.infinity, + height: 80, + decoration: BoxDecoration( + color: MyColors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: isSelected ? Colors.green : Colors.transparent, width: isSelected ? 2.0 : 0.0), + ), + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + children: [ + Container( + width: 24, + height: 24, + decoration: Utils.containerColorRadiusBorderWidth(isSelected ? MyColors.primaryColor : Colors.transparent, 100, Colors.grey, 0.5), + ), + 12.width, + Container( + height: 80.0, + width: 80.0, + padding: EdgeInsets.all(7.0), + child: Image.asset(cardAsset), + ), + Utils.mFlex(1), + isSelected ? Utils.statusContainerChip(text: "Selected", padding: EdgeInsets.symmetric(vertical: 6, horizontal: 10)) : SizedBox(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/button/show_fill_button.dart b/lib/widgets/button/show_fill_button.dart index 6e933e1..644870c 100644 --- a/lib/widgets/button/show_fill_button.dart +++ b/lib/widgets/button/show_fill_button.dart @@ -11,6 +11,7 @@ class ShowFillButton extends StatelessWidget { bool isFlatButton, isBold; EdgeInsets? margin; bool isFilled; + Color borderColor; ShowFillButton({ super.key, @@ -30,6 +31,7 @@ class ShowFillButton extends StatelessWidget { this.horizontalMargin = 0, this.verticalMargin = 0, this.margin, + this.borderColor = MyColors.primaryColor, }); @override @@ -67,7 +69,7 @@ class ShowFillButton extends StatelessWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(radius), - side: isFilled ? BorderSide.none: const BorderSide(width: 3, color: MyColors.darkPrimaryColor), + side: isFilled ? BorderSide.none: BorderSide(width: 2, color: borderColor), ), child: title.toText( fontSize: fontSize,