diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 40039f3..06438d3 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -61,6 +61,11 @@ class ApiConsts { static String ServiceProviderAppointmentRescheduleCancelAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_RescheduleCancelAppointment"; static String AddNewServicesInAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_ServiceItemAdd"; + static String getAppointmentSlots = "${baseUrlServices}api/ServiceProviders/ScheduleSlotsInfo_Get"; + static String updateAppointmentStatus = "${baseUrlServices}api/ServiceProviders/ServiceProvidersAppointmentStatus_Update"; + static String updateAppointmentPaymentStatus = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointmentServiceItemPaymentStatus_Update"; + static String createMergeAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderMergeAppointment_Create"; + //ServiceProvidersServiceID as params // static String servicesGet = "${baseUrlServices}api/ServiceProviders/Services_Get"; @@ -105,10 +110,7 @@ class ApiConsts { static String adsPhotoOfficeAppointmentScheduleSlotGet = "${baseUrlServices}api/Advertisement/PhotoOfficeAppointmentScheduleSlot_Get"; static String adsPhotoOfficeAppointmentCreate = "${baseUrlServices}api/Advertisement/PhotoOfficeAppointment_Create"; static String adsMCBankAccountAdGet = "${baseUrlServices}api/Advertisement/MCBankAccountAd_Get"; - static String getAppointmentSlots = "${baseUrlServices}api/ServiceProviders/ScheduleSlotsInfo_Get"; - static String updateAppointmentStatus = "${baseUrlServices}api/ServiceProviders/ServiceProvidersAppointmentStatus_Update"; - static String updateAppointmentPaymentStatus = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointmentServiceItemPaymentStatus_Update"; - static String createMergeAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderMergeAppointment_Create"; + static String adsReserveCreate = "${baseUrlServices}api/Advertisement/AdsReserve_Create"; //Subscription static String getAllSubscriptions = "${baseUrlServices}api/Common/Subscription_Get"; @@ -158,6 +160,7 @@ class GlobalConsts { static String welcomeVideoUrl = "welcomeVideoUrl"; static String doNotShowWelcomeVideo = "doNotShowWelcomeVideo"; static String demandAmountError = "Amount Cannot be Empty"; + static String reservationCancelError = "Cancellation Reason Cannot be Empty"; static String descriptionError = "Description Cannot be Empty"; static String vehicleVinError = "Vehicle VIN Cannot be Empty"; static String vehicleTitleError = "Vehicle Title Cannot be Empty"; @@ -262,8 +265,6 @@ class MyAssets { static String tamaraEngPng = "${assetPath}icons/payments/tamara_en.png"; static String visaPng = "${assetPath}icons/payments/visa.png"; - - static String whatsAppIcon = "${assetPath}icons/whatsapp_icon.svg"; } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 30ea3a5..8d500e4 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -3,6 +3,7 @@ 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'; import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/views/profile/profile_view.dart'; import 'package:mc_common_app/views/user/change_email_page.dart'; import 'package:mc_common_app/views/user/change_mobile_page.dart'; @@ -83,6 +84,10 @@ class AppRoutes { static const String settingOptionsLanguages = "/settingOptionsLanguages"; static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends"; + //Profile Screen + static const String profileView = "/profileView"; + + //Chat static const String chatView = "/chatView"; @@ -107,6 +112,7 @@ class AppRoutes { changeMobilePage: (context) => ChangeMobilePage(), changeEmailPage: (context) => const ChangeEmailPage(), editAccountPage: (context) => const EditAccountPage(), + profileView: (context) => const ProfileScreen(), }; } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 1a8125d..36172f3 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -460,6 +460,12 @@ extension AdReserveStatusEnum on int { return AdReserveStatus.cancelledByOwner; } else if (this == 3) { return AdReserveStatus.cancelledByAdmin; + } else if (this == 4) { + return AdReserveStatus.timeOver; + } else if (this == 5) { + return AdReserveStatus.dealDone; + } else if (this == 6) { + return AdReserveStatus.fullPaymentVerified; } else { return AdReserveStatus.defaultStatus; } diff --git a/lib/models/advertisment_models/reserved_ads_models.dart b/lib/models/advertisment_models/reserved_ads_models.dart index 83c793f..c6f7441 100644 --- a/lib/models/advertisment_models/reserved_ads_models.dart +++ b/lib/models/advertisment_models/reserved_ads_models.dart @@ -3,18 +3,30 @@ class MyReservedAdsRespModel { int? adsID; int? customerID; int? paymentStatus; + int? adsReserveStatus; int? reservationTimeID; double? reservationBasePrice; double? refundAmount; String? refundDate; - MyReservedAdsRespModel({this.id, this.adsID, this.customerID, this.paymentStatus, this.reservationTimeID, this.reservationBasePrice, this.refundAmount, this.refundDate}); + MyReservedAdsRespModel({ + this.id, + this.adsID, + this.customerID, + this.paymentStatus, + this.adsReserveStatus, + this.reservationTimeID, + this.reservationBasePrice, + this.refundAmount, + this.refundDate, + }); MyReservedAdsRespModel.fromJson(Map json) { id = json['id']; adsID = json['adsID']; customerID = json['customerID']; paymentStatus = json['paymentStatus']; + paymentStatus = json['adsReserveStatus']; reservationTimeID = json['reservationTimeID']; reservationBasePrice = json['reservationBasePrice']; refundAmount = json['refundAmount']; @@ -27,6 +39,7 @@ class MyReservedAdsRespModel { data['adsID'] = adsID; data['customerID'] = customerID; data['paymentStatus'] = paymentStatus; + data['adsReserveStatus'] = adsReserveStatus; data['reservationTimeID'] = reservationTimeID; data['reservationBasePrice'] = reservationBasePrice; data['refundAmount'] = refundAmount; diff --git a/lib/models/chat_models/chat_message_model.dart b/lib/models/chat_models/chat_message_model.dart index 91834f4..7702053 100644 --- a/lib/models/chat_models/chat_message_model.dart +++ b/lib/models/chat_models/chat_message_model.dart @@ -60,8 +60,7 @@ class ChatMessageModel { reqOffer = json['reqOffer'] != null ? ReqOffer.fromJson(json['reqOffer']) : null; isRead = json['isRead']; readOn = json['readOn']; - isMyMessage = (json['senderUserID']).toString().toUpperCase()== myUserId; - + isMyMessage = (json['senderUserID']).toString().toUpperCase() == myUserId; } } @@ -87,7 +86,6 @@ class ReqOffer { }); ReqOffer.fromJson(Map json) { - log("the json: $json"); id = json['id']; requestID = json['requestID']; serviceProviderID = json['serviceProviderID']; diff --git a/lib/models/general_models/generic_resp_model.dart b/lib/models/general_models/generic_resp_model.dart index 83ed015..ffd31e8 100644 --- a/lib/models/general_models/generic_resp_model.dart +++ b/lib/models/general_models/generic_resp_model.dart @@ -11,17 +11,14 @@ class GenericRespModel { int? totalItemsCount; String? message; - - factory GenericRespModel.fromJson(Map json) => - GenericRespModel( + factory GenericRespModel.fromJson(Map json) => GenericRespModel( data: json["data"], messageStatus: json["messageStatus"], totalItemsCount: json["totalItemsCount"], message: json["message"], ); - Map toJson() => - { + Map toJson() => { "data": data, "messageStatus": messageStatus, "totalItemsCount": totalItemsCount, @@ -29,39 +26,6 @@ class GenericRespModel { }; } -var json = { - "ads": {"id": 0, "adsDurationID": 1, "startDate": "2023-04-12T10:10:20.905Z", "countryId": 1, "specialServiceIDs": [], "isMCHandled": false}, - "vehiclePosting": { - "id": 0, - "userID": "1A1597B3-D5A0-433A-098B-08DB189E51EC", - "vehicleType": 1, - "vehicleModelID": 1, - "vehicleModelYearID": 1, - "vehicleColorID": 2, - "vehicleCategoryID": 1, - "vehicleConditionID": 1, - "vehicleMileageID": 1, - "vehicleTransmissionID": 1, - "vehicleSellerTypeID": 1, - "cityID": 1, - "price": 33, - "vehicleVIN": "fdfd", - "vehicleDescription": "dsd", - "vehicleTitle": "fsfs", - "vehicleDescriptionN": "dsdds", - "isFinanceAvailable": true, - "warantyYears": 2, - "demandAmount": 34, - "adStatus": 1, - "vehiclePostingImages": [ - {"id": 0, "imageName": "onon", "imageUrl": "string", "imageStr": null, "vehiclePostingID": 0, "vehiclePosting": null} - ], - "vehiclePostingDamageParts": [ - {"id": 0, "comment": "hhsa", "vehicleImageBase64": null, "vehicleDamagePartID": 1, "vehiclePostingID": 0, "isActive": true} - ] - } -}; - class AdsCreationPayloadModel { Ads? ads; VehiclePosting? vehiclePosting; @@ -282,6 +246,11 @@ class VehiclePostingImages { data['vehiclePosting'] = vehiclePosting; return data; } + + // @override + // String toString() { + // return 'VehiclePostingImages{id: $id, imageName: $imageName, imageUrl: $imageUrl, imageStr: $imageStr, vehiclePostingID: $vehiclePostingID, vehiclePosting: $vehiclePosting}'; + // } } class RequestPostingImages { @@ -304,8 +273,12 @@ class RequestPostingImages { data['requestID'] = requestID; return data; } -} + @override + String toString() { + return 'RequestPostingImages{id: $id, requestImage: $requestImage, requestID: $requestID}'; + } +} class VehiclePostingDamageParts { int? id; @@ -336,4 +309,9 @@ class VehiclePostingDamageParts { data['isActive'] = isActive; return data; } + + // @override + // String toString() { + // return 'VehiclePostingDamageParts{id: $id, comment: $comment, vehicleImageBase64: $vehicleImageBase64, vehicleDamagePartID: $vehicleDamagePartID, vehiclePostingID: $vehiclePostingID, isActive: $isActive}'; + // } } diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index 0f419fa..dcd130e 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -51,7 +51,7 @@ abstract class AdsRepo { Future> getAllAds({required bool isMyAds}); - Future> getMyReservedAds(); + Future> getMyReservedAds(); Future> getMyAds(); @@ -63,7 +63,9 @@ abstract class AdsRepo { Future deleteAd({required int adId}); - Future cancelMyAdReservation({required int adId}); + Future cancelMyAdReservation({required int adId, required int adsReserveStatus, required String reason}); + + Future createReserveAd({required int adId}); } class AdsRepoImp implements AdsRepo { @@ -344,6 +346,7 @@ class AdsRepoImp implements AdsRepo { } }; + log("posting this ads: ${postParams.toString()}"); String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( (json) => GenericRespModel.fromJson(json), @@ -376,7 +379,7 @@ class AdsRepoImp implements AdsRepo { } @override - Future> getMyReservedAds() async { + Future> getMyReservedAds() async { var params = { "userID": appState.getUser.data!.userInfo!.userId ?? "", }; @@ -387,7 +390,31 @@ class AdsRepoImp implements AdsRepo { ApiConsts.myAdsReserveGet, queryParameters: params, ); - List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => MyReservedAdsRespModel.fromJson(adsGenericModel.data[index])); + List reservedAds = List.generate(adsGenericModel.data.length, (index) => MyReservedAdsRespModel.fromJson(adsGenericModel.data[index])); + List selectedIdsString = reservedAds.map((component) => component.adsID.toString()).toList(); + + if (selectedIdsString.isEmpty) { + return []; + } + + return await getAdsPerSpecificIds(ids: selectedIdsString, reservedAds: reservedAds); + } + + Future> getAdsPerSpecificIds({required List ids, required List reservedAds}) async { + var params = { + "AdsIDs": ids, + }; + GenericRespModel adsGenericModel = await apiClient.getJsonForObject( + token: appState.getUser.data!.accessToken, + (json) => GenericRespModel.fromJson(json), + queryParameters: params, + ApiConsts.vehicleAdsGet, + ); + List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true)); + + for (int i = 0; i < vehicleAdsDetails.length; i++) { + vehicleAdsDetails[i].adReserveStatus = (reservedAds[i].adsReserveStatus ?? 0).toAdRserveStatusEnum(); + } return vehicleAdsDetails; } @@ -463,9 +490,14 @@ class AdsRepoImp implements AdsRepo { } @override - Future cancelMyAdReservation({required int adId}) async { + Future cancelMyAdReservation({required int adId, required int adsReserveStatus, required String reason}) async { + int customerID = AppState().getUser.data!.userInfo!.customerId ?? 0; + var postParams = { - "adID": adId, + "adsID": adId, + "customerID": customerID, + "adsReserveStatus": adsReserveStatus, + "comment": reason, }; String token = appState.getUser.data!.accessToken ?? ""; @@ -479,6 +511,27 @@ class AdsRepoImp implements AdsRepo { return Future.value(adsGenericModel); } + @override + Future createReserveAd({required int adId}) async { + int customerID = AppState().getUser.data!.userInfo!.customerId ?? 0; + + var postParams = { + "adsID": adId, + "customerID": customerID, + "adsReserveStatus": 0, + }; + + String token = appState.getUser.data!.accessToken ?? ""; + GenericRespModel adsGenericModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.adsReserveCreate, + postParams, + token: token, + ); + + return Future.value(adsGenericModel); + } + @override Future createAppointmentForAdSpecialService({ required int adId, diff --git a/lib/services/common_services.dart b/lib/services/common_services.dart index 482037a..14a52cd 100644 --- a/lib/services/common_services.dart +++ b/lib/services/common_services.dart @@ -1,15 +1,19 @@ +import 'dart:developer'; import 'dart:io'; import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:mc_common_app/utils/app_permission_handler.dart'; import 'package:mc_common_app/utils/utils.dart'; +import 'package:permission_handler/permission_handler.dart'; abstract class CommonAppServices { Future> pickMultipleImages(); Future pickImageFromPhone(int sourceFlag); - Future pickFile({FileType fileType = FileType.custom, List? allowedExtensions}); + Future?> pickMultipleFiles(BuildContext context); } class CommonServicesImp implements CommonAppServices { @@ -24,23 +28,23 @@ class CommonServicesImp implements CommonAppServices { } @override - Future pickFile({FileType fileType = FileType.custom, List? allowedExtensions}) async { - FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['png', 'pdf', 'jpeg']); + Future?> pickMultipleFiles(BuildContext context) async { + FilePickerResult? result; + final status = await AppPermissions.checkStoragePermissions(context); + if (status) { + result = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: ['pdf']); + } + + List pickedFiles = []; if (result != null) { - File file = File(result.files.single.path ?? ""); - int sizeInBytes = file.lengthSync(); - // double sizeInMb = sizeInBytes / (1024 * 1024); - if (sizeInBytes > 1000) { - Utils.showToast("File is larger then 1KB"); - } else { - return file; + for (var element in result.files) { + if (element.path != null) { + pickedFiles.add(File(element.path!)); + } } - } else { - // User canceled the picker - return null; } - return null; + return pickedFiles; } @override diff --git a/lib/utils/AppPermissionHandler.dart b/lib/utils/AppPermissionHandler.dart deleted file mode 100644 index 56d9082..0000000 --- a/lib/utils/AppPermissionHandler.dart +++ /dev/null @@ -1,67 +0,0 @@ - -import 'package:permission_handler/permission_handler.dart'; - -import 'dialogs_and_bottomsheets.dart'; - -enum ConfirmAction { CANCEL, ACCEPT } - -Future requestPermissionGranted( - context, Permission requestPermissions) async { - var result = await requestPermissions.request(); - - switch (result) { - case PermissionStatus.granted: - // Application has been given permission to use the feature. - return true; - case PermissionStatus.denied: - // Application has been denied permission to use the feature. - return false; - case PermissionStatus.permanentlyDenied: - ConfirmAction? res = await showConfirmDialogs( - context, - 'You was denied Permission. You have give manual permission from app setting. ', - 'Open App Setting', - 'Cancel'); - if (res == ConfirmAction.ACCEPT) { - return false; - } else if (res == ConfirmAction.CANCEL) { - return false; - } - return false; - case PermissionStatus.restricted: - // iOS has restricted access to a specific feature. - return false; - default: - return false; - } -} - - -class AppPermissions{ - static void location(Function(bool) completion) { - Permission.location.isGranted.then((isGranted){ - if(!isGranted){ - Permission.location.request().then((granted){ - completion(granted == PermissionStatus.granted); - }); - } - completion(isGranted); - }); - - } - - static void checkAll(Function(bool) completion){ - [ - Permission.location - ].request().then((value){ - - bool allGranted = false; - value.values.forEach((element) { - allGranted = allGranted && element == PermissionStatus.granted; - }); - - completion(allGranted); - - }); - } -} \ No newline at end of file diff --git a/lib/utils/app_permission_handler.dart b/lib/utils/app_permission_handler.dart new file mode 100644 index 0000000..0d8bfd5 --- /dev/null +++ b/lib/utils/app_permission_handler.dart @@ -0,0 +1,97 @@ +import 'dart:developer'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/widgets/common_widgets/confirm_dialog.dart'; +import 'package:permission_handler/permission_handler.dart'; + +import 'dialogs_and_bottomsheets.dart'; + +enum ConfirmAction { CANCEL, ACCEPT } + +Future requestPermissionGranted(context, Permission requestPermissions) async { + var result = await requestPermissions.request(); + + log("result: $result"); + switch (result) { + case PermissionStatus.granted: + // Application has been given permission to use the feature. + return true; + case PermissionStatus.denied: + // Application has been denied permission to use the feature. + return false; + case PermissionStatus.permanentlyDenied: + ConfirmAction? res = await showConfirmDialogs(context, 'This permission was denied permanently, Please go to settings and allow. ', 'Open App Setting', 'Cancel'); + if (res == ConfirmAction.ACCEPT) { + return false; + } else if (res == ConfirmAction.CANCEL) { + return false; + } + return false; + case PermissionStatus.restricted: + // iOS has restricted access to a specific feature. + return false; + default: + return false; + } +} + +class AppPermissions { + static void location(Function(bool) completion) { + Permission.location.isGranted.then((isGranted) { + if (!isGranted) { + Permission.location.request().then((granted) { + completion(granted == PermissionStatus.granted); + }); + } + completion(isGranted); + }); + } + + static void checkAll(Function(bool) completion) { + [Permission.location].request().then((value) { + bool allGranted = false; + for (var element in value.values) { + allGranted = allGranted && element == PermissionStatus.granted; + } + + completion(allGranted); + }); + } + + static getDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to give storage permission to select files.", + onTap: () { + Navigator.pop(context); + }, + ), + ); + } + + static Future checkStoragePermissions(BuildContext context) async { + bool permissionStatus; + final deviceInfo = await DeviceInfoPlugin().androidInfo; + + if (deviceInfo.version.sdkInt! > 32) { + permissionStatus = await Permission.photos.request().isGranted; + if (permissionStatus) { + return true; + } else { + getDialog(context); + return false; + } + } else { + permissionStatus = await Permission.storage.request().isGranted; + if (permissionStatus) { + return true; + } else { + getDialog(context); + + return false; + } + } + } +} diff --git a/lib/utils/dialogs_and_bottomsheets.dart b/lib/utils/dialogs_and_bottomsheets.dart index 7d1f06f..4360164 100644 --- a/lib/utils/dialogs_and_bottomsheets.dart +++ b/lib/utils/dialogs_and_bottomsheets.dart @@ -1,7 +1,7 @@ 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/utils/AppPermissionHandler.dart'; +import 'package:mc_common_app/utils/app_permission_handler.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; Future showConfirmDialogs(context, msg, positiveText, negativeText) async { diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index b70f540..79c33c6 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -17,6 +17,9 @@ enum AdReserveStatus { reserved, cancelledByOwner, cancelledByAdmin, + timeOver, + dealDone, + fullPaymentVerified, } enum CreatedByRoleEnum { customer, provider, admin, allAds } diff --git a/lib/utils/location/Location.dart b/lib/utils/location/Location.dart index b8ba710..bdb67b4 100644 --- a/lib/utils/location/Location.dart +++ b/lib/utils/location/Location.dart @@ -7,7 +7,7 @@ import 'package:flutter/rendering.dart'; import 'package:geolocator/geolocator.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:mc_common_app/utils/AppPermissionHandler.dart'; +import 'package:mc_common_app/utils/app_permission_handler.dart'; import 'package:mc_common_app/utils/utils.dart'; diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 6fbe9fd..eba6428 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/cupertino.dart'; @@ -75,7 +76,7 @@ class AdVM extends BaseVM { List exploreAdsFilteredList = []; List myAdsFilteredList = []; List myAds = []; - List myReservedAdsRespModel = []; + List myReservedAds = []; List myActiveAdsForHome = []; List vehicleDamageCards = []; @@ -161,11 +162,12 @@ class AdVM extends BaseVM { List myAdsFilterOptions = []; populateAdsFilterList() async { + if (myAdsFilterOptions.isNotEmpty && exploreAdsFilterOptions.isNotEmpty) return; if (myAdsEnums.isEmpty) { - myAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: 18); //TODO: 18 is to get My Ad Filter Enums + myAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: 18); //18 is to get My Ad Filter Enums } if (exploreAdsEnums.isEmpty) { - exploreAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: 23); //TODO: 23 is to get Explore Ad Filter Enums + exploreAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: 23); // 23 is to get Explore Ad Filter Enums } exploreAdsFilterOptions.clear(); @@ -217,10 +219,8 @@ class AdVM extends BaseVM { return; } // this means if the filter is reserved ads - dynamic selectedIds = []; - if (index == 3 && adPostStatusEnum.getIdFromAdPostStatusEnum() == 9) { - selectedIds = myReservedAdsRespModel.map((component) => component.adsID).toList(); - myAdsFilteredList = myAds.where((element) => selectedIds.contains(element.id)).toList(); + if (adPostStatusEnum.getIdFromAdPostStatusEnum() == 9) { + myAdsFilteredList = myReservedAds; for (var ad in myAdsFilteredList) { ad.isReservedByMe = true; } @@ -241,12 +241,25 @@ class AdVM extends BaseVM { setState(ViewState.idle); } - Future getMyReservedAds() async { + Future> getMyReservedAds() async { setState(ViewState.busy); - //TODO: BREAKING - // myReservedAdsRespModel = await adsRepo.getMyReservedAds(); + myReservedAds = await adsRepo.getMyReservedAds(); isFetchingLists = false; setState(ViewState.idle); + return myReservedAds; + } + + Future createReserveAd({required int adId, required BuildContext context}) async { + Utils.showLoading(context); + GenericRespModel genericRespModel = await adsRepo.createReserveAd(adId: adId); + if (genericRespModel.messageStatus == null) { + Utils.hideLoading(context); + + Utils.showToast(genericRespModel.message ?? "Something went wrong!"); + return false; + } + Utils.hideLoading(context); + return true; } Future getExploreAds() async { @@ -316,9 +329,9 @@ class AdVM extends BaseVM { navigateReplaceWithName(context, AppRoutes.dashboard); } - Future cancelMyAdReservation(BuildContext context, {required int adId}) async { + Future cancelMyAdReservation(BuildContext context, {required int adId, required String reason}) async { Utils.showLoading(context); - GenericRespModel respModel = await adsRepo.cancelMyAdReservation(adId: adId); + GenericRespModel respModel = await adsRepo.cancelMyAdReservation(adId: adId, adsReserveStatus: 11, reason: reason); // 11 is to Cancel Reservation if (respModel.messageStatus != 1) { Utils.hideLoading(context); @@ -532,6 +545,22 @@ class AdVM extends BaseVM { vehicleDemandAmount = amount; } + String reservationCancelReason = ""; + String reservationCancelError = ""; + + void updateReservationCancelReason(String value) { + if (value.isNotEmpty) { + reservationCancelError = ""; + } + reservationCancelReason = value; + } + + String completeDealNotesForAdmin = ""; + + void updateCompleteDealNotesForAdmin(String value) { + completeDealNotesForAdmin = value; + } + String vehicleTitle = ""; void updateVehicleTitle(String title) { @@ -854,6 +883,17 @@ class AdVM extends BaseVM { return isValidated; } + bool validateReservationCancelReason() { + bool isValidated = true; + + if (reservationCancelReason.isEmpty) { + reservationCancelError = GlobalConsts.reservationCancelError; + isValidated = false; + notifyListeners(); + } + return isValidated; + } + bool isDamagePartsValidated() { bool isValidated = true; @@ -1034,6 +1074,26 @@ class AdVM extends BaseVM { notifyListeners(); } + List pickedReceiptPdfFiles = []; + + String receiptPdfFileError = ""; + + void removePdfFileFromList(String filePath) { + int index = pickedReceiptPdfFiles.indexWhere((element) => element.path == filePath); + if (index == -1) { + return; + } + pickedReceiptPdfFiles.removeAt(index); + notifyListeners(); + } + + void pickPdfReceiptFile(BuildContext context) async { + List? files = await commonServices.pickMultipleFiles(context); + pickedReceiptPdfFiles.addAll(files!); + if (pickedReceiptPdfFiles.isNotEmpty) receiptPdfFileError = ""; + notifyListeners(); + } + // sourceFlag for Camera = 0 // sourceFlag for Gallery = 1 void pickDamagePartImage(int index) async { diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 118ccc6..e7c1c58 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -257,7 +257,7 @@ class AppointmentsVM extends BaseVM { List myAppointmentsEnum = []; populateAppointmentsFilterList() async { - appointmentsFilterOptions.clear(); + if (appointmentsFilterOptions.isNotEmpty) return; myAppointmentsEnum = await commonRepo.getEnumTypeValues(enumTypeID: 13); //TODO: 13 is to get Appointments Filter Enums @@ -502,7 +502,7 @@ class AppointmentsVM extends BaseVM { } populateBranchesFilterList() { - providersFilterOptions.clear(); + providersFilterOptions.clear(); // TODO: THIS SHOULD BE DYNAMIC AND FILTERS SHOULD COME FORM API providersFilterOptions = [ FilterListModel(title: "All Providers", isSelected: true, id: 0), FilterListModel(title: "Maintenance", isSelected: false, id: 1), diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index f3d58f3..cdd4859 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -37,6 +37,13 @@ class ChatVM extends ChangeNotifier { notifyListeners(); } + int latestOfferId = 0; + + updateLatestOfferId(var value) { + latestOfferId = value; + notifyListeners(); + } + List offerRejectModelList = [ OfferRequestCommentModel( index: 0, @@ -93,8 +100,8 @@ class ChatVM extends ChangeNotifier { return isValidated; } - Future onNewMessageReceived({required List messages, required BuildContext context}) async { - log("message I received in onNewMessageReceived ${messages.first.toString()}"); + Future onNewMessageReceived({required List messages, required BuildContext context, bool isMyOwnOffer = false}) async { + // log("message I received in onNewMessageReceived ${messages.first.toString()}"); if (AppState().currentAppType == AppType.customer) { for (var msg in messages) { int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == msg.senderUserID); @@ -104,12 +111,20 @@ class ChatVM extends ChangeNotifier { } } else { for (var msg in messages) { - int providerIndex = context.read().myFilteredRequests.indexWhere( - (element) => element.customerUserID == msg.receiverUserID, - ); // Where we need to call this function for saving a message in chat we will use receiverUserID and in those cases where received ID is null , it means it is a signal R call - log("here is it: $providerIndex"); - log("here is it: ${msg.receiverUserID}"); - log("here is it: ${context.read().myFilteredRequests.first.customerUserID.toString()}"); + // Where we need to call this function for saving a message in chat we will use receiverUserID and in those cases where received ID is null , it means it is a signal R call + int providerIndex = -1; + if (isMyOwnOffer) { + providerIndex = context.read().myFilteredRequests.indexWhere( + (element) => element.customerUserID == msg.receiverUserID, + ); + } else { + providerIndex = context.read().myFilteredRequests.indexWhere( + (element) => element.customerUserID == msg.senderUserID, + ); + } + // log("here is it: $providerIndex"); + // log("here is it: ${msg.senderUserID}"); + // log("here is it: ${context.read().myFilteredRequests.first.customerUserID.toString()}"); if (providerIndex != -1) { context.read().addChatMessagesInRequestsModel(msg: msg, index: providerIndex); } @@ -199,6 +214,14 @@ class ChatVM extends ChangeNotifier { final userId = AppState().getUser.data!.userInfo!.userId.toString(); final name = AppState().getUser.data!.userInfo!.firstName.toString(); + log("I have saved this: ${({ + "ReceiverUserID": receiverId, + "SenderUserID": userId, + "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), + "ChatText": message, + "RequestID": requestId, + "ReqOfferID": latestOfferId, + }).toString()}"); hubConnection!.invoke( "SendMessageRequestOffer", args: [ @@ -208,7 +231,7 @@ class ChatVM extends ChangeNotifier { "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), "ChatText": message, "RequestID": requestId, - "ReqOfferID": 0, + "ReqOfferID": latestOfferId, } ], ).catchError((e) { @@ -272,6 +295,14 @@ class ChatVM extends ChangeNotifier { Utils.showLoading(context); List chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId); serviceProviderOffersList[providerOfferIndex].chatMessages = chatMessages; + if (serviceProviderOffersList[providerOfferIndex].chatMessages != null) { + for (var message in serviceProviderOffersList[providerOfferIndex].chatMessages!) { + if (message.chatMessageTypeEnum == ChatMessageTypeEnum.offer) { + updateLatestOfferId(message.reqOfferID ?? 0); + log("latestOfferId: $latestOfferId"); + } + } + } Utils.hideLoading(context); notifyListeners(); } catch (e) { diff --git a/lib/view_models/payment_view_model.dart b/lib/view_models/payment_view_model.dart index c3f9f9d..7e1ea9d 100644 --- a/lib/view_models/payment_view_model.dart +++ b/lib/view_models/payment_view_model.dart @@ -194,7 +194,7 @@ class PaymentVM extends ChangeNotifier { // TODO: Handle this case. break; case PaymentTypes.subscription: - // TODO: Handle this case. + await placeThePayment(context: context, paymentTypeEnum: paymentType); break; case PaymentTypes.ads: case PaymentTypes.adReserve: diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index 1774784..56a164b 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -40,7 +40,8 @@ class RequestsVM extends BaseVM { List myRequestsTypeEnum = []; populateRequestsFilterList() async { - requestsTypeFilterOptions.clear(); + if (requestsTypeFilterOptions.isNotEmpty) return; + if (myRequestsTypeEnum.isEmpty) { myRequestsTypeEnum = await commonRepo.getEnumTypeValues(enumTypeID: 16); //TODO: 16 is to get Requests Filter Enums } @@ -445,7 +446,7 @@ class RequestsVM extends BaseVM { comment: message, offerStatusText: "", )); - context.read().onNewMessageReceived(messages: [chatMessageModel], context: context); + context.read().onNewMessageReceived(messages: [chatMessageModel], context: context, isMyOwnOffer: true); if (!isFromChatScreen) { ChatViewArguments chatViewArguments = ChatViewArguments( chatTypeEnum: ChatTypeEnum.requestOffer, diff --git a/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart b/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart index 139ba00..ba0ef7b 100644 --- a/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart +++ b/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart @@ -139,11 +139,7 @@ class AdDuration extends StatelessWidget { children: [ "Contact Details".toText(fontSize: 18, isBold: true), 8.height, - Row( - children: [ - "Do you want to show your number to the buyers?".toText(fontSize: 14), - ], - ), + "Do you want to show your number to the buyers?".toText(fontSize: 14), 6.height, Container( width: 38, @@ -215,7 +211,7 @@ class AdDuration extends StatelessWidget { ), ), const SizedBox(width: 10), - "Is this number registered on WhatsApp?".toString().toText(fontSize: 14) + "Is this number registered on WhatsApp?".toString().toText() ], ), ], diff --git a/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart b/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart index e256a9e..a939613 100644 --- a/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart +++ b/lib/views/advertisement/ad_creation_steps/ad_review_containers.dart @@ -112,11 +112,11 @@ class VehicleDetailsReview extends StatelessWidget { "Vehicle Pictures:".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), if (adVM.pickedVehicleImages.isNotEmpty) ...[ // 10.height, - PickedImagesContainer( - pickedImages: adVM.pickedVehicleImages, + PickedFilesContainer( + pickedFiles: adVM.pickedVehicleImages, onCrossPressedPrimary: adVM.removeImageFromList, isReview: true, - onAddImagePressed: () {}, + onAddFilePressed: () {}, ), ], ], @@ -141,11 +141,11 @@ class DamagePartsReview extends StatelessWidget { ), if (adVM.vehicleDamageCards[index].partImages != null && adVM.vehicleDamageCards[index].partImages!.isNotEmpty) ...[ "Damage Part Pictures:".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - PickedImagesContainer( - pickedImages: adVM.vehicleDamageCards[index].partImages!, + PickedFilesContainer( + pickedFiles: adVM.vehicleDamageCards[index].partImages!, onCrossPressedPrimary: adVM.removeImageFromList, isReview: true, - onAddImagePressed: () {}, + onAddFilePressed: () {}, ), ], if (index != adVM.vehicleDamageCards.length - 1) const Divider(thickness: 2), diff --git a/lib/views/advertisement/ad_creation_steps/damage_parts_container.dart b/lib/views/advertisement/ad_creation_steps/damage_parts_container.dart index 73912f4..a71a318 100644 --- a/lib/views/advertisement/ad_creation_steps/damage_parts_container.dart +++ b/lib/views/advertisement/ad_creation_steps/damage_parts_container.dart @@ -101,13 +101,13 @@ class DamageParts extends StatelessWidget { // }), // 8.height, // vehicleDamageCard.partImages != null ? - PickedImagesContainer( - pickedImages: vehicleDamageCard.partImages ?? [], + PickedFilesContainer( + pickedFiles: vehicleDamageCard.partImages ?? [], onCrossPressedSecondary: (imageIndex, filePath) { adVM.removeDamageImageFromCard(imageIndex, filePath, index); }, index: index, - onAddImagePressed: () { + onAddFilePressed: () { context.read().pickDamagePartImage(index); }, ), 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 8b0b247..e8c4a33 100644 --- a/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart +++ b/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart @@ -278,10 +278,10 @@ class VehicleDetails extends StatelessWidget { ], if (adVM.pickedVehicleImages.isNotEmpty) ...[ 16.height, - PickedImagesContainer( - pickedImages: adVM.pickedVehicleImages, + PickedFilesContainer( + pickedFiles: adVM.pickedVehicleImages, onCrossPressedPrimary: adVM.removeImageFromList, - onAddImagePressed: () { + onAddFilePressed: () { context.read().pickMultipleImages(); }, ), diff --git a/lib/views/advertisement/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view.dart index 959eb15..0698ffa 100644 --- a/lib/views/advertisement/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view.dart @@ -7,6 +7,7 @@ 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/models/advertisment_models/ad_details_model.dart'; +import 'package:mc_common_app/models/advertisment_models/ads_bank_details_model.dart'; import 'package:mc_common_app/models/advertisment_models/special_service_model.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; @@ -18,6 +19,8 @@ import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/view_models/payment_view_model.dart'; import 'package:mc_common_app/views/advertisement/ad_duration_selection_sheet_content.dart'; import 'package:mc_common_app/views/advertisement/ads_images_slider.dart'; +import 'package:mc_common_app/views/advertisement/custom_add_button.dart'; +import 'package:mc_common_app/views/advertisement/picked_images_container.dart'; import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart'; import 'package:mc_common_app/widgets/bottom_sheet.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; @@ -41,7 +44,7 @@ class AdsDetailView extends StatefulWidget { class _AdsDetailViewState extends State { @override void initState() { - log("ad: ${widget.adDetails.id}"); + log("ad: ${widget.adDetails.adReserveStatus}"); scheduleMicrotask(() { onAdDetailsLoaded(); }); @@ -298,7 +301,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "Reservation Amounts".toText(fontSize: 16, isBold: true), + "Reservation Amount".toText(fontSize: 16, isBold: true), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ @@ -360,7 +363,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { ], ), 12.height, - "Special service charges will be added based on desired insurance and delivery Location".toText(fontSize: 12), + "Special service charges will be added based on desired insurance and delivery Location".toText(fontSize: 12, maxLines: 2), 30.height, Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -383,7 +386,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { "Estimated".toText(fontSize: 10, isBold: true), ], ), - 44.height, + 30.height, Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ @@ -393,7 +396,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { size: 19, ).paddingOnly(bottom: 2), 3.width, - "Some services are mandatory while reserving the Ad.".toText( + "Some services are mandatory while reserving Ad.".toText( color: MyColors.adPendingStatusColor, fontSize: 12, isItalic: true, @@ -407,9 +410,12 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { child: ShowFillButton( maxHeight: 55, title: "Complete Reservation", - onPressed: () { - Navigator.pop(context); - navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.adReserve); + onPressed: () async { + // Navigator.pop(context); + bool status = await context.read().createReserveAd(adId: adDetailsModel.id!, context: context); + if (status) { + navigateReplaceWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.adReserve); + } }, ), ), @@ -902,63 +908,69 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { title: "Cancel Reservation".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( + child: Consumer( + builder: (BuildContext context, AdVM adVM, Widget? child) { + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - 12.height, - TxtField( - maxLines: 5, - value: "", - errorValue: "", - keyboardType: TextInputType.text, - hint: "Reason for cancellation", - onChanged: (v) => () {}, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 12.height, + TxtField( + maxLines: 5, + value: adVM.reservationCancelReason, + errorValue: adVM.reservationCancelError, + keyboardType: TextInputType.text, + hint: "Reason for cancellation", + onChanged: (v) => adVM.updateReservationCancelReason(v), + ), + ], ), + 25.height, + ShowFillButton( + title: "Submit", + onPressed: () { + bool status = adVM.validateReservationCancelReason(); + if (status) { + Navigator.pop(context); + return actionConfirmationBottomSheet( + context: context, + title: "Do you want to cancel the reservation?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: "Your ad reservation will be cancelled and this ad will be again visible to everyone to buy.", + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: "Yes", + fontSize: 15, + onPressed: () { + Navigator.pop(context); + adVM.cancelMyAdReservation(context, adId: adDetails.id!, reason: ""); + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: "No", + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } + }, + maxWidth: double.infinity, + ), + 19.height, ], - ), - 25.height, - ShowFillButton( - title: "Submit", - onPressed: () { - Navigator.pop(context); - AdVM adVM = context.read(); - return actionConfirmationBottomSheet( - context: context, - title: "Do you want to cancel the reservation?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44), - subtitle: "Your ad reservation will be cancelled and this ad will be again visible to everyone to buy.", - actionButtonYes: Expanded( - child: ShowFillButton( - maxHeight: 55, - title: "Yes", - fontSize: 15, - onPressed: () { - Navigator.pop(context); - adVM.cancelMyAdReservation(context, adId: adDetails.id!); - }, - ), - ), - actionButtonNo: Expanded( - child: ShowFillButton( - maxHeight: 55, - isFilled: false, - borderColor: MyColors.darkPrimaryColor, - title: "No", - txtColor: MyColors.darkPrimaryColor, - fontSize: 15, - onPressed: () { - Navigator.pop(context); - }, - ), - ), - ); - }, - maxWidth: double.infinity, - ), - 19.height, - ], + ); + }, ), )); }, @@ -984,6 +996,104 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { ); } + Widget completeDealAction(BuildContext context, {required AdDetailsModel adDetails}) { + return Row( + children: [ + Expanded( + child: ShowFillButton( + maxHeight: 55, + backgroundColor: MyColors.darkPrimaryColor, + txtColor: MyColors.white, + isBold: false, + title: "Complete Deal", + onPressed: () { + buildCompleteDealBottomSheet(context, adDetails: adDetails); + }), + ), + ], + ); + } + + Future buildCompleteDealBottomSheet(BuildContext context, {required AdDetailsModel adDetails}) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return InfoBottomSheet( + title: "Upload Bank Receipt".toText(fontSize: 26, isBold: true, letterSpacing: -1.44), + description: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Consumer( + builder: (BuildContext context, AdVM adVM, Widget? child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 12.height, + TxtField( + maxLines: 4, + value: adVM.completeDealNotesForAdmin, + errorValue: "", + keyboardType: TextInputType.text, + hint: "Notes For Admin", + onChanged: (v) => adVM.updateCompleteDealNotesForAdmin(v), + ), + ], + ), + 15.height, + "Attach File".toText(fontSize: 20, isBold: true, letterSpacing: -0.5), + if (adVM.pickedReceiptPdfFiles.isNotEmpty) ...[ + 16.height, + PickedFilesContainer( + pickedFiles: adVM.pickedReceiptPdfFiles, + onCrossPressedPrimary: adVM.removePdfFileFromList, + onAddFilePressed: () { + context.read().pickPdfReceiptFile(context); + }, + isPdf: true, + ), + ] else ...[ + Row( + children: [ + Container( + height: 90, + width: 90, + decoration: BoxDecoration(color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)), + margin: const EdgeInsets.all(8), + alignment: Alignment.center, + child: Container( + height: 24, + width: 24, + decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor), + child: const Icon(Icons.add, color: MyColors.white), + ), + ).onPress(() { + context.read().pickPdfReceiptFile(context); + }), + ], + ), + ], + 15.height, + ShowFillButton( + title: "Submit", + onPressed: () { + //Upload Attachment + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ); + }, + ), + )); + }, + ); + } + Widget expiredAdAction(BuildContext context) { return Row( children: [ @@ -1037,6 +1147,37 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { ); } + Widget reservedAdActions(context, {required AdDetailsModel adDetailsModel, required AdVM adVM}) { + switch (adDetailsModel.adReserveStatus) { + case AdReserveStatus.defaultStatus: + return pendingForReviewAction(pendingText: "Waiting for Admins Response"); + + case AdReserveStatus.reserved: + return cancelReservationAction(context, adDetails: adDetailsModel); + + case AdReserveStatus.cancelledByOwner: + return pendingForReviewAction(pendingText: "Cancelled by Owner"); + + case AdReserveStatus.cancelledByAdmin: + return pendingForReviewAction(pendingText: "Cancelled by Admin"); + + case AdReserveStatus.timeOver: + return pendingForReviewAction(pendingText: "Reservation Time Over"); + + case AdReserveStatus.dealDone: + if (adVM.adsBankDetailsModel != null) { + return completeDealAction(context, adDetails: adDetailsModel); + } + return pendingForReviewAction(pendingText: "Waiting for Admins Response"); + + case AdReserveStatus.fullPaymentVerified: + return pendingForReviewAction(pendingText: "Payment Verified"); + + default: + return pendingForReviewAction(pendingText: "Waiting for Admins Response"); + } + } + @override Widget build(BuildContext context) { switch (adDetailsModel.adPostStatus!) { @@ -1045,7 +1186,13 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { case AdPostStatus.active: return markAsSoldAction(context); case AdPostStatus.reserved: - return cancelReservationAction(context, adDetails: adDetailsModel); + AdVM adVM = context.watch(); + if (adVM.state == ViewState.busy) { + return const CircularProgressIndicator(); + } else { + reservedAdActions(context, adDetailsModel: adDetailsModel, adVM: adVM); + } + break; case AdPostStatus.buyingService: case AdPostStatus.reserveCancel: case AdPostStatus.rejected: diff --git a/lib/views/advertisement/picked_images_container.dart b/lib/views/advertisement/picked_images_container.dart index 257bec8..3971938 100644 --- a/lib/views/advertisement/picked_images_container.dart +++ b/lib/views/advertisement/picked_images_container.dart @@ -5,22 +5,24 @@ import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; -class PickedImagesContainer extends StatelessWidget { - final List pickedImages; +class PickedFilesContainer extends StatelessWidget { + final List pickedFiles; final Function(String filePath)? onCrossPressedPrimary; final Function(int index, String filePath)? onCrossPressedSecondary; final int? index; final bool isReview; - final Function() onAddImagePressed; + final bool isPdf; + final Function() onAddFilePressed; - const PickedImagesContainer({ + const PickedFilesContainer({ Key? key, - required this.pickedImages, + required this.pickedFiles, this.onCrossPressedPrimary, this.onCrossPressedSecondary, this.index, - required this.onAddImagePressed, + required this.onAddFilePressed, this.isReview = false, + this.isPdf = false, }) : super(key: key); @override @@ -32,11 +34,11 @@ class PickedImagesContainer extends StatelessWidget { crossAxisSpacing: 4.0, mainAxisSpacing: 8.0, children: List.generate( - isReview ? pickedImages.length : pickedImages.length + 1, + isReview ? pickedFiles.length : pickedFiles.length + 1, (index) { - if (index == pickedImages.length && !isReview) { + if (index == pickedFiles.length && !isReview) { return Container( - decoration: BoxDecoration( color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)), + decoration: BoxDecoration(color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)), margin: const EdgeInsets.all(8), alignment: Alignment.center, child: Container( @@ -45,17 +47,17 @@ class PickedImagesContainer extends StatelessWidget { decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor), child: const Icon(Icons.add, color: MyColors.white), ), - ).onPress(onAddImagePressed); + ).onPress(onAddFilePressed); } return Center( - child: BuildImageContainer( - file: pickedImages[index], - onCrossPressedPrimary: onCrossPressedPrimary, - onCrossPressedSecondary: onCrossPressedSecondary, - index: index, - isReview: isReview, - ), - ); + child: BuildFilesContainer( + file: pickedFiles[index], + onCrossPressedPrimary: onCrossPressedPrimary, + onCrossPressedSecondary: onCrossPressedSecondary, + index: index, + isReview: isReview, + isPdf: isPdf, + )); }, ), ); @@ -73,37 +75,51 @@ class PickedImagesContainer extends StatelessWidget { } } -class BuildImageContainer extends StatelessWidget { +class BuildFilesContainer extends StatelessWidget { final File file; final Function(String filePath)? onCrossPressedPrimary; final Function(int index, String filePath)? onCrossPressedSecondary; final int? index; final bool isReview; + final bool isPdf; - const BuildImageContainer({ + const BuildFilesContainer({ Key? key, required this.file, this.onCrossPressedPrimary, this.onCrossPressedSecondary, this.index, this.isReview = false, + this.isPdf = false, }) : super(key: key); @override Widget build(BuildContext context) { return Stack( children: [ - Container( + SizedBox( height: 90, width: 90, child: Stack( children: [ - Image.file( - file, - fit: BoxFit.fill, - height: 72, - width: 70, - ).paddingAll(8), + isPdf + ? Container( + height: 72, + width: 70, + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + border: Border.all( + width: 2, + color: MyColors.darkPrimaryColor, + )), + child: const Icon(Icons.picture_as_pdf).paddingAll(8), + ) + : Image.file( + file, + fit: BoxFit.fill, + height: 72, + width: 70, + ).paddingAll(8), !isReview ? Align( alignment: Alignment.topRight, 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 b086514..e1c5e43 100644 --- a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -136,7 +136,7 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Icon( + const Icon( Icons.warning, color: MyColors.adPendingStatusColor, size: 19, @@ -163,7 +163,7 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - // TODO: This Price will be decided according to the service selected + // 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), "SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), ], diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index ce0a20e..45e9de4 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -361,12 +361,8 @@ class _ChatMessageCustomWidgetState extends State { @override Widget build(BuildContext context) { - log("here message type : ${widget.chatMessageModel.chatMessageTypeEnum}"); - log("here message type : ${widget.chatMessageModel.chatText}"); - return Directionality( textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr, - // textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/views/profile/profile_view.dart b/lib/views/profile/profile_view.dart new file mode 100644 index 0000000..5cc7ef1 --- /dev/null +++ b/lib/views/profile/profile_view.dart @@ -0,0 +1,270 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:image_picker/image_picker.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/theme/colors.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.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'; + +class ProfileScreen extends StatelessWidget { + const ProfileScreen({Key? key}) : super(key: key); + + // final ImagePicker _picker = ImagePicker(); + + Widget showItem({required String icon, required String title, required VoidCallback onTap, String? subTitle}) { + return InkWell( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 16, + height: 16, + child: SvgPicture.asset( + icon, + color: subTitle == null ? MyColors.black : MyColors.primaryColor, + ), + ), + 12.width, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + title.toText(isBold: true, fontSize: 14), + if (subTitle != null) subTitle.toText(fontSize: 14, color: MyColors.primaryColor), + ], + ), + ), + const Icon( + Icons.arrow_forward, + size: 16, + ), + ], + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBody: true, + backgroundColor: const Color(0xffefefef), + body: Stack( + children: [ + Column( + children: [ + Expanded( + flex: 3, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(MyAssets.icLogoWhitePng), + fit: BoxFit.cover, + ), + ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 7.0, sigmaY: 7.0), + child: Container( + // height:, + color: Colors.white.withOpacity(0.0), + ), + ), + ), + ), + Expanded( + flex: 8, + child: Container( + width: double.infinity, + color: Colors.white, + child: ListView( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + height: 90, + alignment: Alignment.centerLeft, + child: ClipOval( + child: Image.asset( + MyAssets.carBanner, + width: 90, + height: 90, + fit: BoxFit.fill, + ), + )), + Container( + height: 35, + width: 35, + 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), + ).onPress(() {}) + ], + ).horPaddingMain(), + 10.height, + "Muhammad Ahmad".toText(fontSize: 20).paddingOnly(left: 25), + Column( + children: [ + CustomProfileOptionsTile( + titleText: "Country", + subtitleText: "Saudi Arabia", + needBorderBelow: true, + onTap: () {}, + ), + CustomProfileOptionsTile( + titleText: "Email", + subtitleText: "mail@gmail.com", + needBorderBelow: true, + onTap: () {}, + ), + CustomProfileOptionsTile( + titleText: "Phone Number", + subtitleText: "0504278212", + needBorderBelow: true, + onTap: () {}, + ), + CustomProfileOptionsTile( + titleText: "Password", + subtitleText: "************", + onTap: () {}, + ), + ], + ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), + ], + ), + ), + ), + ], + ), + + CircleAvatar( + radius: 20, + backgroundColor: Colors.white, + child: const Icon(Icons.arrow_back_ios_rounded, color: MyColors.darkIconColor, size: 18).paddingOnly(right: 4), + ).onPress(() { + Navigator.pop(context); + }).paddingOnly(left: 21, right: 21, top: 50), + + // SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // CircleAvatar( + // radius: 20, + // backgroundColor: Colors.white, + // child: const Icon(Icons.arrow_back_ios_rounded, color: MyColors.darkIconColor, size: 18).paddingOnly(right: 4), + // ).onPress(() { + // Navigator.pop(context); + // }), + // ], + // ).paddingOnly(left: 21, right: 21, top: 50), + // Stack( + // children: [ + // Column( + // children: [ + // showItem( + // icon: MyAssets.icEmail, + // title: "Country", + // onTap: () { + // navigateWithName(context, AppRoutes.changeEmailPage); + // }, + // ), + // const Divider( + // height: 1, + // ), + // showItem( + // icon: MyAssets.icPassword, + // title: "Country", + // onTap: () { + // navigateWithName(context, AppRoutes.changePassword); + // }, + // ), + // const Divider( + // height: 1, + // ), + // showItem( + // icon: MyAssets.icPhoneNumber, + // title: "Country", + // onTap: () { + // navigateWithName(context, AppRoutes.changeMobilePage); + // }, + // ), + // const Divider( + // height: 1, + // ), + // ], + // ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.symmetric(horizontal: 21)), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Container( + // height: 68, + // alignment: Alignment.centerLeft, + // child: ClipOval( + // child: Image.asset( + // MyAssets.carBanner, + // width: 68, + // height: 68, + // fit: BoxFit.fill, + // ), + // )), + // InkWell( + // onTap: () {}, + // child: Container( + // padding: const EdgeInsets.only(left: 17, right: 17, top: 8, bottom: 8), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(30), + // color: Colors.black.withOpacity(.21), + // ), + // child: Row( + // children: [ + // const Icon(Icons.photo, color: Colors.white, size: 16), + // 4.width, + // "edit".toText(fontSize: 12, color: Colors.white), + // ], + // ), + // ), + // ), + // ], + // ), + // ], + // ).horPaddingMain(), + // ], + // ), + // ), + // Container( + // height: MediaQuery.of(context).size.height, + // width: MediaQuery.of(context).size.width, + // color: Colors.white, + // child: Column( + // children: [ + // CustomSettingOptionsTile( + // leadingWidget: const Icon(Icons.person, size: 20), titleText: "Invite Friends", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.myRequestsPage)), + // CustomSettingOptionsTile( + // leadingWidget: const Icon(Icons.help, size: 20), titleText: "Help", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs)), + // CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "Account", onTap: () => navigateWithName(context, AppRoutes.profileView)), + // ], + // ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, borderRadius: 0), + // ), + ], + ), + ); + } +} + +// + +// diff --git a/lib/views/requests/create_request_page.dart b/lib/views/requests/create_request_page.dart index 43bc84c..54acc45 100644 --- a/lib/views/requests/create_request_page.dart +++ b/lib/views/requests/create_request_page.dart @@ -192,10 +192,10 @@ class CreateRequestPage extends StatelessWidget { ], if (requestsVM.pickedVehicleImages.isNotEmpty) ...[ 16.height, - PickedImagesContainer( - pickedImages: requestsVM.pickedVehicleImages, + PickedFilesContainer( + pickedFiles: requestsVM.pickedVehicleImages, onCrossPressedPrimary: requestsVM.removeImageFromList, - onAddImagePressed: () { + onAddFilePressed: () { context.read().pickMultipleImages(); }, ), diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart index 6b4652b..40c1a16 100644 --- a/lib/views/requests/offer_list_page.dart +++ b/lib/views/requests/offer_list_page.dart @@ -6,7 +6,6 @@ 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/models/requests_models/provider_offers_model.dart'; -import 'package:mc_common_app/models/requests_models/request_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; @@ -26,7 +25,7 @@ class OfferListPage extends StatelessWidget { return Scaffold( appBar: const CustomAppBar(title: "Offers"), body: serviceProviderOffers.isEmpty - ? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) + ? Center(child: "No Offers to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) : ListView.separated( itemCount: serviceProviderOffers.length, padding: const EdgeInsets.all(16), diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index dc2fb1f..e5cf4c1 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -81,7 +81,7 @@ class RequestItem extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - request.price.toString().toText( + request.price.toInt().toString().toText( fontSize: 20, color: Colors.black, isBold: true, diff --git a/lib/views/setting_options/setting_options_invite_friends.dart b/lib/views/setting_options/setting_options_invite_friends.dart index 91dfea3..cb9512f 100644 --- a/lib/views/setting_options/setting_options_invite_friends.dart +++ b/lib/views/setting_options/setting_options_invite_friends.dart @@ -2,9 +2,7 @@ import 'package:flutter/material.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; -import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/navigator.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'; @@ -28,10 +26,22 @@ class SettingOptionsInviteFriends extends StatelessWidget { Column( children: [ CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.person, size: 20), titleText: "Invite Friends", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.myRequestsPage)), + leadingWidget: const Icon(Icons.person, size: 20), + titleText: "Invite Friends", + needBorderBelow: true, + onTap: () {}, + ), CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.help, size: 20), titleText: "Help", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs)), - CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "Account", onTap: () {}), + leadingWidget: const Icon(Icons.help, size: 20), + titleText: "Help", + needBorderBelow: true, + onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs), + ), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.person, size: 20), + titleText: "Account", + onTap: () => navigateWithName(context, AppRoutes.profileView), + ), ], ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), ], diff --git a/lib/views/setting_options/setting_options_language.dart b/lib/views/setting_options/setting_options_language.dart index ed29f70..cc4ea4f 100644 --- a/lib/views/setting_options/setting_options_language.dart +++ b/lib/views/setting_options/setting_options_language.dart @@ -6,6 +6,7 @@ import 'package:mc_common_app/views/setting_options/widgets/custom_setting_optio 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 SettingOptionsLanguage extends StatelessWidget { const SettingOptionsLanguage({super.key}); @@ -26,7 +27,15 @@ class SettingOptionsLanguage extends StatelessWidget { children: [ Column( children: [ - CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "My Requests", needBorderBelow: true, onTap: () {}), + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.person, size: 20), + titleText: "My Requests", + needBorderBelow: true, + onTap: () { + context.read + Navigator.pop(context); + Navigator.pop(context); + }), CustomSettingOptionsTile(leadingWidget: const Icon(Icons.favorite, size: 20), titleText: "Favorite list", needBorderBelow: true, onTap: () {}), CustomSettingOptionsTile(leadingWidget: const Icon(Icons.settings, size: 20), titleText: "Settings", onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends)), ], 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 b417c7f..2553d44 100644 --- a/lib/views/setting_options/widgets/custom_setting_options_tile.dart +++ b/lib/views/setting_options/widgets/custom_setting_options_tile.dart @@ -1,6 +1,7 @@ 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/theme/colors.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; class CustomSettingOptionsTile extends StatelessWidget { @@ -37,3 +38,37 @@ class CustomSettingOptionsTile extends StatelessWidget { ); } } + +class CustomProfileOptionsTile extends StatelessWidget { + final String titleText; + final bool needBorderBelow; + final Function() onTap; + final String subtitleText; + + const CustomProfileOptionsTile({super.key, required this.onTap, required this.titleText, required this.subtitleText, this.needBorderBelow = false}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + titleText.toText(fontSize: 16), + subtitleText.toText(fontSize: 12, color: MyColors.darkTextColor.withOpacity(0.7)), + ], + ), + const Icon(Icons.edit_note, size: 25), + ], + ).onPress(onTap), + 5.height, + if (needBorderBelow) ...[ + const Divider(thickness: 1), + ], + ], + ); + } +} diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index 8b6c525..19e484c 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -33,7 +33,8 @@ class _LoginWithPasswordState extends State { ClassType type = ClassType.EMAIL; //TODO: ONLY FOR DEVELOPMENT PURPOSE - String phoneNum = "966504278213", password = "Fa@1234"; + // String phoneNum = "966504278213", password = "Fa@1234"; + String phoneNum = "966504278214", password = "Fa@1234"; String email = ""; String countryCode = ""; Country? _country; diff --git a/lib/widgets/common_widgets/confirm_dialog.dart b/lib/widgets/common_widgets/confirm_dialog.dart new file mode 100644 index 0000000..1233320 --- /dev/null +++ b/lib/widgets/common_widgets/confirm_dialog.dart @@ -0,0 +1,67 @@ +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/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/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; + +class ConfirmDialog extends StatelessWidget { + final String? title; + final String message; + final String? okTitle; + final VoidCallback? onTap; + final VoidCallback? onCloseTap; + + const ConfirmDialog({Key? key, this.title, required this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.white, + shape: const RoundedRectangleBorder(), + insetPadding: const EdgeInsets.only(left: 21, right: 21), + child: Padding( + padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + title ?? LocaleKeys.confirm.tr(), + style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, height: 35 / 24, letterSpacing: -0.96), + ).paddingOnly(top: 16), + ), + IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.close), + color: MyColors.darkTextColor, + constraints: const BoxConstraints(), + onPressed: () => onCloseTap ?? Navigator.pop(context), + // onPressed: () => Navigator.pop(context), + ) + ], + ), + message.toText(fontSize: 18, color: MyColors.lightTextColor), + 28.height, + Row( + children: [ + Expanded( + child: ShowFillButton( + title: okTitle ?? "OK", + onPressed: onTap ?? () => Navigator.pop(context), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index f042568..0e3be1a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,12 +17,13 @@ dependencies: provider: ^6.0.0 easy_localization: ^3.0.3 http: ^0.13.3 - permission_handler: any + permission_handler: ^10.2.0 + device_info_plus: any flutter_svg: ^1.0.3 sizer: ^2.0.15 fluttertoast: ^8.0.8 shared_preferences: ^2.0.6 - file_picker: ^4.4.0 + file_picker: ^6.1.1 image_picker: ^0.8.4+4 equatable: ^2.0.3 logger: ^1.1.0