Merge branch 'faiz_development_common' into master_new_changes

# Conflicts:
#	lib/classes/app_state.dart
#	lib/services/payments_service.dart
#	lib/view_models/payment_view_model.dart
#	lib/view_models/service_view_model.dart
#	lib/views/setting_options/setting_options_more.dart
#	lib/views/user/login_with_password_page.dart
master_new_changes
Aamir 1 year ago
commit 8d9c4091fb

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@ -579,7 +579,7 @@
"requestAlreadyInProgress": "هذا الطلب قيد التقدم بالفعل.",
"acceptOfferConfirmation": "هل تريد قبول هذا العرض؟",
"acceptOfferConfirmationMessage": "سيتم قبول هذا العرض وستدفع المبلغ المطلوب للمشتري في وقت معين وإلا سيتم إلغاء هذا العرض.",
"noUpcomingAppointments": "لا يوجد موعد قادم متاح",
"noUpcomingAppointments": "لا مواعيد قادم..",
"addNewAppointment": "إضافة موعد جديد",
"myNearbyBranches": "فروعي القريبة",
"myRecentBranches": "فروعي الأخيرة",
@ -654,5 +654,30 @@
"date": "التاريخ",
"selectAll": "اختر الكل",
"unselectAll": "إلغاء اختيار الكل",
"copySelectedServices": "نسخ الخدمات المحددة"
"copySelectedServices": "نسخ الخدمات المحددة",
"pictures": "الصور",
"noChatMessage": "لا توجد رسائل بعد. أرسل رسالة لبدء المحادثة!",
"shippingManagement": "إدارة الشحن",
"appointmentCancelled": "تم إلغاء الموعد",
"appointmentsPerSlot": "المواعيد لكل فترة زمنية",
"createSchedule": "إنشاء الجدول الزمني",
"addItems": "إضافة عناصر",
"itemName": "اسم العنصر",
"itemDescription": "وصف العنصر",
"itemPrice": "سعر العنصر",
"bookAppointmentForServices": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.",
"showServiceAvailability": "سيظهر هذا الخيار للعميل ما إذا كان بإمكانه الحصول على هذه الخدمة في الورشة أم لا.",
"bookAppointmentAtLocation": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يرغب فيه.",
"appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.",
"workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.",
"appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره.",
"deleteScheduleConfirmation": "هل أنت متأكد أنك تريد حذف هذا الجدول؟",
"deleteScheduleAdConfirmationMessage": "سيتم إزالة جميع فترات المواعيد ولن يتمكن العملاء من حجز مواعيد لهذا الجدول.",
"branchSchedules": "جداول الفرع",
"noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع.",
"inviteFriendsBySMS": "دعوة الأصدقاء عبر الرسائل القصيرة",
"inviteFriendsByWhatsApp": "دعوة الأصدقاء عبر واتساب",
"inviteFriendsByEmail": "دعوة الأصدقاء عبر البريد الإلكتروني",
"noFAQsToShow": "لا توجد أسئلة شائعة في الوقت الحالي. يرجى الاتصال بنا إذا كان لديك أي استفسار.",
"appInfo": "معلومات التطبيق"
}

@ -580,7 +580,7 @@
"upgradeVar": "Upgrade",
"whendoyouWanttoRenew": "When do you want to renew",
"renewVar": "Renew",
"noUpcomingAppointments": "No Upcoming Appointment Available.",
"noUpcomingAppointments": "No Upcoming Appointments.",
"addNewAppointment": "Add New Appointment",
"myNearbyBranches": "My Nearby Branches",
"myRecentBranches": "My Recent Branches",
@ -655,5 +655,27 @@
"date": "Date",
"selectAll": "Select All",
"unselectAll": "Unselect All",
"copySelectedServices": "Copy Selected Services"
"copySelectedServices": "Copy Selected Services",
"pictures": "Pictures",
"noChatMessage": "No messages yet. Send a message to start the conversation!",
"shippingManagement": "Shipping Management",
"appointmentCancelled": "Appointment Cancelled",
"appointmentsPerSlot": "Appointments Per Slot",
"createSchedule": "Create Schedule",
"addItems": "Add Items",
"itemName": "Item Name",
"itemDescription": "Item Description",
"itemPrice": "Item Price",
"bookAppointmentForServices": "This option will allow customer to book appointment for these services.",
"showServiceAvailability": "This option will show to customer that you can avail this service on workshop or not.",
"bookAppointmentAtLocation": "This option will allow customer to book appointment at their desired location.",
"deleteScheduleConfirmation": " Are you sure you want to delete this Schedule?",
"deleteScheduleAdConfirmationMessage": "All the appointment slots will be removed and customers will no longer be able to book appointments for this schedule.",
"branchSchedules": "Branch Schedules",
"noSchedulesFound": "You have not added any schedule for this branch.",
"inviteFriendsBySMS": "Invite Friends By SMS",
"inviteFriendsByWhatsApp": "Invite Friends By WhatsApp",
"inviteFriendsByEmail": "Invite Friends By Email",
"noFAQsToShow": "There are no Frequently asked Questions Right now. Please contact us if you have any query.",
"appInfo": "App Info"
}

