diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 995b20f..0b53219 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -329,7 +329,7 @@ "vehiclePart": "جزء المركبة", "damagePartPictures": "صور الأجزاء المتضررة", "vehicleDamagePart": "جزء المركبة المتضرر", - "duration": ":المدة", + "duration": "المدة", "specialServices": "الخدمات الخاصة", "specialService": "خدمة خاصة", "adContactDetails": "تفاصيل الاتصال بالإعلان", @@ -685,5 +685,25 @@ "noItemsToShow": "لا توجد عناصر لعرضها.", "serviceItem": "عنصر الخدمة", "createdBy": "تم الإنشاء بواسطة", - "createGroupServices": "إنشاء خدمات المجموعة" + "createGroupServices": "إنشاء خدمات المجموعة", + "logoutConfirmation": "هل أنت متأكد أنك تريد تسجيل الخروج؟", + "logoutConfirmationMessage": "سيتم تسجيل خروجك من التطبيق وسيتعين عليك إضافة اسم المستخدم وكلمة المرور مرة أخرى لتسجيل الدخول.", + "addToFavoritesOn": "تمت الإضافة إلى المفضلة في:", + "subscriptionDate": "تاريخ الاشتراك", + "expiresOn": "تنتهي صلاحيته", + "expired": "منتهي الصلاحية", + "upgradeBranches": "قم بترقية اشتراكك لإضافة المزيد من الفروع.", + "upgradeAds": "قم بترقية اشتراكك لإضافة المزيد من الإعلانات.", + "upgradeSubUsers": "قم بترقية اشتراكك لإضافة المزيد من المستخدمين الفرعيين.", + "explore": "استكشاف", + "manageRequests": "إدارة الطلبات", + "serviceNotAvailableAtHomeLocation": "الخدمة المختارة غير متوفرة في الموقع المحدد.", + "noAvailableItems": "لا توجد عناصر متاحة.", + "wantToRescheduleAppointment": "أرغب في إعادة جدولة الموعد.", + "noNeedForService": "لا أحتاج هذه الخدمة بعد الآن.", + "testTheService": "اختبر الخدمة.", + "companyLocation": "موقع الشركة", + "customerLocation": "موقع العميل", + "deliveryAvailable": "التوصيل متاح", + "viewed": "تم المشاهدة" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 63f2094..73fb5af 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -329,7 +329,7 @@ "vehiclePart": "Vehicle Part", "damagePartPictures": "Damage Part Pictures", "vehicleDamagePart": "Vehicle Damage Part", - "duration": "Duration:", + "duration": "Duration", "specialServices": "Special Services", "specialService": "Special Service", "adContactDetails": "Ad Contact Details", @@ -680,8 +680,28 @@ "users": "Users", "adsRemaining": "Ads Remaining", "attachPDF": "Attach PDF", - "noItemsToShow": "No Items to show.", "serviceItem": "Service Item", "createdBy": "Created By", - "createGroupServices": "Create Group Services" + "createGroupServices": "Create Group Services", + "logoutConfirmation": "Are you sure you want want to Logout?", + "logoutConfirmationMessage": "You will be logged out of the application and you have to add your username and password again to login", + "addToFavoritesOn": "Added to Favorites on:", + "subscriptionDate": "Subscription Date", + "expiresOn": "Expires On", + "expired": "Expired", + "upgradeBranches": "Upgrade your subscription to add more Branches.", + "upgradeAds": "Upgrade your subscription to add more Ads.", + "upgradeSubUsers": "Upgrade your subscription to add more Sub Users.", + "explore": "Explore", + "manageRequests": "Manage Requests", + "serviceNotAvailableAtHomeLocation": "The Selected Service is not available at Home Location.", + "noAvailableItems": "There are no available items.", + "wantToRescheduleAppointment": "I want to reschedule the appointment.", + "noNeedForService": "I do not need this service anymore.", + "testTheService": "Test the service.", + "companyLocation": "Company Location", + "customerLocation": "Customer Location", + "deliveryAvailable": "Delivery Available", + "viewed": "Viewed" + } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 59fd5a2..da876fe 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -1,5 +1,9 @@ +import 'dart:io'; + import 'package:flutter/cupertino.dart'; +import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/generated/codegen_loader.g.dart'; +import 'package:mc_common_app/utils/enums.dart'; class ApiConsts { // static String baseUrl = "http://10.200.204.20:2801/"; // Local server @@ -176,14 +180,30 @@ class ApiConsts { //Settings Options static String getAllFAQs = "${baseUrlServices}api/Common/FAQ_Get"; - static String getAppInvitationLink = "${baseUrlServices}api/Common/AppInvitation_Get"; + static String createAppInvitation = "${baseUrlServices}api/Common/AppInvitation_Create"; static String getContactInfo = "${baseUrlServices}api/Master/ContactInfo_Get"; static String getAppInfo = "${baseUrlServices}api/Master/AppInfo_Get"; + static String getTermsAndConditions = "${baseUrlServices}api/Master/TermAndCondition_Get"; static List closingUrls = ["PayFortResponse"]; } class GlobalConsts { + static String appNameCustomer = "MOWATER Customer"; + static String appNameProvider = "MOWATER Provider"; + + //Android + static String appPackageNameCustomerAndroid = "com.cloud.solutions.mowater.ksa.customer"; + static String appPackageNameProviderAndroid = "com.cloud.solutions.mowater.ksa.provider"; + static String appPlayStoreLinkCustomerAndroid = "https://play.google.com/store/apps/details?id=$appPackageNameCustomerAndroid"; + static String appPlayStoreLinkProviderAndroid = "https://play.google.com/store/apps/details?id=$appPackageNameProviderAndroid"; + + //IOS + static String appIDCustomerIOS = ""; + static String appIDProviderIOS = ""; + static String appAppStoreStoreLinkCustomerIos = "https://apps.apple.com/us/app/$appIDCustomerIOS"; + static String appAppStoreStoreLinkProviderIos = "https://apps.apple.com/us/app/$appIDProviderIOS"; + static String isRememberMe = "remember_me"; static String email = "email"; static String password = "password"; @@ -193,7 +213,7 @@ class GlobalConsts { 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 descriptionError = "Description should be more than 5 letters."; static String acceptingThisOffer = "I am accepting this offer."; static String vehicleVinError = "Vehicle VIN Cannot be Empty"; static String vehicleTitleError = "Vehicle Title Cannot be Empty"; @@ -208,6 +228,26 @@ class GlobalConsts { static String fillAllFields = "Please fill out all the fields."; static String requestTypeCannotBeEmpty = "Request type cannot be empty."; static String reserveAdPriceInfo = "Some dummy description to explain the following concept. This price will be for 24 hours and if a user cancels the reservations before 24 hours then the amount will be automatically refunded to the buyer."; + static String appInvitationMessageEn = "🚗 Hey , check out MOWATER to book car services, buy/sell vehicles, and more—join here! [Invite Link]"; + + static String getAppInvitationLink() { + String appInvitationMessageEn = ""; + + if (Platform.isAndroid) { + if (AppState().currentAppType == AppType.customer) { + appInvitationMessageEn = "🚗 Hey , check out $appNameCustomer to book car services, buy/sell vehicles, and more—join here! \n\n $appPlayStoreLinkCustomerAndroid"; + } else { + appInvitationMessageEn = "🚗 Hey , check out $appNameProvider to book car services, buy/sell vehicles, and more—join here! \n\n $appPlayStoreLinkProviderAndroid"; + } + } else if (Platform.isIOS) { + if (AppState().currentAppType == AppType.customer) { + appInvitationMessageEn = "🚗 Hey , check out $appNameCustomer to book car services, buy/sell vehicles, and more—join here! \n\n $appPlayStoreLinkCustomerAndroid"; + } else { + appInvitationMessageEn = "🚗 Hey , check out $appNameProvider to book car services, buy/sell vehicles, and more—join here! \n\n $appPlayStoreLinkProviderAndroid"; + } + } + return appInvitationMessageEn; + } } class MyAssets { diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 3521749..869d16b 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -25,6 +25,7 @@ import 'package:mc_common_app/views/setting_options/setting_options_contact_us.d import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart'; import 'package:mc_common_app/views/setting_options/setting_options_invite_friends.dart'; import 'package:mc_common_app/views/setting_options/setting_options_more.dart'; +import 'package:mc_common_app/views/setting_options/setting_options_terms_and_conditions.dart'; import 'package:mc_common_app/views/shipping_management/shipping_management_view.dart'; import 'package:mc_common_app/views/user/change_email_page.dart'; import 'package:mc_common_app/views/user/change_mobile_page.dart'; @@ -128,7 +129,6 @@ class AppRoutes { static const String paymentMethodsView = "/paymentMethodsView"; //Shipping - static const shippingManagementView = "/shippingManagementView"; //Customer APP: Provider & Services @@ -150,6 +150,7 @@ class AppRoutes { static const String settingOptionsFaqs = "/settingOptionsFaqs"; static const String settingOptionsContactUs = "/settingOptionsContactUs"; static const String settingOptionsAppInfo = "/settingOptionsAppInfo"; + static const String settingOptionsTermsAndConditions = "/settingOptionsTermsAndConditions"; static const String settingOptionsMore = "/settingOptionsLanguages"; static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends"; static const String settingOptionsHelp = "/settingOptionsHelp"; @@ -201,6 +202,7 @@ class AppRoutes { AppRoutes.settingOptionsFaqs: (context) => const SettingOptionsFAQs(), AppRoutes.settingOptionsContactUs: (context) => const SettingOptionsContactUs(), AppRoutes.settingOptionsAppInfo: (context) => const SettingOptionsAppInfo(), + AppRoutes.settingOptionsTermsAndConditions: (context) => const SettingOptionsTermsAndConditions(), AppRoutes.settingOptionsInviteFriends: (context) => const SettingOptionsInviteFriends(), AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypes), //Requests diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 6776ad1..7e6be75 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -345,7 +345,7 @@ class CodegenLoader extends AssetLoader{ "vehiclePart": "جزء المركبة", "damagePartPictures": "صور الأجزاء المتضررة", "vehicleDamagePart": "جزء المركبة المتضرر", - "duration": ":المدة", + "duration": "المدة", "specialServices": "الخدمات الخاصة", "specialService": "خدمة خاصة", "adContactDetails": "تفاصيل الاتصال بالإعلان", @@ -701,7 +701,27 @@ class CodegenLoader extends AssetLoader{ "noItemsToShow": "لا توجد عناصر لعرضها.", "serviceItem": "عنصر الخدمة", "createdBy": "تم الإنشاء بواسطة", - "createGroupServices": "إنشاء خدمات المجموعة" + "createGroupServices": "إنشاء خدمات المجموعة", + "logoutConfirmation": "هل أنت متأكد أنك تريد تسجيل الخروج؟", + "logoutConfirmationMessage": "سيتم تسجيل خروجك من التطبيق وسيتعين عليك إضافة اسم المستخدم وكلمة المرور مرة أخرى لتسجيل الدخول.", + "addToFavoritesOn": "تمت الإضافة إلى المفضلة في:", + "subscriptionDate": "تاريخ الاشتراك", + "expiresOn": "تنتهي صلاحيته", + "expired": "منتهي الصلاحية", + "upgradeBranches": "قم بترقية اشتراكك لإضافة المزيد من الفروع.", + "upgradeAds": "قم بترقية اشتراكك لإضافة المزيد من الإعلانات.", + "upgradeSubUsers": "قم بترقية اشتراكك لإضافة المزيد من المستخدمين الفرعيين.", + "explore": "استكشاف", + "manageRequests": "إدارة الطلبات", + "serviceNotAvailableAtHomeLocation": "الخدمة المختارة غير متوفرة في الموقع المحدد.", + "noAvailableItems": "لا توجد عناصر متاحة.", + "wantToRescheduleAppointment": "أرغب في إعادة جدولة الموعد.", + "noNeedForService": "لا أحتاج هذه الخدمة بعد الآن.", + "testTheService": "اختبر الخدمة.", + "companyLocation": "موقع الشركة", + "customerLocation": "موقع العميل", + "deliveryAvailable": "التوصيل متاح", + "viewed": "تم المشاهدة" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1034,7 +1054,7 @@ static const Map en_US = { "vehiclePart": "Vehicle Part", "damagePartPictures": "Damage Part Pictures", "vehicleDamagePart": "Vehicle Damage Part", - "duration": "Duration:", + "duration": "Duration", "specialServices": "Special Services", "specialService": "Special Service", "adContactDetails": "Ad Contact Details", @@ -1385,10 +1405,29 @@ static const Map en_US = { "users": "Users", "adsRemaining": "Ads Remaining", "attachPDF": "Attach PDF", - "noItemsToShow": "No Items to show.", "serviceItem": "Service Item", "createdBy": "Created By", - "createGroupServices": "Create Group Services" + "createGroupServices": "Create Group Services", + "logoutConfirmation": "Are you sure you want want to Logout?", + "logoutConfirmationMessage": "You will be logged out of the application and you have to add your username and password again to login", + "addToFavoritesOn": "Added to Favorites on:", + "subscriptionDate": "Subscription Date", + "expiresOn": "Expires On", + "expired": "Expired", + "upgradeBranches": "Upgrade your subscription to add more Branches.", + "upgradeAds": "Upgrade your subscription to add more Ads.", + "upgradeSubUsers": "Upgrade your subscription to add more Sub Users.", + "explore": "Explore", + "manageRequests": "Manage Requests", + "serviceNotAvailableAtHomeLocation": "The Selected Service is not available at Home Location.", + "noAvailableItems": "There are no available items.", + "wantToRescheduleAppointment": "I want to reschedule the appointment.", + "noNeedForService": "I do not need this service anymore.", + "testTheService": "Test the service.", + "companyLocation": "Company Location", + "customerLocation": "Customer Location", + "deliveryAvailable": "Delivery Available", + "viewed": "Viewed" }; static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index c45dd17..8d92a5b 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -665,5 +665,25 @@ abstract class LocaleKeys { static const serviceItem = 'serviceItem'; static const createdBy = 'createdBy'; static const createGroupServices = 'createGroupServices'; + static const logoutConfirmation = 'logoutConfirmation'; + static const logoutConfirmationMessage = 'logoutConfirmationMessage'; + static const addToFavoritesOn = 'addToFavoritesOn'; + static const subscriptionDate = 'subscriptionDate'; + static const expiresOn = 'expiresOn'; + static const expired = 'expired'; + static const upgradeBranches = 'upgradeBranches'; + static const upgradeAds = 'upgradeAds'; + static const upgradeSubUsers = 'upgradeSubUsers'; + static const explore = 'explore'; + static const manageRequests = 'manageRequests'; + static const serviceNotAvailableAtHomeLocation = 'serviceNotAvailableAtHomeLocation'; + static const noAvailableItems = 'noAvailableItems'; + static const wantToRescheduleAppointment = 'wantToRescheduleAppointment'; + static const noNeedForService = 'noNeedForService'; + static const testTheService = 'testTheService'; + static const companyLocation = 'companyLocation'; + static const customerLocation = 'customerLocation'; + static const deliveryAvailable = 'deliveryAvailable'; + static const viewed = 'viewed'; } diff --git a/lib/main.dart b/lib/main.dart index 235274f..4d3fd7e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'package:logger/logger.dart'; Logger logger = Logger(printer: PrettyPrinter(printEmojis: false, colors: true, printTime: false)); + bool disableThingsForQA = true; // Language Tile in Settings diff --git a/lib/models/provider_branches_models/branch_detail_model.dart b/lib/models/provider_branches_models/branch_detail_model.dart index 471efad..ed745d9 100644 --- a/lib/models/provider_branches_models/branch_detail_model.dart +++ b/lib/models/provider_branches_models/branch_detail_model.dart @@ -33,21 +33,7 @@ class BranchDetailModel { bool isExpanded = false; bool? isFavorite; -//I/flutter (23146): │ "id": 1021, -// I/flutter (23146): │ "serviceProviderID": 10, -// I/flutter (23146): │ "serviceProviderName": "string", -// I/flutter (23146): │ "cityID": 1, -// I/flutter (23146): │ "cityName": "Riyadh", -// I/flutter (23146): │ "branchName": "Al Arouba Cars", -// I/flutter (23146): │ "branchDescription": "des", -// I/flutter (23146): │ "address": "3289 Hilal Bin Omayah St - حي العليا, حي العليا, الرياض, RHOD3289, Saudi Arabia", -// I/flutter (23146): │ "latitude": "24.708521303151027", -// I/flutter (23146): │ "longitude": "46.666924171149724", -// I/flutter (23146): │ "distanceKM": 0.0, -// I/flutter (23146): │ "openTime": "09:00", -// I/flutter (23146): │ "closeTime": "18:00", -// I/flutter (23146): │ "branchStatus": 3, -// I/flutter (23146): │ "branchStatusText": "ApprovedOrActive", + BranchDetailModel({ this.id, this.serviceProviderId, diff --git a/lib/models/setting_utils_models/app_info_model.dart b/lib/models/setting_utils_models/app_info_model.dart index 1bb141f..d58a307 100644 --- a/lib/models/setting_utils_models/app_info_model.dart +++ b/lib/models/setting_utils_models/app_info_model.dart @@ -6,20 +6,24 @@ class AppInfoModel { String? content; bool? isActive; int? channel; - List? appInfoImages; + List? images; - AppInfoModel({this.id, this.header, this.content, this.isActive, this.channel, this.appInfoImages}); + AppInfoModel({this.id, this.header, this.content, this.isActive, this.channel, this.images}); - AppInfoModel.fromJson(Map json) { + AppInfoModel.fromJson(Map json, {bool isForTermsAndCondition = false}) { + String imagesKey = "appInfoImages"; + if (isForTermsAndCondition) { + imagesKey = "termAndConditionImages"; + } id = json['id']; header = json['header']; content = json['content']; isActive = json['isActive']; channel = json['channel']; - if (json['appInfoImages'] != null) { - appInfoImages = []; - json['appInfoImages'].forEach((v) { - appInfoImages!.add(ImageModel.fromJson(v)); + if (json[imagesKey] != null) { + images = []; + json[imagesKey].forEach((v) { + images!.add(ImageModel.fromJson(v)); }); } } diff --git a/lib/models/subscriptions_models/subscription_model.dart b/lib/models/subscriptions_models/subscription_model.dart index fc77955..89fba26 100644 --- a/lib/models/subscriptions_models/subscription_model.dart +++ b/lib/models/subscriptions_models/subscription_model.dart @@ -10,8 +10,6 @@ import '../../utils/enums.dart'; Subscription subscriptionFromJson(String str) => Subscription.fromJson(json.decode(str)); -String subscriptionToJson(Subscription data) => json.encode(data.toJson()); - class SubscriptionModel { SubscriptionModel({ this.messageStatus, @@ -31,13 +29,6 @@ class SubscriptionModel { data: json["data"] == null ? [] : List.from(json["data"]!.map((x) => Subscription.fromJson(x))), message: json["message"], ); - - Map toJson() => { - "messageStatus": messageStatus, - "totalItemsCount": totalItemsCount, - "data": data == null ? [] : List.from(data!.map((x) => x.toJson())), - "message": message, - }; } class Subscription { @@ -83,8 +74,8 @@ class Subscription { bool? isSubscribed; int? subscriptionAppliedId; int? serviceProviderId; - DateTime? dateStart; - DateTime? dateEnd; + String? dateStart; + String? dateEnd; bool? isExpired; bool? isActive; SubscriptionTypeEnum? subscriptionTypeEnum; @@ -114,8 +105,8 @@ class Subscription { isSubscribed: json["isSubscribed"], subscriptionAppliedId: json["subscriptionAppliedID"], serviceProviderId: json["serviceProviderID"], - dateStart: json["dateStart"] == null ? null : DateTime.parse(json["dateStart"]), - dateEnd: json["dateEnd"] == null ? null : DateTime.parse(json["dateEnd"]), + dateStart: json["dateStart"], + dateEnd: json["dateEnd"], isExpired: json["isExpired"], isActive: json["isActive"], isMyCurrentPackage: false, @@ -131,33 +122,4 @@ class Subscription { subUsersRemaining: json["subUsersRemaining"], adsRemaining: json["adsRemaining"], ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - "durationName": durationName, - "durationDays": durationDays, - "price": price, - "currency": currency, - "countryID": countryId, - "countryName": countryName, - "isSubscribed": isSubscribed, - "subscriptionAppliedID": subscriptionAppliedId, - "serviceProviderID": serviceProviderId, - "dateStart": dateStart?.toIso8601String(), - "dateEnd": dateEnd?.toIso8601String(), - "isExpired": isExpired, - "isActive": isActive, - "isRenewable": isRenewable, - "subscriptionBranches": subscriptionBranches, - "subscriptionSubUsers": subscriptionSubUsers, - "subscriptionAds": subscriptionAds, - "totalBranches": totalBranches, - "totalSubUsers": totalSubUsers, - "totalAds": totalAds, - "branchesRemaining": branchesRemaining, - "subUsersRemaining": subUsersRemaining, - "adsRemaining": adsRemaining, - }; } diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index 39ce5a6..5a3fa6f 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -16,7 +16,7 @@ import 'package:mc_common_app/utils/enums.dart'; abstract class AdsRepo { Future> getAdsDuration(); - Future> getSpecialServices({required int specialServiceId}); + Future> getSpecialServices({required int specialServiceType}); Future createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew}); @@ -63,9 +63,9 @@ class AdsRepoImp implements AdsRepo { } @override - Future> getSpecialServices({required int specialServiceId}) async { + Future> getSpecialServices({required int specialServiceType}) async { var params = { - "SpecialServiceType": specialServiceId.toString(), + "SpecialServiceType": specialServiceType.toString(), }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsSpecialServicesGet, queryParameters: params); List vehicleAdsDuration = List.generate(adsGenericModel.data.length, (index) => SpecialServiceModel.fromJson(adsGenericModel.data[index])); diff --git a/lib/repositories/appointment_repo.dart b/lib/repositories/appointment_repo.dart index bfb47e3..c2755d4 100644 --- a/lib/repositories/appointment_repo.dart +++ b/lib/repositories/appointment_repo.dart @@ -108,16 +108,21 @@ class AppointmentRepoImp implements AppointmentRepo { required List serviceItemIdsForWorkshop, }) async { String t = appState.getUser.data!.accessToken ?? ""; - var queryParameters = [ - // { - // "appointmentType": 2, - // "ServiceItemIDs": serviceItemIdsForHome, - // }, - { + var queryParameters = []; + + if (serviceItemIdsForWorkshop.isNotEmpty) { + queryParameters.add({ "appointmentType": 1, "ServiceItemIDs": serviceItemIdsForWorkshop, - } - ]; + }); + } + + if (serviceItemIdsForHome.isNotEmpty) { + queryParameters.add({ + "appointmentType": 2, + "ServiceItemIDs": serviceItemIdsForHome, + }); + } GenericRespModel adsGenericModel = await apiClient.postJsonForObject( (json) => GenericRespModel.fromJson(json), ApiConsts.getServiceItemAppointmentScheduleSlots, diff --git a/lib/repositories/setting_options_repo.dart b/lib/repositories/setting_options_repo.dart index f4122ca..c26aa28 100644 --- a/lib/repositories/setting_options_repo.dart +++ b/lib/repositories/setting_options_repo.dart @@ -8,6 +8,7 @@ import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart'; import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart'; import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/utils.dart'; abstract class SettingOptionsRepo { @@ -16,6 +17,10 @@ abstract class SettingOptionsRepo { Future> getAllContactInfos(); Future> getAppInfoList(); + + Future> getTermsAndConditions(); + + Future appInvitationCreate({required int channelId}); } class SettingOptionsRepoImp extends SettingOptionsRepo { @@ -24,12 +29,24 @@ class SettingOptionsRepoImp extends SettingOptionsRepo { @override Future> getAllFaqs() async { + int channel = 0; + + if (AppState().currentAppType == AppType.provider) { + channel = 2; + } else { + channel = 3; + } + final params = { + "Channel": channel.toString(), + }; + String token = appState.getUser.data!.accessToken ?? ""; GenericRespModel genericRespModel = await injector.get().getJsonForObject( (json) => GenericRespModel.fromJson(json), ApiConsts.getAllFAQs, token: token, + queryParameters: params, ); if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) { @@ -66,10 +83,22 @@ class SettingOptionsRepoImp extends SettingOptionsRepo { Future> getAppInfoList() async { String token = appState.getUser.data!.accessToken ?? ""; + int channel = 0; + + if (AppState().currentAppType == AppType.provider) { + channel = 2; + } else { + channel = 3; + } + final params = { + "Channel": channel.toString(), + }; + GenericRespModel genericRespModel = await injector.get().getJsonForObject( (json) => GenericRespModel.fromJson(json), ApiConsts.getAppInfo, token: token, + queryParameters: params, ); if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) { @@ -81,4 +110,63 @@ class SettingOptionsRepoImp extends SettingOptionsRepo { return list; } + + @override + Future> getTermsAndConditions() async { + String token = appState.getUser.data!.accessToken ?? ""; + + int channel = 0; + + if (AppState().currentAppType == AppType.provider) { + channel = 2; + } else { + channel = 3; + } + final params = { + "Channel": channel.toString(), + }; + + GenericRespModel genericRespModel = await injector.get().getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getTermsAndConditions, + token: token, + queryParameters: params, + ); + + if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) { + Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } + + List list = List.generate(genericRespModel.data.length, (index) => AppInfoModel.fromJson(genericRespModel.data[index], isForTermsAndCondition: true)); + + return list; + } + + @override + Future appInvitationCreate({required int channelId}) async { + String token = appState.getUser.data!.accessToken ?? ""; + + final params = { + "channelID": channelId.toString(), + "comments": "string", + }; + + if (AppState().currentAppType == AppType.provider) { + params.addAll({ + "customerID": (appState.getUser.data!.userInfo!.customerId ?? "0").toString(), + }); + } else { + params.addAll({ + "providerID": (appState.getUser.data!.userInfo!.providerId ?? "0").toString(), + }); + } + + GenericRespModel genericRespModel = await injector.get().postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.createAppInvitation, + token: token, + params, + ); + } } diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index af0f4c0..9e246ed 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -52,7 +52,7 @@ class MyColors { static const Color pendingColor = Colors.amber; static const Color submittedColor = Colors.blue; - static const Color inProgressColor = Colors.deepOrange; + static const Color inProgressColor = Colors.indigo; static const Color completedColor = Colors.green; static const Color cancelledColor = Colors.red; static const Color paidColor = Colors.teal; diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index 8717e22..be3aa1c 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -19,6 +19,20 @@ class AppEnums { static const int shippingStatusEnumId = -2; // to get the Shipping Filter Enums } +enum DashboardRouteEnum { + fromAdsPayment, + fromAdsSubmit, + + + + + + + none, +} + + + enum VehicleType { car, motorCycle, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 622450e..e709ddd 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -19,6 +19,7 @@ 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/widgets/loading_dialog.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -28,13 +29,7 @@ class Utils { static bool get isLoading => _isLoadingVisible; static void showToast(String message) { - Fluttertoast.showToast(msg: message, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - timeInSecForIosWeb: 2, - backgroundColor: Colors.black54, - textColor: Colors.white, - fontSize: 16.0); + Fluttertoast.showToast(msg: message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.black54, textColor: Colors.white, fontSize: 16.0); } static Future openNumberViaCaller({required String phoneNumber}) async { @@ -49,6 +44,18 @@ class Utils { } } + static Future openEmailViaEmailApp({required String emailAddress}) async { + Uri emailLaunchUrl = Uri.parse('mailto:$emailAddress'); + + if (await canLaunchUrl(emailLaunchUrl)) { + try { + await launchUrl(emailLaunchUrl); + } catch (e) { + await launchUrl(emailLaunchUrl); + } + } + } + static Future openNumberViaWhatsApp({required String phoneNumber}) async { final url = 'https://wa.me/$phoneNumber?text='; @@ -77,11 +84,7 @@ class Utils { return ""; } - return ("${timeOfDay.hour - .toString() - .length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute - .toString() - .length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); + return ("${timeOfDay.hour.toString().length == 1 ? "0" : ""}${timeOfDay.hour}:${timeOfDay.minute.toString().length == 1 ? "0" : ""}${timeOfDay.minute}").toString(); } static dynamic getNotNullValue(List list, int index) { @@ -275,23 +278,20 @@ class Utils { return MyColors.cancelledColor; case RequestOfferStatusEnum.cancel: - return MyColors.redColor; + return MyColors.cancelledColor; } } static String getNameByRequestOfferStatusEnum(RequestOfferStatusEnum requestOfferStatusEnum) { switch (requestOfferStatusEnum) { case RequestOfferStatusEnum.offer: - return "Pending"; - + return "Offer"; case RequestOfferStatusEnum.negotiate: return "Negotiate"; - case RequestOfferStatusEnum.accepted: return "Accepted"; case RequestOfferStatusEnum.rejected: return "Rejected"; - case RequestOfferStatusEnum.cancel: return "Cancelled"; } @@ -383,7 +383,6 @@ class Utils { return Container( decoration: BoxDecoration( color: chipColor, - borderRadius: const BorderRadius.all(Radius.circular(200)), ), padding: padding, child: text.toText(fontSize: 10, color: textColor, letterSpacing: -0.50)); @@ -522,7 +521,7 @@ class Utils { border: Border.all( width: w, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -545,7 +544,7 @@ class Utils { border: Border.all( width: 1, // color: color // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -563,8 +562,7 @@ class Utils { } static String getAdsPaymentBrowserForm({required int paymentId, required int adId}) { - return '
'; + return '
'; } // BOTTOM SHEETS @@ -589,6 +587,9 @@ class Utils { } static Widget buildStatusContainer(String text, {double marginAll = 8, double fontSize = 14}) { + if (text.isEmpty) { + return const SizedBox(); + } return Center( child: text.toText(color: MyColors.lightTextColor, fontSize: fontSize), ).toContainer( @@ -611,4 +612,9 @@ class Utils { } } } + + static Future shareAppInvitation() async { + ShareResult result = await Share.share(GlobalConsts.getAppInvitationLink(), subject: 'Join Mowater'); + return result; + } } diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 11bc0ef..152d9be 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -108,7 +108,7 @@ class AdVM extends BaseVM { vehicleAdsSpecialServices[index].isSelected = true; } specialServiceCards.add(specialServiceCard); - vehicleAdsSpecialServicesId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + selectedVehicleAdsSpecialServicesId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); adSpecialServiceDate = ""; notifyListeners(); } @@ -302,7 +302,7 @@ class AdVM extends BaseVM { } Future getVehicleAdsSpecialServices() async { - vehicleAdsSpecialServices = await adsRepo.getSpecialServices(specialServiceId: 1); + vehicleAdsSpecialServices = await adsRepo.getSpecialServices(specialServiceType: 1); notifyListeners(); } @@ -748,10 +748,10 @@ class AdVM extends BaseVM { notifyListeners(); } - SelectionModel vehicleAdsSpecialServicesId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + SelectionModel selectedVehicleAdsSpecialServicesId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateVehicleAdsSpecialServicesId(SelectionModel id) async { - vehicleAdsSpecialServicesId = id; + void updateSelectedVehicleAdsSpecialServicesId(SelectionModel id) async { + selectedVehicleAdsSpecialServicesId = id; // isFetchingLists = true; // adSSTimeSlots.clear(); // vehicleAdsPhotoServiceDate = SelectionModel(selectedId: -1, selectedOption: ""); @@ -1136,7 +1136,7 @@ class AdVM extends BaseVM { resetValues(); updateIsExploreAds(false); applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.pendingForReview); //pending for review - navigateReplaceWithName(context, AppRoutes.dashboard); + navigateReplaceWithName(context, AppRoutes.dashboard, arguments: DashboardRouteEnum.fromAdsSubmit); } catch (e) { Utils.hideLoading(context); Utils.showToast("Error: ${e.toString()}"); @@ -1285,6 +1285,7 @@ class AdVM extends BaseVM { vehicleAdsSpecialServices.clear(); currentProgressStep = AdCreationSteps.vehicleDetails; vehicleTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + vehicleModelId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); // vehicleAdDurationId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); vehicleBrandId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); vehicleModelYearId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index 0f4b9ae..96db74f 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -12,6 +12,7 @@ import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; import 'package:mc_common_app/models/appointments_models/appointment_slots.dart'; import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/general_models/enums_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; @@ -163,6 +164,54 @@ class AppointmentsVM extends BaseVM { navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment); } + OfferRequestCommentModel selectedCancelAppointmentReason = OfferRequestCommentModel( + index: 0, + isSelected: true, + title: LocaleKeys.changedMind.tr(), + ); + + List cancelAppointmentReasonList = [ + OfferRequestCommentModel( + index: 0, + isSelected: true, + title: LocaleKeys.wantToRescheduleAppointment.tr(), + ), + OfferRequestCommentModel( + index: 1, + isSelected: false, + title: LocaleKeys.noNeedForService.tr(), + ), + OfferRequestCommentModel( + index: 2, + isSelected: false, + title: LocaleKeys.testTheService.tr(), + ), + OfferRequestCommentModel( + index: 3, + isSelected: false, + title: LocaleKeys.otherVar.tr(), + ), + ]; + + void updateSelectedCancelAppointmentReason(int index) { + for (var value in cancelAppointmentReasonList) { + value.isSelected = false; + } + selectedCancelAppointmentReason = cancelAppointmentReasonList[index]; + cancelAppointmentReasonList[index].isSelected = true; + notifyListeners(); + } + + String cancelAppointmentDescription = ""; + String cancelAppointmentDescriptionError = ""; + + void updateCancelAppointmentDescription(String value) { + cancelAppointmentDescription = value; + if (value.isNotEmpty) { + cancelAppointmentDescriptionError = ""; + } + } + Future onCancelAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async { Utils.showLoading(context); try { @@ -498,6 +547,8 @@ class AppointmentsVM extends BaseVM { serviceItemsFromApi[index].isUpdateOrSelected = selected; serviceItemsFromApi[index].isHomeSelected = isHomeTapped; + log(" serviceItemsFromApi[index].isHomeSelected: ${serviceItemsFromApi[index].isHomeSelected}"); + if (selected) { selectedSubServicesCounter = selectedSubServicesCounter + 1; selectSubServicesError = ""; @@ -611,6 +662,7 @@ class AppointmentsVM extends BaseVM { isScrollControlled: true, enableDrag: true, builder: (BuildContext context) { + // TODO: It should be calculated from API double totalKms = 15.3; return InfoBottomSheet( title: LocaleKeys.chargesBreakdown.tr().toText(fontSize: 24, isBold: true), @@ -625,7 +677,7 @@ class AppointmentsVM extends BaseVM { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - "${selectedService.serviceItems![index].price} SAR".toText(fontSize: 12, isBold: true), + "${selectedService.serviceItems![index].price} ${LocaleKeys.sar.tr()}".toText(fontSize: 12, isBold: true), ], ), ), @@ -633,7 +685,7 @@ class AppointmentsVM extends BaseVM { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - "${selectedService.currentTotalServicePrice} SAR".toText(fontSize: 16, isBold: true), + "${selectedService.currentTotalServicePrice} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true), ], ), if (selectedService.isHomeSelected) ...[ @@ -650,7 +702,7 @@ class AppointmentsVM extends BaseVM { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - "${selectedService.rangePricePerKm ?? 0 * totalKms} SAR".toText(fontSize: 16, isBold: true), + "${(double.parse(selectedService.rangePricePerKm!)) * totalKms} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true), ], ), ], @@ -662,7 +714,7 @@ class AppointmentsVM extends BaseVM { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - (selectedService.isHomeSelected ? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}" : "${selectedService.currentTotalServicePrice}") + (selectedService.isHomeSelected ? "${(selectedService.currentTotalServicePrice) + ((double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms))}" : "${selectedService.currentTotalServicePrice}") .toText(fontSize: 29, isBold: true), 2.width, LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), @@ -718,6 +770,8 @@ class AppointmentsVM extends BaseVM { List serviceItemIdsForHome = []; List serviceItemIdsForWorkshop = []; for (var serviceItem in allSelectedItemsInAppointments) { + log("homeselcted for : ${serviceItem.isHomeSelected}"); + if (serviceItem.isHomeSelected!) { serviceItemIdsForHome.add(serviceItem.id!.toString()); } else { @@ -1098,22 +1152,14 @@ class AppointmentsVM extends BaseVM { setState(ViewState.idle); } - Future fetchAllCategories(String countryCode) async { + Future fetchAllCategories() async { if (categoryDropList.isNotEmpty) return; categoryDropList.clear(); setOnlyState(ViewState.busy); Category category = await branchRepo.fetchBranchCategory(); category.data?.forEach((element) { categoryDropList.add( - DropValue( - element.id ?? 0, - ((element.categoryName!.isEmpty - ? "N/A" - : countryCode == "SA" - ? element.categoryNameN - : element.categoryName) ?? - "N/A"), - ""), + DropValue(element.id ?? 0, ((element.categoryName!.isEmpty ? "N/A" : element.categoryName) ?? "N/A"), ""), ); }); setState(ViewState.idle); @@ -1139,7 +1185,7 @@ class AppointmentsVM extends BaseVM { Future populateDataForBranchesFilter() async { await fetchAllProviders(); // saudi arabia - await fetchAllCategories("SA"); // saudi arabia + await fetchAllCategories(); // saudi arabia await fetchAllServices(); // saudi arabia updateBranchFilterCurrentDistance(25.0); } @@ -1231,9 +1277,9 @@ class AppointmentsVM extends BaseVM { } Future populateDataForAppointmentsFilter() async { - await fetchAllProviders(); // saudi arabia - await fetchAllCategories("SA"); // saudi arabia - await fetchAllServices(); // saudi arabia + await fetchAllProviders(); + await fetchAllCategories(); + await fetchAllServices(); } int appointmentFiltersCounter = 0; diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 2825c3a..713126a 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -103,12 +103,6 @@ class ChatVM extends ChangeNotifier { ), ]; - OfferRequestCommentModel selectedOfferRequestCommentModel = OfferRequestCommentModel( - index: 0, - isSelected: true, - title: LocaleKeys.changedMind.tr(), - ); - void updateSelectionInOfferRejectModelList(int index) { for (var value in offerRejectModelList) { value.isSelected = false; @@ -118,6 +112,12 @@ class ChatVM extends ChangeNotifier { notifyListeners(); } + OfferRequestCommentModel selectedOfferRequestCommentModel = OfferRequestCommentModel( + index: 0, + isSelected: true, + title: LocaleKeys.changedMind.tr(), + ); + String rejectOfferDescription = ""; String rejectOfferDescriptionError = ""; @@ -154,8 +154,6 @@ class ChatVM extends ChangeNotifier { if (currentMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer) { for (var chatMessage in serviceProviderOffersList[providerIndex].chatMessages!) { if (chatMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer && chatMessage.reqOffer!.id == currentMessage.reqOffer!.id) { - logger.i("chatMessage reqOfferID: ${chatMessage.reqOffer!.id.toString()}"); - logger.i("currentMessage reqOfferID: ${currentMessage.reqOffer!.id.toString()}"); chatMessage.reqOffer!.requestOfferStatusEnum = currentMessage.reqOffer!.requestOfferStatusEnum; } } @@ -253,6 +251,7 @@ class ChatVM extends ChangeNotifier { required String serviceItemName, required int manufacturedById, required String manufacturedOn, + required List offerImages, required BuildContext context, }) async { if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) { @@ -272,6 +271,7 @@ class ChatVM extends ChangeNotifier { "RequestID": requestId, "Price": double.parse(offerPrice), "ServiceItem": serviceItemName, + "ReqOfferImages": offerImages, "OfferedItemCreatedBy": manufacturedById, // TODO: The ID should be used from the manufacturer database // "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime "ServiceProviderID": providerId, @@ -464,6 +464,7 @@ class ChatVM extends ChangeNotifier { } if (unreadMessageIds.isNotEmpty) { + log("unreadMessageIds: ${unreadMessageIds.toString()}"); await markMessagesAsRead(context, unreadMessageIds, ChatTypeEnum.requestOffer); } Utils.hideLoading(context); diff --git a/lib/view_models/dashboard_view_model_customer.dart b/lib/view_models/dashboard_view_model_customer.dart index d366d6d..b0e52f9 100644 --- a/lib/view_models/dashboard_view_model_customer.dart +++ b/lib/view_models/dashboard_view_model_customer.dart @@ -1,3 +1,4 @@ +import 'dart:developer'; import 'dart:io'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; @@ -5,6 +6,7 @@ import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/models/user_models/image_response.dart'; import 'package:mc_common_app/repositories/user_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; @@ -56,7 +58,8 @@ class DashboardVmCustomer extends BaseVM { } } - Future onRefresh(BuildContext context) async { + Future onRefresh(BuildContext context, {DashboardRouteEnum dashboardRouteEnum = DashboardRouteEnum.none}) async { + log("onRefresh dashboardRouteEnum: $dashboardRouteEnum"); AdVM adVM = Provider.of(context, listen: false); AppointmentsVM appointmentsVM = Provider.of(context, listen: false); RequestsVM requestsVM = Provider.of(context, listen: false); @@ -67,9 +70,13 @@ class DashboardVmCustomer extends BaseVM { requestsVM.populateDataForRequestsFilter(); await appointmentsVM.getMyAppointmentsForCustomer(); await appointmentsVM.applyFilterOnBranches(index: 0); // to get all branches! - await appointmentsVM.getMyRecentBranches(); // to get my recent branches! - await adVM.getMyAds(); - await adVM.getExploreAds(); + await appointmentsVM.getMyRecentBranches(); // to get my recent branches + + if (dashboardRouteEnum != DashboardRouteEnum.fromAdsPayment && dashboardRouteEnum != DashboardRouteEnum.fromAdsSubmit) { + await adVM.getMyAds(); + await adVM.getExploreAds(); + } + await requestsVM.getRequests(); await adVM.getVehicleTypes(); await adVM.getVehicleAdsDuration(); diff --git a/lib/view_models/dashboard_view_model_provider.dart b/lib/view_models/dashboard_view_model_provider.dart index a051790..304c794 100644 --- a/lib/view_models/dashboard_view_model_provider.dart +++ b/lib/view_models/dashboard_view_model_provider.dart @@ -15,7 +15,6 @@ import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/shipping_management_view_model.dart'; import 'package:mc_common_app/widgets/common_widgets/add_phone_num_wiget.dart'; import 'package:provider/provider.dart'; -import 'package:mc_common_app/utils/location/Location.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; @@ -71,29 +70,35 @@ class DashboardVMProvider extends BaseVM { return await userRepo.updateUserImage(image); } - Future onRefresh(BuildContext context) async { + Future onRefresh(BuildContext context, { DashboardRouteEnum dashboardRouteEnum = DashboardRouteEnum.none }) async { final requestsVM = context.read(); final chatVM = context.read(); final appointmentVM = context.read(); final shippingManagementVM = context.read(); final serviceVM = context.read(); - final adVm = context.read(); + final adVM = context.read(); final subscriptionsVM = context.read(); requestsVM.populateDataForRequestsFilter(); appointmentVM.populateAppointmentsFilterList(); shippingManagementVM.populateShippingRequestFilterList(); await serviceVM.getBranchAndServices(); - await appointmentVM.getMyAppointmentsForProvider({"ServiceProviderID": injector.get().getUser.data?.userInfo?.providerId.toString() ?? "0"}); - adVm.populateAdsFilterList(); + await appointmentVM.getMyAppointmentsForProvider({"ServiceProviderID": injector + .get() + .getUser + .data + ?.userInfo + ?.providerId + .toString() ?? "0"}); + adVM.populateAdsFilterList(); await subscriptionsVM.getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true); await chatVM.buildHubConnection(context); - await adVm.getMyAds(); - await adVm.getExploreAds(); - await adVm.getVehicleTypes(); - await adVm.getVehicleAdsDuration(); + if (dashboardRouteEnum != DashboardRouteEnum.fromAdsPayment && dashboardRouteEnum != DashboardRouteEnum.fromAdsSubmit) { + await adVM.getMyAds(); + await adVM.getExploreAds(); + } + await adVM.getVehicleTypes(); + await adVM.getVehicleAdsDuration(); await requestsVM.getRequests(); - // await subscriptionsVM.getMySubscriptionsBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? ""); - // await subscriptionsVM.getMySubscriptionsBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? ""); } void checkUserSubscription(SubscriptionActionTypeEnum actionType, BuildContext context, {required Function() callback}) async { @@ -112,29 +117,25 @@ class DashboardVMProvider extends BaseVM { void performCheckOnBranches(BuildContext context) async { if (AppState().getproviderSubscription.isNotEmpty && AppState().getproviderSubscription.first.branchesRemaining! > 0) { - logger.d(AppState().getproviderSubscription.first.toJson()); await navigateWithName(context, AppRoutes.defineBranch); } else { - Utils.showToast("Upgrade your subscription to add more Branches."); + Utils.showToast(LocaleKeys.upgradeBranches.tr()); } } void performCheckOnAds(BuildContext context) async { - //adsRemaining if (AppState().getproviderSubscription.isNotEmpty && AppState().getproviderSubscription.first.adsRemaining! > 0) { - logger.d(AppState().getproviderSubscription.first.toJson()); await navigateWithName(context, AppRoutes.defineBranch); } else { - Utils.showToast("Upgrade your subscription to add more Ads."); + Utils.showToast(LocaleKeys.upgradeAds.tr()); } } void performCheckOnUsers(BuildContext context, Function() callBack) async { if (AppState().getproviderSubscription.isNotEmpty && AppState().getproviderSubscription.first.subUsersRemaining! > 0) { - logger.d(AppState().getproviderSubscription.first.toJson()); showMyBottomSheet(context, isDismissible: false, child: const AddPhoneNumWidget(), callBackFunc: callBack); } else { - Utils.showToast("Upgrade your subscription to add more Sub Users."); + Utils.showToast(LocaleKeys.upgradeSubUsers.tr()); } } } diff --git a/lib/view_models/payment_view_model.dart b/lib/view_models/payment_view_model.dart index a0624cc..1dbaddc 100644 --- a/lib/view_models/payment_view_model.dart +++ b/lib/view_models/payment_view_model.dart @@ -10,10 +10,9 @@ import 'package:mc_common_app/services/payments_service.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; -import 'package:mc_common_app/view_models/base_view_model.dart'; +import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_provider.dart'; -import 'package:mc_common_app/view_models/subscriptions_view_model.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -192,12 +191,15 @@ class PaymentVM extends ChangeNotifier { } void onAdsPaymentSuccess(BuildContext context) { + context.read().updateIsExploreAds(false); if (AppState().currentAppType == AppType.provider) { context.read().onNavbarTapped(3); + context.read().applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.active); } else { context.read().onNavbarTapped(3); + context.read().applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.pendingForPost); } - navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); + navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard, arguments: DashboardRouteEnum.fromAdsPayment); } void onSubscriptionPaymentSuccess(BuildContext context) { diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index ecb3210..ff8ecb1 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -796,6 +796,13 @@ class RequestsVM extends BaseVM { serviceItem = value; } + bool isDeliveryAvailableStatus = false; + + void updateIsDeliveryAvailableStatus(bool value) { + isDeliveryAvailableStatus = value; + notifyListeners(); + } + String serviceItemCreatedOn = ""; void updateServiceItemCreatedOn(String value) { @@ -807,7 +814,7 @@ class RequestsVM extends BaseVM { void updateOfferDescription(String value) { offerDescription = value; - if (value.isNotEmpty) { + if (value.isNotEmpty || value.length <= 5) { offerDescriptionError = ""; } } @@ -885,6 +892,7 @@ class RequestsVM extends BaseVM { serviceItem = ""; serviceItemCreatedOn = ""; itemManufacturer = ""; + pickedVehicleImages.clear(); notifyListeners(); } @@ -903,6 +911,14 @@ class RequestsVM extends BaseVM { }) async { if (isSendOfferValidated()) { final chatVM = context.read(); + + Utils.showLoading(context); + + List vehicleImages = await getVehiclePostingImageList(); + List offerImages = []; + vehicleImages.forEach((element) { + offerImages.add(element.toJson()); + }); bool status = await chatVM.onOfferSendForRequest( receiverId: receiverId, chatMessageType: ChatMessageTypeEnum.offer, @@ -912,52 +928,58 @@ class RequestsVM extends BaseVM { manufacturedById: manufacturedById, manufacturedOn: manufacturedOn, serviceItemName: serviceItemName, + offerImages: offerImages, context: context, ); + Utils.hideLoading(context); + log("status from onOfferSendForRequest: $status"); - if (status) { - final senderName = AppState().getUser.data!.userInfo!.firstName; - final senderId = AppState().getUser.data!.userInfo!.userId; - resetSendOfferBottomSheet(); - Navigator.pop(context); - ChatMessageModel chatMessageModel = ChatMessageModel( - isMyMessage: true, - chatText: message, - messageType: ChatMessageTypeEnum.offer.getIdFromChatMessageTypeEnum(), - senderName: senderName, - senderUserID: senderId, - receiverUserID: receiverId, - chatMessageTypeEnum: ChatMessageTypeEnum.offer, - requestID: requestModel.id, + if (!status) { + Utils.showToast(LocaleKeys.somethingWrong.tr()); + return; + } + + final senderName = AppState().getUser.data!.userInfo!.firstName; + final senderId = AppState().getUser.data!.userInfo!.userId; + resetSendOfferBottomSheet(); + Navigator.pop(context); + ChatMessageModel chatMessageModel = ChatMessageModel( + isMyMessage: true, + chatText: message, + messageType: ChatMessageTypeEnum.offer.getIdFromChatMessageTypeEnum(), + senderName: senderName, + senderUserID: senderId, + receiverUserID: receiverId, + chatMessageTypeEnum: ChatMessageTypeEnum.offer, + requestID: requestModel.id, + offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(), + reqOffer: ReqOffer( offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(), - reqOffer: ReqOffer( - offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(), - requestID: requestModel.id, - price: double.parse(offerPrice), - manufacturedById: manufacturedById, - manufacturedOn: manufacturedOn, - serviceItemName: serviceItemName, - requestOfferStatusEnum: RequestOfferStatusEnum.offer, - comment: message, - offerStatusText: "", - ), + requestID: requestModel.id, + price: double.parse(offerPrice), + manufacturedById: manufacturedById, + manufacturedOn: manufacturedOn, + serviceItemName: serviceItemName, + requestOfferStatusEnum: RequestOfferStatusEnum.offer, + comment: message, + offerStatusText: "", + ), + ); + context.read().onNewMessageReceivedForRequestOffer(messages: [chatMessageModel], requestsVM: this, isMyOwnOffer: true); + + if (!isFromChatScreen) { + ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest( + chatTypeEnum: ChatTypeEnum.requestOffer, + requestId: requestModel.id, + receiverId: requestModel.customerID, + senderId: senderId ?? "", + requestIndex: requestIndex, + providerIndex: -1, + requestModel: requestModel, ); - context.read().onNewMessageReceivedForRequestOffer(messages: [chatMessageModel], requestsVM: this, isMyOwnOffer: true); - - if (!isFromChatScreen) { - ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest( - chatTypeEnum: ChatTypeEnum.requestOffer, - requestId: requestModel.id, - receiverId: requestModel.customerID, - senderId: senderId ?? "", - requestIndex: requestIndex, - providerIndex: -1, - requestModel: requestModel, - ); - - ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest); - navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments); - } + + ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest); + navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments); } } } diff --git a/lib/view_models/setting_options_view_model.dart b/lib/view_models/setting_options_view_model.dart index d48d294..07b9788 100644 --- a/lib/view_models/setting_options_view_model.dart +++ b/lib/view_models/setting_options_view_model.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart'; import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart'; @@ -6,6 +7,7 @@ import 'package:mc_common_app/repositories/setting_options_repo.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; +import 'package:share_plus/share_plus.dart'; class SettingOptionsVM extends BaseVM { final SettingOptionsRepo settingOptionsRepo; @@ -56,4 +58,36 @@ class SettingOptionsVM extends BaseVM { setState(ViewState.idle); } } + + List termsAndConditionsList = []; + + Future getTermsAndConditions() async { + setState(ViewState.busy); + try { + termsAndConditionsList = await settingOptionsRepo.getTermsAndConditions(); + setState(ViewState.idle); + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + setState(ViewState.idle); + } + } + + Future appInvitationCreate({required BuildContext context, required int channelId}) async { + ShareResult result = await Utils.shareAppInvitation(); + + if (result.status != ShareResultStatus.success) { + return; + } + Utils.showLoading(context); + try { + await settingOptionsRepo.appInvitationCreate(channelId: channelId); + Utils.hideLoading(context); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + } + } } 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 5487cdc..ede81cf 100644 --- a/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart +++ b/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart @@ -282,7 +282,7 @@ class AdDuration extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - (LocaleKeys.description.tr() + ": ").toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + ("${LocaleKeys.description.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), (specialServicesCard.description ?? "").toText(fontSize: 12, isBold: true).expand(), ], ), 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 4c072e6..51f1b7e 100644 --- a/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart +++ b/lib/views/advertisement/ad_creation_steps/vehicle_details_container.dart @@ -283,7 +283,7 @@ class VehicleDetails extends StatelessWidget { ).paddingOnly(right: 10) ], if (adVM.pickedPostingImages.isNotEmpty) ...[ - 16.height, + 8.height, PickedFilesContainer( pickedFiles: adVM.pickedPostingImages, onCrossPressedPrimary: adVM.removeImageFromList, diff --git a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart index 9cc20a2..a3ae474 100644 --- a/lib/views/advertisement/ads_detail_view/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view/ads_detail_view.dart @@ -15,6 +15,7 @@ import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/view_models/payment_view_model.dart'; import 'package:mc_common_app/views/advertisement/ads_detail_view/components.dart'; +import 'package:mc_common_app/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart'; import 'package:mc_common_app/views/advertisement/bottom_sheets/ads_damage_part_pictures_sheet.dart'; import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart'; import 'package:mc_common_app/widgets/bottom_sheet.dart'; @@ -252,34 +253,79 @@ class _AdsDetailViewState extends State { }).toWhiteContainer(width: double.infinity, allPading: 12); } + Future onExtendAdPressed() async { + final AdVM adVM = context.read(); + return actionConfirmationBottomSheet( + context: context, + title: LocaleKeys.updateAdDetails.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: LocaleKeys.durationExtendingAd.tr(), + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.yes.tr(), + fontSize: 15, + onPressed: () async { + Utils.showLoading(context); + await adVM.getVehicleTypes(); + await adVM.getVehicleBrandsByVehicleTypeId(vehicleIdForEditAd: widget.adDetails.vehicle!.vehicleType ?? -1); + Utils.hideLoading(context); + adVM.onEditUpdateAdPressed(context: context, previousDetails: widget.adDetails, isFromExtendAd: true); + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: LocaleKeys.no.tr(), + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + adVM.updateSelectionVehicleTypeId( + SelectionModel(selectedId: widget.adDetails.vehicle!.vehicleType!, selectedOption: (widget.adDetails.vehicle!.vehicleType!).toVehicleTypeString(), errorValue: ""), + ); + + showMyBottomSheet(context, child: AdDurationSelectionSheet(isFromExtendAd: true, isUpdateAdSelected: false, adsID: widget.adDetails.id ?? -1)); + }, + ), + ), + ); + } + Widget buildAdStartEndDates() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - ("${LocaleKeys.startDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), - widget.adDetails.startdate != null - ? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.startdate!)).toText( - fontSize: 10, - ) - : const SizedBox(), + Row( + children: [ + ("${LocaleKeys.startDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), + widget.adDetails.startdate != null + ? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.startdate!)).toText( + fontSize: 10, + ) + : const SizedBox(), + ], + ), + Row( + children: [ + ("${LocaleKeys.endDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), + widget.adDetails.enddate != null ? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.enddate!)).toText(fontSize: 10) : const SizedBox(), + ], + ), ], ), + 3.height, Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - ("${LocaleKeys.endDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), - widget.adDetails.enddate != null - ? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.enddate!)).toText( - fontSize: 10, - ) - : const SizedBox(), + (LocaleKeys.extendAd.tr()).toText(fontSize: 12, color: MyColors.darkPrimaryColor, isUnderLine: true).onPress(() async => await onExtendAdPressed()), ], - ), + ) ], - ).paddingOnly(top: 5, bottom: 5).onPress(() { - showMyBottomSheet(context, child: AdDamagePartPicturesSheet(adDamageReportList: widget.adDetails.vehicle!.damagereport ?? [])); - }).toWhiteContainer(width: double.infinity, allPading: 12); + ).paddingOnly(top: 5, bottom: 2).toWhiteContainer(width: double.infinity, allPading: 12); } @override diff --git a/lib/views/advertisement/bottom_sheets/ad_special_service_sheet.dart b/lib/views/advertisement/bottom_sheets/ad_special_service_sheet.dart index 2db3127..ecaec46 100644 --- a/lib/views/advertisement/bottom_sheets/ad_special_service_sheet.dart +++ b/lib/views/advertisement/bottom_sheets/ad_special_service_sheet.dart @@ -5,6 +5,7 @@ import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/time_slots.dart'; @@ -149,7 +150,7 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { const BottomSheetAdSpecialServiceContent({Key? key}) : super(key: key); bool isButtonTappable(AdVM adVM) { - bool status = (adVM.vehicleAdsSpecialServicesId.selectedId != -1) && (adVM.vehicleAdsSpecialServicesId.selectedOption != ""); + bool status = (adVM.selectedVehicleAdsSpecialServicesId.selectedId != -1) && (adVM.selectedVehicleAdsSpecialServicesId.selectedOption != ""); if (status) { return true; } @@ -157,21 +158,16 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { } Widget descriptionCard({required String description}) { - return Container( - height: 100, - decoration: BoxDecoration(color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: description.toText( - fontSize: 15, - color: MyColors.lightTextColor, - ), - ), - ], - ), - ); + if (description.isEmpty) { + return const SizedBox(); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ("${LocaleKeys.description.tr()}:").toText(fontSize: 14), + description.toText(color: MyColors.lightTextColor), + ], + ).paddingOnly(left: 2); } @override @@ -179,7 +175,7 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { return Consumer( builder: (BuildContext context, AdVM adVM, Widget? child) { return SizedBox( - height: MediaQuery.of(context).size.height * 0.85, + height: MediaQuery.of(context).size.height * 0.7, child: Column( children: [ Container( @@ -204,19 +200,19 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { } } return DropdownField( - (DropValue value) => adVM.updateVehicleAdsSpecialServicesId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), + (DropValue value) => adVM.updateSelectedVehicleAdsSpecialServicesId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)), list: vehicleAdsSpecialServices, hint: LocaleKeys.selectService.tr(), - dropdownValue: adVM.vehicleAdsSpecialServicesId.selectedId != -1 ? DropValue(adVM.vehicleAdsSpecialServicesId.selectedId, adVM.vehicleAdsSpecialServicesId.selectedOption, "") : null, + dropdownValue: adVM.selectedVehicleAdsSpecialServicesId.selectedId != -1 ? DropValue(adVM.selectedVehicleAdsSpecialServicesId.selectedId, adVM.selectedVehicleAdsSpecialServicesId.selectedOption, "") : null, ); }, ), - if (adVM.vehicleAdsSpecialServicesId.selectedId != -1) ...[ + if (adVM.selectedVehicleAdsSpecialServicesId.selectedId != -1) ...[ if (adVM.isFetchingLists) ...[ Center(child: const CircularProgressIndicator().paddingAll(20)), ] else ...[ 8.height, - if ((adVM.vehicleAdsSpecialServicesId.selectedId == 1 && adVM.ssPhotoScheduleModel != null)) ...[ + if ((adVM.selectedVehicleAdsSpecialServicesId.selectedId == 1 && adVM.ssPhotoScheduleModel != null)) ...[ // Builder( // builder: (context) { // List vehicleAdsSpecialServiceDates = adVM.populateScheduleDatesForAdPhotoService(); @@ -239,7 +235,7 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { // }, // ), // 22.height, - ] else if ((adVM.vehicleAdsSpecialServicesId.selectedId == 3 && adVM.ssCarCheckScheduleModel != null)) ...[ + ] else if ((adVM.selectedVehicleAdsSpecialServicesId.selectedId == 3 && adVM.ssCarCheckScheduleModel != null)) ...[ // Builder( // builder: (context) { // List vehicleAdsSpecialServiceDates = adVM.populateScheduleDatesForAdCarCheckService(); @@ -263,9 +259,9 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { // ), // 22.height, ], - if (adVM.vehicleAdsSpecialServicesId.selectedId != 3 && adVM.vehicleAdsSpecialServicesId.selectedId != 1) ...[ + if (adVM.selectedVehicleAdsSpecialServicesId.selectedId != 3 && adVM.selectedVehicleAdsSpecialServicesId.selectedId != 1) ...[ descriptionCard( - description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.vehicleAdsSpecialServicesId.selectedId).description ?? "Some Dummy Service Description!!", + description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.selectedVehicleAdsSpecialServicesId.selectedId).description ?? "", ), ], if (adVM.adSSTimeSlots.isNotEmpty) ...[ @@ -281,7 +277,7 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - adVM.vehicleAdsSpecialServicesId.itemPrice.toText(fontSize: 20, isBold: true), + adVM.selectedVehicleAdsSpecialServicesId.itemPrice.toText(fontSize: 20, isBold: true), SizedBox(width: 1.w), LocaleKeys.sar.tr().toText(fontSize: 12, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 2), ], @@ -294,7 +290,7 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { SizedBox( width: double.infinity, child: ShowFillButton( - backgroundColor: !isButtonTappable(adVM) ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.primaryColor, + backgroundColor: !isButtonTappable(adVM) ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.darkPrimaryColor, title: LocaleKeys.addService.tr(), onPressed: () { if (!isButtonTappable(adVM)) { @@ -304,8 +300,8 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget { adVM.addNewSpecialServiceCard( specialServiceCard: SpecialServiceCard( - serviceSelectedId: adVM.vehicleAdsSpecialServicesId, - description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.vehicleAdsSpecialServicesId.selectedId).description ?? "", + serviceSelectedId: adVM.selectedVehicleAdsSpecialServicesId, + description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.selectedVehicleAdsSpecialServicesId.selectedId).description ?? "", duration: "", serviceDate: "", serviceDateError: "", diff --git a/lib/views/appointments/appointment_detail_view.dart b/lib/views/appointments/appointment_detail_view.dart index 52a16bf..78d2e43 100644 --- a/lib/views/appointments/appointment_detail_view.dart +++ b/lib/views/appointments/appointment_detail_view.dart @@ -4,16 +4,21 @@ import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/services_models/service_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart'; import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/common_widgets/card_button_with_icon.dart'; +import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:sizer/sizer.dart'; @@ -81,7 +86,7 @@ class AppointmentDetailView extends StatelessWidget { alignment: Alignment.bottomCenter, child: Row( children: [ - getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: LocaleKeys.cancel.tr()), + getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => buildCancelAppointmentReasonsBottomSheet(context: context), text: LocaleKeys.cancel.tr()), 12.width, getBaseActionButtonWidget( color: MyColors.greenColor, @@ -97,7 +102,7 @@ class AppointmentDetailView extends StatelessWidget { alignment: Alignment.bottomCenter, child: Row( children: [ - getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"), + getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => buildCancelAppointmentReasonsBottomSheet(context: context), text: LocaleKeys.cancel.tr()), ], ), ); @@ -128,7 +133,7 @@ class AppointmentDetailView extends StatelessWidget { alignment: Alignment.bottomCenter, child: Row( children: [ - getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: LocaleKeys.cancel.tr()), + getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => buildCancelAppointmentReasonsBottomSheet(context: context), text: LocaleKeys.cancel.tr()), 12.width, getBaseActionButtonWidget( color: MyColors.greenColor, @@ -177,6 +182,77 @@ class AppointmentDetailView extends StatelessWidget { ); } + Future buildCancelAppointmentReasonsBottomSheet({required BuildContext context}) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return Consumer(builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) { + return InfoBottomSheet( + title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + description: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 12.height, + ListView.separated( + shrinkWrap: true, + itemCount: appointmentsVM.cancelAppointmentReasonList.length, + separatorBuilder: (BuildContext context, int index) { + return const Divider(thickness: 0.5); + }, + itemBuilder: (BuildContext context, int index) { + OfferRequestCommentModel offerRequestCommentModel = appointmentsVM.cancelAppointmentReasonList[index]; + return CircleCheckBoxWithTitle( + isChecked: offerRequestCommentModel.isSelected ?? false, + title: '${offerRequestCommentModel.title}', + onSelected: () { + appointmentsVM.updateSelectedCancelAppointmentReason(index); + }, + selectedColor: MyColors.darkPrimaryColor, + ); + }, + ), + if (appointmentsVM.selectedCancelAppointmentReason.index == appointmentsVM.cancelAppointmentReasonList.length - 1) ...[ + // comparing if the "other" is selected + 12.height, + TxtField( + maxLines: 5, + value: appointmentsVM.cancelAppointmentDescription, + errorValue: appointmentsVM.cancelAppointmentDescriptionError, + keyboardType: TextInputType.text, + hint: LocaleKeys.description.tr(), + onChanged: (v) => appointmentsVM.updateCancelAppointmentDescription(v), + ), + ], + ], + ), + 25.height, + ShowFillButton( + title: LocaleKeys.submit.tr(), + onPressed: () { + if (appointmentsVM.selectedCancelAppointmentReason.index == 0) { + appointmentsVM.onRescheduleAppointmentPressed(context: context, appointmentListModel: appointmentListModel); + } else { + context.read().onCancelAppointmentPressed(context: context, appointmentListModel: appointmentListModel); + } + }, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + )); + }); + }, + ); + } + @override Widget build(BuildContext context) { AppointmentsVM appointmentsVM = context.read(); diff --git a/lib/views/appointments/appointments_filter_view.dart b/lib/views/appointments/appointments_filter_view.dart index 06650d3..45b2faf 100644 --- a/lib/views/appointments/appointments_filter_view.dart +++ b/lib/views/appointments/appointments_filter_view.dart @@ -96,6 +96,7 @@ class _AppointmentsFilterViewState extends State { children: [ SizedBox( height: 5.h, + width: 5.h, child: const CircularProgressIndicator(), ), ], diff --git a/lib/views/appointments/book_appointment_schedules_view.dart b/lib/views/appointments/book_appointment_schedules_view.dart index 5752f34..b46c46a 100644 --- a/lib/views/appointments/book_appointment_schedules_view.dart +++ b/lib/views/appointments/book_appointment_schedules_view.dart @@ -14,6 +14,7 @@ import 'package:mc_common_app/widgets/common_widgets/time_slots.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:sizer/sizer.dart'; class ScreenArgumentsForAppointmentDetailPage { final int routeFlag; // 1 = coming from create appointment || 2 = coming from reschedule appointment @@ -63,7 +64,8 @@ class BookAppointmentSchedulesView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ (LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(), + 2.width, + (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.customerLocation.tr() : LocaleKeys.companyLocation.tr()).toText(fontSize: 12, isBold: true).expand(), ], ), Column( @@ -110,9 +112,7 @@ class BookAppointmentSchedulesView extends StatelessWidget { SizedBox( width: double.infinity, child: BuildTimeSlots( - timeSlots: appointmentsVM.serviceAppointmentScheduleList[scheduleIndex] - .customTimeDateSlotList![appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!].availableSlots ?? - [], + timeSlots: appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!].availableSlots ?? [], onPressed: (slotIndex) { appointmentsVM.updateSelectedAppointmentSlotByDate(scheduleIndex: scheduleIndex, slotIndex: slotIndex); }, diff --git a/lib/views/appointments/book_appointment_services_view.dart b/lib/views/appointments/book_appointment_services_view.dart index ec933d2..9092246 100644 --- a/lib/views/appointments/book_appointment_services_view.dart +++ b/lib/views/appointments/book_appointment_services_view.dart @@ -67,17 +67,17 @@ class BookAppointmentServicesView extends StatelessWidget { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "${itemData.name}: ".toText(fontSize: 13, color: MyColors.lightTextColor, isBold: true), + "${itemData.name}: ".toText(fontSize: 13, color: MyColors.lightTextColor), ("${itemData.price}").toText(fontSize: 13, isBold: true).expand(), ], ); }), ), - 8.height, + // 8.height, Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - (LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), + (LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor), 4.width, (serviceData.isHomeSelected ? serviceData.homeLocation : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(), ], @@ -86,12 +86,12 @@ class BookAppointmentServicesView extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - ((appointmentsVM.servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice).toString()).toText(fontSize: 32, isBold: true), + ((serviceData.currentTotalServicePrice).toString()).toText(fontSize: 32, isBold: true), 2.width, LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), const Icon(Icons.arrow_drop_down, size: 30) ], - ).onPress(() => appointmentsVM.priceBreakDownClicked(context, appointmentsVM.servicesInCurrentAppointment[serviceIndex])), + ).onPress(() => appointmentsVM.priceBreakDownClicked(context, serviceData)), ], ], ).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 10)); diff --git a/lib/views/appointments/book_appointments_item_view.dart b/lib/views/appointments/book_appointments_item_view.dart index bf38c2e..65ca7ee 100644 --- a/lib/views/appointments/book_appointments_item_view.dart +++ b/lib/views/appointments/book_appointments_item_view.dart @@ -7,6 +7,7 @@ import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/services_models/item_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/appointments_view_model.dart'; import 'package:mc_common_app/views/appointments/widgets/service_item_with_price_checkbox.dart'; @@ -63,31 +64,32 @@ class BookAppointmentsItemView extends StatelessWidget { const Divider(), ], ).horPaddingMain(), - appointmentsVM.serviceItemsFromApi.isEmpty - ? Expanded(child: Center(child: LocaleKeys.noItemsToShow.toText(fontSize: 16, color: MyColors.lightTextColor))) - : ListView.separated( - separatorBuilder: (BuildContext context, int index) => const Divider(), - itemCount: appointmentsVM.serviceItemsFromApi.length, - itemBuilder: (BuildContext context, int index) { - ItemData itemData = appointmentsVM.serviceItemsFromApi[index]; - return ServiceItemWithPriceCheckBox( - description: itemData.description ?? LocaleKeys.someDescriptionSubServices.tr(), - title: itemData.name!, - isSelected: itemData.isUpdateOrSelected!, - onSelection: (bool value) { - print("itemId: ${itemData.id}"); - appointmentsVM.onItemUpdateOrSelected(index, !itemData.isUpdateOrSelected!, itemData.id!); + appointmentsVM.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) + : appointmentsVM.serviceItemsFromApi.isEmpty + ? Expanded(child: Center(child: LocaleKeys.noAvailableItems.toText(fontSize: 16, color: MyColors.lightTextColor))) + : ListView.separated( + separatorBuilder: (BuildContext context, int index) => const Divider(), + itemCount: appointmentsVM.serviceItemsFromApi.length, + itemBuilder: (BuildContext context, int index) { + ItemData itemData = appointmentsVM.serviceItemsFromApi[index]; + return ServiceItemWithPriceCheckBox( + description: itemData.description ?? LocaleKeys.someDescriptionSubServices.tr(), + title: itemData.name!, + isSelected: itemData.isUpdateOrSelected!, + onSelection: (bool value) { + appointmentsVM.onItemUpdateOrSelected(index, !itemData.isUpdateOrSelected!, itemData.id!); + }, + priceWidget: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + itemData.price!.split(".").first.toText(fontSize: 30, isBold: true), + LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + ], + ), + ); }, - priceWidget: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - itemData.price!.split(".").first.toText(fontSize: 30, isBold: true), - LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), - ], - ), - ); - }, - ).expand(), + ).expand(), Column( children: [ const Divider(height: 1, thickness: 0.7), diff --git a/lib/views/appointments/pick_items_view.dart b/lib/views/appointments/pick_items_view.dart index 0dc143d..6d54f03 100644 --- a/lib/views/appointments/pick_items_view.dart +++ b/lib/views/appointments/pick_items_view.dart @@ -1,170 +1,170 @@ -import 'package:flutter/material.dart'; -import 'package:mc_common_app/classes/consts.dart'; -import 'package:mc_common_app/extensions/int_extensions.dart'; -import 'package:mc_common_app/extensions/string_extensions.dart'; -import 'package:mc_common_app/generated/locale_keys.g.dart'; -import 'package:mc_common_app/theme/colors.dart'; -import 'package:mc_common_app/widgets/button/show_fill_button.dart'; -import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; -import 'package:mc_common_app/widgets/common_widgets/branch_details_card.dart'; -import 'package:mc_common_app/widgets/common_widgets/time_slots.dart'; -import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; -import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; -import 'package:easy_localization/easy_localization.dart'; - -class BookProviderAppView extends StatefulWidget { - const BookProviderAppView({Key? key}) : super(key: key); - - @override - State createState() => _BookProviderAppViewState(); -} - -class _BookProviderAppViewState extends State { - bool isReview = false; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: CustomAppBar( - title: LocaleKeys.appointment.tr(), - profileImageUrl: MyAssets.bnCar, - isRemoveBackButton: false, - isDrawerEnabled: false, - ), - body: Container( - color: MyColors.backgroundColor, - width: double.infinity, - height: double.infinity, - child: Column( - children: [ - Expanded( - child: ListView( - children: [ - BranchDetailCard( - onCardTapped: () {}, - providerImageUrl: MyAssets.bnCar, - providerLocation: "3km", - title: "Al Ahmed Maintenance", - providerRatings: 4.9, - ), - 12.height, - isReview ? const ReviewAppointmentSection() : const ServicesSelectionSection(), - 10.height, - ], - ), - ), - 10.height, - Padding( - padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), - child: ShowFillButton( - title: LocaleKeys.bookAppointment.tr(), - maxWidth: double.infinity, - onPressed: () { - isReview = !isReview; - setState(() {}); - }, - ), - ), - ], - ), - ), - ); - } -} - -class ServicesSelectionSection extends StatelessWidget { - const ServicesSelectionSection({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - List dropList = [ - DropValue(0, "Maintenance", ""), - DropValue(1, "Car Wash", ""), - DropValue(2, "Monthly Checkup", ""), - DropValue(3, "Friendly Visit", ""), - DropValue(4, "Muftaa", ""), - ]; - return Container( - padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.selectServicesYouWant.tr().toText(fontSize: 18, isBold: true), - 8.height, - DropdownField( - (DropValue value) {}, - list: dropList, - hint: LocaleKeys.selectServiceType.tr(), - ), - 8.height, - DropdownField( - (DropValue value) {}, - list: dropList, - hint: LocaleKeys.selectServiceType.tr(), - ), - 8.height, - DropdownField( - (DropValue value) {}, - list: dropList, - hint: LocaleKeys.selectServiceType.tr(), - ), - 22.height, - LocaleKeys.selectDateAndTime.tr().toText(fontSize: 18, isBold: true), - 8.height, - DropdownField( - (DropValue value) {}, - list: dropList, - hint: LocaleKeys.selectServiceType.tr(), - ), - 22.height, - LocaleKeys.availableSlots.tr().toText(fontSize: 15, isBold: true), - 8.height, - BuildTimeSlots(onPressed: (index) {}, timeSlots: []), - 22.height, - LocaleKeys.totalAmount.tr().toText(fontSize: 18, isBold: true), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - "3000".toText(fontSize: 20, isBold: true), - LocaleKeys.sar.tr().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), - ], - ), - 10.height, - ], - ).toWhiteContainer(width: double.infinity, allPading: 12), - ); - } -} - -class ReviewAppointmentSection extends StatelessWidget { - const ReviewAppointmentSection({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.reviewAppointment.tr().toText(fontSize: 18, isBold: true), - 15.height, - LocaleKeys.services.tr().toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - LocaleKeys.carEngineCheck.tr().toText(fontSize: 18, isBold: true), - 13.height, - LocaleKeys.dateAndTime.tr().toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - "2 Feb, 2023 at 09:00am".toText(fontSize: 18, isBold: true), - 13.height, - LocaleKeys.totalAmount.tr().toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - "3000".toText(fontSize: 20, isBold: true), - LocaleKeys.sar.tr().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), - ], - ), - 100.height, - ], - ).toWhiteContainer(width: double.infinity, allPading: 12), - ); - } -} +// import 'package:flutter/material.dart'; +// import 'package:mc_common_app/classes/consts.dart'; +// import 'package:mc_common_app/extensions/int_extensions.dart'; +// import 'package:mc_common_app/extensions/string_extensions.dart'; +// import 'package:mc_common_app/generated/locale_keys.g.dart'; +// import 'package:mc_common_app/theme/colors.dart'; +// import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +// import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +// import 'package:mc_common_app/widgets/common_widgets/branch_details_card.dart'; +// import 'package:mc_common_app/widgets/common_widgets/time_slots.dart'; +// import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; +// import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +// import 'package:easy_localization/easy_localization.dart'; +// +// class BookProviderAppView extends StatefulWidget { +// const BookProviderAppView({Key? key}) : super(key: key); +// +// @override +// State createState() => _BookProviderAppViewState(); +// } +// +// class _BookProviderAppViewState extends State { +// bool isReview = false; +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: CustomAppBar( +// title: LocaleKeys.appointment.tr(), +// profileImageUrl: MyAssets.bnCar, +// isRemoveBackButton: false, +// isDrawerEnabled: false, +// ), +// body: Container( +// color: MyColors.backgroundColor, +// width: double.infinity, +// height: double.infinity, +// child: Column( +// children: [ +// Expanded( +// child: ListView( +// children: [ +// BranchDetailCard( +// onCardTapped: () {}, +// providerImageUrl: MyAssets.bnCar, +// providerLocation: "3km", +// title: "Al Ahmed Maintenance", +// providerRatings: 4.9, +// ), +// 12.height, +// isReview ? const ReviewAppointmentSection() : const ServicesSelectionSection(), +// 10.height, +// ], +// ), +// ), +// 10.height, +// Padding( +// padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), +// child: ShowFillButton( +// title: LocaleKeys.bookAppointment.tr(), +// maxWidth: double.infinity, +// onPressed: () { +// isReview = !isReview; +// setState(() {}); +// }, +// ), +// ), +// ], +// ), +// ), +// ); +// } +// } +// +// class ServicesSelectionSection extends StatelessWidget { +// const ServicesSelectionSection({Key? key}) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// List dropList = [ +// DropValue(0, "Maintenance", ""), +// DropValue(1, "Car Wash", ""), +// DropValue(2, "Monthly Checkup", ""), +// DropValue(3, "Friendly Visit", ""), +// DropValue(4, "Muftaa", ""), +// ]; +// return Container( +// padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// LocaleKeys.selectServicesYouWant.tr().toText(fontSize: 18, isBold: true), +// 8.height, +// DropdownField( +// (DropValue value) {}, +// list: dropList, +// hint: LocaleKeys.selectServiceType.tr(), +// ), +// 8.height, +// DropdownField( +// (DropValue value) {}, +// list: dropList, +// hint: LocaleKeys.selectServiceType.tr(), +// ), +// 8.height, +// DropdownField( +// (DropValue value) {}, +// list: dropList, +// hint: LocaleKeys.selectServiceType.tr(), +// ), +// 22.height, +// LocaleKeys.selectDateAndTime.tr().toText(fontSize: 18, isBold: true), +// 8.height, +// DropdownField( +// (DropValue value) {}, +// list: dropList, +// hint: LocaleKeys.selectServiceType.tr(), +// ), +// 22.height, +// LocaleKeys.availableSlots.tr().toText(fontSize: 15, isBold: true), +// 8.height, +// BuildTimeSlots(onPressed: (index) {}, timeSlots: []), +// 22.height, +// LocaleKeys.totalAmount.tr().toText(fontSize: 18, isBold: true), +// Row( +// crossAxisAlignment: CrossAxisAlignment.end, +// children: [ +// "3000".toText(fontSize: 20, isBold: true), +// LocaleKeys.sar.tr().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), +// ], +// ), +// 10.height, +// ], +// ).toWhiteContainer(width: double.infinity, allPading: 12), +// ); +// } +// } +// +// class ReviewAppointmentSection extends StatelessWidget { +// const ReviewAppointmentSection({Key? key}) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// return Container( +// padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// LocaleKeys.reviewAppointment.tr().toText(fontSize: 18, isBold: true), +// 15.height, +// LocaleKeys.services.tr().toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), +// LocaleKeys.carEngineCheck.tr().toText(fontSize: 18, isBold: true), +// 13.height, +// LocaleKeys.dateAndTime.tr().toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), +// "2 Feb, 2023 at 09:00am".toText(fontSize: 18, isBold: true), +// 13.height, +// LocaleKeys.totalAmount.tr().toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), +// Row( +// crossAxisAlignment: CrossAxisAlignment.end, +// children: [ +// "3000".toText(fontSize: 20, isBold: true), +// LocaleKeys.sar.tr().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor), +// ], +// ), +// 100.height, +// ], +// ).toWhiteContainer(width: double.infinity, allPading: 12), +// ); +// } +// } diff --git a/lib/views/appointments/review_appointment_view.dart b/lib/views/appointments/review_appointment_view.dart index 1ecfa41..67021ff 100644 --- a/lib/views/appointments/review_appointment_view.dart +++ b/lib/views/appointments/review_appointment_view.dart @@ -1,5 +1,8 @@ +import 'dart:developer'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; @@ -9,15 +12,104 @@ import 'package:mc_common_app/models/services_models/item_model.dart'; import 'package:mc_common_app/models/services_models/service_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/appointments_view_model.dart'; +import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; class ReviewAppointment extends StatelessWidget { const ReviewAppointment({Key? key}) : super(key: key); + // buildProviderContactInfoBottomSheet(BuildContext context) { + // final appointmentsVM = context.read(); + // log(": ${appointmentsVM.selectedBranchModel}"); + // return showModalBottomSheet( + // context: context, + // isScrollControlled: true, + // enableDrag: true, + // builder: (BuildContext context) { + // return InfoBottomSheet( + // title: LocaleKeys.contactDetails.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + // description: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // if (requestVM.currentSelectedOffer!.mobileNo != null && requestVM.currentSelectedOffer!.mobileNo!.isNotEmpty) ...[ + // CustomSettingOptionsTile( + // leadingWidget: const Icon( + // Icons.call, + // size: 20, + // color: MyColors.greyColor, + // ), + // titleText: LocaleKeys.phoneNumber.tr(), + // subTitle: " ${requestVM.currentSelectedOffer!.mobileNo ?? ""}", + // needBorderBelow: true, + // showTrailingArrow: false, + // subtitleTextColor: MyColors.greyColor, + // onTap: () { + // Utils.openNumberViaCaller(phoneNumber: requestVM.currentSelectedOffer!.mobileNo ?? ""); + // }, + // ), + // ], + // if (requestVM.currentSelectedOffer!.email != null && requestVM.currentSelectedOffer!.email!.isNotEmpty) ...[ + // CustomSettingOptionsTile( + // leadingWidget: const Icon( + // Icons.email_outlined, + // size: 20, + // color: MyColors.greyColor, + // ), + // titleText: LocaleKeys.emailAddress.tr(), + // subTitle: " ${requestVM.currentSelectedOffer!.email ?? ""}", + // needBorderBelow: false, + // showTrailingArrow: false, + // subtitleTextColor: MyColors.greyColor, + // onTap: () { + // Utils.openEmailViaEmailApp(emailAddress: requestVM.currentSelectedOffer!.email ?? ""); + // }, + // ), + // ], + // 30.height, + // ], + // ), + // ); + // }, + // ); + // } + + Widget showItem(String title, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "$title: ".toText(color: MyColors.lightTextColor, letterSpacing: -0.48), + 3.width, + Flexible(child: value.toText(isBold: true, overflow: TextOverflow.ellipsis)), + ], + ); + } + + Widget buildPersonalInformationCard({required BuildContext context}) { + return Padding( + padding: const EdgeInsets.only( + bottom: 0, + left: 21, + right: 21, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + (LocaleKeys.personalInformation.tr()).toText(fontSize: 16, isBold: true), + showItem(LocaleKeys.name.tr(), "${AppState().getUser.data!.userInfo!.firstName ?? ""} ${AppState().getUser.data!.userInfo!.lastName ?? ""}"), + showItem(LocaleKeys.phoneNumber.tr(), AppState().getUser.data!.userInfo!.mobileNo ?? ""), + showItem(LocaleKeys.email.tr(), AppState().getUser.data!.userInfo!.email ?? ""), + ], + ).toWhiteContainer(width: double.infinity, allPading: 12), + ); + } + Widget buildBranchInfoCard({required BuildContext context}) { AppointmentsVM appointmentsVM = context.read(); return Padding( @@ -28,8 +120,7 @@ class ReviewAppointment extends StatelessWidget { ), child: Row( children: [ - Image.asset( - MyAssets.bnCar, + appointmentsVM.selectedBranchModel!.branchProfileImage.buildNetworkImage( width: 80, height: 60, fit: BoxFit.cover, @@ -53,10 +144,38 @@ class ReviewAppointment extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( - child: (appointmentsVM.selectedBranchModel!.branchDescription ?? "").toText(fontSize: 12), + child: (appointmentsVM.selectedBranchModel!.branchDescription ?? "").toText(fontSize: 12, color: MyColors.lightTextColor), ), ], ), + if ((appointmentsVM.selectedBranchModel!.latitude != null && appointmentsVM.selectedBranchModel!.latitude!.isNotEmpty) && + (appointmentsVM.selectedBranchModel!.longitude != null && appointmentsVM.selectedBranchModel!.longitude!.isNotEmpty)) ...[ + Row( + children: [ + LocaleKeys.openMapLocation.tr().toText( + fontSize: 12, + isBold: true, + color: MyColors.primaryColor, + isUnderLine: true, + ), + 4.width, + Image.asset( + MyAssets.icRightUpPng, + height: 6, + width: 6, + color: MyColors.primaryColor, + ), + ], + ).onPress(() async { + double latitude, longitude = 0.0; + + latitude = double.parse(appointmentsVM.selectedBranchModel!.latitude!); + longitude = double.parse(appointmentsVM.selectedBranchModel!.longitude!); + await Utils.openLocationInMaps(latitude: latitude, longitude: longitude); + }), + ] else ...[ + const SizedBox(), + ], ], ), ), @@ -80,7 +199,7 @@ class ReviewAppointment extends StatelessWidget { ), ), ], - ).onPress(() {}).toWhiteContainer(width: double.infinity, allPading: 12)); + ).toWhiteContainer(width: double.infinity, allPading: 12)); } Widget buildServicesInfoCard({required BuildContext context}) { @@ -135,13 +254,20 @@ class ReviewAppointment extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - ("${itemData.name}: ${itemData.price} ${LocaleKeys.sar.tr()}").toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor), + ("${itemData.name}: ${itemData.price} ${LocaleKeys.sar.tr()}").toText(fontSize: 13, color: MyColors.lightTextColor), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ("${itemData.description}").toText(fontSize: 11, color: MyColors.lightTextColor), ], ), ], ); }), - ), + ).paddingOnly(bottom: 10), ], ); }), @@ -156,12 +282,12 @@ class ReviewAppointment extends StatelessWidget { LocaleKeys.timeLocation.tr().toText(fontSize: 16, isBold: true), 3.height, Row(children: [ - "${LocaleKeys.dateAndTime.tr()}: ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - "${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at $selectedTimeSlot".toText(fontSize: 12, isBold: true), + "${LocaleKeys.dateAndTime.tr()}: ".toText(fontSize: 12, color: MyColors.lightTextColor), + "${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at $selectedTimeSlot".toText(fontSize: 12), ]), Row(children: [ - ("${LocaleKeys.location.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), - (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true), + ("${LocaleKeys.location.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor), + (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12), ]), ], ), @@ -169,6 +295,7 @@ class ReviewAppointment extends StatelessWidget { 10.height, const Divider(thickness: 0.7), Builder(builder: (BuildContext context) { + //TODO: THIS SHOULD BE FINALIZED FROM API double totalServicePrice = 0.0; double totalKms = 15.3; double rangePricePerKm = 5; @@ -181,7 +308,7 @@ class ReviewAppointment extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.serviceCharges.tr().toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true), + LocaleKeys.serviceCharges.tr().toText(fontSize: 14, color: MyColors.lightTextColor), "${scheduleData.amountTotal.toString()} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true), ], ), @@ -189,7 +316,7 @@ class ReviewAppointment extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - ("${LocaleKeys.locationCharges.tr()} ($rangePricePerKm x $totalKms )").toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true), + ("${LocaleKeys.locationCharges.tr()} ($rangePricePerKm x $totalKms )").toText(fontSize: 14, color: MyColors.lightTextColor), ("${totalServicePrice.toString()} ${LocaleKeys.sar.tr()}").toText(fontSize: 16, isBold: true), ], ), @@ -289,6 +416,14 @@ class ReviewAppointment extends StatelessWidget { isRemoveBackButton: false, isDrawerEnabled: false, onBackButtonTapped: () => Navigator.pop(context), + actions: [ + IconButton( + onPressed: () { + // buildProviderContactInfoBottomSheet(context); + }, + icon: const Icon(Icons.help_outline_outlined).paddingOnly(right: 21), + ), + ], ), body: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -299,6 +434,8 @@ class ReviewAppointment extends StatelessWidget { buildBranchInfoCard(context: context), 10.height, buildServicesInfoCard(context: context), + 10.height, + buildPersonalInformationCard(context: context), ], ).expand(), buildNextButtonFooter(context: context), 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 288b681..add3b01 100644 --- a/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart +++ b/lib/views/appointments/widgets/appointment_service_pick_bottom_sheet.dart @@ -99,7 +99,7 @@ class AppointmentServicePickBottomSheet extends StatelessWidget { if (appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) { appointmentsVM.updateIsHomeTapped(true); } else { - Utils.showToast("The Selected Service is not available at Home Location."); + Utils.showToast(LocaleKeys.serviceNotAvailableAtHomeLocation.tr()); } }, ), diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index b2557d0..397f692 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; @@ -20,8 +21,10 @@ import 'package:mc_common_app/views/advertisement/components/picked_images_conta import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart'; import 'package:mc_common_app/views/chat/widgets/chat_message_widget.dart'; import 'package:mc_common_app/views/requests/request_bottomsheets.dart'; +import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; @@ -129,10 +132,9 @@ class _ChatViewState extends State { ), ); } + return Scaffold( - appBar: CustomAppBar( - title: LocaleKeys.chat.tr(), - ), + appBar: CustomAppBar(title: LocaleKeys.chat.tr()), body: Consumer2(builder: (BuildContext context, ChatVM chatVM, RequestsVM requestVM, Widget? child) { List chatMessages = []; if (chatTypeEnum == ChatTypeEnum.general) { diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index cdadefb..a671369 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; @@ -21,6 +23,7 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart' as lcl; +import 'package:sizer/sizer.dart'; class ChatMessageCustomWidget extends StatefulWidget { final ChatMessageModel chatMessageModel; @@ -407,10 +410,20 @@ class _ChatMessageCustomWidgetState extends State { return widget.toCircle(borderRadius: 100); } - Widget buildFreeTextDetailsInMessage() { + Widget buildFreeTextDetailsInMessage({required ChatMessageTypeEnum chatMessageTypeEnum}) { + log(": ${widget.chatMessageModel.isMyMessage}"); return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ + if (chatMessageTypeEnum == ChatMessageTypeEnum.offer && (widget.chatMessageModel.isMyMessage == true)) ...[ + Row( + children: [ + const Icon(Icons.remove_red_eye_outlined, size: 12, color: MyColors.lightTextColor), + 4.width, + LocaleKeys.viewed.tr().toText(fontSize: 10, color: MyColors.lightTextColor, fontWeight: MyFonts.Medium), + ], + ), + ], Expanded( child: Directionality( textDirection: TextDirection.ltr, @@ -421,17 +434,12 @@ class _ChatMessageCustomWidgetState extends State { ), ), ), + ], ); } - Widget buildImageGridWidget({ - required List messagesImages, - double gridItemSize = 50.0, - double spacing = 8.0, - double borderWidth = 1, - Color borderColor = MyColors.textColor, - }) { + Widget buildImageGridWidget({required List messagesImages, double gridItemSize = 50.0, double spacing = 8.0, double borderWidth = 1, Color borderColor = MyColors.textColor}) { int imageCount = messagesImages.length; if (imageCount == 1) { return Container( @@ -529,7 +537,7 @@ class _ChatMessageCustomWidgetState extends State { } switch (chatMessageTypeEnum) { case ChatMessageTypeEnum.freeText: - messageTypeWidget = Column(children: [buildFreeTextDetailsInMessage(), 10.height]); + messageTypeWidget = Column(children: [buildFreeTextDetailsInMessage(chatMessageTypeEnum: chatMessageTypeEnum), 10.height]); break; case ChatMessageTypeEnum.image: messageTypeWidget = buildImageGridWidget(messagesImages: widget.chatMessageModel.messageImages ?? []); @@ -540,7 +548,7 @@ class _ChatMessageCustomWidgetState extends State { case ChatMessageTypeEnum.offer: messageTypeWidget = Column( children: [ - buildFreeTextDetailsInMessage(), + buildFreeTextDetailsInMessage(chatMessageTypeEnum: chatMessageTypeEnum), 10.height, buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum!, chatMessageModel: widget.chatMessageModel, context: context), ], diff --git a/lib/views/common_fragments/requests_fragment.dart b/lib/views/common_fragments/requests_fragment.dart index 0c0f924..4c93f06 100644 --- a/lib/views/common_fragments/requests_fragment.dart +++ b/lib/views/common_fragments/requests_fragment.dart @@ -76,7 +76,7 @@ class MyRequestsFragment extends StatelessWidget { return Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) { return Scaffold( appBar: CustomAppBar( - title: LocaleKeys.myRequests.tr(), + title: LocaleKeys.manageRequests.tr(), isRemoveBackButton: true, actions: [ Padding( diff --git a/lib/views/profile/profile_view.dart b/lib/views/profile/profile_view.dart index fbf1e91..dfb243b 100644 --- a/lib/views/profile/profile_view.dart +++ b/lib/views/profile/profile_view.dart @@ -1,3 +1,4 @@ +import 'dart:developer'; import 'dart:ui'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -7,7 +8,10 @@ import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/subscriptions_models/subscription_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/date_helper.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/user_view_model.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; @@ -34,10 +38,26 @@ class _ProfileScreenState extends State { @override Widget build(BuildContext context) { + Subscription? mySubscription; return Scaffold( extendBody: true, backgroundColor: const Color(0xffefefef), body: Consumer(builder: (_, model, __) { + String startDate = ""; + String endDate = ""; + String freeTrialName = ""; + if (AppState().currentAppType == AppType.provider && AppState().getproviderSubscription.isNotEmpty) { + mySubscription = AppState().getproviderSubscription.first; + log("mySubscription!.dateStart.toString(): ${mySubscription!.dateStart}"); + log("mySubscription!.dateEnd.toString(): ${mySubscription!.dateEnd}"); + + if (mySubscription!.id == 1) { + freeTrialName = mySubscription!.name ?? ""; + } else { + startDate = (mySubscription!.dateStart != null && mySubscription!.dateStart!.isNotEmpty) ? DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(mySubscription!.dateStart!))) : ""; + endDate = (mySubscription!.dateEnd != null && mySubscription!.dateEnd!.isNotEmpty) ? DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(mySubscription!.dateEnd!))) : ""; + } + } return Stack( children: [ Column( @@ -84,6 +104,15 @@ class _ProfileScreenState extends State { "${AppState().getUser.data!.userInfo!.firstName} ${AppState().getUser.data!.userInfo!.lastName ?? ""}".toText(fontSize: 20).paddingOnly(left: 25), Column( children: [ + if (AppState().currentAppType == AppType.provider && mySubscription != null) ...[ + CustomProfileOptionsTile( + titleText: LocaleKeys.mySubscription.tr(), + subtitleText: freeTrialName.isNotEmpty ? freeTrialName : "${startDate.isNotEmpty ? "${LocaleKeys.startDate.tr()}: $startDate" : ""} ${endDate.isNotEmpty ? "${LocaleKeys.expiresOn.tr()}: $endDate" : ""}", + needBorderBelow: true, + needEditButton: false, + onTap: () {}, + ), + ], CustomProfileOptionsTile( titleText: LocaleKeys.country.tr(), subtitleText: "Saudi Arabia", @@ -163,7 +192,7 @@ class _ProfileScreenState extends State { child: Container( height: 40, width: 40, - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), decoration: BoxDecoration(color: MyColors.white, shape: BoxShape.circle, border: Border.all(color: MyColors.darkTextColor, width: 0.1)), child: MyAssets.icEdit.buildSvg(), ).onPress( diff --git a/lib/views/requests/create_request_page.dart b/lib/views/requests/create_request_page.dart index 031fb6a..25f2436 100644 --- a/lib/views/requests/create_request_page.dart +++ b/lib/views/requests/create_request_page.dart @@ -27,6 +27,10 @@ class CreateRequestPage extends StatelessWidget { return Scaffold( appBar: CustomAppBar( title: LocaleKeys.createRequest.tr(), + onBackButtonTapped: () { + context.read().resetRequestCreationForm(); + pop(context); + }, ), body: Consumer(builder: (context, requestsVM, widget) { return Column( @@ -133,8 +137,7 @@ class CreateRequestPage extends StatelessWidget { return DropdownField( (DropValue value) => requestsVM.updateSelectionVehicleConditionId(SelectionModel(selectedId: value.id, selectedOption: value.value)), list: vehicleOwnerDrop, - dropdownValue: - requestsVM.vehicleConditionId.selectedId != -1 ? DropValue(requestsVM.vehicleConditionId.selectedId, requestsVM.vehicleConditionId.selectedOption, "") : null, + dropdownValue: requestsVM.vehicleConditionId.selectedId != -1 ? DropValue(requestsVM.vehicleConditionId.selectedId, requestsVM.vehicleConditionId.selectedOption, "") : null, hint: LocaleKeys.condition.tr(), errorValue: requestsVM.vehicleConditionId.errorValue, ); diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart index f3ca520..8b10f42 100644 --- a/lib/views/requests/offer_list_page.dart +++ b/lib/views/requests/offer_list_page.dart @@ -39,7 +39,7 @@ class OfferListPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Utils.statusContainerChip( - text: "Offer ${Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)}", + text: Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!), chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!), ), Row( @@ -54,7 +54,7 @@ class OfferListPage extends StatelessWidget { fontSize: 10, ), ).toContainer( - backgroundColor: MyColors.redColor, + backgroundColor: MyColors.cancelledColor, borderRadius: 100, paddingAll: 1, width: 22, @@ -85,6 +85,7 @@ class OfferListPage extends StatelessWidget { ], ).onPress(() async { context.read().currentSelectedOffer = offersModel; + ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest( chatTypeEnum: ChatTypeEnum.requestOffer, receiverId: "${offersModel.providerUserId}", @@ -106,9 +107,7 @@ class OfferListPage extends StatelessWidget { requestId: offerListPageArguments.requestId ?? 0, providerOfferIndex: index, ) - .whenComplete( - () => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments), - ); + .whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments)); }).toContainer(isShadowEnabled: true); }, separatorBuilder: (context, index) => 16.height, diff --git a/lib/views/requests/request_bottomsheets.dart b/lib/views/requests/request_bottomsheets.dart index ce26d33..0f6ec1a 100644 --- a/lib/views/requests/request_bottomsheets.dart +++ b/lib/views/requests/request_bottomsheets.dart @@ -1,4 +1,6 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; @@ -9,9 +11,12 @@ import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; +import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart'; +import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -81,6 +86,50 @@ Future buildSendOfferBottomSheet({required BuildContext context, required Reques hint: LocaleKeys.description.tr(), onChanged: (v) => requestsVM.updateOfferDescription(v), ), + 12.height, + if (requestDetail.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeStatusEnum()) ...[ + LocaleKeys.deliveryAvailable.tr().toText(fontSize: 16), + 8.height, + Container( + width: 50, + height: 30, + decoration: BoxDecoration( + color: requestsVM.isDeliveryAvailableStatus ? MyColors.darkPrimaryColor : MyColors.white, + borderRadius: BorderRadius.circular(25.0), + border: Border.all(color: MyColors.black, width: 1), + ), + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + activeColor: MyColors.darkPrimaryColor, + trackColor: MyColors.white, + thumbColor: MyColors.grey98Color, + value: requestsVM.isDeliveryAvailableStatus, + onChanged: (value) { + requestsVM.updateIsDeliveryAvailableStatus(value); + }, + ), + ), + ), + ], + 12.height, + if (requestsVM.pickedVehicleImages.isEmpty) ...[ + DottedRectContainer( + onTap: () => context.read().pickMultipleImages(), + text: LocaleKeys.attachImage.tr(), + icon: MyAssets.attachmentIcon.buildSvg(), + ), + ], + if (requestsVM.pickedVehicleImages.isNotEmpty) ...[ + 16.height, + PickedFilesContainer( + pickedFiles: requestsVM.pickedVehicleImages, + onCrossPressedPrimary: requestsVM.removeImageFromList, + onAddFilePressed: () { + context.read().pickMultipleImages(); + }, + ), + ], ], ), 25.height, diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index d0e41e1..fea77d6 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -137,7 +137,7 @@ class RequestDetailPage extends StatelessWidget { children: [ ImagesCorouselWidget(imagesList: requestDetailPageArguments.requestModel.requestImages ?? []), 24.height, - buildRequestContainer(context), + buildRequestDetailsContainer(context), ], ).toWhiteContainer( width: double.infinity, @@ -169,7 +169,7 @@ class RequestDetailPage extends StatelessWidget { ); } - Widget buildRequestContainer(BuildContext context) { + Widget buildRequestDetailsContainer(BuildContext context) { final requestDetail = requestDetailPageArguments.requestModel; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -184,8 +184,9 @@ class RequestDetailPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ requestDetail.vehicleTypeName.toText(fontSize: 16, letterSpacing: -0.64), - showItem("${LocaleKeys.manufacturer.tr()}: ", requestDetail.brand), - showItem("${LocaleKeys.model.tr()}: ", "${requestDetail.year}"), + showItem("${LocaleKeys.brand.tr()}: ", requestDetail.brand), + showItem("${LocaleKeys.model.tr()}: ", requestDetail.model), + showItem("${LocaleKeys.year.tr()}: ", "${requestDetail.year}"), ], ), ), @@ -205,7 +206,9 @@ class RequestDetailPage extends StatelessWidget { ), ], ), - showItem("${LocaleKeys.customerName.tr()}: ", requestDetail.customerName), + if (requestDetail.customerName.isNotEmpty) ...[ + showItem("${LocaleKeys.customerName.tr()}: ", requestDetail.customerName), + ], showItem("${LocaleKeys.description.tr()}: ", requestDetail.description), 16.height, // showItem("${LocaleKeys.priceRange.tr()}:", ""), diff --git a/lib/views/requests/review_request_offer.dart b/lib/views/requests/review_request_offer.dart index 2fc20ce..9af95fe 100644 --- a/lib/views/requests/review_request_offer.dart +++ b/lib/views/requests/review_request_offer.dart @@ -7,6 +7,7 @@ import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/user_models/user.dart'; +import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/date_helper.dart'; import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart'; import 'package:mc_common_app/utils/enums.dart'; @@ -16,6 +17,7 @@ import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_review_containers.dart'; import 'package:mc_common_app/views/location_views/pick_location_page.dart'; +import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; @@ -255,6 +257,61 @@ class _ReviewRequestOfferState extends State { ); } + buildProviderContactInfoBottomSheet(BuildContext context) { + final requestVM = context.read(); + + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return InfoBottomSheet( + title: LocaleKeys.contactDetails.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + description: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (requestVM.currentSelectedOffer!.mobileNo != null && requestVM.currentSelectedOffer!.mobileNo!.isNotEmpty) ...[ + CustomSettingOptionsTile( + leadingWidget: const Icon( + Icons.call, + size: 20, + color: MyColors.greyColor, + ), + titleText: LocaleKeys.phoneNumber.tr(), + subTitle: " ${requestVM.currentSelectedOffer!.mobileNo ?? ""}", + needBorderBelow: true, + showTrailingArrow: false, + subtitleTextColor: MyColors.greyColor, + onTap: () { + Utils.openNumberViaCaller(phoneNumber: requestVM.currentSelectedOffer!.mobileNo ?? ""); + }, + ), + ], + if (requestVM.currentSelectedOffer!.email != null && requestVM.currentSelectedOffer!.email!.isNotEmpty) ...[ + CustomSettingOptionsTile( + leadingWidget: const Icon( + Icons.email_outlined, + size: 20, + color: MyColors.greyColor, + ), + titleText: LocaleKeys.emailAddress.tr(), + subTitle: " ${requestVM.currentSelectedOffer!.email ?? ""}", + needBorderBelow: false, + showTrailingArrow: false, + subtitleTextColor: MyColors.greyColor, + onTap: () { + Utils.openEmailViaEmailApp(emailAddress: requestVM.currentSelectedOffer!.email ?? ""); + }, + ), + ], + 30.height, + ], + ), + ); + }, + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -265,7 +322,7 @@ class _ReviewRequestOfferState extends State { onBackButtonTapped: () => Navigator.pop(context), actions: [ IconButton( - onPressed: () => Utils.showHelpBottomSheet(context), + onPressed: () => buildProviderContactInfoBottomSheet(context), icon: const Icon(Icons.help_outline_outlined).paddingOnly(right: 21), ), ], diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index b934574..52afc08 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -35,10 +35,10 @@ class RequestItem extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.statusContainerChip(text: request.requestStatusName, chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)), + Utils.statusContainerChip(text: request.requestStatusName.toLowerCase() == "InProgress".toLowerCase() ? "In Progress" : request.requestStatusName, chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)), 6.height, "${request.brand} ${request.model} | ${request.id}".toText(fontSize: 16, letterSpacing: -0.64), - showItem("${LocaleKeys.model.tr()}:", "${request.year}"), + showItem("${LocaleKeys.year.tr()}:", "${request.year}"), if (request.customerName.isNotEmpty) ...[ showItem("${LocaleKeys.customerName.tr()}:", request.customerName), ], diff --git a/lib/views/setting_options/setting_option_help.dart b/lib/views/setting_options/setting_option_help.dart index 38fc246..21b62d0 100644 --- a/lib/views/setting_options/setting_option_help.dart +++ b/lib/views/setting_options/setting_option_help.dart @@ -57,7 +57,7 @@ class SettingOptionsHelp extends StatelessWidget { ), titleText: LocaleKeys.termPrivacy.tr(), needBorderBelow: true, - onTap: () {}, + onTap: () => navigateWithName(context, AppRoutes.settingOptionsTermsAndConditions), ), CustomSettingOptionsTile( leadingWidget: const Icon( diff --git a/lib/views/setting_options/setting_options_app_info.dart b/lib/views/setting_options/setting_options_app_info.dart index 998a514..d44e46c 100644 --- a/lib/views/setting_options/setting_options_app_info.dart +++ b/lib/views/setting_options/setting_options_app_info.dart @@ -2,15 +2,15 @@ import 'dart:async'; import 'package:flutter/cupertino.dart'; 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/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart'; -import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart'; -import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; -import 'package:mc_common_app/view_models/ad_view_model.dart'; +import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/setting_options_view_model.dart'; import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -76,7 +76,7 @@ class _SettingOptionsAppInfoState extends State { 16.height, Expanded( child: RefreshIndicator( - onRefresh: () async => await settingsOptionsVM.getAllFaqs(), + onRefresh: () async => await settingsOptionsVM.getAppInfoList(), child: (settingsOptionsVM.state == ViewState.busy) ? const Center(child: CircularProgressIndicator()) : settingsOptionsVM.appInfoList.isEmpty @@ -95,12 +95,22 @@ class _SettingOptionsAppInfoState extends State { (appInfoModel.header ?? "").toString().toText(fontSize: 16), 5.height, (appInfoModel.content ?? "").toString().toText(fontSize: 14, color: MyColors.lightTextColor), - if (appInfoModel.appInfoImages != null && appInfoModel.appInfoImages!.isNotEmpty) ...[ + if (appInfoModel.images != null && appInfoModel.images!.isNotEmpty) ...[ PickedFilesContainer( - pickedFiles: appInfoModel.appInfoImages ?? [], + pickedFiles: appInfoModel.images ?? [], isReview: true, onAddFilePressed: () {}, - ), + ).onPress(() { + List images = []; + for (var image in appInfoModel.images!) { + images.add(MessageImageModel( + id: image.id, + isFromNetwork: true, + imageUrl: image.filePath, + )); + } + navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: images); + }), ] ], ).toContainer(isShadowEnabled: true); diff --git a/lib/views/setting_options/setting_options_contact_us.dart b/lib/views/setting_options/setting_options_contact_us.dart index 027990d..817c3de 100644 --- a/lib/views/setting_options/setting_options_contact_us.dart +++ b/lib/views/setting_options/setting_options_contact_us.dart @@ -2,12 +2,16 @@ import 'dart:async'; import 'package:flutter/cupertino.dart'; 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/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/setting_options_view_model.dart'; import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; @@ -92,7 +96,14 @@ class _SettingOptionsContactUsState extends State { children: [ contactInfoModel.companyName.toString().toText(fontSize: 16), 5.height, - LocaleKeys.openMapLocation.tr().toText(isUnderLine: true, color: MyColors.darkPrimaryColor, fontSize: 10).onPress(() {}), + if (contactInfoModel.latitude != null && contactInfoModel.latitude!.isNotEmpty && contactInfoModel.longitude != null && contactInfoModel.longitude!.isNotEmpty) ...[ + LocaleKeys.openMapLocation.tr().toText(isUnderLine: true, color: MyColors.darkPrimaryColor, fontSize: 10).onPress(() async { + double latitude, longitude = 0.0; + latitude = double.parse(contactInfoModel.latitude!); + longitude = double.parse(contactInfoModel.longitude!); + await Utils.openLocationInMaps(latitude: latitude, longitude: longitude); + }), + ], showData("${LocaleKeys.phoneNumber.tr()}:", "${contactInfoModel.phoneNo}"), showData("${LocaleKeys.email.tr()}:", "${contactInfoModel.email}"), if (contactInfoModel.address != null && contactInfoModel.address!.isNotEmpty) ...[ @@ -104,7 +115,17 @@ class _SettingOptionsContactUsState extends State { pickedFiles: contactInfoModel.contactInfoImages ?? [], isReview: true, onAddFilePressed: () {}, - ), + ).onPress(() { + List images = []; + for (var image in contactInfoModel.contactInfoImages!) { + images.add(MessageImageModel( + id: image.id, + isFromNetwork: true, + imageUrl: image.filePath, + )); + } + navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: images); + }), ] ], ).toContainer(isShadowEnabled: true); diff --git a/lib/views/setting_options/setting_options_invite_friends.dart b/lib/views/setting_options/setting_options_invite_friends.dart index d09c9ab..843babd 100644 --- a/lib/views/setting_options/setting_options_invite_friends.dart +++ b/lib/views/setting_options/setting_options_invite_friends.dart @@ -1,15 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/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/setting_options_view_model.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:provider/provider.dart'; class SettingOptionsInviteFriends extends StatelessWidget { const SettingOptionsInviteFriends({super.key}); @@ -21,22 +26,24 @@ class SettingOptionsInviteFriends extends StatelessWidget { enableDrag: true, builder: (BuildContext context) { return InfoBottomSheet( - description: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 20.height, - CustomSettingOptionsTile( - leadingWidget: MyAssets.whatsAppIcon.buildSvg( - height: 20, - width: 20, - color: MyColors.greyColor, - ), - titleText: LocaleKeys.inviteFriendsByWhatsApp.tr(), - needBorderBelow: true, - showTrailingArrow: false, - onTap: () {}, + title: LocaleKeys.inviteFriends.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + description: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomSettingOptionsTile( + leadingWidget: MyAssets.whatsAppIcon.buildSvg( + height: 20, + width: 20, + color: MyColors.greyColor, ), - CustomSettingOptionsTile( + titleText: LocaleKeys.inviteFriendsByWhatsApp.tr(), + needBorderBelow: true, + showTrailingArrow: false, + onTap: () async { + await context.read().appInvitationCreate(context: context, channelId: 1); + }, + ), + CustomSettingOptionsTile( leadingWidget: const Icon( Icons.sms_outlined, size: 20, @@ -45,23 +52,26 @@ class SettingOptionsInviteFriends extends StatelessWidget { titleText: LocaleKeys.inviteFriendsBySMS.tr(), needBorderBelow: true, showTrailingArrow: false, - onTap: () {}, - ), - CustomSettingOptionsTile( - leadingWidget: const Icon( - Icons.email_outlined, - size: 20, - color: MyColors.greyColor, - ), - titleText: LocaleKeys.inviteFriendsByEmail.tr(), - needBorderBelow: false, - showTrailingArrow: false, - onTap: () {}, + onTap: () async { + await context.read().appInvitationCreate(context: context, channelId: 2); + }), + CustomSettingOptionsTile( + leadingWidget: const Icon( + Icons.email_outlined, + size: 20, + color: MyColors.greyColor, ), - 30.height, - ], - ), - title: const SizedBox()); + titleText: LocaleKeys.inviteFriendsByEmail.tr(), + needBorderBelow: false, + showTrailingArrow: false, + onTap: () async { + await context.read().appInvitationCreate(context: context, channelId: 3); + }, + ), + 30.height, + ], + ), + ); }, ); } @@ -82,14 +92,16 @@ class SettingOptionsInviteFriends extends StatelessWidget { children: [ Column( children: [ - CustomSettingOptionsTile( - leadingWidget: const Icon(Icons.group, size: 20), - titleText: LocaleKeys.inviteFriends.tr(), - needBorderBelow: true, - onTap: () { - return buildInviteFriendsBottomSheet(context); - }, - ), + if (AppState().currentAppType == AppType.customer) ...[ + CustomSettingOptionsTile( + leadingWidget: const Icon(Icons.group, size: 20), + titleText: LocaleKeys.inviteFriends.tr(), + needBorderBelow: true, + onTap: () { + return buildInviteFriendsBottomSheet(context); + }, + ), + ], CustomSettingOptionsTile( leadingWidget: const Icon(Icons.question_mark_outlined, size: 20), titleText: LocaleKeys.help.tr(), diff --git a/lib/views/setting_options/setting_options_more.dart b/lib/views/setting_options/setting_options_more.dart index 8f0642d..2bb5078 100644 --- a/lib/views/setting_options/setting_options_more.dart +++ b/lib/views/setting_options/setting_options_more.dart @@ -5,9 +5,11 @@ import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/main.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/user_view_model.dart'; @@ -25,6 +27,38 @@ class SettingOptionsMore extends StatefulWidget { } class _SettingOptionsMoreState extends State { + void logoutConfirmationSheet(BuildContext context) { + return actionConfirmationBottomSheet( + context: context, + title: LocaleKeys.logoutConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: LocaleKeys.logoutConfirmationMessage.tr(), + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: LocaleKeys.yes.tr(), + fontSize: 15, + onPressed: () { + Navigator.pop(context); + context.read().logout(context); + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: LocaleKeys.no.tr(), + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -109,10 +143,7 @@ class _SettingOptionsMoreState extends State { leadingWidget: SizedBox( width: 16, height: 16, - child: SvgPicture.asset( - MyAssets.icGroupStar, - // color: MyColors.primaryColor, - ), + child: SvgPicture.asset(MyAssets.icGroupStar), ), titleText: LocaleKeys.subscriptions.tr(), subTitle: null, @@ -196,7 +227,7 @@ class _SettingOptionsMoreState extends State { maxHeight: 55, title: LocaleKeys.logOut.tr(), onPressed: () { - context.read().logout(context); + logoutConfirmationSheet(context); }, ), ), @@ -207,5 +238,3 @@ class _SettingOptionsMoreState extends State { ); } } - - diff --git a/lib/views/setting_options/setting_options_terms_and_conditions.dart b/lib/views/setting_options/setting_options_terms_and_conditions.dart new file mode 100644 index 0000000..c00db1b --- /dev/null +++ b/lib/views/setting_options/setting_options_terms_and_conditions.dart @@ -0,0 +1,127 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +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/generated/locale_keys.g.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; +import 'package:mc_common_app/models/setting_utils_models/app_info_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/setting_options_view_model.dart'; +import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:provider/provider.dart'; + +class SettingOptionsTermsAndConditions extends StatefulWidget { + const SettingOptionsTermsAndConditions({super.key}); + + @override + State createState() => _SettingOptionsTermsAndConditionsState(); +} + +class _SettingOptionsTermsAndConditionsState extends State { + late SettingOptionsVM settingsOptionsVM; + + @override + void initState() { + settingsOptionsVM = context.read(); + scheduleMicrotask(() async { + await settingsOptionsVM.getTermsAndConditions(); + }); + super.initState(); + } + + Widget showData(String title, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.toText( + fontSize: 13, + color: MyColors.darkTextColor, + ), + if (title.isNotEmpty) 5.width, + Flexible( + child: value.toText( + fontSize: 13, + color: MyColors.lightTextColor, + ), + ) + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: LocaleKeys.termPrivacy.tr(), + isRemoveBackButton: false, + isDrawerEnabled: false, + onBackButtonTapped: () => Navigator.pop(context), + ), + body: Consumer( + builder: (BuildContext context, SettingOptionsVM settingsOptionsVM, Widget? child) { + return Container( + color: MyColors.backgroundColor, + width: double.infinity, + height: double.infinity, + child: Column( + children: [ + 16.height, + Expanded( + child: RefreshIndicator( + onRefresh: () async => await settingsOptionsVM.getTermsAndConditions(), + child: (settingsOptionsVM.state == ViewState.busy) + ? const Center(child: CircularProgressIndicator()) + : settingsOptionsVM.termsAndConditionsList.isEmpty + ? Padding( + padding: const EdgeInsets.all(21), + child: Center(child: LocaleKeys.somethingWrong.tr().toText(textAlign: TextAlign.center, fontSize: 16, color: MyColors.lightTextColor)), + ) + : ListView.separated( + itemCount: settingsOptionsVM.termsAndConditionsList.length, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + AppInfoModel appInfoModel = settingsOptionsVM.termsAndConditionsList[index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (appInfoModel.header ?? "").toString().toText(fontSize: 16), + 5.height, + (appInfoModel.content ?? "").toString().toText(fontSize: 14, color: MyColors.lightTextColor), + if (appInfoModel.images != null && appInfoModel.images!.isNotEmpty) ...[ + PickedFilesContainer( + pickedFiles: appInfoModel.images ?? [], + isReview: true, + onAddFilePressed: () {}, + ).onPress(() { + List images = []; + for (var image in appInfoModel.images!) { + images.add(MessageImageModel( + id: image.id, + isFromNetwork: true, + imageUrl: image.filePath, + )); + } + navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: images); + }), + ] + ], + ).toContainer(isShadowEnabled: true); + }, + separatorBuilder: (context, index) => 16.height, + ), + )), + ], + ), + ); + }, + )); + } +} diff --git a/lib/views/setting_options/widgets/custom_setting_options_tile.dart b/lib/views/setting_options/widgets/custom_setting_options_tile.dart index 2ecd66a..5958bf5 100644 --- a/lib/views/setting_options/widgets/custom_setting_options_tile.dart +++ b/lib/views/setting_options/widgets/custom_setting_options_tile.dart @@ -15,6 +15,7 @@ class CustomSettingOptionsTile extends StatelessWidget { final bool showTrailingArrow; final Function() onTap; final String? subTitle; + final Color subtitleTextColor; const CustomSettingOptionsTile({ super.key, @@ -25,6 +26,7 @@ class CustomSettingOptionsTile extends StatelessWidget { this.isForLanguage = false, this.showTrailingArrow = true, this.subTitle, + this.subtitleTextColor = MyColors.primaryColor, }); @override @@ -48,7 +50,7 @@ class CustomSettingOptionsTile extends StatelessWidget { Row( children: [ 28.width, - subTitle!.toText(fontSize: 14, color: MyColors.primaryColor), + subTitle!.toText(fontSize: 14, color: subtitleTextColor), ], ), ], diff --git a/lib/views/user/complete_profile_page.dart b/lib/views/user/complete_profile_page.dart index 6d34181..d484d6f 100644 --- a/lib/views/user/complete_profile_page.dart +++ b/lib/views/user/complete_profile_page.dart @@ -1,12 +1,15 @@ import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/user_models/register_user.dart'; +import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/view_models/user_view_model.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -119,13 +122,12 @@ class _CompleteProfilePageState extends State { Consumer(builder: (BuildContext context, UserVM userVM, Widget? child) { return Checkbox( value: userVM.completeProfilePageCheckbox, - activeColor: Colors.blue, + activeColor: MyColors.darkPrimaryColor, onChanged: (value) { userVM.updateCompleteProfilePageCheckbox(value!); }, ); }), - Expanded( child: Text.rich( TextSpan( @@ -144,7 +146,9 @@ class _CompleteProfilePageState extends State { )) ], ), - ), + ).onPress(() { + navigateWithName(context, AppRoutes.settingOptionsTermsAndConditions); + }), ) // Column( // children: [ @@ -162,29 +166,35 @@ class _CompleteProfilePageState extends State { ], ), 16.height, - ShowFillButton( - title: LocaleKeys.save.tr(), - maxWidth: double.infinity, - onPressed: () { - bool validateStatus = userVM.dataValidation( - password: password, - firstName: firstName, - lastName: lastName, - email: email, - ); - if (validateStatus) { - userVM.performCompleteProfile( - context, + Consumer(builder: (BuildContext context, UserVM userVM, Widget? child) { + return ShowFillButton( + title: LocaleKeys.save.tr(), + maxWidth: double.infinity, + isDisabled: !userVM.completeProfilePageCheckbox, + onPressed: () { + if (!userVM.completeProfilePageCheckbox) { + return; + } + bool validateStatus = userVM.dataValidation( password: password, - confirmPassword: confirmPassword!, - firstName: firstName!, - lastName: lastName!, - email: email!, - userId: widget.user.data!.userId ?? "", - isNeedToPassToken: widget.user.data!.isNeedToPassToken, + firstName: firstName, + lastName: lastName, + email: email, ); - } - }), + if (validateStatus) { + userVM.performCompleteProfile( + context, + password: password, + confirmPassword: confirmPassword!, + firstName: firstName!, + lastName: lastName!, + email: email!, + userId: widget.user.data!.userId ?? "", + isNeedToPassToken: widget.user.data!.isNeedToPassToken, + ); + } + }); + }), 16.height, ], ), diff --git a/lib/widgets/common_widgets/bottom_nav_bar.dart b/lib/widgets/common_widgets/bottom_nav_bar.dart index c65aadf..1ad2cd7 100644 --- a/lib/widgets/common_widgets/bottom_nav_bar.dart +++ b/lib/widgets/common_widgets/bottom_nav_bar.dart @@ -40,7 +40,7 @@ class CustomBottomNavbar extends StatelessWidget { color: MyColors.lightIconColor, ).paddingAll(5), activeIcon: SvgPicture.asset(MyAssets.homeIcon, color: MyColors.darkIconColor).paddingAll(5), - label: LocaleKeys.home.tr(), + label: LocaleKeys.explore.tr(), ), BottomNavigationBarItem( icon: SvgPicture.asset(MyAssets.announcementIcon).paddingAll(5), diff --git a/lib/widgets/common_widgets/provider_details_card.dart b/lib/widgets/common_widgets/provider_details_card.dart index 1bffb86..d02772b 100644 --- a/lib/widgets/common_widgets/provider_details_card.dart +++ b/lib/widgets/common_widgets/provider_details_card.dart @@ -17,6 +17,7 @@ class ProviderDetailCard extends StatelessWidget { final String description; final List branches; final Function() onCardTapped; + final bool isFromFavoriteList; const ProviderDetailCard({ Key? key, @@ -27,6 +28,7 @@ class ProviderDetailCard extends StatelessWidget { required this.description, required this.startedSince, required this.onCardTapped, + this.isFromFavoriteList = false, }) : super(key: key); @override @@ -57,21 +59,23 @@ class ProviderDetailCard extends StatelessWidget { title.toText(fontSize: 16, isBold: true), Row( children: [ - ("Total Branches:").toText(color: MyColors.lightTextColor, fontSize: 12), + ("${LocaleKeys.totalBranches.tr()}:").toText(color: MyColors.lightTextColor, fontSize: 12), 4.width, totalBranches.toText(fontSize: 12, isBold: true), ], ), + if (description.isNotEmpty) ...[ + Row( + children: [ + ("${LocaleKeys.description.tr()}:").toText(color: MyColors.lightTextColor, fontSize: 12), + 4.width, + description.toText(fontSize: 12, isBold: true), + ], + ), + ], Row( children: [ - ("${LocaleKeys.description.tr()}:").toText(color: MyColors.lightTextColor, fontSize: 12), - 4.width, - description.toText(fontSize: 12, isBold: true), - ], - ), - Row( - children: [ - ("Since:").toText(color: MyColors.lightTextColor, fontSize: 12), + (isFromFavoriteList ? LocaleKeys.addToFavoritesOn.tr() : LocaleKeys.memberSince.tr()).toText(color: MyColors.lightTextColor, fontSize: 12), 4.width, startedSince.toText(fontSize: 12, isBold: true), ], diff --git a/lib/widgets/txt_field.dart b/lib/widgets/txt_field.dart index 4bc13da..2bbbd2b 100644 --- a/lib/widgets/txt_field.dart +++ b/lib/widgets/txt_field.dart @@ -202,7 +202,7 @@ class TxtField extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - errorValue.toText(fontSize: 14, color: Colors.red), + errorValue.toText(fontSize: 12, color: Colors.red), ], ).paddingOnly(right: 10), ],