@ -65,7 +65,7 @@ class AppState {
List<Subscription>? _providerSubscription;
List<Subscription> get getproviderSubscription => _providerSubscription ?? [new Subscription(name: "")];
List<Subscription> get getproviderSubscription => _providerSubscription ?? [Subscription(name: "")];
set setproviderSubscription(List<Subscription> value) {
_providerSubscription = value;

@ -136,6 +136,7 @@ class ApiConsts {
// Payment
static String paymentWebViewUrl = "https://ms.hmg.com/pay/PaymentHome";
static String payForOrderDetailGet = "${baseUrlServices}api/Payment/PayFortOrderDetail_Get";
static String payForOrderIsPaidGet = "${baseUrlServices}api/Payment/PayFortOrderIsPaid_Get";
//Duplicate Services
static String getMatchedServices = "${baseUrlServices}api/ServiceProviders/ServiceProviderBranchServicesMapping_Get";
@ -144,8 +145,6 @@ class ApiConsts {
//Branch Users
static String getAllProviderDealers = "${baseUrlServices}api/ServiceProviders/DealershipUserBranchWise_Get";
// /api/ServiceProviders/BranchUser_Get
//DealershipUserBranchWise_Get
static String getBranchUser = "${baseUrlServices}api/ServiceProviders/BranchUser_Get";
static String assignDealerToBranch = "${baseUrlServices}api/ServiceProviders/BranchUser_Create";
static String removeDealerFromBranch = "${baseUrlServices}api/ServiceProviders/BranchUser_Update";
@ -163,6 +162,10 @@ class ApiConsts {
static String requestOffersSpsGet = "${baseUrlServices}api/RequestManagement/Request_OfferSPs_Get";
static String getServiceRequestsForProvider = "${baseUrlServices}api/RequestManagement/Request_ServiceProvider";
//Shipping
static String shippingRequestStatusUpdate = "${baseUrlServices}api/RequestManagement/ShippingRequestStatus_Update";
static String shippingRequestStatusGet = "${baseUrlServices}api/RequestManagement/ShippingRequestStatus_Get";
//Chat
static String chatHubUrl = "$baseUrlServices/McHub";
static String messageIsReadUpdateForRequests = "${baseUrlServices}api/RequestManagement/ReqOfferChatIsRead_Update";
@ -171,6 +174,12 @@ class ApiConsts {
static String getChatMessagesForAds = "${baseUrlServices}api/Advertisement/AdsChat_Get";
static String getChatBuyersForAds = "${baseUrlServices}api/Advertisement/AdsChatBuyer_Get";
//Settings Options
static String getAllFAQs = "${baseUrlServices}api/Common/FAQ_Get";
static String getAppInvitationLink = "${baseUrlServices}api/Common/AppInvitation_Get";
static String getContactInfo = "${baseUrlServices}api/Master/ContactInfo_Get";
static String getAppInfo = "${baseUrlServices}api/Master/AppInfo_Get";
static List<String> closingUrls = ["PayFortResponse"];
}
@ -292,6 +301,7 @@ class MyAssets {
static String whatsAppIcon = "${assetPath}icons/whatsapp_icon.svg";
static const String arrowRight = "${assetPath}icons/ic_arrow_right.svg";
static const String brokenImage = "${assetPath}images/broken_image.png";
}
RegExp numReg = RegExp(r".*[0-9].*");

@ -12,6 +12,8 @@ import 'package:mc_common_app/repositories/branch_repo.dart';
import 'package:mc_common_app/repositories/chat_repo.dart';
import 'package:mc_common_app/repositories/common_repo.dart';
import 'package:mc_common_app/repositories/payments_repo.dart';
import 'package:mc_common_app/repositories/setting_options_repo.dart';
import 'package:mc_common_app/repositories/shipping_repo.dart';
import 'package:mc_common_app/repositories/user_repo.dart';
import 'package:mc_common_app/services/common_services.dart';
import 'package:mc_common_app/services/firebase_service.dart';
@ -42,6 +44,8 @@ class AppDependencies {
injector.registerSingleton<AppointmentRepo>(() => AppointmentRepoImp());
injector.registerSingleton<ChatRepo>(() => ChatRepoImp());
injector.registerSingleton<BranchRepo>(() => BranchRepoImp());
injector.registerSingleton<ShippingRepo>(() => ShippingRepoImp());
injector.registerSingleton<SettingOptionsRepo>(() => SettingOptionsRepoImp());
//
}

@ -1,5 +1,6 @@
import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart';
import 'package:mc_common_app/models/chat_models/buyers_chat_for_ads_model.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/models/user_models/register_user.dart';
@ -19,9 +20,12 @@ import 'package:mc_common_app/views/requests/requests_filter_view.dart';
import 'package:mc_common_app/views/requests/review_request_offer.dart';
import 'package:mc_common_app/views/setting_options/provider_license_page.dart';
import 'package:mc_common_app/views/setting_options/setting_option_help.dart';
import 'package:mc_common_app/views/setting_options/setting_options_app_info.dart';
import 'package:mc_common_app/views/setting_options/setting_options_contact_us.dart';
import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart';
import 'package:mc_common_app/views/setting_options/setting_options_invite_friends.dart';
import 'package:mc_common_app/views/setting_options/setting_options_language.dart';
import 'package:mc_common_app/views/setting_options/setting_options_more.dart';
import 'package:mc_common_app/views/shipping_management/shipping_management_view.dart';
import 'package:mc_common_app/views/user/change_email_page.dart';
import 'package:mc_common_app/views/user/change_mobile_page.dart';
import 'package:mc_common_app/views/user/change_password_page.dart';
@ -40,6 +44,7 @@ import 'package:mc_common_app/views/user/register_selection_page.dart';
import 'package:mc_common_app/views/splash/splash_page.dart';
import 'package:mc_common_app/views/user/vertify_password_page.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/widgets/image_viewer/image_viewer_screen.dart';
class AppRoutes {
//User
@ -67,7 +72,7 @@ class AppRoutes {
static const String dashboard = "/dashboard";
static const String bookProviderAppView = "/bookProviderAppView";
// Appoinments
// Appointments
static const String appointmentDetailView = "/appointmentDetailView";
static const String bookAppointmenServicesView = "/bookAppointmenServicesView";
static const String bookAppointmenSchedulesView = "/bookAppointmenSchedulesView";
@ -85,6 +90,10 @@ class AppRoutes {
// Payments
static const String paymentMethodsView = "/paymentMethodsView";
//Shipping
static const shippingManagementView = "/shippingManagementView";
//Customer APP: Provider & Services
static const String branchDetailView = "/branchDetailPage";
static const String branchSearchFilterPage = "/branchSearchFilterPage";
@ -106,10 +115,15 @@ class AppRoutes {
//Setting Options
static const String settingOptionsFaqs = "/settingOptionsFaqs";
static const String settingOptionsLanguages = "/settingOptionsLanguages";
static const String settingOptionsContactUs = "/settingOptionsContactUs";
static const String settingOptionsAppInfo = "/settingOptionsAppInfo";
static const String settingOptionsMore = "/settingOptionsLanguages";
static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends";
static const String settingOptionsHelp = "/settingOptionsHelp";
//MediaViewer
static const String mediaViewerScreen = "/mediaViewer";
//Profile Screen
static const String profileView = "/profileView";
@ -118,9 +132,12 @@ class AppRoutes {
//Provider User Page
static const dealerUser = "/dealerUser";
// HomePage Provider
static const String generalChatsListForProvider = "/generalChatsListForProvider";
//Chat
static const String chatView = "/chatView";
static const String initialRoute = splash;
static final Map<String, WidgetBuilder> routes = {
@ -137,13 +154,13 @@ class AppRoutes {
completeProfile: (context) => CompleteProfilePage(ModalRoute.of(context)!.settings.arguments as RegisterUserRespModel),
verifyPassword: (context) => VerifyPasswordPage(),
confirmNewPasswordPage: (context) => ConfirmNewPasswordPage(ModalRoute.of(context)!.settings.arguments as String),
changePassword: (context) => const ChangePasswordPage(),
forgetPasswordMethodPage: (context) => ForgetPasswordMethodPage(ModalRoute.of(context)!.settings.arguments as String),
changeMobilePage: (context) => ChangeMobilePage(),
changeEmailPage: (context) => const ChangeEmailPage(),
changePassword: (context) => const ChangePasswordPage(),
editAccountPage: (context) => const EditAccountPage(),
profileView: (context) => const ProfileScreen(),
settingOptionsLanguages: (context) => const SettingOptionsLanguage(),
settingOptionsMore: (context) => const SettingOptionsMore(),
settingOptionsHelp: (context) => const SettingOptionsHelp(),
providerLicensePage: (context) => const ProviderLicensePage(),
@ -151,10 +168,12 @@ class AppRoutes {
AppRoutes.adsDetailView: (context) => AdsDetailView(adDetails: ModalRoute.of(context)!.settings.arguments as AdDetailsModel),
AppRoutes.createAdView: (context) => const CreateAdView(),
AppRoutes.adsFilterView: (context) => const AdsFilterView(),
AppRoutes.selectAdTypeView: (context) => SelectAdTypeView(arguments: ModalRoute.of(context)!.settings.arguments as List<bool>),
AppRoutes.selectAdTypeView: (context) => SelectAdTypeView(arguments: ModalRoute.of(context)!.settings.arguments as List<dynamic>),
AppRoutes.chatView: (context) => ChatView(chatViewArguments: ModalRoute.of(context)!.settings.arguments as ChatViewArguments),
AppRoutes.adsBuyerChatsListView: (context) => AdsBuyerChatsView(buyersListViewArguments: ModalRoute.of(context)!.settings.arguments as List<BuyersChatForAdsModel>),
AppRoutes.settingOptionsFaqs: (context) => const SettingOptionsFAQs(),
AppRoutes.settingOptionsContactUs: (context) => const SettingOptionsContactUs(),
AppRoutes.settingOptionsAppInfo: (context) => const SettingOptionsAppInfo(),
AppRoutes.settingOptionsInviteFriends: (context) => const SettingOptionsInviteFriends(),
AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypes),
//Requests
@ -163,6 +182,15 @@ class AppRoutes {
AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments),
AppRoutes.reviewRequestOffer: (context) => const ReviewRequestOffer(),
AppRoutes.requestsFilterView: (context) => const RequestsFilterView(),
//MediaViewer
AppRoutes.mediaViewerScreen: (context) => MediaViewerScreen(images: ModalRoute.of(context)!.settings.arguments as List<MessageImageModel>),
// ChatsList Provider
AppRoutes.generalChatsListForProvider: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments),
//Shipping
AppRoutes.shippingManagementView: (context) => const ShippingManagementView(),
};
}

@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/enums.dart';
@ -283,6 +282,31 @@ extension AppointmentStatusToInt on AppointmentStatusEnum {
}
}
extension AppointmentTypeEnumExt on int {
AppointmentTypeEnum toAppointmentTypeEnum() {
if (this == 1) {
return AppointmentTypeEnum.workshop;
} else if (this == 2) {
return AppointmentTypeEnum.home;
} else {
return AppointmentTypeEnum.workshop;
}
}
}
extension AppointmentTypeEnumExtString on AppointmentTypeEnum {
int getIdFromAppointmentTypeEnum() {
switch (this) {
case AppointmentTypeEnum.workshop:
return 1;
case AppointmentTypeEnum.home:
return 2;
default:
return 1;
}
}
}
extension AppointmentStatusToString on AppointmentStatusEnum {
String getAppointmentNameFromEnum() {
switch (this) {
@ -455,16 +479,12 @@ extension PaymentTypesToInt on PaymentTypes {
switch (this) {
case PaymentTypes.subscription:
return 1;
case PaymentTypes.appointment:
return 2;
case PaymentTypes.adReserve:
return 4;
case PaymentTypes.ads:
return 3;
case PaymentTypes.request:
return 5;
case PaymentTypes.extendAds:
@ -478,6 +498,30 @@ extension PaymentTypesToInt on PaymentTypes {
}
}
extension PaymentIdToPaymentTypes on int {
PaymentTypes getPaymentTypeFromId() {
switch (this) {
case 1:
return PaymentTypes.subscription;
case 2:
return PaymentTypes.appointment;
case 4:
return PaymentTypes.adReserve;
case 3:
return PaymentTypes.ads;
case 5:
return PaymentTypes.request;
case 6:
return PaymentTypes.extendAds;
case 7:
return PaymentTypes.partialAppointment;
default:
return PaymentTypes.ads;
}
}
}
extension CreatedByRoleEnumToInt on CreatedByRoleEnum {
int getIdFromCreatedByRoleEnum() {
switch (this) {
@ -577,7 +621,7 @@ extension DateTimeConversions on DateTime {
final date2 = DateTime.now();
final difference = date2.difference(this);
if ((difference.inDays / 7).floor() >= 2) {
return DateHelper.formatAsDayMonthYear(date2);
return DateHelper.formatAsDayMonthYear(this);
} else if ((difference.inDays / 7).floor() >= 1) {
return (numericDates) ? '1 week ago' : 'Last week';
} else if (difference.inDays >= 2) {
@ -863,3 +907,38 @@ extension SubscriptionTypeEnumToString on SubscriptionTypeEnum {
}
}
}
extension ShippingStatusEnumExt on int {
ShippingRequestStatusEnum toShippingStatusEnum() {
if (this == 0) {
return ShippingRequestStatusEnum.allRequests;
} else if (this == 1) {
return ShippingRequestStatusEnum.initiated;
} else if (this == 2) {
return ShippingRequestStatusEnum.inTransit;
} else if (this == 3) {
return ShippingRequestStatusEnum.outForDelivery;
} else if (this == 4) {
return ShippingRequestStatusEnum.delivered;
}
return ShippingRequestStatusEnum.initiated;
}
}
extension ShippingStatusEnumToInt on ShippingRequestStatusEnum {
int getIdFromShippingStatusEnum() {
switch (this) {
case ShippingRequestStatusEnum.initiated:
return 1;
case ShippingRequestStatusEnum.inTransit:
return 2;
case ShippingRequestStatusEnum.outForDelivery:
return 3;
case ShippingRequestStatusEnum.delivered:
return 4;
default:
return 0;
}
}
}

@ -595,7 +595,7 @@ class CodegenLoader extends AssetLoader{
"requestAlreadyInProgress": "هذا الطلب قيد التقدم بالفعل.",
"acceptOfferConfirmation": "هل تريد قبول هذا العرض؟",
"acceptOfferConfirmationMessage": "سيتم قبول هذا العرض وستدفع المبلغ المطلوب للمشتري في وقت معين وإلا سيتم إلغاء هذا العرض.",
"noUpcomingAppointments": "لا يوجد موعد قادم متاح",
"noUpcomingAppointments": "لا مواعيد قادم..",
"addNewAppointment": "إضافة موعد جديد",
"myNearbyBranches": "فروعي القريبة",
"myRecentBranches": "فروعي الأخيرة",
@ -670,7 +670,32 @@ class CodegenLoader extends AssetLoader{
"date": "التاريخ",
"selectAll": "اختر الكل",
"unselectAll": "إلغاء اختيار الكل",
"copySelectedServices": "نسخ الخدمات المحددة"
"copySelectedServices": "نسخ الخدمات المحددة",
"pictures": "الصور",
"noChatMessage": "لا توجد رسائل بعد. أرسل رسالة لبدء المحادثة!",
"shippingManagement": "إدارة الشحن",
"appointmentCancelled": "تم إلغاء الموعد",
"appointmentsPerSlot": "المواعيد لكل فترة زمنية",
"createSchedule": "إنشاء الجدول الزمني",
"addItems": "إضافة عناصر",
"itemName": "اسم العنصر",
"itemDescription": "وصف العنصر",
"itemPrice": "سعر العنصر",
"bookAppointmentForServices": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.",
"showServiceAvailability": "سيظهر هذا الخيار للعميل ما إذا كان بإمكانه الحصول على هذه الخدمة في الورشة أم لا.",
"bookAppointmentAtLocation": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يرغب فيه.",
"appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.",
"workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.",
"appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره.",
"deleteScheduleConfirmation": "هل أنت متأكد أنك تريد حذف هذا الجدول؟",
"deleteScheduleAdConfirmationMessage": "سيتم إزالة جميع فترات المواعيد ولن يتمكن العملاء من حجز مواعيد لهذا الجدول.",
"branchSchedules": "جداول الفرع",
"noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع.",
"inviteFriendsBySMS": "دعوة الأصدقاء عبر الرسائل القصيرة",
"inviteFriendsByWhatsApp": "دعوة الأصدقاء عبر واتساب",
"inviteFriendsByEmail": "دعوة الأصدقاء عبر البريد الإلكتروني",
"noFAQsToShow": "لا توجد أسئلة شائعة في الوقت الحالي. يرجى الاتصال بنا إذا كان لديك أي استفسار.",
"appInfo": "معلومات التطبيق"
};
static const Map<String,dynamic> en_US = {
"firstTimeLogIn": "First Time Log In",
@ -1254,7 +1279,7 @@ static const Map<String,dynamic> en_US = {
"upgradeVar": "Upgrade",
"whendoyouWanttoRenew": "When do you want to renew",
"renewVar": "Renew",
"noUpcomingAppointments": "No Upcoming Appointment Available.",
"noUpcomingAppointments": "No Upcoming Appointments.",
"addNewAppointment": "Add New Appointment",
"myNearbyBranches": "My Nearby Branches",
"myRecentBranches": "My Recent Branches",
@ -1329,7 +1354,29 @@ static const Map<String,dynamic> en_US = {
"date": "Date",
"selectAll": "Select All",
"unselectAll": "Unselect All",
"copySelectedServices": "Copy Selected Services"
"copySelectedServices": "Copy Selected Services",
"pictures": "Pictures",
"noChatMessage": "No messages yet. Send a message to start the conversation!",
"shippingManagement": "Shipping Management",
"appointmentCancelled": "Appointment Cancelled",
"appointmentsPerSlot": "Appointments Per Slot",
"createSchedule": "Create Schedule",
"addItems": "Add Items",
"itemName": "Item Name",
"itemDescription": "Item Description",
"itemPrice": "Item Price",
"bookAppointmentForServices": "This option will allow customer to book appointment for these services.",
"showServiceAvailability": "This option will show to customer that you can avail this service on workshop or not.",
"bookAppointmentAtLocation": "This option will allow customer to book appointment at their desired location.",
"deleteScheduleConfirmation": " Are you sure you want to delete this Schedule?",
"deleteScheduleAdConfirmationMessage": "All the appointment slots will be removed and customers will no longer be able to book appointments for this schedule.",
"branchSchedules": "Branch Schedules",
"noSchedulesFound": "You have not added any schedule for this branch.",
"inviteFriendsBySMS": "Invite Friends By SMS",
"inviteFriendsByWhatsApp": "Invite Friends By WhatsApp",
"inviteFriendsByEmail": "Invite Friends By Email",
"noFAQsToShow": "There are no Frequently asked Questions Right now. Please contact us if you have any query.",
"appInfo": "App Info"
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": ar_SA, "en_US": en_US};
}

@ -634,5 +634,30 @@ abstract class LocaleKeys {
static const selectAll = 'selectAll';
static const unselectAll = 'unselectAll';
static const copySelectedServices = 'copySelectedServices';
static const pictures = 'pictures';
static const noChatMessage = 'noChatMessage';
static const shippingManagement = 'shippingManagement';
static const appointmentCancelled = 'appointmentCancelled';
static const appointmentsPerSlot = 'appointmentsPerSlot';
static const createSchedule = 'createSchedule';
static const addItems = 'addItems';
static const itemName = 'itemName';
static const itemDescription = 'itemDescription';
static const itemPrice = 'itemPrice';
static const bookAppointmentForServices = 'bookAppointmentForServices';
static const showServiceAvailability = 'showServiceAvailability';
static const bookAppointmentAtLocation = 'bookAppointmentAtLocation';
static const appointmentBookingOption = 'appointmentBookingOption';
static const workshopAvailabilityOption = 'workshopAvailabilityOption';
static const appointmentLocationOption = 'appointmentLocationOption';
static const deleteScheduleConfirmation = 'deleteScheduleConfirmation';
static const deleteScheduleAdConfirmationMessage = 'deleteScheduleAdConfirmationMessage';
static const branchSchedules = 'branchSchedules';
static const noSchedulesFound = 'noSchedulesFound';
static const inviteFriendsBySMS = 'inviteFriendsBySMS';
static const inviteFriendsByWhatsApp = 'inviteFriendsByWhatsApp';
static const inviteFriendsByEmail = 'inviteFriendsByEmail';
static const noFAQsToShow = 'noFAQsToShow';
static const appInfo = 'appInfo';
}

@ -2,6 +2,9 @@ import 'package:logger/logger.dart';
Logger logger = Logger(printer: PrettyPrinter(printEmojis: false, colors: true, printTime: false));
bool disableThingsForQA = true;
// Language Tile in Settings
// todo terminal command to genertate translation files
// flutter pub run easy_localization:generate --source-dir ./assets/langs

@ -16,6 +16,8 @@ class AppointmentListModel {
String? customerName;
String? customerMobileNum;
int? appointmentType;
AppointmentTypeEnum? appointmentTypeEnum;
String? providerName;
String? duration;
String? appointmentDate;
@ -45,6 +47,7 @@ class AppointmentListModel {
this.customerName,
this.customerMobileNum,
this.appointmentType,
this.appointmentTypeEnum,
this.providerName,
this.duration,
this.branchName,
@ -76,6 +79,7 @@ class AppointmentListModel {
customerName = json['customerName'];
customerMobileNum = json['customerMobile'];
appointmentType = json['appointmentType'];
appointmentTypeEnum = json['appointmentType'] != null ? (json['appointmentType'] as int).toAppointmentTypeEnum() : AppointmentTypeEnum.workshop;
providerName = json['providerName'];
duration = json['duration'];
appointmentDate = json['appointmentDate'];

@ -1,6 +1,7 @@
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/utils/enums.dart';
class CustomTimeDateSlotModel {
TimeSlotModel? date;
@ -19,6 +20,7 @@ class ServiceAppointmentScheduleModel {
double? amountTotal;
double? amountRem;
int? appointmentType;
AppointmentTypeEnum? appointmentTypeEnum;
ServiceAppointmentScheduleModel({
this.serviceSlotList,
@ -30,6 +32,7 @@ class ServiceAppointmentScheduleModel {
this.amountTotal,
this.amountRem,
this.appointmentType,
this.appointmentTypeEnum,
});
List<CustomTimeDateSlotModel> getFormattedDateTimeSlotPackage() {
@ -105,6 +108,7 @@ class ServiceAppointmentScheduleModel {
amountTotal = json['amountTotal'];
amountRem = json['amountRem'];
appointmentType = json['appointmentType'];
appointmentTypeEnum = json['appointmentType'] != null ? (json['appointmentType'] as int).toAppointmentTypeEnum() : AppointmentTypeEnum.workshop;
}
}

@ -163,4 +163,9 @@ class MessageImageModel {
'reqOfferID': reqOfferID, // We don't include 'isFromNetwork' since it's set manually when parsing JSON
};
}
@override
String toString() {
return 'MessageImageModel{id: $id, imageUrl: $imageUrl, imageStr: $imageStr, imagePath: $imagePath, isFromNetwork: $isFromNetwork, reqOfferID: $reqOfferID}';
}
}

@ -1,5 +1,7 @@
import 'dart:io';
import 'package:mc_common_app/utils/utils.dart';
class GenericRespModel {
GenericRespModel({
this.data,
@ -13,12 +15,19 @@ class GenericRespModel {
int? totalItemsCount;
String? message;
factory GenericRespModel.fromJson(Map<String, dynamic> json) => GenericRespModel(
data: json["data"],
messageStatus: json["messageStatus"],
totalItemsCount: json["totalItemsCount"],
message: json["message"],
);
factory GenericRespModel.fromJson(Map<String, dynamic> json) {
if (json.containsKey('StatusMessage')) {
if ((json['StatusMessage'] as String).contains('Internal server error')) {
Utils.showToast("${json['StatusMessage']}");
}
}
return GenericRespModel(
data: json["data"],
messageStatus: json["messageStatus"],
totalItemsCount: json["totalItemsCount"],
message: json["message"],
);
}
Map<String, dynamic> toJson() => {
"data": data,

@ -22,17 +22,17 @@ class Document {
String? message;
factory Document.fromJson(Map<String, dynamic> json) => Document(
totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"],
totalItemsCount: json["totalItemsCount"],
data: json["data"] == null ? null : List<DocumentData>.from(json["data"].map((x) => DocumentData.fromJson(x))),
messageStatus: json["messageStatus"] == null ? null : json["messageStatus"],
message: json["message"] == null ? null : json["message"],
messageStatus: json["messageStatus"],
message: json["message"],
);
Map<String, dynamic> toJson() => {
"totalItemsCount": totalItemsCount == null ? null : totalItemsCount,
"totalItemsCount": totalItemsCount,
"data": data == null ? null : List<dynamic>.from(data!.map((x) => x.toJson())),
"messageStatus": messageStatus == null ? null : messageStatus,
"message": message == null ? null : message,
"messageStatus": messageStatus,
"message": message,
};
}
@ -43,6 +43,7 @@ class DocumentData {
this.documentId,
this.documentUrl,
this.status,
this.statusText,
this.comment,
this.isActive,
this.document,
@ -54,35 +55,37 @@ class DocumentData {
int? id;
int? serviceProviderId;
int? documentId;
dynamic? documentUrl;
String? documentUrl;
int? status;
dynamic? comment;
String? comment;
bool? isActive;
String? document;
String? fileExt;
String? statusText;
String? documentName;
bool? isLocalFile;
factory DocumentData.fromJson(Map<String, dynamic> json) => DocumentData(
id: json["id"] == null ? null : json["id"],
serviceProviderId: json["serviceProviderID"] == null ? null : json["serviceProviderID"],
documentId: json["documentID"] == null ? null : json["documentID"],
id: json["id"],
serviceProviderId: json["serviceProviderID"],
documentId: json["documentID"],
documentUrl: json["documentURL"],
status: json["status"] == null ? null : json["status"],
status: json["status"],
statusText: json["statusText"],
comment: json["comment"],
isActive: json["isActive"] == null ? null : json["isActive"],
isActive: json["isActive"],
document: null,
fileExt: null,
documentName: json["documentName"] == null ? null : json["documentName"],
documentName: json["documentName"],
isLocalFile: false);
Map<String, dynamic> toJson() => {
"id": id == null ? null : id,
"serviceProviderID": serviceProviderId == null ? null : serviceProviderId,
"documentID": documentId == null ? null : documentId,
"id": id,
"serviceProviderID": serviceProviderId,
"documentID": documentId,
"documentURL": documentUrl,
"status": status == null ? null : status,
"status": status,
"comment": comment,
"isActive": isActive == null ? null : isActive,
"isActive": isActive,
};
}

@ -1,7 +1,9 @@
import 'dart:developer';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/utils/enums.dart';
class ProviderOffersModel {
int? id;
@ -61,6 +63,7 @@ class ServiceProvidersOffers {
String? email;
String? companyName;
String? createdOn;
RequestOfferStatusEnum? requestOfferStatusEnum;
int? offerCount;
List<ChatMessageModel>? chatMessages;
@ -74,6 +77,7 @@ class ServiceProvidersOffers {
this.chatMessages,
this.providerUserId,
this.createdOn,
this.requestOfferStatusEnum,
});
ServiceProvidersOffers.fromJson(Map<String, dynamic> json) {
@ -85,11 +89,12 @@ class ServiceProvidersOffers {
companyName = json['companyName'];
offerCount = json['offerCount'];
createdOn = json['createdOn'];
requestOfferStatusEnum = ((json['offerStatusLast']) as int).toRequestOfferStatusEnum();
chatMessages = [];
}
@override
String toString() {
return 'ServiceProvidersOffers{providerUserId: $providerUserId, providerId: $providerId, name: $name, mobileNo: $mobileNo, email: $email, companyName: $companyName,offeredItemCreatedOn: $createdOn, offerCount: $offerCount, chatMessages: $chatMessages}';
return 'ServiceProvidersOffers{providerUserId: $providerUserId, providerId: $providerId, name: $name, mobileNo: $mobileNo, email: $email, companyName: $companyName,offeredItemCreatedOn: $createdOn, requestOfferStatusEnum: $requestOfferStatusEnum, offerCount: $offerCount, chatMessages: $chatMessages}';
}
}

@ -8,6 +8,8 @@ class RequestModel {
String requestTypeName;
String requestStatusName;
RequestStatusEnum requestStatus;
int? shippingStatus;
ShippingRequestStatusEnum? shippingStatusEnum;
String cityName;
String vehicleTypeName;
String countryName;
@ -46,6 +48,8 @@ class RequestModel {
required this.requestTypeName,
required this.requestStatusName,
required this.requestStatus,
required this.shippingStatus,
required this.shippingStatusEnum,
required this.cityName,
required this.vehicleTypeName,
required this.countryName,
@ -96,6 +100,8 @@ class RequestModel {
requestTypeName: json["requestTypeName"],
requestStatusName: json["requestStatusName"],
requestStatus: (json['requestStatus'] as int).toRequestStatusEnum(),
shippingStatus: json['shippingRequestStatus'],
shippingStatusEnum: json['shippingRequestStatus'] != null ? (json['shippingRequestStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.initiated,
cityName: json["cityName"],
vehicleTypeName: json["vehicleTypeName"],
countryName: json["countryName"],

@ -42,7 +42,7 @@ class ItemData {
final String? price;
final String? manufactureDate;
final String? description;
final dynamic pictureUrl;
final String? pictureUrl;
final int? companyId;
final int? serviceProviderServiceId;
final String? serviceProviderServiceDescription;

@ -0,0 +1,26 @@
import 'package:mc_common_app/view_models/ad_view_model.dart';
class AppInfoModel {
int? id;
String? header;
String? content;
bool? isActive;
int? channel;
List<ImageModel>? appInfoImages;
AppInfoModel({this.id, this.header, this.content, this.isActive, this.channel, this.appInfoImages});
AppInfoModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
header = json['header'];
content = json['content'];
isActive = json['isActive'];
channel = json['channel'];
if (json['appInfoImages'] != null) {
appInfoImages = <ImageModel>[];
json['appInfoImages'].forEach((v) {
appInfoImages!.add(ImageModel.fromJson(v));
});
}
}
}

@ -0,0 +1,36 @@
import 'package:mc_common_app/view_models/ad_view_model.dart';
class ContactInfoModel {
int? id;
String? companyName;
String? mobileNo;
String? phoneNo;
String? email;
String? address;
String? latitude;
String? longitude;
int? channel;
bool? isActive;
List<ImageModel>? contactInfoImages;
ContactInfoModel({this.id, this.companyName, this.mobileNo, this.phoneNo, this.email, this.address, this.latitude, this.longitude, this.channel, this.isActive, this.contactInfoImages});
ContactInfoModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
companyName = json['companyName'];
mobileNo = json['mobileNo'];
phoneNo = json['phoneNo'];
email = json['email'];
address = json['address'];
latitude = json['latitude'];
longitude = json['longitude'];
channel = json['channel'];
isActive = json['isActive'];
if (json['contactInfoImages'] != null) {
contactInfoImages = <ImageModel>[];
json['contactInfoImages'].forEach((v) {
contactInfoImages!.add(ImageModel.fromJson(v));
});
}
}
}

@ -0,0 +1,32 @@
class FAQsModel {
int? id;
String? question;
String? answer;
int? sequenceNo;
bool? isActive;
int? channel;
bool? isCollapsed;
FAQsModel({this.id, this.question, this.answer, this.sequenceNo, this.isActive, this.channel, this.isCollapsed});
FAQsModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
question = json['question'];
answer = json['answer'];
sequenceNo = json['sequenceNo'];
isActive = json['isActive'];
channel = json['channel'];
isCollapsed = false;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['question'] = question;
data['answer'] = answer;
data['sequenceNo'] = sequenceNo;
data['isActive'] = isActive;
data['channel'] = channel;
return data;
}
}

@ -0,0 +1,27 @@
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/utils/enums.dart';
class ShippingRequestModel {
int? id;
int? requestID;
RequestModel? request;
int? shippingStatus;
ShippingRequestStatusEnum? shippingStatusEnum;
String? deliveredOn;
String? comment;
String? createdOn;
ShippingRequestModel({this.id, this.requestID, this.request, this.shippingStatus, this.deliveredOn, this.comment, this.createdOn});
ShippingRequestModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestID = json['requestID'];
request = json['request'];
shippingStatus = json['shippingStatus'];
shippingStatusEnum = json['shippingStatus'] != null ? (json['shippingStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.initiated;
deliveredOn = json['deliveredOn'];
comment = json['comment'];
createdOn = json['createdOn'];
}
}

@ -20,7 +20,7 @@ abstract class AdsRepo {
Future<GenericRespModel> createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew});
Future<List<AdDetailsModel>> getAllAds({required bool isMyAds});
Future<List<AdDetailsModel>> getAllAds({required bool isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum});
Future<List<AdDetailsModel>> getExploreAdsBasedOnFilters({List<String>? cityIdsList, List<String>? vehicleModelYearIdsList, List<String>? vehicleBrandIdsList, List<String>? createdByRolesIdsList});
@ -30,7 +30,7 @@ abstract class AdsRepo {
Future<AdsBankDetailsModel?> getAdBankingAccountInfo({required int adId});
Future<GenericRespModel> updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate});
Future<GenericRespModel> updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate, String? comment});
Future<GenericRespModel> createAppointmentForAdSpecialService({required int adId, required int photoOfficeID, required int photoOfficeSlotID, required int adsSpecialServiceID});
@ -140,16 +140,26 @@ class AdsRepoImp implements AdsRepo {
}
@override
Future<List<AdDetailsModel>> getAllAds({required isMyAds}) async {
var onlyMyAdsParams = {
Future<List<AdDetailsModel>> getAllAds({required isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum}) async {
Map<String, dynamic> onlyMyAdsParams = {
"userID": appState.getUser.data!.userInfo!.userId ?? "",
};
if (isMyAds && adPostStatus != null) {
onlyMyAdsParams.addAll({
"AdsStatuses": ["${adPostStatus.getIdFromAdPostStatusEnum()}"]
});
}
var allAdsParams = {
"AdsStatuses": ["${AdPostStatus.active.getIdFromAdPostStatusEnum()}"], //only Active ADS
"isActive": "true", //only Active ADS
"isExplore": "true"
};
if (!isMyAds && createdByRoleEnum != null) {
allAdsParams.addAll({
"CreatedByRoles": ["${createdByRoleEnum.getIdFromCreatedByRoleEnum()}"],
});
}
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),
@ -263,11 +273,11 @@ class AdsRepoImp implements AdsRepo {
}
@override
Future<GenericRespModel> updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate}) async {
Future<GenericRespModel> updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate, String? comment}) async {
var postParams = {
"id": adId,
"status": adStatusToUpdate.getIdFromAdPostStatusEnum().toString(),
"comment": "",
"comment": comment ?? "",
};
String token = appState.getUser.data!.accessToken ?? "";

@ -4,11 +4,13 @@ import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/models/appointments_models/schedule_model.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/services.dart';
import 'package:mc_common_app/utils/enums.dart';
abstract class AppointmentRepo {
Future<List<AppointmentListModel>> getMyAppointmentsForProvider(Map<String, dynamic> map);
@ -20,7 +22,7 @@ abstract class AppointmentRepo {
List<String>? branchIdsList,
});
Future<GenericRespModel> updateAppointmentStatus(Map<String, dynamic> map);
Future<GenericRespModel> updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum});
Future<GenericRespModel> updateAppointmentPaymentStatus(Map<String, dynamic> map);
@ -240,9 +242,13 @@ class AppointmentRepoImp implements AppointmentRepo {
}
@override
Future<GenericRespModel> updateAppointmentStatus(Map<String, dynamic> map) async {
Future<GenericRespModel> updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum}) async {
String t = appState.getUser.data!.accessToken ?? "";
return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.updateAppointmentStatus, map, token: t);
var params = {
"appointmentID": appointmentId.toString(),
"appointmentStatusID": appointmentStatusEnum.getIdFromAppointmentStatusEnum().toString(),
};
return await apiClient.postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.updateAppointmentStatus, params, token: t);
}
@override

@ -196,7 +196,14 @@ class BranchRepoImp implements BranchRepo {
List<Map<String, dynamic>> map = [];
for (int i = 0; i < documents!.length; i++) {
if (documents[i].document != null) {
var postParams = {"id": documents[i].id, "serviceProviderID": documents[i].serviceProviderId, "documentID": documents[i].documentId, "documentExt": documents[i].fileExt, "documentImage": documents[i].document, "isActive": true};
var postParams = {
"id": documents[i].id,
"serviceProviderID": documents[i].serviceProviderId,
"documentID": documents[i].documentId,
"documentExt": documents[i].fileExt,
"documentImage": documents[i].document,
"isActive": true,
};
map.add(postParams);
}
}

@ -33,6 +33,15 @@ abstract class ChatRepo {
int pageIndex = 0,
int pageSize = 0,
});
Future<List<ChatMessageModel>> getUsersChatMessagesForGeneralChat({
String? userID,
int? adID,
int? adsChatBuyerId,
required bool isForBuyer,
int pageIndex = 0,
int pageSize = 0,
});
}
class ChatRepoImp implements ChatRepo {
@ -66,6 +75,8 @@ class ChatRepoImp implements ChatRepo {
url = ApiConsts.messageIsReadUpdateForAds;
} else if (chatTypeEnum == ChatTypeEnum.requestOffer) {
url = ApiConsts.messageIsReadUpdateForRequests;
} else if (chatTypeEnum == ChatTypeEnum.general) {
url = ApiConsts.messageIsReadUpdateForRequests;
}
var queryParameters = {"iDs": messageIds};
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
@ -144,4 +155,38 @@ class ChatRepoImp implements ChatRepo {
}
return [];
}
@override
Future<List<ChatMessageModel>> getUsersChatMessagesForGeneralChat({String? userID, int? adID, int? adsChatBuyerId, required bool isForBuyer, int pageIndex = 0, int pageSize = 0}) async {
var parameterForBuyer = {
"UserID": userID.toString(),
"AdsID": adID.toString(),
"IsAdsBuyer": isForBuyer.toString(),
"PageSize": pageSize.toString(),
"PageIndex": pageIndex.toString(),
};
var parameterForSeller = {
"AdsChatBuyerID": adsChatBuyerId.toString(),
"IsAdsBuyer": isForBuyer.toString(),
"PageSize": pageSize.toString(),
"PageIndex": pageIndex.toString(),
};
var queryParameters = isForBuyer ? parameterForBuyer : parameterForSeller;
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.getChatMessagesForAds,
queryParameters: queryParameters,
token: appState.getUser.data!.accessToken,
);
if (genericRespModel.messageStatus == 1 && genericRespModel.data != null) {
List<ChatMessageModel> chatMessages = List.generate(
genericRespModel.data.length,
(index) => ChatMessageModel.fromJson(genericRespModel.data[index]),
);
return chatMessages;
}
return [];
}
}

@ -11,6 +11,7 @@ import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/user_models/cities.dart';
import 'package:mc_common_app/models/user_models/country.dart';
import 'package:mc_common_app/models/user_models/role.dart';
import 'package:mc_common_app/utils/utils.dart';
abstract class CommonRepo {
Future<Country> getAllCountries();
@ -129,6 +130,22 @@ class CommonRepoImp implements CommonRepo {
{"id": 2, "enumTypeID": 0, "enumValueStr": "Used", "enumValue": 2, "isActive": true},
],
};
List<EnumsModel> enums = List.generate((json['data'] as List).length, (index) => EnumsModel.fromJson(json['data'][index]));
return enums;
}
if (enumTypeID == -2) {
// This is for the ShippingRequestStatus which will be added to API later
final Map<String, dynamic> json = {
"data": [
{"id": 1, "enumTypeID": 0, "enumValueStr": "Initiated", "enumValue": 1, "isActive": true},
{"id": 2, "enumTypeID": 0, "enumValueStr": "In Transit", "enumValue": 2, "isActive": true},
{"id": 3, "enumTypeID": 0, "enumValueStr": "Out for Delivery", "enumValue": 3, "isActive": true},
{"id": 4, "enumTypeID": 0, "enumValueStr": "Delivered", "enumValue": 4, "isActive": true},
],
};
List<EnumsModel> enums = List.generate((json['data'] as List).length, (index) => EnumsModel.fromJson(json['data'][index]));
return enums;
}

@ -20,7 +20,6 @@ class ItemsRepoImp implements ItemsRepo {
@override
Future<GenericRespModel> createServiceItems(Map map) async {
String t = AppState().getUser.data!.accessToken ?? "";
debugPrint(t);
return await injector.get<ApiClient>().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.createItems, map, token: t);
}
@ -30,7 +29,6 @@ class ItemsRepoImp implements ItemsRepo {
"ServiceProviderServiceID": serviceId.toString(),
};
String? token = AppState().getUser.data?.accessToken;
debugPrint(token);
return await injector
.get<ApiClient>()
.getJsonForObject((json) => ItemModel.fromJson(json), ApiConsts.getServiceItems, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? "");
@ -39,7 +37,6 @@ class ItemsRepoImp implements ItemsRepo {
@override
Future<GenericRespModel> updateServiceItem(Map map) async {
String t = AppState().getUser.data!.accessToken ?? "";
debugPrint(t);
return await injector.get<ApiClient>().postJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.updateServiceItem, map, token: t);
}

@ -2,11 +2,15 @@ import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/payment_models/pay_order_detail_resp_model.dart';
import 'package:mc_common_app/utils/enums.dart';
abstract class PaymentsRepo {
Future<PayOrderDetailRespModel> getPayOrderDetails({required int paymentId, required int adId});
Future<bool> verifyPayment({required int paymentTypeId, required int id, bool isForAppointments = false, List<int>? appointmentIds});
Future<PayOrderDetailRespModel> getPayOrderDetails({required int paymentTypeId, required int adId});
}
class PaymentsRepoImp implements PaymentsRepo {
@ -14,9 +18,67 @@ class PaymentsRepoImp implements PaymentsRepo {
AppState appState = injector.get<AppState>();
@override
Future<PayOrderDetailRespModel> getPayOrderDetails({required int paymentId, required int adId}) async {
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.payForOrderDetailGet);
Future<PayOrderDetailRespModel> getPayOrderDetails({required int paymentTypeId, required int adId}) async {
var queryParameters = {
"AdsID": adId.toString(),
"PaymentType": paymentTypeId.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
queryParameters: queryParameters,
(json) => GenericRespModel.fromJson(json),
ApiConsts.payForOrderDetailGet,
);
PayOrderDetailRespModel payOrderDetailRespModel = PayOrderDetailRespModel.fromJson(adsGenericModel.data);
return payOrderDetailRespModel;
}
String getKeyByPaymentType({required int paymentType}) {
PaymentTypes paymentTypeEnum = paymentType.getPaymentTypeFromId();
switch (paymentTypeEnum) {
case PaymentTypes.subscription:
return "SubscriptionAppliedID";
case PaymentTypes.appointment:
case PaymentTypes.partialAppointment:
return "appointmentIds";
case PaymentTypes.ads:
case PaymentTypes.adReserve:
case PaymentTypes.extendAds:
return "AdsID";
case PaymentTypes.request:
return "RequestID";
}
}
@override
Future<bool> verifyPayment({required int paymentTypeId, required int id, bool isForAppointments = false, List<int>? appointmentIds}) async {
Map<String, dynamic> queryParameters = {
"ID": id.toString(),
"PaymentType": paymentTypeId.toString(),
};
if (isForAppointments) {
List<String> ids = [];
if (appointmentIds != null) {
for (var element in appointmentIds) {
ids.add(element.toString());
}
}
queryParameters.removeWhere((key, value) => key == "ID");
queryParameters.addAll({"IDs": ids});
}
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
queryParameters: queryParameters,
(json) => GenericRespModel.fromJson(json),
ApiConsts.payForOrderIsPaidGet,
);
if (adsGenericModel.data == null) {
return false;
}
bool isPaid = adsGenericModel.data['isPaid'] ?? false;
return isPaid;
}
}

@ -0,0 +1,84 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart';
import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart';
import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart';
import 'package:mc_common_app/utils/utils.dart';
abstract class SettingOptionsRepo {
Future<List<FAQsModel>> getAllFaqs();
Future<List<ContactInfoModel>> getAllContactInfos();
Future<List<AppInfoModel>> getAppInfoList();
}
class SettingOptionsRepoImp extends SettingOptionsRepo {
ApiClient apiClient = injector.get<ApiClient>();
AppState appState = injector.get<AppState>();
@override
Future<List<FAQsModel>> getAllFaqs() async {
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.getAllFAQs,
token: token,
);
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<FAQsModel> list = List.generate(genericRespModel.data.length, (index) => FAQsModel.fromJson(genericRespModel.data[index]));
return list;
}
@override
Future<List<ContactInfoModel>> getAllContactInfos() async {
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.getContactInfo,
token: token,
);
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<ContactInfoModel> list = List.generate(genericRespModel.data.length, (index) => ContactInfoModel.fromJson(genericRespModel.data[index]));
return list;
}
@override
Future<List<AppInfoModel>> getAppInfoList() async {
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.getAppInfo,
token: token,
);
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<AppInfoModel> list = List.generate(genericRespModel.data.length, (index) => AppInfoModel.fromJson(genericRespModel.data[index]));
return list;
}
}

@ -0,0 +1,72 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/shipping_models/shipping_status_model.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
abstract class ShippingRepo {
Future<List<ShippingRequestModel>> getShippingRequestListByStatus({ShippingRequestStatusEnum? shippingStatusEnum, int? requestId});
Future<GenericRespModel> updateShippingRequestStatus({required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId, String? comment});
}
class ShippingRepoImp extends ShippingRepo {
ApiClient apiClient = injector.get<ApiClient>();
AppState appState = injector.get<AppState>();
@override
Future<List<ShippingRequestModel>> getShippingRequestListByStatus({ShippingRequestStatusEnum? shippingStatusEnum, int? requestId}) async {
String token = appState.getUser.data!.accessToken ?? "";
Map<String, String> queryParameters = {
"requestID": "${requestId ?? 0}",
"shippingStatus": "${shippingStatusEnum != null ? shippingStatusEnum.getIdFromShippingStatusEnum() : 0}",
};
GenericRespModel genericRespModel = await injector.get<ApiClient>().postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.shippingRequestStatusGet,
queryParameters,
token: token,
);
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<ShippingRequestModel> list = List.generate(genericRespModel.data.length, (index) => ShippingRequestModel.fromJson(genericRespModel.data[index]));
return list;
}
@override
Future<GenericRespModel> updateShippingRequestStatus({required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId, String? comment}) async {
String token = appState.getUser.data!.accessToken ?? "";
Map<String, String> queryParameters = {
"id": "$shippingRequestId",
"shippingStatus": "${shippingStatusEnum.getIdFromShippingStatusEnum()}",
"comment": comment ?? "",
};
GenericRespModel genericRespModel = await injector.get<ApiClient>().postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.shippingRequestStatusUpdate,
queryParameters,
token: token,
);
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
}
return genericRespModel;
}
}

@ -79,7 +79,7 @@ class CommonServicesImp implements CommonAppServices {
final status = await AppPermissions.checkStoragePermissions(context);
if (status) {
result = await FilePicker.platform.pickFiles(allowMultiple: true, type: fileType, allowedExtensions: allowedExtensions);
result = await FilePicker.platform.pickFiles(allowMultiple: false, type: fileType, allowedExtensions: allowedExtensions);
}
List<File> pickedFiles = [];
@ -109,6 +109,4 @@ class CommonServicesImp implements CommonAppServices {
return 0.0;
}
}
}

@ -26,36 +26,16 @@ abstract class PaymentService {
class PaymentServiceImp implements PaymentService {
MyInAppBrowser? myInAppBrowser;
var inAppBrowserOptions = InAppBrowserClassOptions(
inAppWebViewGroupOptions: InAppWebViewGroupOptions(crossPlatform: InAppWebViewOptions(useShouldOverrideUrlLoading: true, transparentBackground: false), ios: IOSInAppWebViewOptions(applePayAPIEnabled: true)),
crossPlatform: InAppBrowserOptions(hideUrlBar: true, toolbarTopBackgroundColor: Colors.black),
android: AndroidInAppBrowserOptions(),
ios: IOSInAppBrowserOptions(hideToolbarBottom: true, toolbarBottomBackgroundColor: Colors.white, closeButtonColor: Colors.white, presentationStyle: IOSUIModalPresentationStyle.OVER_FULL_SCREEN));
var inAppBrowserOptions = InAppBrowserClassSettings(
webViewSettings: InAppWebViewSettings(
useShouldOverrideUrlLoading: false,
transparentBackground: false,
isInspectable: false,
applePayAPIEnabled: true,
cacheEnabled: false,
),
browserSettings: InAppBrowserSettings(
hideUrlBar: true,
hideTitleBar: true,
hideDefaultMenuItems: false,
toolbarBottomBackgroundColor: Colors.black,
closeButtonColor: Colors.white,
presentationStyle: ModalPresentationStyle.OVER_FULL_SCREEN,
// toolbarTopBackgroundColor: Colors.black
),
);
@override
Future<void> placePayment({
required int id,
List<int>? appointmentIds,
required PaymentTypes paymentType,
required Function() onSuccess,
required Function() onFailure,
}) async {
String getUrlRequestByPaymentId({required int id, List<int>? appointmentIds, required PaymentTypes paymentType}) {
String urlRequest = "";
int customerId = AppState().getUser.data!.userInfo!.customerId ?? 0;
switch (paymentType) {
case PaymentTypes.subscription:
urlRequest = "${ApiConsts.paymentWebViewUrl}?PaymentType=${paymentType.getIdFromPaymentTypesEnum()}&OrderProviderSubscriptionID=$id";
@ -86,24 +66,29 @@ class PaymentServiceImp implements PaymentService {
urlRequest = "${ApiConsts.paymentWebViewUrl}?PaymentType=${paymentType.getIdFromPaymentTypesEnum()}&CustomerID=$customerId&RequestID=$id";
break;
}
return urlRequest;
}
@override
Future<void> placePayment({
required int id,
List<int>? appointmentIds,
required PaymentTypes paymentType,
required Function() onSuccess,
required Function() onFailure,
}) async {
String urlRequest = getUrlRequestByPaymentId(id: id, paymentType: paymentType, appointmentIds: appointmentIds);
log("PaymentUrl: $urlRequest");
myInAppBrowser = MyInAppBrowser(
onExitCallback: () {
log("Browser Exited");
},
onLoadStartCallback: (String url) {
log("Browser LoadStart for : $url");
onBrowserLoadStart(onFailure: onFailure, onSuccess: onSuccess, url: url);
},
);
myInAppBrowser = MyInAppBrowser(onExitCallback: () {
log("Browser Exited");
}, onLoadStartCallback: (String url) {
log("Browser LoadStart for : $url");
onBrowserLoadStart(onFailure: onFailure, onSuccess: onSuccess, url: url);
});
await myInAppBrowser!.openUrlRequest(
urlRequest: URLRequest(
url: WebUri('${Uri.parse(urlRequest)}', forceToStringRawValue: true),
allowsCellularAccess: true,
allowsConstrainedNetworkAccess: true,
allowsExpensiveNetworkAccess: true,
),
settings: inAppBrowserOptions,
urlRequest: URLRequest(url: Uri.parse(urlRequest)),
options: inAppBrowserOptions,
);
}

@ -16,6 +16,7 @@ class AppEnums {
static const int appointmentsFilterEnumId = 13; // Appointments Filter Enums
static const int requestStatusesFilterEnumId = 15; // Appointments Filter Enums
static const int conditionEnumId = -1; // to get the Condition Filter Enums
static const int shippingStatusEnumId = -2; // to get the Shipping Filter Enums
}
enum VehicleType {
@ -78,7 +79,15 @@ enum PaymentMethods {
tamara,
}
enum PaymentTypes { subscription, appointment, adReserve, ads, request, extendAds, partialAppointment }
enum PaymentTypes {
subscription,
appointment,
adReserve,
ads,
request,
extendAds,
partialAppointment,
}
enum AdCreationSteps {
vehicleDetails,
@ -138,6 +147,11 @@ enum BranchStatusEnum {
deactivated // 6
}
enum AppointmentTypeEnum {
workshop,
home,
}
enum AppointmentStatusEnum {
booked,
confirmed,
@ -197,3 +211,11 @@ enum SubscriptionActionTypeEnum {
users,
branches,
}
enum ShippingRequestStatusEnum {
allRequests,
initiated,
inTransit,
outForDelivery,
delivered,
}

@ -8,6 +8,7 @@ import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:path/path.dart' as p;
import 'package:fluttertoast/fluttertoast.dart';
@ -196,7 +197,7 @@ class Utils {
return MyColors.adPendingStatusColor;
case AdPostStatus.pendingForPayment:
return MyColors.adPendingStatusColor;
return MyColors.submittedColor;
case AdPostStatus.rejected:
return MyColors.adCancelledStatusColor;
@ -205,10 +206,10 @@ class Utils {
return MyColors.adCancelledStatusColor;
case AdPostStatus.pendingForPost:
return MyColors.adPendingStatusColor;
return MyColors.inProgressColor;
case AdPostStatus.active:
return MyColors.adActiveStatusColor;
return MyColors.greenColor;
case AdPostStatus.expired:
return MyColors.adCancelledStatusColor;
@ -249,6 +250,80 @@ class Utils {
}
}
static Color getChipColorByRequestOfferStatusEnum(RequestOfferStatusEnum requestOfferStatusEnum) {
switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer:
return MyColors.pendingColor;
case RequestOfferStatusEnum.negotiate:
return MyColors.submittedColor;
case RequestOfferStatusEnum.accepted:
return MyColors.completedColor;
case RequestOfferStatusEnum.rejected:
return MyColors.cancelledColor;
case RequestOfferStatusEnum.cancel:
return MyColors.redColor;
}
}
static String getNameByRequestOfferStatusEnum(RequestOfferStatusEnum requestOfferStatusEnum) {
switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer:
return "Pending";
case RequestOfferStatusEnum.negotiate:
return "Negotiate";
case RequestOfferStatusEnum.accepted:
return "Accepted";
case RequestOfferStatusEnum.rejected:
return "Rejected";
case RequestOfferStatusEnum.cancel:
return "Cancelled";
}
}
static String getNameByShippingRequestStatusEnum(ShippingRequestStatusEnum shippingRequestStatusEnum) {
switch (shippingRequestStatusEnum) {
case ShippingRequestStatusEnum.allRequests:
return "All Requests";
case ShippingRequestStatusEnum.initiated:
return "Initiated";
case ShippingRequestStatusEnum.inTransit:
return "In Transit";
case ShippingRequestStatusEnum.outForDelivery:
return "Out For Delivery";
case ShippingRequestStatusEnum.delivered:
return "Delivered";
}
}
static Color getChipColorByShippingRequestStatusEnum(ShippingRequestStatusEnum shippingRequestStatusEnum) {
switch (shippingRequestStatusEnum) {
case ShippingRequestStatusEnum.allRequests:
return MyColors.submittedColor;
case ShippingRequestStatusEnum.initiated:
return MyColors.submittedColor;
case ShippingRequestStatusEnum.inTransit:
return MyColors.shippingColor;
case ShippingRequestStatusEnum.outForDelivery:
return MyColors.deliveryColor;
case ShippingRequestStatusEnum.delivered:
return MyColors.greenColor;
}
}
static Color getChipColorByBranchStatus(BranchStatusEnum branchStatusEnum) {
switch (branchStatusEnum) {
case BranchStatusEnum.pending:
@ -498,4 +573,16 @@ class Utils {
actionButtonNo: const SizedBox(),
);
}
static Widget buildStatusContainer(String text) {
return Center(
child: text.toText(color: MyColors.lightTextColor, fontSize: 14),
).toContainer(
marginAll: 8,
paddingAll: 15,
borderRadius: 8,
width: double.infinity,
backgroundColor: MyColors.grey98Color.withOpacity(0.1),
);
}
}

@ -198,7 +198,7 @@ class AdVM extends BaseVM {
notifyListeners();
}
applyFilterOnExploreAds({required CreatedByRoleEnum createdByRoleFilter}) {
applyFilterOnExploreAds({required CreatedByRoleEnum createdByRoleFilter}) async {
if (exploreAdsFilterOptions.isEmpty) return;
int index = exploreAdsFilterOptions.indexWhere((element) => element.id.toCreatedByRoleEnum() == createdByRoleFilter);
@ -211,13 +211,18 @@ class AdVM extends BaseVM {
notifyListeners();
return;
}
exploreAdsFilteredList = exploreAds.where((element) => element.createdByRoleEnum == createdByRoleFilter).toList();
setState(ViewState.busy);
exploreAdsFilteredList = await getAdsByFilter(createdByRoleEnum: createdByRoleFilter, isMyAds: false);
setState(ViewState.idle);
notifyListeners();
}
applyFilterOnMyAds({required AdPostStatus adPostStatusEnum}) {
Future<List<AdDetailsModel>> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum}) async {
return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum);
}
applyFilterOnMyAds({required AdPostStatus adPostStatusEnum}) async {
if (myAdsFilterOptions.isEmpty) return;
int index = myAdsFilterOptions.indexWhere((element) => element.id.toAdPostEnum() == adPostStatusEnum);
@ -240,8 +245,9 @@ class AdVM extends BaseVM {
notifyListeners();
return;
}
myAdsFilteredList = myAds.where((element) => element.statusID! == adPostStatusEnum.getIdFromAdPostStatusEnum()).toList();
setState(ViewState.busy);
myAdsFilteredList = await getAdsByFilter(adPostStatus: adPostStatusEnum, isMyAds: true);
setState(ViewState.idle);
notifyListeners();
}
@ -323,7 +329,7 @@ class AdVM extends BaseVM {
return;
}
Utils.hideLoading(context);
Utils.showToast(respModel.message ?? "A has been marked as sold successfully!");
Utils.showToast(respModel.message ?? "Ad has been marked as sold successfully!");
updateIsExploreAds(false);
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.sold); //pending for review
navigateReplaceWithName(context, AppRoutes.dashboard);
@ -361,9 +367,9 @@ class AdVM extends BaseVM {
navigateReplaceWithName(context, AppRoutes.dashboard);
}
Future<void> deactivateTheAd(BuildContext context, {required int adId}) async {
Future<void> deactivateTheAd(BuildContext context, {required int adId, String? comment}) async {
Utils.showLoading(context);
GenericRespModel respModel = await adsRepo.updateAdStatus(adId: adId, adStatusToUpdate: AdPostStatus.cancelled);
GenericRespModel respModel = await adsRepo.updateAdStatus(adId: adId, adStatusToUpdate: AdPostStatus.cancelled, comment: comment);
if (respModel.messageStatus != 1) {
Utils.hideLoading(context);
@ -372,8 +378,9 @@ class AdVM extends BaseVM {
}
Utils.hideLoading(context);
Utils.showToast(LocaleKeys.adDeactivatedSuccessfully.tr());
updateDeactivateAdReasonDescription('');
updateIsExploreAds(false);
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.cancelled); //pending for review
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.cancelled); //Cacncelled
navigateReplaceWithName(context, AppRoutes.dashboard);
}
@ -580,6 +587,76 @@ class AdVM extends BaseVM {
reservationCancelReason = value;
}
List<OfferRequestCommentModel> deActivateAdModelList = [
OfferRequestCommentModel(
index: 0,
isSelected: true,
title: LocaleKeys.changedMind.tr(),
),
OfferRequestCommentModel(
index: 1,
isSelected: false,
title: LocaleKeys.veryHighPrice.tr(),
),
OfferRequestCommentModel(
index: 2,
isSelected: false,
title: LocaleKeys.alreadySold.tr(),
),
OfferRequestCommentModel(
index: 3,
isSelected: false,
title: LocaleKeys.customerNotResponding.tr(),
),
OfferRequestCommentModel(
index: 4,
isSelected: false,
title: LocaleKeys.otherVar.tr(),
),
];
OfferRequestCommentModel selectedDeActivateAdCommentModel = OfferRequestCommentModel(
index: 0,
isSelected: true,
title: LocaleKeys.changedMind.tr(),
);
void updateSelectionInDeActivateAdModelList(int index) {
for (var value in deActivateAdModelList) {
value.isSelected = false;
}
selectedDeActivateAdCommentModel = deActivateAdModelList[index];
deActivateAdModelList[index].isSelected = true;
notifyListeners();
}
String deactivateAdReasonDescription = "";
String deactivateAdReasonDescriptionError = "";
void updateDeactivateAdReasonDescription(String value) {
deactivateAdReasonDescription = value;
if (value.isNotEmpty) {
deactivateAdReasonDescriptionError = "";
}
}
bool isDeActivateReasonSubmitValidated() {
if (selectedDeActivateAdCommentModel.index != deActivateAdModelList.length - 1) {
return true;
}
bool isValidated = true;
if (deactivateAdReasonDescription.isEmpty) {
deactivateAdReasonDescriptionError = GlobalConsts.descriptionError;
isValidated = false;
} else {
deactivateAdReasonDescriptionError = "";
}
notifyListeners();
return isValidated;
}
String completeDealNotesForAdmin = "";
void updateCompleteDealNotesForAdmin(String value) {
@ -1909,6 +1986,12 @@ class ImageModel {
ImageModel({this.id, this.filePath, this.isFromNetwork});
ImageModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
filePath = json['image'];
isFromNetwork = true;
}
@override
String toString() {
return 'ImageModel{id: $id, isFromNetwork: $isFromNetwork, filePath: $filePath}';

@ -2,6 +2,7 @@ import 'dart:developer';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
@ -225,6 +226,8 @@ class AppointmentsVM extends BaseVM {
branchSelectedServiceId = id;
currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId);
log("currentServiceSelection isAllowAppointmentHome: ${currentServiceSelection!.isAllowAppointmentHome}");
log("currentServiceSelection serviceDescription: ${currentServiceSelection!.serviceDescription}");
notifyListeners();
}
@ -382,10 +385,10 @@ class AppointmentsVM extends BaseVM {
setState(ViewState.idle);
}
updateAppointmentStatus(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum, bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
try {
GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentStatus(map);
GenericRespModel genericRespModel = await appointmentRepo.updateAppointmentStatus(appointmentId: appointmentId, appointmentStatusEnum: appointmentStatusEnum);
if (genericRespModel.messageStatus == 1) {
Utils.showToast(LocaleKeys.appointmentStatusUpdated.tr());
@ -1496,4 +1499,44 @@ class AppointmentsVM extends BaseVM {
}
setState(ViewState.idle);
}
// Chat Related
Future<void> getGeneralChatsListForProvider({required BuildContext context}) async {
try {
// Utils.showLoading(context);
// List<BuyersChatForAdzsModel> buyersChatList = await appointmentRepo.getChatBuyersForAds(adsID: adsID);
// Utils.hideLoading(context);
// buyersChatListForAds.clear();
// buyersChatListForAds = buyersChatList;
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
}
}
void onGeneralChatMessagesButtonPressed({required BuildContext context}) async {
final myUserID = AppState().getUser.data!.userInfo!.userId;
// if (adDetailsModel.isMyAd != null && adDetailsModel.isMyAd!) {
// await getGeneralChatsListForProvider(context: context);
// // navigateWithName(context, AppRoutes.generalChatsListForProvider, arguments: buyersChatListForAds);
// } else {
// ChatViewArgumentsForAd chatViewArgumentsForAd = ChatViewArgumentsForAd(receiverUserID: adDetailsModel.userID, adsID: adDetailsModel.id);
// ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.ads, chatViewArgumentsForAd: chatViewArgumentsForAd);
// final chatVM = context.read<ChatVM>();
// await chatVM
// .getUsersChatMessagesForAd(
// context: context,
// isForBuyer: true,
// adsChatBuyerId: 1,
// adID: adDetailsModel.id,
// userID: myUserID,
// senderName: adDetailsModel.adOwnerName,
// )
// .whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments));
// }
}
}

@ -1,7 +1,8 @@
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/utils/enums.dart';
class BaseVM extends ChangeNotifier {
ViewState _state = ViewState.idle;
bool isInternetConnection = true;

@ -193,14 +193,6 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
}
Future<void> onNewMessageReceivedForAds({required List<ChatMessageModel> messages}) async {
for (var msg in messages) {
currentMessagesForAds.add(msg);
}
scrollChatDown();
notifyListeners();
}
void subscribeToReceiveRequestOfferMessages(BuildContext context) {
hubConnection!.on(SignalrConsts.receiveMessageRequestOffer, (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
@ -215,18 +207,19 @@ class ChatVM extends ChangeNotifier {
});
}
void subscribeToReceiveAdMessages(BuildContext context) {
hubConnection!.on(SignalrConsts.receiveMessageAds, (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];
for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>);
chat.add(chatMessage);
}
onNewMessageReceivedForAds(messages: chat);
logger.i(arguments);
// Utils.showToast(arguments.toString());
});
Future<bool> markMessagesAsRead(BuildContext context, List<int> messageIds, ChatTypeEnum chatTypeEnum) async {
bool status = false;
try {
// Utils.showLoading(context);
status = await chatRepo.markMessageAsRead(messageIds: messageIds, chatTypeEnum: chatTypeEnum);
// Utils.hideLoading(context);
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
// Utils.hideLoading(context);
status = false;
}
return status;
}
Future<void> buildHubConnection(BuildContext context) async {
@ -236,6 +229,7 @@ class ChatVM extends ChangeNotifier {
await hubConnection!.start();
subscribeToReceiveRequestOfferMessages(context);
subscribeToReceiveAdMessages(context);
subscribeToReceiveGeneralMessages(context);
hubConnection!.onclose((exception) {
logger.i("onClose: ${exception.toString()}");
buildHubConnection(context);
@ -303,11 +297,7 @@ class ChatVM extends ChangeNotifier {
MessageImageModel convertFileToMessageImageModel({required File file}) {
List<int> imageBytes = file.readAsBytesSync();
String image = base64Encode(imageBytes);
MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image,
id: 0,
isFromNetwork: false,
reqOfferID: latestOfferId,
imagePath: file.path);
MessageImageModel vehiclePostingImages = MessageImageModel(imageStr: image, id: 0, isFromNetwork: false, reqOfferID: latestOfferId, imagePath: file.path);
return vehiclePostingImages;
}
@ -376,7 +366,7 @@ class ChatVM extends ChangeNotifier {
"ReceiverUserID": receiverId,
"SenderUserID": userId,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"ChatText": message,
"ChatText": chatMessageType == ChatMessageTypeEnum.image ? "${messageImages.length} Image(s)" : message,
"RequestID": requestId,
"ReqOfferID": latestOfferId,
};
@ -413,10 +403,7 @@ class ChatVM extends ChangeNotifier {
serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel);
}
} else {
int providerIndex = context
.read<RequestsVM>()
.myFilteredRequests
.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId));
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId));
log("providerIndex2:$providerIndex");
if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex);
@ -444,7 +431,7 @@ class ChatVM extends ChangeNotifier {
}
}
Future<void> getUsersChatMessagesForCustomer({
Future<void> getRequestsChatMessagesForCustomer({
required BuildContext context,
required int providerId,
required int requestOfferId,
@ -468,6 +455,17 @@ class ChatVM extends ChangeNotifier {
}
}
}
List<int> unreadMessageIds = [];
for (var msg in chatMessages) {
if (!msg.isRead! && !msg.isMyMessage!) {
unreadMessageIds.add(msg.id!);
}
}
if (unreadMessageIds.isNotEmpty) {
await markMessagesAsRead(context, unreadMessageIds, ChatTypeEnum.requestOffer);
}
Utils.hideLoading(context);
notifyListeners();
} catch (e) {
@ -477,7 +475,7 @@ class ChatVM extends ChangeNotifier {
}
}
Future<void> getUsersChatMessagesForProvider({
Future<void> getRequestsChatMessagesForProvider({
required BuildContext context,
required int customerId,
required int requestOfferId,
@ -489,6 +487,15 @@ class ChatVM extends ChangeNotifier {
Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessagesForRequests(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
context.read<RequestsVM>().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex, context: context);
List<int> unreadMessageIds = [];
for (var msg in chatMessages) {
if (!msg.isRead! && !msg.isMyMessage!) {
unreadMessageIds.add(msg.id!);
}
}
if (unreadMessageIds.isNotEmpty) {
await markMessagesAsRead(context, unreadMessageIds, ChatTypeEnum.requestOffer);
}
Utils.hideLoading(context);
} catch (e) {
logger.i(e.toString());
@ -578,7 +585,7 @@ class ChatVM extends ChangeNotifier {
for (var msg in currentMessagesForAds) {
msg.senderName = senderName ?? "";
if (!msg.isRead!) {
if (!msg.isRead! && !msg.isMyMessage!) {
unreadMessageIds.add(msg.id!);
}
}
@ -595,22 +602,137 @@ class ChatVM extends ChangeNotifier {
}
}
Future<bool> markMessagesAsRead(BuildContext context, List<int> messageIds, ChatTypeEnum chatTypeEnum) async {
bool status = false;
void subscribeToReceiveAdMessages(BuildContext context) {
hubConnection!.on(SignalrConsts.receiveMessageAds, (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];
for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>);
chat.add(chatMessage);
}
onNewMessageReceivedForAds(messages: chat);
logger.i(arguments);
// Utils.showToast(arguments.toString());
});
}
Future<void> onNewMessageReceivedForAds({required List<ChatMessageModel> messages}) async {
for (var msg in messages) {
currentMessagesForAds.add(msg);
}
scrollChatDown();
notifyListeners();
}
Future<bool> onTextMessageSendForAds({
required String receiverId,
required ChatMessageTypeEnum chatMessageType,
required String message,
required int adId,
required BuildContext context,
}) async {
if (message.isEmpty) return false;
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
logger.i("Hub Not Connected!!");
await buildHubConnection(context);
}
if (hubConnection!.state == HubConnectionState.connected) {
final userId = AppState().getUser.data!.userInfo!.userId.toString();
final name = AppState().getUser.data!.userInfo!.firstName.toString();
final obj = <String, dynamic>{
"ReceiverUserID": receiverId,
// "SenderUserID": userId,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"ChatText": message,
"AdsID": adId,
};
logger.i("$obj");
hubConnection!.invoke(
SignalrConsts.sendMessageAds,
args: <Object>[obj],
).catchError((e) {
logger.i("error in invoking SendMessage: ${e.toString()}");
Utils.showToast(e.toString());
return false;
});
ChatMessageModel chatMessageModel = ChatMessageModel(
messageType: chatMessageType.getIdFromChatMessageTypeEnum(),
chatText: message,
isMyMessage: true,
senderName: name,
senderUserID: userId,
chatMessageTypeEnum: ChatMessageTypeEnum.freeText,
receiverUserID: receiverId,
);
currentMessagesForAds.add(chatMessageModel);
notifyListeners();
if (AppState().currentAppType == AppType.customer) {
} else {}
return true;
}
return false;
}
// ========================General ==================
List<ChatMessageModel> currentMessagesForGeneralChat = [];
Future<void> getUsersChatMessagesForGeneralChat({required BuildContext context, int? adID, int? adsChatBuyerId, String? userID, String? senderName, required bool isForBuyer}) async {
try {
Utils.showLoading(context);
status = await chatRepo.markMessageAsRead(messageIds: messageIds, chatTypeEnum: chatTypeEnum);
currentMessagesForGeneralChat = await chatRepo.getUsersChatMessagesForGeneralChat(userID: userID, adID: adID, isForBuyer: isForBuyer, adsChatBuyerId: adsChatBuyerId);
Utils.hideLoading(context);
List<int> unreadMessageIds = [];
for (var msg in currentMessagesForGeneralChat) {
msg.senderName = senderName ?? "";
if (!msg.isRead! && !msg.isMyMessage!) {
unreadMessageIds.add(msg.id!);
}
}
if (unreadMessageIds.isNotEmpty) {
await markMessagesAsRead(context, unreadMessageIds, ChatTypeEnum.general);
}
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
status = false;
}
return status;
}
Future<bool> onTextMessageSendForAds({
Future<void> onNewMessageReceivedForGeneral({required List<ChatMessageModel> messages}) async {
for (var msg in messages) {
currentMessagesForAds.add(msg);
}
scrollChatDown();
notifyListeners();
}
void subscribeToReceiveGeneralMessages(BuildContext context) {
hubConnection!.on(SignalrConsts.receiveMessageGeneral, (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];
for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>);
chat.add(chatMessage);
}
onNewMessageReceivedForGeneral(messages: chat);
logger.i(arguments);
// Utils.showToast(arguments.toString());
});
}
Future<bool> onTextMessageSendForGeneralChat({
required String receiverId,
required ChatMessageTypeEnum chatMessageType,
required String message,
@ -629,15 +751,13 @@ class ChatVM extends ChangeNotifier {
final name = AppState().getUser.data!.userInfo!.firstName.toString();
final obj = <String, dynamic>{
"ReceiverUserID": receiverId,
// "SenderUserID": userId,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"ChatText": message,
"AdsID": adId,
};
logger.i("$obj");
hubConnection!.invoke(
SignalrConsts.sendMessageAds,
SignalrConsts.sendMessageGeneral,
args: <Object>[obj],
).catchError((e) {
logger.i("error in invoking SendMessage: ${e.toString()}");
@ -655,11 +775,11 @@ class ChatVM extends ChangeNotifier {
receiverUserID: receiverId,
);
currentMessagesForAds.add(chatMessageModel);
currentMessagesForGeneralChat.add(chatMessageModel);
notifyListeners();
if (AppState().currentAppType == AppType.customer) {} else {}
if (AppState().currentAppType == AppType.customer) {
} else {}
return true;
}
return false;

@ -1,15 +1,10 @@
import 'dart:developer';
import 'dart:io';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/user_models/image_response.dart';
import 'package:mc_common_app/repositories/user_repo.dart';
import 'package:mc_common_app/services/common_services.dart';
import 'package:mc_common_app/services/firebase_service.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';

@ -1,20 +1,20 @@
import 'dart:developer';
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/payment_models/pay_order_detail_resp_model.dart';
import 'package:mc_common_app/repositories/payments_repo.dart';
import 'package:mc_common_app/services/payments_service.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/subscriptions_view_model.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
import '../classes/app_state.dart';
class PaymentVM extends ChangeNotifier {
final PaymentService paymentService;
@ -47,6 +47,7 @@ class PaymentVM extends ChangeNotifier {
void updateAppointmentIdsForPayment({required List<int> ids}) {
appointmentIdsForPayment = ids;
log("appointmentIdsForPayment: ${appointmentIdsForPayment}");
}
updateSelectedPaymentMethod(PaymentMethods selectedMethod) {
@ -57,62 +58,43 @@ class PaymentVM extends ChangeNotifier {
Future<void> onContinuePressed(BuildContext context, {required PaymentTypes paymentType}) async {
switch (selectedPaymentMethod) {
case PaymentMethods.mada:
// TODO: Handle this case.
break;
case PaymentMethods.visa:
await onVisaCardSelected(context, paymentType);
break;
case PaymentMethods.applePay:
// TODO: Handle this case.
break;
case PaymentMethods.masterCard:
// TODO: Handle this case.
break;
case PaymentMethods.tamara:
// TODO: Handle this case.
break;
}
return;
}
Future<void> onAdsPaymentSuccess({required BuildContext context, required int currentAdId, required int paymentTypeId}) async {
Utils.showLoading(context);
PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentId: paymentTypeId, adId: currentAdId);
await Future.delayed(const Duration(seconds: 2));
Utils.hideLoading(context);
log("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}");
if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) {
Utils.showToast(LocaleKeys.paymentFailed.tr());
return;
}
if (payOrderDetailRespModel.isPaid != null && payOrderDetailRespModel.isPaid!) {
Future<void> verifyPayments({required BuildContext context, required int id, List<int>? appointmentIds, required int paymentTypeId, required Function() onVerified, bool isForAppointment = false}) async {
try {
Utils.showLoading(context);
bool isPaid = await paymentRepo.verifyPayment(
paymentTypeId: paymentTypeId,
id: id,
isForAppointments: isForAppointment,
appointmentIds: appointmentIds,
);
Utils.hideLoading(context);
if (!isPaid) {
Utils.showToast(LocaleKeys.paymentFailed.tr());
pop(context);
return;
}
Utils.showToast(LocaleKeys.paymentSuccessful.tr());
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
}
Future<void> onAppointmentPaymentSuccess({required BuildContext context, required int currentAdId, required int paymentTypeId}) async {
Utils.showLoading(context);
//TODO: CONFIRM FROM ZAHOOR THAT WILL THIS METHOD WORK FOR APPOINTMENT
PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentId: paymentTypeId, adId: currentAdId);
await Future.delayed(const Duration(seconds: 2));
Utils.hideLoading(context);
log("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}");
if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) {
Utils.showToast(LocaleKeys.paymentFailed.tr());
onVerified();
} catch (e) {
Utils.showToast(e.toString());
return;
}
if (payOrderDetailRespModel.isPaid != null && payOrderDetailRespModel.isPaid!) {
Utils.showToast(LocaleKeys.paymentSuccessful.tr());
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
}
int getIdTypeByPaymentType(PaymentTypes paymentTypes) {
@ -141,66 +123,58 @@ class PaymentVM extends ChangeNotifier {
Utils.showToast(LocaleKeys.paymentFailed.tr());
switch (paymentTypeEnum) {
case PaymentTypes.subscription:
// TODO: Handle this case.
Utils.showToast("Appointment Payment has been Failed!!");
break;
case PaymentTypes.request:
// TODO: Handle this case.
Utils.showToast("Request Payment has been Failed!!");
break;
case PaymentTypes.appointment:
case PaymentTypes.adReserve:
// TODO: Handle this case.
Utils.showToast("adReserve Payment has been Failed!!");
break;
case PaymentTypes.ads:
// TODO: Handle this case.
Utils.showToast("ads Payment has been Failed!!");
break;
case PaymentTypes.request:
case PaymentTypes.extendAds:
// TODO: Handle this case.
Utils.showToast("extendAds Payment has been Failed!!");
break;
case PaymentTypes.appointment:
// TODO: Handle this case.
Utils.showToast("Appointment Payment has been Failed!!");
break;
case PaymentTypes.partialAppointment:
Utils.showToast("partialAppointment Payment has been Failed!!");
break;
}
},
onSuccess: () async {
// TOD0: we have to take payment confirmation methods from Backend team and make success callbacks like onAdsPaymentSuccess
Utils.showToast("Verifying Payment..");
switch (paymentTypeEnum) {
case PaymentTypes.subscription:
onSubscriptionPaymentSuccess(context);
await verifyPayments(
context: context,
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
id: orderProviderSubscriptionId,
onVerified: () => onSubscriptionPaymentSuccess(context),
);
break;
case PaymentTypes.appointment:
Utils.showToast("Appointment Payment has been Succeeded");
break;
case PaymentTypes.partialAppointment:
Utils.showToast("Partial Appointment Payment has been Succeeded");
await verifyPayments(
id: 0,
isForAppointment: true,
appointmentIds: appointmentIdsForPayment,
context: context,
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
onVerified: () => onAppointmentSuccess(context),
);
break;
case PaymentTypes.request:
Utils.showToast("Request Payment has been Succeeded");
onRequestPaymentSuccess(context);
await verifyPayments(
context: context,
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
id: requestId,
onVerified: () => onRequestPaymentSuccess(context),
);
break;
case PaymentTypes.adReserve:
case PaymentTypes.ads:
case PaymentTypes.extendAds:
await onAdsPaymentSuccess(context: context, paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), currentAdId: currentAdId);
break;
await verifyPayments(
context: context,
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
id: currentAdId,
onVerified: () => onAdsPaymentSuccess(context),
);
}
},
);
@ -208,15 +182,22 @@ class PaymentVM extends ChangeNotifier {
void onRequestPaymentSuccess(BuildContext context) {
context.read<DashboardVmCustomer>().onNavbarTapped(4);
navigateReplaceWithName(context, AppRoutes.dashboard);
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
void onAdsPaymentSuccess(BuildContext context) {
context.read<DashboardVmCustomer>().onNavbarTapped(3);
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
onSubscriptionPaymentSuccess(BuildContext context) {
void onSubscriptionPaymentSuccess(BuildContext context) {
pop(context);
// context.read<SubscriptionsVM>().getMySubscriptions(AppState().getUser.data?.userInfo?.providerId.toString() ?? "");
// context.read<SubscriptionsVM>().getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
// context.read<SubscriptionsVM>().mySubscriptionsBySp = [];
// context.read<SubscriptionsVM>().getMySubscriptionsBySP();
// context.read<SubscriptionsVM>().getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
void onAppointmentSuccess(BuildContext context) {
context.read<DashboardVmCustomer>().onNavbarTapped(1);
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
@ -93,6 +94,14 @@ class ServiceVM extends BaseVM {
Future<void> getServiceProviderDocument(int providerId) async {
setState(ViewState.busy);
document = await branchRepo.getServiceProviderDocument(providerId);
if (document != null && document!.data != null && document!.data!.isNotEmpty) {
for (var doc in document!.data!) {
log("doc: ${doc.status}");
if (doc.status != 3) {
updateIsAllDocsApproved(false);
}
}
}
setState(ViewState.idle);
}
@ -121,7 +130,6 @@ class ServiceVM extends BaseVM {
filterUserBranchCategories() {
categories = [];
print("Filter Branch Executed");
List<BranchDetailModel>? localbranches;
if (branches!.data != null) {
if (selectedBranchStatus == 3) {
@ -202,13 +210,20 @@ class ServiceVM extends BaseVM {
// return null;
// }
bool isAllDocsApproved = false;
updateIsAllDocsApproved(var value) {
isAllDocsApproved = value;
notifyListeners();
}
Future<void> pickPdfReceiptFile(BuildContext context, int documentID, int index) async {
List<ImageModel> imageModels = [];
List<File>? files = await commonServices.pickMultipleFiles(
context,
allowMultiple: false,
);
if (files == null) return null;
if (files == null) return;
for (var element in files) {
imageModels.add(ImageModel(
filePath: element.path,
@ -218,8 +233,8 @@ class ServiceVM extends BaseVM {
documentID == 1
? commerceCertificates.addAll(imageModels)
: documentID == 2
? commercialCertificates.addAll(imageModels)
: vatCertificates.addAll(imageModels);
? commercialCertificates.addAll(imageModels)
: vatCertificates.addAll(imageModels);
document!.data![index].document = Utils.convertFileToBase64(files.first);
document!.data![index].fileExt = Utils.checkFileExt(files.first.path);
document!.data![index].documentUrl = files.first.path;
@ -236,16 +251,6 @@ class ServiceVM extends BaseVM {
notifyListeners();
}
Future<void> removeNetworkImag(String filePath) async {
int index = document!.data!.indexWhere((element) => element.documentUrl == filePath);
if (index == -1) {
return;
}
document!.data![index].documentUrl = null;
document!.data![index].isLocalFile = true;
notifyListeners();
}
Future<void> commercialRemove(String filePath) async {
int index = commercialCertificates.indexWhere((element) => element.filePath == filePath);
if (index == -1) {
@ -264,6 +269,16 @@ class ServiceVM extends BaseVM {
notifyListeners();
}
Future<void> removeNetworkImage(String filePath) async {
int index = document!.data!.indexWhere((element) => element.documentUrl == filePath);
if (index == -1) {
return;
}
document!.data![index].documentUrl = null;
document!.data![index].isLocalFile = true;
notifyListeners();
}
Future<GenericRespModel> updateDocument(List<DocumentData>? data) async {
return await branchRepo.serviceProviderDocumentsUpdate(data);
}
@ -272,8 +287,11 @@ class ServiceVM extends BaseVM {
Future<void> getBranchAndServices() async {
setState(ViewState.busy);
branches = await branchRepo.getBranchAndServices();
if (branches!.data != null)
if (branches!.data == null) {
homePageBranches = [];
} else {
homePageBranches = branches!.data!.serviceProviderBranch!.where((element) => element.branchStatus == BranchStatusEnum.approvedOrActive).toList();
}
setState(ViewState.idle);
}
@ -390,10 +408,10 @@ class ServiceVM extends BaseVM {
DropValue(
element.id ?? 0,
((element.categoryName!.isEmpty
? "N/A"
: countryCode == "SA"
? element.categoryNameN
: element.categoryName) ??
? "N/A"
: countryCode == "SA"
? element.categoryNameN
: element.categoryName) ??
"N/A"),
"",
),
@ -405,6 +423,7 @@ class ServiceVM extends BaseVM {
Future<void> fetchServicesByCategoryId(int categoryId) async {
servicesDropList = [];
setState(ViewState.busy);
//TODO: here
services = await branchRepo.fetchServicesByCategoryId(serviceCategoryId: categoryId); // to get all the services
for (var element in services!.data!) {
servicesDropList.add(
@ -575,9 +594,7 @@ class ServiceVM extends BaseVM {
File file = File(imageModel.filePath!);
List<int> imageBytes = await file.readAsBytes();
String image = base64Encode(imageBytes);
String fileName = file.path
.split('/')
.last;
String fileName = file.path.split('/').last;
branchPostingImages = BranchPostingImages(
imageName: fileName,
imageStr: image,
@ -622,8 +639,8 @@ class ServiceVM extends BaseVM {
GenericRespModel res = await branchRepo.createBranch(
branchName: branchName,
branchDescription: branchDesc,
openTime: branchDesc,
closeTime: branchDesc,
openTime: openTime,
closeTime: closeTime,
cityId: cityID,
address: address,
latitude: latitude,
@ -657,6 +674,14 @@ class ServiceVM extends BaseVM {
required String latitude,
required String longitude,
}) async {
// log(branchName);
// log(branchDesc);
// log(openTime);
// log(closedTime);
// log(cityID.toString());
// log(address);
// log(latitude);
// log(longitude);
if (branchName.isEmpty || branchDesc.isEmpty || address.isEmpty) {
branchErrorScreen = GlobalConsts.fillAllFields;
notifyListeners();

@ -0,0 +1,59 @@
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart';
import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart';
import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart';
import 'package:mc_common_app/repositories/setting_options_repo.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
class SettingOptionsVM extends BaseVM {
final SettingOptionsRepo settingOptionsRepo;
SettingOptionsVM({required this.settingOptionsRepo});
List<FAQsModel> faqsList = [];
Future<void> getAllFaqs() async {
setState(ViewState.busy);
try {
faqsList = await settingOptionsRepo.getAllFaqs();
setState(ViewState.idle);
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
setState(ViewState.idle);
}
}
List<ContactInfoModel> contactInfosList = [];
Future<void> getAllContactInfosList() async {
setState(ViewState.busy);
try {
contactInfosList = await settingOptionsRepo.getAllContactInfos();
setState(ViewState.idle);
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
setState(ViewState.idle);
}
}
List<AppInfoModel> appInfoList = [];
Future<void> getAppInfoList() async {
setState(ViewState.busy);
try {
appInfoList = await settingOptionsRepo.getAppInfoList();
setState(ViewState.idle);
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
setState(ViewState.idle);
}
}
}

@ -0,0 +1,110 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/general_models/enums_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/shipping_models/shipping_status_model.dart';
import 'package:mc_common_app/repositories/common_repo.dart';
import 'package:mc_common_app/repositories/shipping_repo.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
class ShippingManagementVM extends BaseVM {
final ShippingRepo shippingRepo;
final CommonRepo commonRepo;
ShippingManagementVM({required this.shippingRepo, required this.commonRepo});
List<ShippingRequestModel> shippingRequestsList = [];
List<EnumsModel> shippingStatusEnums = [];
List<FilterListModel> shippingRequestFilterOptions = [];
List<FilterListModel> shippingRequestStatusesList = [];
String requestStatusComments = "";
void updateRequestStatusComments(var value) {
requestStatusComments = value;
}
resetFilters() {
if (shippingRequestFilterOptions.isEmpty) return;
for (var value in shippingRequestFilterOptions) {
value.isSelected = false;
}
shippingRequestFilterOptions[0].isSelected = true;
requestStatusComments = "";
}
Future<void> populateShippingRequestFilterList() async {
if (shippingRequestFilterOptions.isNotEmpty && shippingRequestStatusesList.isNotEmpty) return;
shippingStatusEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.shippingStatusEnumId);
shippingRequestFilterOptions.clear();
shippingRequestStatusesList.clear();
for (int i = 0; i < shippingStatusEnums.length; i++) {
shippingRequestFilterOptions.add(FilterListModel(title: shippingStatusEnums[i].enumValueStr, isSelected: false, id: shippingStatusEnums[i].enumValue));
shippingRequestStatusesList.add(FilterListModel(title: shippingStatusEnums[i].enumValueStr, isSelected: false, id: shippingStatusEnums[i].enumValue));
}
shippingRequestFilterOptions.insert(0, FilterListModel(title: Utils.getNameByShippingRequestStatusEnum(0.toShippingStatusEnum()), isSelected: true, id: 0));
notifyListeners();
}
Future<void> applyFiltersOnShippingRequests({required ShippingRequestStatusEnum shippingRequestStatusEnum}) async {
for (var value in shippingRequestFilterOptions) {
value.isSelected = false;
}
if (shippingRequestStatusEnum == ShippingRequestStatusEnum.allRequests) {
shippingRequestFilterOptions[0].isSelected = true;
await getShippingRequestsListByFilters();
notifyListeners();
return;
}
shippingRequestFilterOptions[shippingRequestStatusEnum.getIdFromShippingStatusEnum()].isSelected = true; // -1 to match with the index
await getShippingRequestsListByFilters(shippingStatusEnum: shippingRequestStatusEnum);
notifyListeners();
}
void updateSelectionInShippingRequestStatuses(int index) {
for (var value in shippingRequestStatusesList) {
value.isSelected = false;
}
shippingRequestStatusesList[index].isSelected = true;
notifyListeners();
}
Future<void> getShippingRequestsListByFilters({ShippingRequestStatusEnum? shippingStatusEnum}) async {
setState(ViewState.busy);
try {
shippingRequestsList = await shippingRepo.getShippingRequestListByStatus(shippingStatusEnum: shippingStatusEnum);
setState(ViewState.idle);
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
setState(ViewState.idle);
}
}
Future<bool> onUpdateShippingStatusTapped({required BuildContext context, required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId}) async {
Utils.showLoading(context);
try {
GenericRespModel? genericRespModel = await shippingRepo.updateShippingRequestStatus(shippingStatusEnum: shippingStatusEnum, shippingRequestId: shippingRequestId);
Utils.showToast(genericRespModel.message.toString());
Utils.hideLoading(context);
return genericRespModel.messageStatus == 1;
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
return false;
}
}
}

@ -1,10 +1,6 @@
import 'dart:convert';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/subscriptions_models/branch_user_selection_model.dart';
import 'package:mc_common_app/models/subscriptions_models/provider_subscription_model.dart';
import 'package:mc_common_app/models/subscriptions_models/subscription_model.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
@ -118,12 +114,7 @@ class SubscriptionsVM extends BaseVM {
}
Future<GenericRespModel> createSubscriptionOrder(int subscriptionId, bool isStartNow, bool isRenew, {bool isDegrade = false, List<int>? listOfBranches, List<int>? listOfUsers}) async {
Map<String, dynamic> map = {
"providerID": AppState().getUser.data?.userInfo?.providerId.toString() ?? "",
"subscriptionID": subscriptionId.toString(),
"isStartNow": isStartNow.toString(),
"isRenew": isRenew.toString()
};
Map<String, dynamic> map = {"providerID": AppState().getUser.data?.userInfo?.providerId.toString() ?? "", "subscriptionID": subscriptionId.toString(), "isStartNow": isStartNow.toString(), "isRenew": isRenew.toString()};
GenericRespModel genericRespModel = await subscriptionRepo.payForProviderSubscription(map);
return genericRespModel;
}

@ -264,11 +264,13 @@ class VehicleDetails extends StatelessWidget {
28.height,
LocaleKeys.vehiclePictures.tr().toText(fontSize: 18, isBold: true),
8.height,
DottedRectContainer(
onTap: () => context.read<AdVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
),
if (adVM.pickedPostingImages.isEmpty) ...[
DottedRectContainer(
onTap: () => context.read<AdVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
),
],
if (adVM.vehicleImageError != "") ...[
10.height,
Row(

@ -57,7 +57,7 @@ class AdsBuyerChatsView extends StatelessWidget {
(chatForAdsModel.buyerName ?? "").toText(fontSize: 16, isBold: true),
if (chatForAdsModel.unReadMessagesCount != null && chatForAdsModel.unReadMessagesCount! > 0) ...[
Center(
child: "${chatForAdsModel.unReadMessagesCount ?? "1"}".toText(
child: "${chatForAdsModel.unReadMessagesCount}".toText(
color: Colors.white,
isBold: true,
fontSize: 10,

@ -7,6 +7,7 @@ import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
@ -271,10 +272,40 @@ class _AdsDetailViewState extends State<AdsDetailView> {
}).toWhiteContainer(width: double.infinity, allPading: 12);
}
Widget buildAdStartEndDates() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
("${LocaleKeys.startDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
widget.adDetails.startdate != null
? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.startdate!)).toText(
fontSize: 10,
)
: const SizedBox(),
],
),
Row(
children: [
("${LocaleKeys.endDate.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
widget.adDetails.enddate != null
? DateHelper.formatAsDayMonthYear(DateTime.parse(widget.adDetails.enddate!)).toText(
fontSize: 10,
)
: const SizedBox(),
],
),
],
).paddingOnly(top: 5, bottom: 5).onPress(() {
showMyBottomSheet(context, child: AdDamagePartPicturesSheet(adDamageReportList: widget.adDetails.vehicle!.damagereport ?? []));
}).toWhiteContainer(width: double.infinity, allPading: 12);
}
@override
Widget build(BuildContext context) {
Widget actionWidget = const SizedBox();
if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active)) {
if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active) && (widget.adDetails.adPostStatus != AdPostStatus.pendingForPost)) {
actionWidget = IconButton(
icon: const Icon(Icons.delete_outline, color: MyColors.redColor),
onPressed: () {
@ -318,6 +349,10 @@ class _AdsDetailViewState extends State<AdsDetailView> {
if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) ...[
buildDamagePartDetails(),
],
if (widget.adDetails.isMyAd ?? false) ...[
12.height,
buildAdStartEndDates(),
],
12.height,
if (widget.adDetails.adPostStatus == AdPostStatus.rejected) ...[
buildAdRejectionDetails(),
@ -329,6 +364,7 @@ class _AdsDetailViewState extends State<AdsDetailView> {
child: Column(
children: [
if (!(widget.adDetails.isMyAd ?? false)) ...[
const Divider(thickness: 1, height: 1),
const Divider(thickness: 1, height: 1),
18.height,
Row(

@ -7,6 +7,7 @@ import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart';
import 'package:mc_common_app/models/advertisment_models/special_service_model.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
@ -19,6 +20,7 @@ import 'package:mc_common_app/views/advertisement/components/picked_images_conta
import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart';
import 'package:mc_common_app/widgets/bottom_sheet.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/common_widgets/time_slots.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
@ -277,11 +279,11 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.setDateandTime.tr().toText(fontSize: 16, isBold: true, letterSpacing: -1.44, height: 1.2),
title: LocaleKeys.setDateandTime.tr().toText(fontSize: 16, height: 1.2),
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
25.height,
20.height,
adVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: Builder(
@ -343,7 +345,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
],
),
],
19.height,
30.height,
],
));
});
@ -496,11 +498,11 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
}
Widget pendingForReviewAction({required String pendingText}) {
bool isForShippingOrDelivery = false;
bool isForShippingOrDelivery = true;
return Center(
child: pendingText.toText(
color: isForShippingOrDelivery ? MyColors.lightTextColor : MyColors.adPendingStatusColor,
fontSize: isForShippingOrDelivery ? 14 : 16,
fontSize: isForShippingOrDelivery ? 15 : 16,
// isItalic: true,
),
).toContainer(
@ -512,22 +514,6 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
);
}
Widget buildStatusContainer(String text, {bool isForShippingOrDelivery = true}) {
return Center(
child: text.toText(
color: isForShippingOrDelivery ? MyColors.lightTextColor : MyColors.adPendingStatusColor,
fontSize: isForShippingOrDelivery ? 14 : 16,
// isItalic: true,
),
).toContainer(
marginAll: 12,
paddingAll: 12,
borderRadius: 8,
width: double.infinity,
backgroundColor: isForShippingOrDelivery ? MyColors.grey98Color.withOpacity(0.1) : MyColors.adPendingStatusColor.withOpacity(0.16),
);
}
Widget pendingForPaymentAction(BuildContext context, {required AdDetailsModel ad}) {
SpecialServiceModelForAds? photoSpecialServiceModel;
for (var element in ad.specialservice!) {
@ -592,36 +578,18 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
maxHeight: 55,
title: LocaleKeys.markAsSold.tr(),
isBold: false,
onPressed: () {
adVM.markAdAsSold(context, adId: adDetailsModel.id!);
},
),
),
],
),
8.height,
Row(
children: [
Expanded(
child: ShowFillButton(
isFilled: false,
borderColor: MyColors.redColor,
maxHeight: 55,
title: LocaleKeys.deactivateAd.tr(),
txtColor: MyColors.redColor,
onPressed: () {
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.doWantDeactivateAd.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.stoptheBuyers.tr(),
title: LocaleKeys.markAsSold.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.markAsSold.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () {
Navigator.pop(context);
adVM.deactivateTheAd(context, adId: adDetailsModel.id!);
adVM.markAdAsSold(context, adId: adDetailsModel.id!);
},
),
),
@ -644,10 +612,139 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
),
],
),
8.height,
Row(
children: [
Expanded(
child: ShowFillButton(
isFilled: false,
borderColor: MyColors.redColor,
maxHeight: 55,
title: LocaleKeys.deactivateAd.tr(),
txtColor: MyColors.redColor,
onPressed: () {
buildAdDeactivateReasonsBottomSheet(context, adDetails: adDetailsModel);
},
),
),
],
),
],
);
}
Future buildAdDeactivateReasonsBottomSheet(BuildContext context, {required AdDetailsModel adDetails}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
ListView.separated(
shrinkWrap: true,
itemCount: adVM.deActivateAdModelList.length,
separatorBuilder: (BuildContext context, int index) {
if (adVM.deActivateAdModelList[index].index == 3) {
return const SizedBox();
}
return const Divider(thickness: 0.5);
},
itemBuilder: (BuildContext context, int index) {
if (adVM.deActivateAdModelList[index].index == 3) {
return const SizedBox();
}
OfferRequestCommentModel offerRequestCommentModel = adVM.deActivateAdModelList[index];
return CircleCheckBoxWithTitle(
isChecked: offerRequestCommentModel.isSelected ?? false,
title: '${offerRequestCommentModel.title}',
onSelected: () {
adVM.updateSelectionInDeActivateAdModelList(index);
},
selectedColor: MyColors.darkPrimaryColor,
);
},
),
if (adVM.selectedDeActivateAdCommentModel.index == adVM.deActivateAdModelList.length - 1) ...[
// comparing if the "other" is selected
12.height,
TxtField(
maxLines: 5,
value: adVM.deactivateAdReasonDescription,
errorValue: adVM.deactivateAdReasonDescriptionError,
keyboardType: TextInputType.text,
hint: LocaleKeys.reason.tr(),
onChanged: (v) => adVM.updateDeactivateAdReasonDescription(v),
),
],
],
),
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
onPressed: () {
String comments = "";
if (adVM.selectedDeActivateAdCommentModel.index == adVM.deActivateAdModelList.length - 1) //Other
{
comments = adVM.deactivateAdReasonDescription;
} else {
comments = adVM.selectedDeActivateAdCommentModel.title ?? "";
}
if (!adVM.isDeActivateReasonSubmitValidated()) {
return;
}
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.doWantDeactivateAd.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.stoptheBuyers.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () {
adVM.deactivateTheAd(context, adId: adDetailsModel.id!, comment: comments);
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
Future buildCancelReservationBottomSheet(BuildContext context, {required AdDetailsModel adDetails}) {
return showModalBottomSheet(
context: context,
@ -947,6 +1044,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
case AdPostStatus.rejected:
return pendingForReviewAction(pendingText: LocaleKeys.rejectedFormAdmin.tr());
case AdPostStatus.cancelled:
return pendingForReviewAction(pendingText: LocaleKeys.cancelledByOwner.tr());
case AdPostStatus.pendingForPost:
return pendingForReviewAction(pendingText: LocaleKeys.waitingAdminPost.tr());

@ -30,6 +30,7 @@ class AdsListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (isAdsFragment && adsList.isEmpty) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
@ -137,9 +138,7 @@ class AdCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.start,
children: [
(adDetails.vehicle!.cityName ?? "").toText(
color: MyColors.lightTextColor,
),
(adDetails.vehicle!.cityName ?? "").toText(color: MyColors.lightTextColor),
adDetails.createdOn != null
? DateTime.parse(adDetails.createdOn!).getTimeAgo().toText(
color: MyColors.lightTextColor,
@ -159,7 +158,7 @@ class AdCard extends StatelessWidget {
children: [
(adDetails.vehicle!.demandAmount!.toInt().toString()).toText(fontSize: 19, isBold: true, letterSpacing: -1.16, height: 29 / 19),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, letterSpacing: -0.4, height: 16 / 10),
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, letterSpacing: -0.4, height: 16 / 10).paddingOnly(bottom: 3),
],
),
),

@ -11,10 +11,11 @@ class PickedFilesContainer extends StatelessWidget {
final Function(String filePath)? onCrossPressedPrimary;
final Function(int index, String filePath)? onCrossPressedSecondary;
final int? index;
final Function() onAddFilePressed;
final bool isReview;
final bool isPdf;
final bool isFromNetwork;
final Function() onAddFilePressed;
final bool allowAdButton;
const PickedFilesContainer({
Key? key,
@ -26,6 +27,7 @@ class PickedFilesContainer extends StatelessWidget {
this.isReview = false,
this.isPdf = false,
this.isFromNetwork = false,
this.allowAdButton = true,
}) : super(key: key);
@override
@ -37,9 +39,13 @@ class PickedFilesContainer extends StatelessWidget {
crossAxisSpacing: 4.0,
mainAxisSpacing: 8.0,
children: List.generate(
isReview ? pickedFiles.length : pickedFiles.length + 1,
!allowAdButton
? pickedFiles.length
: (isReview)
? pickedFiles.length
: pickedFiles.length + 1,
(index) {
if (index == pickedFiles.length && !isReview) {
if (index == pickedFiles.length && !isReview && allowAdButton) {
return Container(
decoration: BoxDecoration(color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)),
margin: const EdgeInsets.all(8),
@ -107,34 +113,36 @@ class BuildFilesContainer extends StatelessWidget {
child: const Icon(Icons.picture_as_pdf).paddingAll(8),
)
: image.isFromNetwork!
? Image.network(
image.filePath!,
fit: BoxFit.fill,
height: 75,
width: 73,
).paddingAll(8)
: Image.file(
File(image.filePath!),
fit: BoxFit.fill,
height: 72,
width: 70,
).paddingAll(8),
!isReview
? Align(
alignment: Alignment.topRight,
child: MyAssets.closeWithOrangeBg.buildSvg(
fit: BoxFit.fill,
height: 30,
width: 30,
),
).onPress(() {
if (onCrossPressedPrimary == null) {
onCrossPressedSecondary!(index!, image.filePath!);
return;
}
onCrossPressedPrimary!(image.filePath!);
})
: const SizedBox()
? image.filePath!
.buildNetworkImage(
fit: BoxFit.fill,
height: 75,
width: 73,
)
.paddingAll(8)
: image.filePath!
.buildFileImage(
fit: BoxFit.fill,
height: 72,
width: 70,
)
.paddingAll(8),
if (!isReview) ...[
Align(
alignment: Alignment.topRight,
child: MyAssets.closeWithOrangeBg.buildSvg(
fit: BoxFit.fill,
height: 30,
width: 30,
),
).onPress(() {
if (onCrossPressedPrimary == null) {
onCrossPressedSecondary!(index!, image.filePath!);
return;
}
onCrossPressedPrimary!(image.filePath!);
})
]
],
)),
],

@ -1,6 +1,7 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
@ -46,6 +47,14 @@ class SelectAdTypeView extends StatelessWidget {
if (arguments.length > 2) {
adsId = arguments[2] ?? -1;
}
int totalAds = 0;
int remainingAds = 0;
if (AppState().getproviderSubscription.isNotEmpty && AppState().getproviderSubscription.first.adsRemaining != null) {
totalAds = AppState().getproviderSubscription.first.totalAds ?? 0;
remainingAds = AppState().getproviderSubscription.first.adsRemaining ?? 0;
}
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.selectAdType.tr(),
@ -138,7 +147,7 @@ class SelectAdTypeView extends StatelessWidget {
children: [
Row(
children: [
"5 of 10 ".toText(fontSize: 29, isBold: true, letterSpacing: 0, height: 1),
"$remainingAds of $totalAds ".toText(fontSize: 29, isBold: true, letterSpacing: 0, height: 1),
LocaleKeys.adsRemainingVar.tr().toText(fontSize: 17, color: MyColors.lightTextColor, isBold: true),
],
),

@ -8,6 +8,7 @@ import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
@ -15,6 +16,7 @@ import 'package:mc_common_app/widgets/common_widgets/card_button_with_icon.dart'
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:sizer/sizer.dart';
class AppointmentDetailView extends StatelessWidget {
final AppointmentListModel appointmentListModel;
@ -43,15 +45,9 @@ class AppointmentDetailView extends StatelessWidget {
case AppointmentPaymentStatusEnum.payPartial:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.grey98Color.withOpacity(0.1),
textColor: MyColors.lightTextColor,
onPressed: () {},
text: LocaleKeys.workInProgress.tr(),
),
],
child: SizedBox(
height: 8.h,
child: Utils.buildStatusContainer(LocaleKeys.workInProgress.tr()),
),
);
case AppointmentPaymentStatusEnum.payNow:
@ -85,7 +81,7 @@ class AppointmentDetailView extends StatelessWidget {
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: LocaleKeys.cancel.tr()),
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
@ -108,15 +104,9 @@ class AppointmentDetailView extends StatelessWidget {
case AppointmentStatusEnum.visitCompleted:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.grey98Color.withOpacity(0.3),
textColor: MyColors.lightTextColor,
onPressed: () {},
text: LocaleKeys.visitCompleted.tr(),
),
],
child: SizedBox(
height: 8.h,
child: Utils.buildStatusContainer(LocaleKeys.visitCompleted.tr()),
),
);
@ -126,20 +116,19 @@ class AppointmentDetailView extends StatelessWidget {
case AppointmentStatusEnum.cancelled:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "Cancelled"),
],
child: SizedBox(
height: 8.h,
child: Utils.buildStatusContainer(LocaleKeys.appointmentCancelled.tr()),
),
);
case AppointmentStatusEnum.allAppointments:
return SizedBox();
return const SizedBox();
case AppointmentStatusEnum.rescheduled:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: LocaleKeys.cancel.tr()),
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
@ -151,7 +140,7 @@ class AppointmentDetailView extends StatelessWidget {
),
);
default:
return SizedBox();
return const SizedBox();
}
}
@ -224,44 +213,40 @@ class AppointmentDetailView extends StatelessWidget {
Column(
children: List.generate(appointmentListModel.appointmentServicesList!.length, (index) {
ServiceModel service = appointmentListModel.appointmentServicesList![index];
return Column(
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// MyAssets.maintenanceIcon.buildSvg(
// height: 10,
// width: 10,
// fit: BoxFit.fill,
// ),
// 10.width,
"${index + 1}. ${service.providerServiceDescription}".toText(fontSize: 14, isBold: true),
if (service.serviceItems != null && service.serviceItems!.isNotEmpty) ...[
Column(
children: List.generate(
service.serviceItems!.length,
(index) => "${service.serviceItems![index].name}".toText(
textAlign: TextAlign.start,
fontSize: 12,
color: MyColors.lightTextColor,
),
),
).paddingOnly(left: 15),
],
],
),
if (service.serviceItems != null && service.serviceItems!.isNotEmpty) ...[
Column(
children: List.generate(
service.serviceItems!.length,
(index) => "${service.serviceItems![index].name}".toText(
textAlign: TextAlign.start,
fontSize: 12,
color: MyColors.lightTextColor,
),
),
).paddingOnly(left: 15),
],
5.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
((service.currentTotalServicePrice).toString()).toText(fontSize: 25, isBold: true),
((service.currentTotalServicePrice).toString()).toText(fontSize: 19, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
const Icon(Icons.arrow_drop_down, size: 30)
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 5),
const Icon(Icons.arrow_drop_down, size: 25)
],
).onPress(() => appointmentsVM.priceBreakDownClicked(context, service)),
],
);
).paddingOnly(bottom: 15);
}),
),
],

@ -5,6 +5,7 @@ import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
@ -62,7 +63,7 @@ class BookAppointmentSchedulesView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
(scheduleData.appointmentType == 2 ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(),
(scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(),
],
),
Column(

@ -65,6 +65,7 @@ class BookAppointmentServicesView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(LocaleKeys.serviceLocation.tr()).toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
4.width,
(serviceData.isHomeSelected ? serviceData.homeLocation : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true).expand(),
],
),

@ -49,15 +49,16 @@ class BookAppointmentsItemView extends StatelessWidget {
children: [
LocaleKeys.workshopFullAccessServices.tr().toText(fontSize: 12, isItalic: true, color: MyColors.lightTextColor),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.changeLocationService.tr().toText(fontSize: 14, isBold: true),
LocaleKeys.edit.tr().toText(fontSize: 14, isBold: true, isUnderLine: true, color: MyColors.adPendingStatusColor),
5.width,
MyAssets.icEdit.buildSvg(width: 17),
],
).onPress(() {}),
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// LocaleKeys.changeLocationService.tr().toText(fontSize: 14, isBold: true),
// 5.width,
// LocaleKeys.edit.tr().toText(fontSize: 14, isBold: true, isUnderLine: true, color: MyColors.adPendingStatusColor),
// 5.width,
// MyAssets.icEdit.buildSvg(width: 17),
// ],
// ).onPress(() {}),
16.height,
const Divider(),
],

@ -8,6 +8,7 @@ import 'package:mc_common_app/models/appointments_models/service_schedule_model.
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
@ -160,7 +161,7 @@ class ReviewAppointment extends StatelessWidget {
]),
Row(children: [
("${LocaleKeys.location.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
(scheduleData.appointmentType == 2 ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true),
(scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true),
]),
],
),
@ -184,7 +185,7 @@ class ReviewAppointment extends StatelessWidget {
"${scheduleData.amountTotal.toString()} ${LocaleKeys.sar.tr()}".toText(fontSize: 16, isBold: true),
],
),
if (scheduleData.appointmentType == 1) ...[
if (scheduleData.appointmentTypeEnum == AppointmentTypeEnum.home) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -217,7 +218,7 @@ class ReviewAppointment extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Divider(thickness: 0.7, height: 3),
const Divider(thickness: 0.7, height: 3),
8.height,
if (appointmentsVM.amountToPayForAppointment > 0) ...[
Row(

@ -8,6 +8,7 @@ import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/views/location_views/pick_location_page.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
@ -94,7 +95,13 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
maxHeight: 48,
title: LocaleKeys.home.tr(),
txtColor: appointmentsVM.isHomeTapped ? MyColors.white : MyColors.darkTextColor,
onPressed: () => appointmentsVM.updateIsHomeTapped(true),
onPressed: () {
if (appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) {
appointmentsVM.updateIsHomeTapped(true);
} else {
Utils.showToast("The Selected Service is not available at Home Location.");
}
},
),
),
12.width,
@ -116,8 +123,8 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
errorValue: appointmentsVM.pickHomeLocationError,
value: appointmentsVM.pickedHomeLocation,
isNeedClickAll: true,
postfixData: Icons.location_on,
postFixDataColor: MyColors.darkTextColor,
postfixData: Icons.add_location_outlined,
postFixDataColor: MyColors.darkPrimaryColor,
onTap: () {
navigateTo(
context,
@ -148,14 +155,14 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
width: double.infinity,
child: Column(
children: [
if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices) ...[
if (appointmentsVM.isHomeTapped && !appointmentsVM.isFetchingServices && appointmentsVM.currentServiceSelection != null && (appointmentsVM.currentServiceSelection!.isAllowAppointmentHome ?? false)) ...[
const Divider(thickness: 1, height: 1),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// TODO: This Price will be decided according to the service selected, We will calculate the KMs and multiple it with price per KMs
150.toString().toText(fontSize: 30, isBold: true),
LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
appointmentsVM.currentServiceSelection!.rangePricePerKm.toString().toText(fontSize: 30, isBold: true),
"${LocaleKeys.sar.tr()}/km".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
LocaleKeys.additionalChargesNotice.tr().toText(fontSize: 12, color: MyColors.lightTextColor),

@ -14,6 +14,7 @@ import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/widgets/common_widgets/custom_add_button_widget.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';

@ -50,6 +50,9 @@ class _ChatViewState extends State<ChatView> {
chatViewArgumentsForAd = widget.chatViewArguments.chatViewArgumentsForAd;
} else if (chatTypeEnum == ChatTypeEnum.requestOffer) {
chatViewArgumentsForRequest = widget.chatViewArguments.chatViewArgumentsForRequest;
} else if (chatTypeEnum == ChatTypeEnum.general) {
// For General Chat
chatViewArgumentsForAd = widget.chatViewArguments.chatViewArgumentsForAd;
}
chatVM = context.read<ChatVM>();
chatVM.isUserOnChatScreen = true;
@ -85,7 +88,15 @@ class _ChatViewState extends State<ChatView> {
adId: chatViewArgumentsForAd!.adsID!,
context: context,
);
} else if (chatTypeEnum == ChatTypeEnum.general) {}
} else if (chatTypeEnum == ChatTypeEnum.general) {
status = await chatVM.onTextMessageSendForGeneralChat(
receiverId: chatViewArgumentsForAd!.receiverUserID ?? "",
chatMessageType: ChatMessageTypeEnum.freeText,
message: chatVM.chatMessageText,
adId: chatViewArgumentsForAd!.adsID!,
context: context,
);
}
Utils.hideLoading(context);
return status;
@ -93,11 +104,40 @@ class _ChatViewState extends State<ChatView> {
@override
Widget build(BuildContext context) {
Widget? appBarHeadlines;
final requestVM = context.read<RequestsVM>();
if (chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest != null &&
(requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.shipping || requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.delivery)) {
appBarHeadlines = Container(
width: double.infinity,
color: MyColors.darkIconColor,
padding: const EdgeInsets.symmetric(horizontal: 21, vertical: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
"Your Delivery / Shipping Status is: ".toText(
fontSize: 12,
color: Colors.white,
decorationColor: MyColors.white,
),
Utils.getNameByShippingRequestStatusEnum(requestVM.currentSelectedRequest?.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated).toText(
fontSize: 16,
color: Colors.white,
),
],
),
);
}
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.chat.tr()),
appBar: CustomAppBar(
title: LocaleKeys.chat.tr(),
),
body: Consumer2<ChatVM, RequestsVM>(builder: (BuildContext context, ChatVM chatVM, RequestsVM requestVM, Widget? child) {
List<ChatMessageModel> chatMessages = [];
if (chatTypeEnum == ChatTypeEnum.ads) {
if (chatTypeEnum == ChatTypeEnum.general) {
chatMessages = chatVM.currentMessagesForGeneralChat;
} else if (chatTypeEnum == ChatTypeEnum.ads) {
chatMessages = chatVM.currentMessagesForAds;
} else if (chatTypeEnum == ChatTypeEnum.requestOffer) {
if (AppState().currentAppType == AppType.customer && chatVM.serviceProviderOffersList.isNotEmpty) {
@ -108,16 +148,24 @@ class _ChatViewState extends State<ChatView> {
}
return Column(
children: [
if (appBarHeadlines != null) ...[
appBarHeadlines,
5.height,
],
Expanded(
child: chatMessages.isEmpty
? Center(child: LocaleKeys.noRequestsShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor))
? Center(child: LocaleKeys.noChatMessage.tr().toText(fontSize: 16, color: MyColors.lightTextColor, textAlign: TextAlign.center)).paddingAll(22)
: ListView.separated(
controller: chatVM.scrollController,
itemCount: chatMessages.length,
separatorBuilder: (BuildContext context, int index) => 20.height,
itemBuilder: (BuildContext context, int index) {
ChatMessageModel chatMessageModel = chatMessages[index];
return ChatMessageCustomWidget(chatMessageModel: chatMessageModel, requestStatusEnum: requestVM.currentSelectedRequest!.requestStatus);
return ChatMessageCustomWidget(
chatMessageModel: chatMessageModel,
requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus,
chatTypeEnum: chatTypeEnum,
);
},
).horPaddingMain(),
),
@ -190,7 +238,7 @@ class _ChatViewState extends State<ChatView> {
onAddFilePressed: () => chatVM.pickMultipleImages(),
),
),
] else ...[
] else if (chatTypeEnum == ChatTypeEnum.requestOffer) ...[
Expanded(
flex: 1,
child: const Icon(
@ -199,6 +247,8 @@ class _ChatViewState extends State<ChatView> {
size: 30,
).onPress(() => chatVM.pickMultipleImages()),
),
],
if (chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 8,
child: TxtField(

@ -0,0 +1,113 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class GeneralChatListPage extends StatelessWidget {
final OfferListPageArguments generalChatListPageArguments;
const GeneralChatListPage({super.key, required this.generalChatListPageArguments});
@override
Widget build(BuildContext context) {
final List<ServiceProvidersOffers> serviceProviderOffers = generalChatListPageArguments.serviceProviderOffers;
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.offers.tr()),
body: serviceProviderOffers.isEmpty
? Center(child: LocaleKeys.noOffersShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor))
: ListView.separated(
itemCount: serviceProviderOffers.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
ServiceProvidersOffers offersModel = serviceProviderOffers[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.statusContainerChip(text: "Offer ${Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)}", chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(offersModel.name ?? "").toText(fontSize: 16, isBold: true),
Center(
child: "${offersModel.offerCount}".toText(
color: Colors.white,
isBold: true,
fontSize: 10,
),
).toContainer(
backgroundColor: MyColors.redColor,
borderRadius: 100,
paddingAll: 1,
width: 22,
height: 22,
),
],
),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
"${offersModel.companyName}".toText(color: MyColors.lightTextColor, fontSize: 14),
if (offersModel.createdOn != null && offersModel.createdOn!.isNotEmpty) ...[
" | ${DateTime.parse(offersModel.createdOn!).getTimeAgo()}".toText(color: MyColors.lightTextColor, fontSize: 14),
],
// " | 1 hour ago".toText(
// color: MyColors.lightTextColor,
// fontSize: 12,
// ),
],
),
const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18),
],
),
],
).onPress(() async {
ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest(
chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: "${offersModel.providerUserId}",
senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestId: generalChatListPageArguments.requestId,
providerIndex: index,
requestIndex: -1, // This will be only send in case of provider
);
ChatViewArguments chatViewArguments = ChatViewArguments(chatTypeEnum: ChatTypeEnum.requestOffer, chatViewArgumentsForRequest: chatViewArgumentsForRequest);
final chatVM = context.read<ChatVM>();
await chatVM
.getRequestsChatMessagesForCustomer(
context: context,
providerId: offersModel.providerId ?? 0,
requestOfferId: 0,
requestId: generalChatListPageArguments.requestId ?? 0,
providerOfferIndex: index,
)
.whenComplete(
() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments),
);
}).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 16.height,
),
);
}
}

@ -9,6 +9,7 @@ import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/base_view_model.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';

@ -1,9 +1,8 @@
import 'dart:developer';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
@ -11,6 +10,7 @@ import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
@ -24,9 +24,10 @@ import 'package:easy_localization/easy_localization.dart' as lcl;
class ChatMessageCustomWidget extends StatefulWidget {
final ChatMessageModel chatMessageModel;
final RequestStatusEnum requestStatusEnum;
final RequestStatusEnum? requestStatusEnum;
final ChatTypeEnum chatTypeEnum;
const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum});
const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum, required this.chatTypeEnum});
@override
State<ChatMessageCustomWidget> createState() => _ChatMessageCustomWidgetState();
@ -424,12 +425,13 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
);
}
Widget buildImageGridWidget(
{required List<MessageImageModel> messagesImages,
double gridItemSize = 50.0, // Default fixed size for each grid item
double spacing = 8.0,
double borderWidth = 1,
Color borderColor = MyColors.textColor}) {
Widget buildImageGridWidget({
required List<MessageImageModel> messagesImages,
double gridItemSize = 50.0,
double spacing = 8.0,
double borderWidth = 1,
Color borderColor = MyColors.textColor,
}) {
int imageCount = messagesImages.length;
if (imageCount == 1) {
return Container(
@ -447,7 +449,9 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
)
: messagesImages[0].imagePath.buildFileImage(fit: BoxFit.cover),
),
);
).onPress(() {
navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages);
});
}
return SizedBox(
// height: (gridItemSize * 2) + (spacing * 2), // Fixed height for 2 rows including spacing
@ -468,7 +472,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
return Stack(
fit: StackFit.expand,
children: [
(messagesImages[0].isFromNetwork ?? false)
(messagesImages[index].isFromNetwork ?? false)
? messagesImages[index].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
width: gridItemSize,
@ -490,7 +494,9 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
),
),
],
);
).onPress(() {
navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages);
});
} else {
return Container(
width: gridItemSize,
@ -507,7 +513,9 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
height: gridItemSize,
),
),
);
).onPress(() {
navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages);
});
}
},
),
@ -534,7 +542,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
children: [
buildFreeTextDetailsInMessage(),
10.height,
buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum, chatMessageModel: widget.chatMessageModel, context: context),
buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum!, chatMessageModel: widget.chatMessageModel, context: context),
],
);
break;

@ -125,10 +125,19 @@ class AdsFragment extends StatelessWidget {
Expanded(
child: RefreshIndicator(
onRefresh: () async {
await adVM.getExploreAds();
await adVM.getMyAds();
if (adVM.isExploreAdsTapped) {
CreatedByRoleEnum createdByRoleEnum = adVM.exploreAdsFilterOptions.firstWhere((element) => element.isSelected).id.toCreatedByRoleEnum();
adVM.applyFilterOnExploreAds(createdByRoleFilter: createdByRoleEnum);
} else {
AdPostStatus adPostStatusEnum = adVM.myAdsFilterOptions.firstWhere((element) => element.isSelected).id.toAdPostEnum();
adVM.applyFilterOnMyAds(adPostStatusEnum: adPostStatusEnum);
}
},
child: AdsListWidget(isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM)),
child: adVM.state == ViewState.busy
? const Center(
child: CircularProgressIndicator(),
)
: AdsListWidget(isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM)),
),
)
],

@ -110,7 +110,7 @@ class MyRequestsFragment extends StatelessWidget {
8.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async => await requestsVM.getRequests(isNeedToRebuild: true),
onRefresh: () async => await requestsVM.getRequestsBasedOnFilters(),
child: requestsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: requestsVM.myFilteredRequests.isEmpty

@ -8,6 +8,7 @@ import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/user_view_model.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
@ -79,7 +80,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
color: Colors.white,
child: ListView(
children: [
20.height,
60.height,
"${AppState().getUser.data!.userInfo!.firstName} ${AppState().getUser.data!.userInfo!.lastName ?? ""}".toText(fontSize: 20).paddingOnly(left: 25),
Column(
children: [
@ -87,6 +88,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
titleText: LocaleKeys.country.tr(),
subtitleText: "Saudi Arabia",
needBorderBelow: true,
needEditButton: false,
onTap: () {},
),
CustomProfileOptionsTile(
@ -98,7 +100,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
},
),
CustomProfileOptionsTile(
titleText: LocaleKeys.phoneNumber.tr(),
titleText: LocaleKeys.phone.tr(),
subtitleText: "${AppState().getUser.data!.userInfo!.email}",
needBorderBelow: true,
onTap: () {
@ -121,16 +123,16 @@ class _ProfileScreenState extends State<ProfileScreen> {
],
),
Positioned(
top: MediaQuery.of(context).size.height * 0.25,
top: MediaQuery.of(context).size.height * 0.24,
child: Container(
height: 90,
height: 100,
alignment: Alignment.centerLeft,
child: ClipOval(
child: AppState().getUser.data!.userInfo!.userLocalImage != null
? Image.file(
AppState().getUser.data!.userInfo!.userLocalImage!,
width: 90,
height: 90,
width: 100,
height: 100,
fit: BoxFit.fill,
)
: CachedNetworkImage(
@ -145,11 +147,11 @@ class _ProfileScreenState extends State<ProfileScreen> {
),
placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(Icons.error),
width: 90,
height: 90,
width: 100,
height: 100,
fit: BoxFit.fill,
fadeInCurve: Curves.easeIn,
fadeInDuration: Duration(milliseconds: 1000),
fadeInDuration: const Duration(milliseconds: 1000),
useOldImageOnUrlChange: false,
),
),
@ -157,18 +159,32 @@ class _ProfileScreenState extends State<ProfileScreen> {
),
Positioned(
top: MediaQuery.of(context).size.height * 0.30,
right: MediaQuery.of(context).size.height * 0.036,
right: MediaQuery.of(context).size.height * 0.03,
child: Container(
height: 35,
width: 35,
height: 40,
width: 40,
padding: EdgeInsets.all(8),
decoration: BoxDecoration(color: MyColors.white, shape: BoxShape.circle, border: Border.all(color: MyColors.darkTextColor, width: 0.1)),
child: const Icon(Icons.edit_note, color: MyColors.darkIconColor, size: 27).paddingOnly(left: 5),
child: MyAssets.icEdit.buildSvg(),
).onPress(
() async {
model.updateUserImage(context);
await model.updateUserImage(context).whenComplete(() => setState(() {}));
},
),
),
Positioned(
top: MediaQuery.of(context).size.height * 0.05,
left: MediaQuery.of(context).size.height * 0.03,
child: const Icon(Icons.arrow_back_ios, color: Colors.black, size: 18)
.toContainer(
padding: const EdgeInsets.only(left: 5),
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
height: 40,
width: 40,
)
.onPress(() => pop(context))),
],
);
}),

@ -7,7 +7,6 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart';
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/views/location_views/pick_location_page.dart';
@ -121,7 +120,7 @@ class CreateRequestPage extends StatelessWidget {
vehicleOwnerDrop.add(DropValue(element.id.toInt(), element.enumValueStr, ""));
}
return DropdownField(
(DropValue value) => requestsVM.updateSelectionVehicleConditionId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
(DropValue value) => requestsVM.updateSelectionVehicleConditionId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
list: vehicleOwnerDrop,
dropdownValue: requestsVM.vehicleConditionId.selectedId != -1 ? DropValue(requestsVM.vehicleConditionId.selectedId, requestsVM.vehicleConditionId.selectedOption, "") : null,
hint: LocaleKeys.condition.tr(),

@ -10,6 +10,7 @@ import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
@ -34,7 +35,9 @@ class OfferListPage extends StatelessWidget {
itemBuilder: (context, index) {
ServiceProvidersOffers offersModel = serviceProviderOffers[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.statusContainerChip(text: "Offer ${Utils.getNameByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)}", chipColor: Utils.getChipColorByRequestOfferStatusEnum(offersModel.requestOfferStatusEnum!)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
@ -91,7 +94,7 @@ class OfferListPage extends StatelessWidget {
final chatVM = context.read<ChatVM>();
await chatVM
.getUsersChatMessagesForCustomer(
.getRequestsChatMessagesForCustomer(
context: context,
providerId: offersModel.providerId ?? 0,
requestOfferId: 0,

@ -38,9 +38,9 @@ class RequestDetailPage extends StatelessWidget {
case RequestStatusEnum.inProgress:
if (requestTypeEnum == RequestsTypeEnum.serviceRequest) {
return buildStatusContainer(LocaleKeys.awaitingPaymentFromCustomer.tr());
return Utils.buildStatusContainer(LocaleKeys.awaitingPaymentFromCustomer.tr());
} else {
return buildStatusContainer(LocaleKeys.awaitingResponseFromCustomer.tr());
return Utils.buildStatusContainer(LocaleKeys.awaitingResponseFromCustomer.tr());
}
case RequestStatusEnum.paid:
@ -56,13 +56,13 @@ class RequestDetailPage extends StatelessWidget {
},
);
} else {
return buildStatusContainer(LocaleKeys.awaitingResponseFromProvider.tr());
return Utils.buildStatusContainer(LocaleKeys.awaitingResponseFromProvider.tr());
}
case RequestStatusEnum.shipping:
case RequestStatusEnum.delivery:
if (AppState().currentAppType == AppType.provider) {
return buildStatusContainer(LocaleKeys.shippingManagementInstruction.tr(), isForShippingOrDelivery: true);
return Utils.buildStatusContainer(LocaleKeys.shippingManagementInstruction.tr());
} else {
return ShowFillButton(
maxWidth: double.infinity,
@ -78,26 +78,10 @@ class RequestDetailPage extends StatelessWidget {
case RequestStatusEnum.cancelled:
case RequestStatusEnum.expired:
case RequestStatusEnum.pending:
return buildStatusContainer(statusText);
return Utils.buildStatusContainer(statusText);
}
}
Widget buildStatusContainer(String text, {bool isForShippingOrDelivery = true}) {
return Center(
child: text.toText(
color: isForShippingOrDelivery ? MyColors.lightTextColor : MyColors.adPendingStatusColor,
fontSize: isForShippingOrDelivery ? 14 : 16,
// isItalic: true,
),
).toContainer(
marginAll: 12,
paddingAll: 12,
borderRadius: 8,
width: double.infinity,
backgroundColor: isForShippingOrDelivery ? MyColors.grey98Color.withOpacity(0.1) : MyColors.adPendingStatusColor.withOpacity(0.16),
);
}
Future<void> onViewChatTapped(BuildContext context) async {
ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest(
chatTypeEnum: ChatTypeEnum.requestOffer,
@ -115,7 +99,7 @@ class RequestDetailPage extends StatelessWidget {
);
final chatVM = context.read<ChatVM>();
await chatVM
.getUsersChatMessagesForProvider(
.getRequestsChatMessagesForProvider(
customerId: requestDetailPageArguments.requestModel.customerId,
context: context,
requestOfferId: 0,

@ -43,7 +43,7 @@ class RequestItem extends StatelessWidget {
children: [
Utils.statusContainerChip(text: request.requestStatusName, chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)),
6.height,
"${request.brand} ${request.model}".toText(fontSize: 16, letterSpacing: -0.64),
"${request.brand} ${request.model} | ${request.id}".toText(fontSize: 16, letterSpacing: -0.64),
showItem("${LocaleKeys.model.tr()}:", "${request.year}"),
if (request.customerName.isNotEmpty) ...[
showItem("${LocaleKeys.customerName.tr()}:", request.customerName),

@ -1,7 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
@ -9,14 +7,15 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/document.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/service_view_model.dart';
import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart';
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
@ -40,9 +39,9 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
@override
void initState() {
super.initState();
scheduleMicrotask(() {
// branchVM = Provider.of<ServiceVM>(context, listen: false);
context.read<ServiceVM>().getServiceProviderDocument(AppState().getUser.data!.userInfo!.providerId ?? 0);
scheduleMicrotask(() async {
final serviceVm = context.read<ServiceVM>();
await serviceVm.getServiceProviderDocument(AppState().getUser.data!.userInfo!.providerId ?? 0);
});
}
@ -53,51 +52,55 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
title: LocaleKeys.defineLicences.tr(),
isRemoveBackButton: false,
),
body: Consumer<ServiceVM>(builder: (_, model, __) {
body: Consumer<ServiceVM>(builder: (_, serviceVM, __) {
if (serviceVM.state == ViewState.busy) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(0.0),
child: Column(
children: [
showWidget(model),
],
),
),
),
child: SingleChildScrollView(child: buildContent(serviceVM)),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: ShowFillButton(
title: LocaleKeys.continu.tr(),
maxWidth: double.infinity,
onPressed: () {
if (AppState().getUser.data!.userInfo!.roleId == 5) {
if (validation(model)) {
updateDocument(model);
if (!serviceVM.isAllDocsApproved) ...[
Padding(
padding: const EdgeInsets.all(12.0),
child: ShowFillButton(
title: LocaleKeys.continu.tr(),
maxWidth: double.infinity,
onPressed: () async {
bool status = false;
if (AppState().getUser.data!.userInfo!.roleId == 5) {
if (validateDocuments(serviceVM)) {
status = await updateDocument(serviceVM);
} else {
Utils.showToast(LocaleKeys.allDocumentMandatoryDealershipProvider.tr());
return;
}
} else {
Utils.showToast(LocaleKeys.allDocumentMandatoryDealershipProvider.tr());
status = await updateDocument(serviceVM);
}
} else {
updateDocument(model);
}
Future.delayed(const Duration(seconds: 1), () {
Navigator.of(context).pop();
});
},
Future.delayed(const Duration(seconds: 1), () {
if (status) {
pop(context);
}
});
},
),
),
),
],
],
);
}),
);
}
validation(ServiceVM model) {
validateDocuments(ServiceVM model) {
bool valid = true;
for (var element in model.document!.data!) {
log("documentUrl: ${element.documentUrl}");
if (element.documentUrl == null) {
valid = false;
}
@ -105,126 +108,101 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
return valid;
}
updateDocument(ServiceVM model) async {
Utils.showLoading(context);
GenericRespModel res = await model.updateDocument(model.document!.data);
Utils.hideLoading(context);
if (res.messageStatus == 1) {
Utils.showToast(LocaleKeys.documentsUploadedSuccessfully.tr());
Navigator.of(context).pop();
} else {
Utils.showToast(res.message ?? "");
Future<bool> updateDocument(ServiceVM model) async {
try {
Utils.showLoading(context);
GenericRespModel res = await model.updateDocument(model.document!.data);
Utils.hideLoading(context);
if (res.messageStatus == 1) {
Utils.showToast(LocaleKeys.documentsUploadedSuccessfully.tr());
Navigator.of(context).pop();
return true;
} else {
Utils.showToast(res.message ?? "");
return false;
}
} catch (e, s) {
return false;
}
}
Widget showWidget(ServiceVM model) {
if (model.state == ViewState.idle) {
return model.document!.data!.isEmpty
? Text(LocaleKeys.somethingWrong.tr())
: ListView.separated(
itemBuilder: (context, index) {
DocumentData? document = model.document?.data![index];
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
10.height,
(document!.documentName! ?? "").toText(fontSize: 16, letterSpacing: -0.56, fontWeight: MyFonts.SemiBold),
Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 4, bottom: 8),
child: LocaleKeys.enter_licence_detail.tr().toText(fontSize: 14, color: MyColors.lightTextColor, textAlign: TextAlign.center),
),
TxtField(
hint: LocaleKeys.description.tr(),
maxLines: 3,
isBackgroundEnabled: true,
),
10.height,
if (isNeedToShow(model: model, document: document)) ...[
PickedFilesContainer(
// isReview: document.status == 3 ? true : false,
pickedFiles: isLocalOrNetworkFiles(model: model, document: document),
onCrossPressedPrimary: chkIsLocalOrNetwork(document: document)
? model.removeNetworkImag
: document.documentId == 1
? model.commerceRemove
: document.documentId == 2
? model.commercialRemove
: model.vatRemove,
isPdf: model.document!.data![index].fileExt == "pdf",
isFromNetwork: document.isLocalFile! ? false : true,
onAddFilePressed: () {
model.pickPdfReceiptFile(context, document.documentId!, index);
},
),
if (document.comment != null && document.comment.isNotEmpty && document.status == 4) ...[buildStatusContainer(document: document)]
] else
...[
Widget buildContent(ServiceVM serviceVM) {
return serviceVM.document!.data!.isEmpty
? Text(LocaleKeys.somethingWrong.tr())
: ListView.separated(
separatorBuilder: (context, index) {
return 20.height;
},
itemCount: serviceVM.document!.data!.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.symmetric(horizontal: 20),
itemBuilder: (context, index) {
DocumentData? document = serviceVM.document?.data![index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
10.height,
InkWell(
onTap: () async {
model.pickPdfReceiptFile(context, document.documentId!, index) ?? "";
},
child: Container(
width: double.infinity,
height: 45,
decoration: BoxDecoration(
color: Colors.transparent,
border: Border.all(color: MyColors.greyACColor, width: 2),
borderRadius: const BorderRadius.all(Radius.circular(0)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(
Icons.attach_file,
size: 18,
color: MyColors.darkPrimaryColor,
),
8.width,
Text(
LocaleKeys.attachFile.tr(),
style: const TextStyle(
color: MyColors.darkPrimaryColor,
),
),
const Icon(
Icons.attach_file,
size: 18,
color: Colors.transparent,
),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
(document!.documentName!).toText(fontSize: 16, letterSpacing: -0.56, fontWeight: MyFonts.SemiBold),
if (document.statusText != null && document.statusText!.isNotEmpty) ...[
10.width,
Utils.statusContainerChip(text: document.statusText!.replaceFirst('OrActive', ''), chipColor: getColorByStatus(document.status ?? 1)),
],
],
),
if (document.status != 1 && document.status != 3) ...[
Padding(
padding: const EdgeInsets.only(top: 4, bottom: 8),
child: LocaleKeys.enter_licence_detail.tr().toText(fontSize: 14, color: MyColors.lightTextColor),
),
TxtField(
hint: LocaleKeys.description.tr(),
maxLines: 3,
isBackgroundEnabled: true,
),
],
10.height,
if (isNeedToShow(model: serviceVM, document: document)) ...[
PickedFilesContainer(
isReview: document.status == 4,
allowAdButton: false,
pickedFiles: isLocalOrNetworkFiles(model: serviceVM, document: document),
onCrossPressedPrimary: isNetworkImage(document: document)
? serviceVM.removeNetworkImage
: document.documentId == 1
? serviceVM.commerceRemove
: document.documentId == 2
? serviceVM.commercialRemove
: serviceVM.vatRemove,
isPdf: serviceVM.document!.data![index].fileExt == "pdf",
isFromNetwork: !(document.isLocalFile ?? false),
onAddFilePressed: () {
serviceVM.pickPdfReceiptFile(context, document.documentId!, index);
},
),
buildCommentContainer(document: document),
] else ...[
10.height,
DottedRectContainer(
onTap: () => serviceVM.pickPdfReceiptFile(context, document.documentId!, index) ?? "",
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
),
],
],
],
);
},
);
},
separatorBuilder: (context, index) {
return 20.height;
},
itemCount: model.document!.data!.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 20),
);
} else {
return Column(
children: [
20.height,
const Center(
child: CircularProgressIndicator(),
),
],
);
}
}
List<ImageModel> isLocalOrNetworkFiles({required ServiceVM model, required DocumentData document}) {
bool isNetworkImage = false;
if (!document.isLocalFile!) {
isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false;
isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false;
}
if (isNetworkImage) {
return [ImageModel(id: document.id, isFromNetwork: isNetworkImage, filePath: document.documentUrl)];
@ -239,7 +217,7 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
bool isNeedToShow({required ServiceVM model, required DocumentData document}) {
bool allow = false;
bool isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false;
bool isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty && !(document.isLocalFile ?? true);
if (isNetworkImage) {
allow = true;
} else {
@ -257,7 +235,7 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
}
dynamic checkOnCrossPress({required ServiceVM model, required DocumentData document}) async {
bool isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false;
bool isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false;
if (isNetworkImage) {
return document.documentUrl;
} else {
@ -273,27 +251,47 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
}
}
bool chkIsLocalOrNetwork({required DocumentData document}) {
bool isNetworkImage({required DocumentData document}) {
bool isNetworkImage = false;
if (!document.isLocalFile!) {
isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false;
isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false;
}
return isNetworkImage;
}
Widget buildStatusContainer({required DocumentData document}) {
return Center(
child: document.comment.toString().toText(
color: MyColors.adCancelledStatusColor,
fontSize: 14,
// isItalic: true,
),
).toContainer(
marginAll: 10,
paddingAll: 10,
Widget buildCommentContainer({required DocumentData document}) {
String comment = "";
if (document.status == 4 && document.comment != null) {
comment = document.comment ?? "";
}
if (comment.isEmpty) {
return const SizedBox();
}
return Center(child: comment.toString().toText(color: MyColors.adCancelledStatusColor, fontSize: 14)).toContainer(
borderRadius: 8,
margin: const EdgeInsets.only(top: 10),
width: double.infinity,
backgroundColor: MyColors.adCancelledStatusColor.withOpacity(0.16),
);
}
Color getColorByStatus(int docStatus) {
switch (docStatus) {
case 1:
return MyColors.adPendingStatusColor;
case 2:
return MyColors.adActiveStatusColor;
case 3:
return MyColors.greenColor;
case 4:
return MyColors.adCancelledStatusColor;
default:
return MyColors.adPendingStatusColor;
}
}
}

@ -3,7 +3,9 @@ import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:easy_localization/easy_localization.dart';
@ -28,31 +30,46 @@ class SettingOptionsHelp extends StatelessWidget {
Column(
children: [
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.info_rounded, size: 20),
leadingWidget: const Icon(
Icons.info_rounded,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.faqs.tr(),
needBorderBelow: true,
onTap: () {},
onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs),
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.mail, size: 20),
leadingWidget: const Icon(
Icons.mail,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.contactUs.tr(),
needBorderBelow: true,
onTap: () => navigateWithName(
context, AppRoutes.settingOptionsFaqs),
onTap: () => navigateWithName(context, AppRoutes.settingOptionsContactUs),
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.lock, size: 20),
leadingWidget: const Icon(
Icons.lock,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.termPrivacy.tr(),
onTap: () =>
navigateWithName(context, AppRoutes.profileView),
needBorderBelow: true,
onTap: () {},
),
CustomSettingOptionsTile(
leadingWidget: const Icon(
Icons.info_rounded,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.appInfo.tr(),
onTap: () => navigateWithName(context, AppRoutes.settingOptionsAppInfo),
),
],
).toContainer(
width: double.infinity,
isShadowEnabled: true,
paddingAll: 10,
margin: const EdgeInsets.fromLTRB(24, 24, 24, 0),
borderRadius: 0),
).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(21, 24, 21, 0), borderRadius: 0),
],
),
),
@ -61,46 +78,3 @@ class SettingOptionsHelp extends StatelessWidget {
);
}
}
class CustomSettingOptionsTile extends StatelessWidget {
final Widget leadingWidget;
final String titleText;
final bool needBorderBelow;
final bool isForLanguage;
final Function() onTap;
const CustomSettingOptionsTile(
{super.key,
required this.leadingWidget,
required this.onTap,
required this.titleText,
this.needBorderBelow = false,
this.isForLanguage = false});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
leadingWidget,
12.width,
titleText.toText(fontSize: 16),
],
),
isForLanguage
? const Icon(Icons.language, size: 18)
: const Icon(Icons.arrow_forward, size: 18)
],
).onPress(onTap),
5.height,
if (needBorderBelow) ...[
const Divider(thickness: 1),
],
],
);
}
}

@ -0,0 +1,117 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart';
import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart';
import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/setting_options_view_model.dart';
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:provider/provider.dart';
class SettingOptionsAppInfo extends StatefulWidget {
const SettingOptionsAppInfo({super.key});
@override
State<SettingOptionsAppInfo> createState() => _SettingOptionsAppInfoState();
}
class _SettingOptionsAppInfoState extends State<SettingOptionsAppInfo> {
late SettingOptionsVM settingsOptionsVM;
@override
void initState() {
settingsOptionsVM = context.read<SettingOptionsVM>();
scheduleMicrotask(() async {
await settingsOptionsVM.getAppInfoList();
});
super.initState();
}
Widget showData(String title, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText(
fontSize: 13,
color: MyColors.darkTextColor,
),
if (title.isNotEmpty) 5.width,
Flexible(
child: value.toText(
fontSize: 13,
color: MyColors.lightTextColor,
),
)
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.appInfo.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
),
body: Consumer(
builder: (BuildContext context, SettingOptionsVM settingsOptionsVM, Widget? child) {
return Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Column(
children: [
16.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async => await settingsOptionsVM.getAllFaqs(),
child: (settingsOptionsVM.state == ViewState.busy)
? const Center(child: CircularProgressIndicator())
: settingsOptionsVM.appInfoList.isEmpty
? Padding(
padding: const EdgeInsets.all(21),
child: Center(child: LocaleKeys.somethingWrong.tr().toText(textAlign: TextAlign.center, fontSize: 16, color: MyColors.lightTextColor)),
)
: ListView.separated(
itemCount: settingsOptionsVM.appInfoList.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
AppInfoModel appInfoModel = settingsOptionsVM.appInfoList[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(appInfoModel.header ?? "").toString().toText(fontSize: 16),
5.height,
(appInfoModel.content ?? "").toString().toText(fontSize: 14, color: MyColors.lightTextColor),
if (appInfoModel.appInfoImages != null && appInfoModel.appInfoImages!.isNotEmpty) ...[
PickedFilesContainer(
pickedFiles: appInfoModel.appInfoImages ?? [],
isReview: true,
onAddFilePressed: () {},
),
]
],
).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 16.height,
),
)),
],
),
);
},
));
}
}

@ -0,0 +1,123 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart';
import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/setting_options_view_model.dart';
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:provider/provider.dart';
class SettingOptionsContactUs extends StatefulWidget {
const SettingOptionsContactUs({super.key});
@override
State<SettingOptionsContactUs> createState() => _SettingOptionsContactUsState();
}
class _SettingOptionsContactUsState extends State<SettingOptionsContactUs> {
late SettingOptionsVM settingsOptionsVM;
@override
void initState() {
settingsOptionsVM = context.read<SettingOptionsVM>();
scheduleMicrotask(() async {
await settingsOptionsVM.getAllContactInfosList();
});
super.initState();
}
Widget showData(String title, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText(
fontSize: 13,
color: MyColors.darkTextColor,
),
if (title.isNotEmpty) 5.width,
Flexible(
child: value.toText(
fontSize: 13,
color: MyColors.lightTextColor,
),
)
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.contactUs.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
),
body: Consumer(
builder: (BuildContext context, SettingOptionsVM settingsOptionsVM, Widget? child) {
return Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Column(
children: [
16.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async => await settingsOptionsVM.getAllFaqs(),
child: (settingsOptionsVM.state == ViewState.busy)
? const Center(child: CircularProgressIndicator())
: settingsOptionsVM.contactInfosList.isEmpty
? Padding(
padding: const EdgeInsets.all(21),
child: Center(child: LocaleKeys.somethingWrong.tr().toText(textAlign: TextAlign.center, fontSize: 16, color: MyColors.lightTextColor)),
)
: ListView.separated(
itemCount: settingsOptionsVM.contactInfosList.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
ContactInfoModel contactInfoModel = settingsOptionsVM.contactInfosList[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
contactInfoModel.companyName.toString().toText(fontSize: 16),
5.height,
LocaleKeys.openMapLocation.tr().toText(isUnderLine: true, color: MyColors.darkPrimaryColor, fontSize: 10).onPress(() {}),
showData("${LocaleKeys.phoneNumber.tr()}:", "${contactInfoModel.phoneNo}"),
showData("${LocaleKeys.email.tr()}:", "${contactInfoModel.email}"),
if (contactInfoModel.address != null && contactInfoModel.address!.isNotEmpty) ...[
showData("${LocaleKeys.address.tr()}:", ''),
"${contactInfoModel.address}".toText(height: 1.5, fontSize: 13, color: MyColors.lightTextColor),
],
if (contactInfoModel.contactInfoImages != null && contactInfoModel.contactInfoImages!.isNotEmpty) ...[
PickedFilesContainer(
pickedFiles: contactInfoModel.contactInfoImages ?? [],
isReview: true,
onAddFilePressed: () {},
),
]
],
).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 16.height,
),
)),
],
),
);
},
));
}
}

@ -1,61 +1,104 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/setting_options_view_model.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:provider/provider.dart';
class SettingOptionsFAQs extends StatelessWidget {
class SettingOptionsFAQs extends StatefulWidget {
const SettingOptionsFAQs({super.key});
@override
State<SettingOptionsFAQs> createState() => _SettingOptionsFAQsState();
}
class _SettingOptionsFAQsState extends State<SettingOptionsFAQs> {
late SettingOptionsVM settingsOptionsVM;
@override
void initState() {
settingsOptionsVM = context.read<SettingOptionsVM>();
scheduleMicrotask(() async {
await settingsOptionsVM.getAllFaqs();
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.more.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
),
body: Column(
children: [
Expanded(
child: ListView(
children: [
Column(
children: [
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.info, size: 20),
titleText: LocaleKeys.faqs.tr(),
needBorderBelow: true,
onTap: () {}),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.mail, size: 20),
titleText: LocaleKeys.contactUs.tr(),
needBorderBelow: true,
onTap: () {}),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.settings, size: 20),
titleText: LocaleKeys.termPrivacy.tr(),
onTap: () {}),
],
).toContainer(
width: double.infinity,
isShadowEnabled: true,
paddingAll: 10,
margin: const EdgeInsets.fromLTRB(24, 24, 24, 0),
borderRadius: 0),
],
),
),
Row(
children: [
"Version: 1.0.0".toText(fontSize: 14),
],
).paddingAll(21),
],
),
);
appBar: CustomAppBar(
title: LocaleKeys.faqs.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
),
body: Consumer(
builder: (BuildContext context, SettingOptionsVM settingsOptionsVM, Widget? child) {
return Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Column(
children: [
16.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async => await settingsOptionsVM.getAllFaqs(),
child: (settingsOptionsVM.state == ViewState.busy)
? const Center(child: CircularProgressIndicator())
: settingsOptionsVM.faqsList.isEmpty
? Padding(
padding: const EdgeInsets.all(21),
child: Center(child: LocaleKeys.noFAQsToShow.tr().toText(textAlign: TextAlign.center, fontSize: 16, color: MyColors.lightTextColor)),
)
: ListView.separated(
itemCount: settingsOptionsVM.faqsList.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
FAQsModel faqModel = settingsOptionsVM.faqsList[index];
return ExpansionTile(
tilePadding: EdgeInsets.zero,
shape: const Border(),
title: (faqModel.question ?? "").toText(fontSize: 16).paddingOnly(left: 8, right: 8),
onExpansionChanged: (value) {
setState(() {
settingsOptionsVM.faqsList[index].isCollapsed = value;
});
},
backgroundColor: Colors.transparent,
collapsedBackgroundColor: Colors.transparent,
initiallyExpanded: settingsOptionsVM.faqsList[index].isCollapsed ?? false,
trailing: (settingsOptionsVM.faqsList[index].isCollapsed ?? false) ? const Icon(Icons.keyboard_arrow_up) : const Icon(Icons.keyboard_arrow_down),
subtitle: ("Channel: ${faqModel.channel.toString()}").toText(color: MyColors.lightTextColor).paddingOnly(left: 8, right: 8),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
5.height,
(faqModel.answer ?? "").toText(color: MyColors.lightTextColor, fontSize: 14, fontWeight: MyFonts.Medium),
10.height,
],
).paddingOnly(left: 8, right: 8)
],
).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 12.height,
),
)),
],
),
);
},
));
}
}

@ -1,21 +1,76 @@
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:easy_localization/easy_localization.dart';
class SettingOptionsInviteFriends extends StatelessWidget {
const SettingOptionsInviteFriends({super.key});
buildInviteFriendsBottomSheet(BuildContext context) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return InfoBottomSheet(
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
20.height,
CustomSettingOptionsTile(
leadingWidget: MyAssets.whatsAppIcon.buildSvg(
height: 20,
width: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.inviteFriendsByWhatsApp.tr(),
needBorderBelow: true,
showTrailingArrow: false,
onTap: () {},
),
CustomSettingOptionsTile(
leadingWidget: const Icon(
Icons.sms_outlined,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.inviteFriendsBySMS.tr(),
needBorderBelow: true,
showTrailingArrow: false,
onTap: () {},
),
CustomSettingOptionsTile(
leadingWidget: const Icon(
Icons.email_outlined,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.inviteFriendsByEmail.tr(),
needBorderBelow: false,
showTrailingArrow: false,
onTap: () {},
),
30.height,
],
),
title: const SizedBox());
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.more.tr(),
title: LocaleKeys.settings.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
@ -28,31 +83,34 @@ class SettingOptionsInviteFriends extends StatelessWidget {
Column(
children: [
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.person, size: 20),
leadingWidget: const Icon(Icons.group, size: 20),
titleText: LocaleKeys.inviteFriends.tr(),
needBorderBelow: true,
onTap: () {},
onTap: () {
return buildInviteFriendsBottomSheet(context);
},
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.help, size: 20),
leadingWidget: const Icon(Icons.question_mark_outlined, size: 20),
titleText: LocaleKeys.help.tr(),
needBorderBelow: true,
onTap: () => navigateWithName(
context, AppRoutes.settingOptionsFaqs),
onTap: () => navigateWithName(context, AppRoutes.settingOptionsHelp),
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.person, size: 20),
titleText: LocaleKeys.account.tr(),
onTap: () =>
navigateWithName(context, AppRoutes.profileView),
),
leadingWidget: const Icon(Icons.person, size: 20),
titleText: LocaleKeys.account.tr(),
needBorderBelow: false,
onTap: () {
navigateWithName(context, AppRoutes.profileView);
}),
],
).toContainer(
width: double.infinity,
isShadowEnabled: true,
paddingAll: 10,
margin: const EdgeInsets.fromLTRB(24, 24, 24, 0),
borderRadius: 0),
width: double.infinity,
isShadowEnabled: true,
paddingAll: 10,
margin: const EdgeInsets.fromLTRB(21, 21, 21, 0),
borderRadius: 0,
),
],
),
),
@ -61,46 +119,3 @@ class SettingOptionsInviteFriends extends StatelessWidget {
);
}
}
class CustomSettingOptionsTile extends StatelessWidget {
final Widget leadingWidget;
final String titleText;
final bool needBorderBelow;
final bool isForLanguage;
final Function() onTap;
const CustomSettingOptionsTile(
{super.key,
required this.leadingWidget,
required this.onTap,
required this.titleText,
this.needBorderBelow = false,
this.isForLanguage = false});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
leadingWidget,
12.width,
titleText.toText(fontSize: 16),
],
),
isForLanguage
? const Icon(Icons.language, size: 18)
: const Icon(Icons.arrow_forward, size: 18)
],
).onPress(onTap),
5.height,
if (needBorderBelow) ...[
const Divider(thickness: 1),
],
],
);
}
}

@ -1,223 +0,0 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.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/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/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/user_view_model.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class SettingOptionsLanguage extends StatefulWidget {
const SettingOptionsLanguage({super.key});
@override
State<SettingOptionsLanguage> createState() => _SettingOptionsLanguageState();
}
class _SettingOptionsLanguageState extends State<SettingOptionsLanguage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.more.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
),
body: Column(
children: [
// (AppState().currentAppType == AppType.provider)
// ?
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.zero,
children: [
Column(
children: [
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.quickreply_outlined, size: 20),
titleText: LocaleKeys.requests.tr(),
needBorderBelow: true,
onTap: () {
context.read<DashboardVmCustomer>().onNavbarTapped(4);
Navigator.pop(context);
}),
if (AppState().currentAppType == AppType.customer) ...[
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.favorite, size: 20),
titleText: LocaleKeys.favoriteList.tr(),
needBorderBelow: true,
onTap: () => navigateWithName(context, AppRoutes.favoriteListView),
),
],
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.settings, size: 20),
titleText: LocaleKeys.settings.tr(),
needBorderBelow: true,
onTap: () {},
//navigateWithName(context, AppRoutes.settingOptionsInviteFriends),
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.question_mark_outlined, size: 20),
titleText: LocaleKeys.help.tr(),
needBorderBelow: true,
onTap: () => navigateWithName(context, AppRoutes.settingOptionsHelp),
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.person, size: 20),
titleText: LocaleKeys.account.tr(),
needBorderBelow: false,
onTap: () {
navigateWithName(context, AppRoutes.profileView);
// context.read<DashboardVmCustomer>().onNavbarTapped(4);
// Navigator.pop(context);
}),
],
).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0),
10.height,
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.translate, size: 20),
titleText: LocaleKeys.language.tr(),
isForLanguage: true,
onTap: () {
context.read<UserVM>().changeLanguage(context);
Future.delayed(Duration(seconds: 1), () {
setState(() {});
});
},
).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0),
10.height,
(AppState().currentAppType == AppType.provider)
? Column(
children: [
CustomSettingOptionsTile(
leadingWidget: SizedBox(
width: 16,
height: 16,
child: SvgPicture.asset(
MyAssets.icStar,
color: MyColors.primaryColor,
),
),
titleText: LocaleKeys.mySubscription.tr(),
subTitle: AppState().getproviderSubscription.first.name,
isForLanguage: false,
needBorderBelow: true,
onTap: () {
navigateWithName(context, AppRoutes.mySubscriptionsPage);
},
),
CustomSettingOptionsTile(
leadingWidget: SizedBox(
width: 16,
height: 16,
child: SvgPicture.asset(
MyAssets.icGroupStar,
// color: MyColors.primaryColor,
),
),
titleText: LocaleKeys.subscriptions.tr(),
subTitle: null,
isForLanguage: false,
needBorderBelow: true,
onTap: () {
navigateWithName(context, AppRoutes.subscriptionsPage);
},
),
CustomSettingOptionsTile(
leadingWidget: SizedBox(
width: 16,
height: 16,
child: SvgPicture.asset(
MyAssets.icGroupStar,
// color: MyColors.primaryColor,
),
),
titleText: LocaleKeys.defineLicenses.tr(),
subTitle: null,
isForLanguage: false,
needBorderBelow: false,
onTap: () {
navigateWithName(context, AppRoutes.providerLicensePage);
},
),
// CustomSettingOptionsTile(
// leadingWidget: SizedBox(
// width: 16,
// height: 16,
// child: SvgPicture.asset(
// MyAssets.providersIcon,
// // color: MyColors.primaryColor,
// ),
// ),
// titleText: "Users",
// subTitle: null,
// isForLanguage: false,
// needBorderBelow: false,
// onTap: () {
// navigateWithName(context, AppRoutes.dealerUser, arguments: AppState().getUser.data!.userInfo!.id.toString());
// },
// )
],
).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0)
: const SizedBox(),
(AppState().currentAppType == AppType.provider) ? 10.height : const SizedBox(),
],
)),
// : Expanded(
// child: ListView(
// children: [
// Column(
// children: [
// CustomSettingOptionsTile(
// leadingWidget: const Icon(Icons.person, size: 20),
// titleText: "My Requests",
// needBorderBelow: true,
// onTap: () {
// context.read<DashboardVmCustomer>().onNavbarTapped(4);
// Navigator.pop(context);
// }),
// CustomSettingOptionsTile(leadingWidget: const Icon(Icons.favorite, size: 20), titleText: "Favorite list", needBorderBelow: true, onTap: () {}),
// CustomSettingOptionsTile(
// leadingWidget: const Icon(Icons.settings, size: 20), titleText: "Settings", onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends)),
// ],
// ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0),
// CustomSettingOptionsTile(leadingWidget: const Icon(Icons.translate, size: 20), titleText: "Language", isForLanguage: true, onTap: () {})
// .toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, marginAll: 21, borderRadius: 0),
// ],
// ),
// ),
(AppState().currentAppType == AppType.provider) ? Text(LocaleKeys.provider.tr()) : Text(LocaleKeys.customer.tr()),
Row(
children: [
Expanded(
child: ShowFillButton(
borderColor: MyColors.redColor,
txtColor: MyColors.redColor,
isFilled: false,
fontSize: 16,
maxHeight: 55,
title: LocaleKeys.logOut.tr(),
onPressed: () {
context.read<UserVM>().logout(context);
},
),
),
],
).paddingAll(0),
],
).paddingAll(21),
);
}
}

@ -0,0 +1,210 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.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/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/user_view_model.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class SettingOptionsMore extends StatefulWidget {
const SettingOptionsMore({super.key});
@override
State<SettingOptionsMore> createState() => _SettingOptionsMoreState();
}
class _SettingOptionsMoreState extends State<SettingOptionsMore> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.more.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
),
body: Column(
children: [
// (AppState().currentAppType == AppType.provider)
// ?
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.zero,
children: [
Column(
children: [
if (AppState().currentAppType == AppType.customer) ...[
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.favorite, size: 20),
titleText: LocaleKeys.favoriteList.tr(),
needBorderBelow: true,
onTap: () => navigateWithName(context, AppRoutes.favoriteListView),
),
] else ...[
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.local_shipping, size: 20),
titleText: LocaleKeys.shippingManagement.tr(),
needBorderBelow: true,
onTap: () {
navigateWithName(context, AppRoutes.shippingManagementView);
},
),
],
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.settings, size: 20),
titleText: LocaleKeys.settings.tr(),
needBorderBelow: false,
onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends),
),
],
).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0),
10.height,
if (!disableThingsForQA) ...[
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.translate, size: 20),
titleText: LocaleKeys.language.tr(),
isForLanguage: true,
onTap: () {
context.read<UserVM>().changeLanguage(context);
Future.delayed(const Duration(seconds: 1), () {
setState(() {});
});
},
).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0),
10.height,
],
(AppState().currentAppType == AppType.provider)
? Column(
children: [
CustomSettingOptionsTile(
leadingWidget: SizedBox(
width: 16,
height: 16,
child: SvgPicture.asset(
MyAssets.icStar,
color: MyColors.primaryColor,
),
),
titleText: LocaleKeys.mySubscription.tr(),
subTitle: AppState().getproviderSubscription.first.name ?? "",
isForLanguage: false,
needBorderBelow: true,
onTap: () {
navigateWithName(context, AppRoutes.mySubscriptionsPage);
},
),
CustomSettingOptionsTile(
leadingWidget: SizedBox(
width: 16,
height: 16,
child: SvgPicture.asset(
MyAssets.icGroupStar,
// color: MyColors.primaryColor,
),
),
titleText: LocaleKeys.subscriptions.tr(),
subTitle: null,
isForLanguage: false,
needBorderBelow: true,
onTap: () {
navigateWithName(context, AppRoutes.subscriptionsPage);
},
),
CustomSettingOptionsTile(
leadingWidget: SizedBox(
width: 16,
height: 16,
child: SvgPicture.asset(
MyAssets.icGroupStar,
// color: MyColors.primaryColor,
),
),
titleText: LocaleKeys.defineLicenses.tr(),
subTitle: null,
isForLanguage: false,
needBorderBelow: false,
onTap: () {
navigateWithName(context, AppRoutes.providerLicensePage);
},
),
// CustomSettingOptionsTile(
// leadingWidget: SizedBox(
// width: 16,
// height: 16,
// child: SvgPicture.asset(
// MyAssets.providersIcon,
// // color: MyColors.primaryColor,
// ),
// ),
// titleText: "Users",
// subTitle: null,
// isForLanguage: false,
// needBorderBelow: false,
// onTap: () {
// navigateWithName(context, AppRoutes.dealerUser, arguments: AppState().getUser.data!.userInfo!.id.toString());
// },
// )
],
).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0)
: const SizedBox(),
(AppState().currentAppType == AppType.provider) ? 10.height : const SizedBox(),
],
)),
// : Expanded(
// child: ListView(
// children: [
// Column(
// children: [
// CustomSettingOptionsTile(
// leadingWidget: const Icon(Icons.person, size: 20),
// titleText: "My Requests",
// needBorderBelow: true,
// onTap: () {
// context.read<BaseVM>().onNavbarTapped(4);
// Navigator.pop(context);
// }),
// CustomSettingOptionsTile(leadingWidget: const Icon(Icons.favorite, size: 20), titleText: "Favorite list", needBorderBelow: true, onTap: () {}),
// CustomSettingOptionsTile(
// leadingWidget: const Icon(Icons.settings, size: 20), titleText: "Settings", onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends)),
// ],
// ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0),
// CustomSettingOptionsTile(leadingWidget: const Icon(Icons.translate, size: 20), titleText: "Language", isForLanguage: true, onTap: () {})
// .toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, marginAll: 21, borderRadius: 0),
// ],
// ),
// ),
Row(
children: [
Expanded(
child: ShowFillButton(
borderColor: MyColors.redColor,
txtColor: MyColors.redColor,
isFilled: false,
fontSize: 16,
maxHeight: 55,
title: LocaleKeys.logOut.tr(),
onPressed: () {
context.read<UserVM>().logout(context);
},
),
),
],
).paddingAll(0),
],
).paddingAll(21),
);
}
}

@ -1,17 +1,18 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:sizer/sizer.dart';
class CustomSettingOptionsTile extends StatelessWidget {
final Widget leadingWidget;
final String titleText;
final bool needBorderBelow;
final bool isForLanguage;
final bool showTrailingArrow;
final Function() onTap;
final String? subTitle;
@ -22,6 +23,7 @@ class CustomSettingOptionsTile extends StatelessWidget {
required this.titleText,
this.needBorderBelow = false,
this.isForLanguage = false,
this.showTrailingArrow = true,
this.subTitle,
});
@ -61,7 +63,9 @@ class CustomSettingOptionsTile extends StatelessWidget {
// )
//
: const Icon(Icons.arrow_forward, size: 18)
: showTrailingArrow
? const Icon(Icons.arrow_forward, size: 18)
: const SizedBox()
],
).onPress(onTap),
subTitle != null ? 3.height : 5.height,
@ -76,10 +80,11 @@ class CustomSettingOptionsTile extends StatelessWidget {
class CustomProfileOptionsTile extends StatelessWidget {
final String titleText;
final bool needBorderBelow;
final bool needEditButton;
final Function() onTap;
final String subtitleText;
const CustomProfileOptionsTile({super.key, required this.onTap, required this.titleText, required this.subtitleText, this.needBorderBelow = false});
const CustomProfileOptionsTile({super.key, required this.onTap, required this.titleText, required this.subtitleText, this.needBorderBelow = false, this.needEditButton = true});
@override
Widget build(BuildContext context) {
@ -95,7 +100,7 @@ class CustomProfileOptionsTile extends StatelessWidget {
subtitleText.toText(fontSize: 12, color: MyColors.darkTextColor.withOpacity(0.7)),
],
),
const Icon(Icons.edit_note, size: 25),
if (needEditButton) ...[MyAssets.icEdit.buildSvg()],
],
).onPress(onTap),
5.height,

@ -0,0 +1,199 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/shipping_models/shipping_status_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/shipping_management_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class ShippingManagementView extends StatefulWidget {
const ShippingManagementView({super.key});
@override
State<ShippingManagementView> createState() => _ShippingManagementViewState();
}
class _ShippingManagementViewState extends State<ShippingManagementView> {
late ShippingManagementVM shippingViewModel;
@override
void initState() {
shippingViewModel = context.read<ShippingManagementVM>();
scheduleMicrotask(() async {
if (shippingViewModel.shippingRequestFilterOptions.isNotEmpty) {
await shippingViewModel.populateShippingRequestFilterList();
}
await shippingViewModel.getShippingRequestsListByFilters();
});
super.initState();
}
Future buildUpdateShippingStatusBottomSheet({required int shippingRequestId}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, ShippingManagementVM shippingManagementVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
ListView.separated(
shrinkWrap: true,
itemCount: shippingManagementVM.shippingRequestStatusesList.length,
separatorBuilder: (BuildContext context, int index) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 3.0),
child: Divider(thickness: 0.5),
);
},
itemBuilder: (BuildContext context, int index) {
List<FilterListModel> list = shippingManagementVM.shippingRequestStatusesList;
FilterListModel shippingFilterListModel = list[index];
return CircleCheckBoxWithTitle(
isChecked: shippingFilterListModel.isSelected,
title: shippingFilterListModel.title,
onSelected: () {
shippingManagementVM.updateSelectionInShippingRequestStatuses(index);
},
selectedColor: MyColors.darkPrimaryColor,
);
},
),
12.height,
TxtField(
maxLines: 4,
value: shippingManagementVM.requestStatusComments,
errorValue: "",
keyboardType: TextInputType.text,
hint: LocaleKeys.comment.tr(),
onChanged: (v) => shippingManagementVM.updateRequestStatusComments(v),
),
],
),
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
onPressed: () async {
FilterListModel value = shippingManagementVM.shippingRequestStatusesList.firstWhere((element) => element.isSelected);
ShippingRequestStatusEnum shippingStatusEnum = value.id.toShippingStatusEnum();
bool status = await shippingManagementVM.onUpdateShippingStatusTapped(shippingStatusEnum: shippingStatusEnum, shippingRequestId: shippingRequestId, context: context);
if (status) {
pop(context);
await shippingManagementVM.getShippingRequestsListByFilters();
}
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
@override
void dispose() {
shippingViewModel.resetFilters();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Consumer(builder: (BuildContext context, ShippingManagementVM shippingVm, Widget? child) {
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.shippingManagement.tr()),
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Column(
children: [
16.height,
FiltersList(
filterList: shippingVm.shippingRequestFilterOptions,
onFilterTapped: (index, selectedFilterId) {
shippingVm.applyFiltersOnShippingRequests(shippingRequestStatusEnum: selectedFilterId.toShippingStatusEnum());
},
),
8.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async {
int index = shippingVm.shippingRequestFilterOptions.indexWhere((element) => element.isSelected);
await shippingVm.getShippingRequestsListByFilters(shippingStatusEnum: index.toShippingStatusEnum());
},
child: (shippingVm.state == ViewState.busy)
? const Center(
child: CircularProgressIndicator(),
)
: shippingVm.shippingRequestsList.isEmpty
? Center(child: LocaleKeys.noRequeststoShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor))
: ListView.separated(
itemCount: shippingVm.shippingRequestsList.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
ShippingRequestModel shippingRequest = shippingVm.shippingRequestsList[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.statusContainerChip(
text: Utils.getNameByShippingRequestStatusEnum(shippingRequest.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated),
chipColor: Utils.getChipColorByShippingRequestStatusEnum(shippingRequest.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated),
),
("Request Name | ${shippingRequest.id.toString()}").toText(fontSize: 16),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
"customer name".toText(color: MyColors.lightTextColor, fontSize: 14),
if (shippingRequest.createdOn != null && shippingRequest.createdOn!.isNotEmpty) ...[
" | ${DateTime.parse(shippingRequest.createdOn!).getTimeAgo()}".toText(color: MyColors.lightTextColor, fontSize: 14),
],
],
),
const Icon(Icons.arrow_forward, color: MyColors.darkIconColor, size: 18),
],
),
],
).onPress(() {
buildUpdateShippingStatusBottomSheet(shippingRequestId: shippingRequest.id!);
}).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 16.height,
),
)),
],
),
));
});
}
}

@ -31,7 +31,9 @@ class _ChangeEmailPageState extends State<ChangeEmailPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar( isRemoveBackButton: true, title: LocaleKeys.changeEmail.tr()),
appBar: CustomAppBar(
title: LocaleKeys.changeEmail.tr(),
),
body: SingleChildScrollView(
child: Container(
// width: double.infinity,
@ -39,7 +41,11 @@ class _ChangeEmailPageState extends State<ChangeEmailPage> {
padding: const EdgeInsets.all(20),
child: Column(
children: [
LocaleKeys.enterEmail.tr().toText(height: 23 / 24, fontSize: 24, letterSpacing: -1.44,),
LocaleKeys.enterEmail.tr().toText(
height: 23 / 24,
fontSize: 24,
letterSpacing: -1.44,
),
12.height,
TxtField(
hint: LocaleKeys.enterNewEmail.tr(),

@ -31,15 +31,19 @@ class _ChangeMobilePageState extends State<ChangeMobilePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar( isRemoveBackButton: true, title: LocaleKeys.changeMobile.tr()),
appBar: CustomAppBar(title: LocaleKeys.changeMobile.tr()),
body: SingleChildScrollView(
child: Container(
// width: double.infinity,
// height: double.infinity,
padding: EdgeInsets.all(20),
padding: const EdgeInsets.all(20),
child: Column(
children: [
LocaleKeys.enterNewPhoneNumber.tr().toText(height: 23 / 24, fontSize: 24, letterSpacing: -1.44,),
LocaleKeys.enterNewPhoneNumber.tr().toText(
height: 23 / 24,
fontSize: 24,
letterSpacing: -1.44,
),
12.height,
TxtField(
hint: LocaleKeys.enterNewPhoneNumber.tr(),

@ -31,7 +31,7 @@ class _ChangePasswordPageState extends State<ChangePasswordPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar( isRemoveBackButton: true, title: LocaleKeys.changePassword.tr()),
appBar: CustomAppBar(title: LocaleKeys.changePassword.tr()),
body: SingleChildScrollView(
child: Container(
// width: double.infinity,
@ -39,7 +39,11 @@ class _ChangePasswordPageState extends State<ChangePasswordPage> {
padding: const EdgeInsets.all(20),
child: Column(
children: [
LocaleKeys.enterNewPassword.tr().toText(height: 23 / 24, fontSize: 24, letterSpacing: -1.44,),
LocaleKeys.enterNewPassword.tr().toText(
height: 23 / 24,
fontSize: 24,
letterSpacing: -1.44,
),
12.height,
TxtField(
hint: LocaleKeys.enterOldPassword.tr(),

@ -73,7 +73,6 @@ class _EditAccountPageState extends State<EditAccountPage> {
),
],
).toWhiteContainer(width: double.infinity, pading: EdgeInsets.symmetric(horizontal: 12)),
Column(
children: [
CustomSettingOptionsTile(

@ -34,10 +34,9 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
//TODO: ONLY FOR DEVELOPMENT PURPOSE
// String phoneNum = "966504278212", password = "Fa@1234";
// String phoneNum = "966504278213", password = "Fa@1234";
String phoneNum = "", password = "";
// String phoneNum = "966504278213", password = "Fa@1234";
// String phoneNum = "", password = "";
String phoneNum = "", password = "";
String email = "";
String countryCode = "";
Country? _country;
@ -52,23 +51,26 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
scheduleMicrotask(() {
userVM = Provider.of(context, listen: false);
context.read<UserVM>().getAvailBio();
// if (AppState().currentAppType == AppType.provider) {
// phoneNum = "966530896018";
// password = "Amir@1234";
// //
// // // phoneNum = "966569755630";
// // // password = "Amir12345@"; //AAmir Sal
// //
// // // phoneNum = "966598646795";
// // // password = "Zahoor@123";
// //
// // // User Details
// // // phoneNum = "966554870506";
// // // password = "Sikander123@"; // "lastName": "sik",
// // // "email": "sikander@gmail.com",
// // // phoneNum = "966569755630";
// // // password = "Amir12345@";
// }
if (AppState().currentAppType == AppType.provider) {
// phoneNum = "966530896018";
// password = "Amir@1234";
//
// phoneNum = "966530896020"; // JAMES BOND
// password = "Amir@1234";
//
// // phoneNum = "966569755630";
// // password = "Amir12345@"; //AAmir Sal
//
// // phoneNum = "966598646795";
// // password = "Zahoor@123";
//
// // User Details
// // phoneNum = "966554870506";
// // password = "Sikander123@"; // "lastName": "sik",
// // "email": "sikander@gmail.com",
// // phoneNum = "966569755630";
// // password = "Amir12345@";
}
getCountryList();
});
@ -91,10 +93,15 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
child: Column(
children: [
12.height,
LocaleKeys.login.tr().toText(
fontSize: 20,
letterSpacing: -1.44,
),
(AppState().currentAppType == AppType.provider ? "Provider" : "Customer").toText(
fontSize: 25,
letterSpacing: -1.44,
),
8.height,
(LocaleKeys.login.tr()).toText(
fontSize: 20,
letterSpacing: -1.44,
),
// 20.height,
// (type == ClassType.NUMBER ? LocaleKeys.\enterPhoneNumber.tr() : LocaleKeys.enterEmail.tr()).toText14(
// color: MyColors.lightTextColor,

@ -1,10 +1,10 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:sizer/sizer.dart';
class DropValue {
int id;
@ -55,40 +55,43 @@ class _DropdownFieldState extends State<DropdownField> {
dropdownValue = widget.dropdownValue;
return Column(
children: [
Container(
decoration: widget.showAppointmentPickerVariant ? null : Utils.containerColorRadiusBorderWidth(MyColors.white, 0, MyColors.darkPrimaryColor, 2),
margin: const EdgeInsets.all(0),
padding: const EdgeInsets.only(left: 8, right: 8),
width: widget.showAppointmentPickerVariant ? 170 : null,
child: DropdownButton<DropValue>(
value: dropdownValue,
icon: const Icon(Icons.keyboard_arrow_down_sharp),
elevation: 16,
iconSize: widget.showAppointmentPickerVariant ? 26 : 16,
iconEnabledColor: borderColor,
iconDisabledColor: borderColor,
isExpanded: true,
style: const TextStyle(
color: borderColor,
fontSize: 15,
),
hint: (widget.hint ?? "").toText(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium),
underline: Container(height: 0),
onChanged: (DropValue? newValue) {
setState(() {
dropdownValue = newValue!;
widget.onSelect(newValue);
});
},
items: (widget.list ?? defaultV).map<DropdownMenuItem<DropValue>>(
(DropValue value) {
return DropdownMenuItem<DropValue>(
value: value,
enabled: value.isEnabled ?? true,
child: value.value.toText(fontSize: 15, color: value.isEnabled == false ? MyColors.darkTextColor : null, fontWeight: MyFonts.Medium),
);
IgnorePointer(
ignoring: !widget.isSelectAble,
child: Container(
decoration: widget.showAppointmentPickerVariant ? null : Utils.containerColorRadiusBorderWidth(MyColors.white, 0, widget.isSelectAble ? MyColors.darkPrimaryColor : MyColors.greyACColor, 2),
margin: const EdgeInsets.all(0),
padding: const EdgeInsets.only(left: 8, right: 8),
width: widget.showAppointmentPickerVariant ? 170 : null,
child: DropdownButton<DropValue>(
value: dropdownValue,
icon: Icon(Icons.keyboard_arrow_down_sharp, color: !widget.isSelectAble ? Colors.transparent : null),
elevation: 16,
iconSize: widget.showAppointmentPickerVariant ? 26 : 16,
iconEnabledColor: borderColor,
iconDisabledColor: borderColor,
isExpanded: true,
style: const TextStyle(
color: borderColor,
fontSize: 15,
),
hint: (widget.hint ?? "").toText(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium),
underline: Container(height: 0),
onChanged: (DropValue? newValue) {
setState(() {
dropdownValue = newValue!;
widget.onSelect(newValue);
});
},
).toList(),
items: (widget.list ?? defaultV).map<DropdownMenuItem<DropValue>>(
(DropValue value) {
return DropdownMenuItem<DropValue>(
value: value,
enabled: value.isEnabled ?? true,
child: value.value.toText(fontSize: 15, color: value.isEnabled == false ? MyColors.darkTextColor : null, fontWeight: MyFonts.Medium),
);
},
).toList(),
),
),
),
if (widget.errorValue != "")

@ -337,7 +337,6 @@ extension xDouble on int {
extension BuildSVG on String? {
Widget buildFileImage({double? height, double? width, BoxFit fit = BoxFit.contain, Color? color}) {
logger.i("!File(this!).existsSync(): ${!File(this!).existsSync()}");
if (this == null || this!.isEmpty || !File(this!).existsSync()) {
return SizedBox(
height: height,
@ -369,14 +368,19 @@ extension BuildSVG on String? {
return SizedBox(
height: height,
width: width,
child: SvgPicture.asset(MyAssets.providersIcon, color: MyColors.darkPrimaryColor).paddingAll(15),
child: Image.asset(MyAssets.brokenImage).paddingAll(10),
);
}
return Image.network(
this!,
errorBuilder: (BuildContext context, Object obj, StackTrace? s) {
return SizedBox(height: height, width: width, child: const Icon(Icons.signal_wifi_connected_no_internet_4_outlined));
return Container(
height: height,
width: width,
decoration: BoxDecoration(border: Border.all(color: MyColors.roundedCrossBgColor)),
child: const Icon(Icons.image_not_supported_outlined, color: MyColors.roundedCrossBgColor),
);
},
loadingBuilder: (BuildContext context, Widget? child, ImageChunkEvent? imageChunk) {
if (imageChunk == null) {

@ -0,0 +1,114 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class MediaViewerScreen extends StatefulWidget {
final List<MessageImageModel> images; // List of image URLs or file paths
const MediaViewerScreen({Key? key, required this.images}) : super(key: key);
@override
MediaViewerScreenState createState() => MediaViewerScreenState();
}
class MediaViewerScreenState extends State<MediaViewerScreen> {
int selectedIndex = 0; // Track selected image index
@override
Widget build(BuildContext context) {
debugPrint("loaded");
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.pictures.tr()),
body: Column(
children: [
// Main Image Display (selected image)
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: InteractiveViewer(
panEnabled: true,
// Allow panning
scaleEnabled: true,
// Allow zooming
minScale: 1.0,
// Minimum zoom level
maxScale: 4.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: (widget.images[selectedIndex].isFromNetwork ?? false)
? widget.images[selectedIndex].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
)
: widget.images[selectedIndex].imagePath.buildFileImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
),
),
),
),
),
// Thumbnail List at the bottom
Container(
height: 100, // Set height for thumbnail list
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: widget.images.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index; // Change the selected image
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Container(
width: 80, // Thumbnail size
height: 80,
decoration: BoxDecoration(
border: Border.all(
color: selectedIndex == index ? MyColors.darkPrimaryColor : MyColors.greyShadowColor, // Highlight selected image
width: selectedIndex == index ? 2 : 1,
),
borderRadius: BorderRadius.circular(10),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: (widget.images[index].isFromNetwork ?? false)
? widget.images[index].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
)
: widget.images[index].imagePath.buildFileImage(
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
),
),
),
);
},
),
),
10.height,
],
),
);
}
}
Loading…
Cancel
Save