Added Settings Modules

faiz_development_common
Faiz Hashmi 2 days ago
parent 0230afc5c7
commit 6e74801852

@ -665,11 +665,19 @@
"itemName": "اسم العنصر",
"itemDescription": "وصف العنصر",
"itemPrice": "سعر العنصر",
"bookAppointmentForServices": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.",
"showServiceAvailability": "سيظهر هذا الخيار للعميل ما إذا كان بإمكانه الحصول على هذه الخدمة في الورشة أم لا.",
"bookAppointmentAtLocation": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يرغب فيه.",
"appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.",
"workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.",
"appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره.",
"deleteScheduleConfirmation": "هل أنت متأكد أنك تريد حذف هذا الجدول؟",
"deleteScheduleAdConfirmationMessage": "سيتم إزالة جميع فترات المواعيد ولن يتمكن العملاء من حجز مواعيد لهذا الجدول.",
"branchSchedules": "جداول الفرع",
"noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع."
"noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع.",
"inviteFriendsBySMS": "دعوة الأصدقاء عبر الرسائل القصيرة",
"inviteFriendsByWhatsApp": "دعوة الأصدقاء عبر واتساب",
"inviteFriendsByEmail": "دعوة الأصدقاء عبر البريد الإلكتروني",
"noFAQsToShow": "لا توجد أسئلة شائعة في الوقت الحالي. يرجى الاتصال بنا إذا كان لديك أي استفسار.",
"appInfo": "معلومات التطبيق"
}

@ -672,5 +672,10 @@
"deleteScheduleConfirmation": " Are you sure you want to delete this Schedule?",
"deleteScheduleAdConfirmationMessage": "All the appointment slots will be removed and customers will no longer be able to book appointments for this schedule.",
"branchSchedules": "Branch Schedules",
"noSchedulesFound": "You have not added any schedule for this branch."
"noSchedulesFound": "You have not added any schedule for this branch.",
"inviteFriendsBySMS": "Invite Friends By SMS",
"inviteFriendsByWhatsApp": "Invite Friends By WhatsApp",
"inviteFriendsByEmail": "Invite Friends By Email",
"noFAQsToShow": "There are no Frequently asked Questions Right now. Please contact us if you have any query.",
"appInfo": "App Info"
}

@ -174,6 +174,12 @@ class ApiConsts {
static String getChatMessagesForAds = "${baseUrlServices}api/Advertisement/AdsChat_Get";
static String getChatBuyersForAds = "${baseUrlServices}api/Advertisement/AdsChatBuyer_Get";
//Settings Options
static String getAllFAQs = "${baseUrlServices}api/Common/FAQ_Get";
static String getAppInvitationLink = "${baseUrlServices}api/Common/AppInvitation_Get";
static String getContactInfo = "${baseUrlServices}api/Master/ContactInfo_Get";
static String getAppInfo = "${baseUrlServices}api/Master/AppInfo_Get";
static List<String> closingUrls = ["PayFortResponse"];
}

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

@ -20,9 +20,11 @@ import 'package:mc_common_app/views/requests/requests_filter_view.dart';
import 'package:mc_common_app/views/requests/review_request_offer.dart';
import 'package:mc_common_app/views/setting_options/provider_license_page.dart';
import 'package:mc_common_app/views/setting_options/setting_option_help.dart';
import 'package:mc_common_app/views/setting_options/setting_options_app_info.dart';
import 'package:mc_common_app/views/setting_options/setting_options_contact_us.dart';
import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart';
import 'package:mc_common_app/views/setting_options/setting_options_invite_friends.dart';
import 'package:mc_common_app/views/setting_options/setting_options_language.dart';
import 'package:mc_common_app/views/setting_options/setting_options_more.dart';
import 'package:mc_common_app/views/shipping_management/shipping_management_view.dart';
import 'package:mc_common_app/views/user/change_email_page.dart';
import 'package:mc_common_app/views/user/change_mobile_page.dart';
@ -113,7 +115,9 @@ class AppRoutes {
//Setting Options
static const String settingOptionsFaqs = "/settingOptionsFaqs";
static const String settingOptionsLanguages = "/settingOptionsLanguages";
static const String settingOptionsContactUs = "/settingOptionsContactUs";
static const String settingOptionsAppInfo = "/settingOptionsAppInfo";
static const String settingOptionsMore = "/settingOptionsLanguages";
static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends";
static const String settingOptionsHelp = "/settingOptionsHelp";
@ -128,9 +132,12 @@ class AppRoutes {
//Provider User Page
static const dealerUser = "/dealerUser";
// HomePage Provider
static const String generalChatsListForProvider = "/generalChatsListForProvider";
//Chat
static const String chatView = "/chatView";
static const String initialRoute = splash;
static final Map<String, WidgetBuilder> routes = {
@ -153,7 +160,7 @@ class AppRoutes {
changePassword: (context) => const ChangePasswordPage(),
editAccountPage: (context) => const EditAccountPage(),
profileView: (context) => const ProfileScreen(),
settingOptionsLanguages: (context) => const SettingOptionsLanguage(),
settingOptionsMore: (context) => const SettingOptionsMore(),
settingOptionsHelp: (context) => const SettingOptionsHelp(),
providerLicensePage: (context) => const ProviderLicensePage(),
@ -165,6 +172,8 @@ class AppRoutes {
AppRoutes.chatView: (context) => ChatView(chatViewArguments: ModalRoute.of(context)!.settings.arguments as ChatViewArguments),
AppRoutes.adsBuyerChatsListView: (context) => AdsBuyerChatsView(buyersListViewArguments: ModalRoute.of(context)!.settings.arguments as List<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
@ -177,6 +186,9 @@ class AppRoutes {
//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(),
};

@ -681,13 +681,21 @@ class CodegenLoader extends AssetLoader{
"itemName": "اسم العنصر",
"itemDescription": "وصف العنصر",
"itemPrice": "سعر العنصر",
"bookAppointmentForServices": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.",
"showServiceAvailability": "سيظهر هذا الخيار للعميل ما إذا كان بإمكانه الحصول على هذه الخدمة في الورشة أم لا.",
"bookAppointmentAtLocation": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يرغب فيه.",
"appointmentBookingOption": "سيسمح هذا الخيار للعميل بحجز موعد لهذه الخدمات.",
"workshopAvailabilityOption": "سيظهر هذا الخيار للعميل إذا كان يمكنه الحصول على هذه الخدمة في الورشة أم لا.",
"appointmentLocationOption": "سيسمح هذا الخيار للعميل بحجز موعد في الموقع الذي يختاره.",
"deleteScheduleConfirmation": "هل أنت متأكد أنك تريد حذف هذا الجدول؟",
"deleteScheduleAdConfirmationMessage": "سيتم إزالة جميع فترات المواعيد ولن يتمكن العملاء من حجز مواعيد لهذا الجدول.",
"branchSchedules": "جداول الفرع",
"noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع."
"noSchedulesFound": "لم تقم بإضافة أي جدول لهذا الفرع.",
"inviteFriendsBySMS": "دعوة الأصدقاء عبر الرسائل القصيرة",
"inviteFriendsByWhatsApp": "دعوة الأصدقاء عبر واتساب",
"inviteFriendsByEmail": "دعوة الأصدقاء عبر البريد الإلكتروني",
"noFAQsToShow": "لا توجد أسئلة شائعة في الوقت الحالي. يرجى الاتصال بنا إذا كان لديك أي استفسار.",
"appInfo": "معلومات التطبيق"
};
static const Map<String,dynamic> en_US = {
"firstTimeLogIn": "First Time Log In",
@ -1363,7 +1371,12 @@ static const Map<String,dynamic> en_US = {
"deleteScheduleConfirmation": " Are you sure you want to delete this Schedule?",
"deleteScheduleAdConfirmationMessage": "All the appointment slots will be removed and customers will no longer be able to book appointments for this schedule.",
"branchSchedules": "Branch Schedules",
"noSchedulesFound": "You have not added any schedule for this branch."
"noSchedulesFound": "You have not added any schedule for this branch.",
"inviteFriendsBySMS": "Invite Friends By SMS",
"inviteFriendsByWhatsApp": "Invite Friends By WhatsApp",
"inviteFriendsByEmail": "Invite Friends By Email",
"noFAQsToShow": "There are no Frequently asked Questions Right now. Please contact us if you have any query.",
"appInfo": "App Info"
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": ar_SA, "en_US": en_US};
}

@ -1,6 +1,6 @@
// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart
abstract class LocaleKeys {
abstract class LocaleKeys {
static const firstTimeLogIn = 'firstTimeLogIn';
static const signUp = 'signUp';
static const changeMobile = 'changeMobile';
@ -47,7 +47,7 @@ abstract class LocaleKeys {
static const emailChangedSuccessfully = 'emailChangedSuccessfully';
static const passwordIsUpdated = 'passwordIsUpdated';
static const passwordShouldContains = 'passwordShouldContains';
static const successfullyRegistered = ' successfullyRegistered';
static const successfullyRegistered = ' successfullyRegistered';
static const pleaseEnterSamePassword = 'pleaseEnterSamePassword';
static const firstNameMandatory = 'firstNameMandatory';
static const surnameNameMandatory = 'surnameNameMandatory';
@ -644,6 +644,9 @@ abstract class LocaleKeys {
static const itemName = 'itemName';
static const itemDescription = 'itemDescription';
static const itemPrice = 'itemPrice';
static const bookAppointmentForServices = 'bookAppointmentForServices';
static const showServiceAvailability = 'showServiceAvailability';
static const bookAppointmentAtLocation = 'bookAppointmentAtLocation';
static const appointmentBookingOption = 'appointmentBookingOption';
static const workshopAvailabilityOption = 'workshopAvailabilityOption';
static const appointmentLocationOption = 'appointmentLocationOption';
@ -651,8 +654,10 @@ abstract class LocaleKeys {
static const deleteScheduleAdConfirmationMessage = 'deleteScheduleAdConfirmationMessage';
static const branchSchedules = 'branchSchedules';
static const noSchedulesFound = 'noSchedulesFound';
static const showServiceAvailability = 'showServiceAvailability';
static const bookAppointmentForServices = 'bookAppointmentForServices';
static const bookAppointmentAtLocation = 'bookAppointmentAtLocation';
static const inviteFriendsBySMS = 'inviteFriendsBySMS';
static const inviteFriendsByWhatsApp = 'inviteFriendsByWhatsApp';
static const inviteFriendsByEmail = 'inviteFriendsByEmail';
static const noFAQsToShow = 'noFAQsToShow';
static const appInfo = 'appInfo';
}

@ -55,9 +55,9 @@ class DocumentData {
int? id;
int? serviceProviderId;
int? documentId;
dynamic? documentUrl;
String? documentUrl;
int? status;
dynamic? comment;
String? comment;
bool? isActive;
String? document;
String? fileExt;

@ -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;
}
}

@ -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 [];
}
}

@ -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;
}
}

@ -1,4 +1,3 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:mc_common_app/api/api_client.dart';

@ -211,8 +211,9 @@ class AdVM extends BaseVM {
notifyListeners();
return;
}
setState(ViewState.busy);
exploreAdsFilteredList = await getAdsByFilter(createdByRoleEnum: createdByRoleFilter, isMyAds: false);
setState(ViewState.idle);
notifyListeners();
}
@ -328,7 +329,7 @@ class AdVM extends BaseVM {
return;
}
Utils.hideLoading(context);
Utils.showToast(respModel.message ?? "A has been marked as sold successfully!");
Utils.showToast(respModel.message ?? "Ad has been marked as sold successfully!");
updateIsExploreAds(false);
applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.sold); //pending for review
navigateReplaceWithName(context, AppRoutes.dashboard);
@ -1985,6 +1986,12 @@ class ImageModel {
ImageModel({this.id, this.filePath, this.isFromNetwork});
ImageModel.fromJson(Map<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';
@ -1498,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));
// }
}
}

@ -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;
}
@ -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);
@ -615,19 +602,26 @@ class ChatVM extends ChangeNotifier {
}
}
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;
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);
}
return status;
scrollChatDown();
notifyListeners();
}
Future<bool> onTextMessageSendForAds({
@ -679,7 +673,113 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
if (AppState().currentAppType == AppType.customer) {} else {}
if (AppState().currentAppType == AppType.customer) {
} else {}
return true;
}
return false;
}
// ========================General ==================
List<ChatMessageModel> currentMessagesForGeneralChat = [];
Future<void> getUsersChatMessagesForGeneralChat({required BuildContext context, int? adID, int? adsChatBuyerId, String? userID, String? senderName, required bool isForBuyer}) async {
try {
Utils.showLoading(context);
currentMessagesForGeneralChat = await chatRepo.getUsersChatMessagesForGeneralChat(userID: userID, adID: adID, isForBuyer: isForBuyer, adsChatBuyerId: adsChatBuyerId);
Utils.hideLoading(context);
List<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);
}
}
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,
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,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"ChatText": message,
};
logger.i("$obj");
hubConnection!.invoke(
SignalrConsts.sendMessageGeneral,
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,
);
currentMessagesForGeneralChat.add(chatMessageModel);
notifyListeners();
if (AppState().currentAppType == AppType.customer) {
} else {}
return true;
}
return false;

@ -94,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);
}
@ -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;
@ -237,18 +252,6 @@ class ServiceVM extends BaseVM {
notifyListeners();
}
Future<void> removeNetworkImag(String filePath) async {
log("removeNetworkImag with: $filePath");
int index = document!.data!.indexWhere((element) => element.documentUrl == filePath);
if (index == -1) {
return;
}
document!.data![index].documentUrl = null;
document!.data![index].isLocalFile = true;
notifyListeners();
}
Future<void> commercialRemove(String filePath) async {
log("commercialRemove with: $filePath");
@ -271,6 +274,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);
}
@ -400,10 +413,10 @@ class ServiceVM extends BaseVM {
DropValue(
element.id ?? 0,
((element.categoryName!.isEmpty
? "N/A"
: countryCode == "SA"
? element.categoryNameN
: element.categoryName) ??
? "N/A"
: countryCode == "SA"
? element.categoryNameN
: element.categoryName) ??
"N/A"),
"",
),
@ -586,9 +599,7 @@ class ServiceVM extends BaseVM {
File file = File(imageModel.filePath!);
List<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,

@ -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);
}
}
}

@ -279,11 +279,11 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.setDateandTime.tr().toText(fontSize: 16, isBold: true, letterSpacing: -1.44, height: 1.2),
title: LocaleKeys.setDateandTime.tr().toText(fontSize: 16, height: 1.2),
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
25.height,
20.height,
adVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: Builder(
@ -345,7 +345,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
],
),
],
19.height,
30.height,
],
));
});
@ -579,7 +579,34 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
title: LocaleKeys.markAsSold.tr(),
isBold: false,
onPressed: () {
adVM.markAdAsSold(context, adId: adDetailsModel.id!);
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.markAsSold.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.markAsSold.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () {
adVM.markAdAsSold(context, adId: adDetailsModel.id!);
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
},
),
),
@ -1017,6 +1044,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
case AdPostStatus.rejected:
return pendingForReviewAction(pendingText: LocaleKeys.rejectedFormAdmin.tr());
case AdPostStatus.cancelled:
return pendingForReviewAction(pendingText: LocaleKeys.cancelledByOwner.tr());
case AdPostStatus.pendingForPost:
return pendingForReviewAction(pendingText: LocaleKeys.waitingAdminPost.tr());

@ -11,11 +11,11 @@ class PickedFilesContainer extends StatelessWidget {
final Function(String filePath)? onCrossPressedPrimary;
final Function(int index, String filePath)? onCrossPressedSecondary;
final int? index;
final Function() onAddFilePressed;
final bool isReview;
final bool isPdf;
final bool isFromNetwork;
final bool allowAdButton;
final Function() onAddFilePressed;
const PickedFilesContainer({
Key? key,

@ -218,7 +218,7 @@ class ReviewAppointment extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Divider(thickness: 0.7, height: 3),
const Divider(thickness: 0.7, height: 3),
8.height,
if (appointmentsVM.amountToPayForAppointment > 0) ...[
Row(

@ -23,7 +23,6 @@ import 'package:mc_common_app/views/requests/request_bottomsheets.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/row_with_arrow.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart' as lcl;
@ -51,6 +50,9 @@ class _ChatViewState extends State<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;
@ -86,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;
@ -125,7 +135,9 @@ class _ChatViewState extends State<ChatView> {
),
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) {
@ -252,7 +264,6 @@ class _ChatViewState extends State<ChatView> {
flex: 1,
child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress(
() async {
debugPrint("AppState().getUser.data.userInfo.userId: ${AppState().getUser.data!.userInfo!.userId}");
ChatMessageTypeEnum chatMessageTypeEnum = ChatMessageTypeEnum.freeText;
if (chatVM.pickedImagesForMessage.isNotEmpty) {

@ -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,
),
);
}
}

@ -125,8 +125,13 @@ class AdsFragment extends StatelessWidget {
Expanded(
child: RefreshIndicator(
onRefresh: () async {
await adVM.getExploreAds();
await adVM.getMyAds();
if (adVM.isExploreAdsTapped) {
CreatedByRoleEnum createdByRoleEnum = adVM.exploreAdsFilterOptions.firstWhere((element) => element.isSelected).id.toCreatedByRoleEnum();
adVM.applyFilterOnExploreAds(createdByRoleFilter: createdByRoleEnum);
} else {
AdPostStatus adPostStatusEnum = adVM.myAdsFilterOptions.firstWhere((element) => element.isSelected).id.toAdPostEnum();
adVM.applyFilterOnMyAds(adPostStatusEnum: adPostStatusEnum);
}
},
child: adVM.state == ViewState.busy
? const Center(

@ -1,8 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
@ -10,7 +7,6 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/document.dart';
import 'package:mc_common_app/theme/colors.dart';
@ -27,7 +23,6 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:sizer/sizer.dart';
class ProviderLicensePage extends StatefulWidget {
const ProviderLicensePage({super.key});
@ -40,35 +35,25 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
late ServiceVM branchVM;
bool showAttachment = false;
String? attachedFile;
bool isAllApproved = true;
@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);
});
}
@override
Widget build(BuildContext context) {
final serviceVm = context.read<ServiceVM>();
if (serviceVm.document != null && serviceVm.document!.data != null && serviceVm.document!.data!.isNotEmpty) {
for (var doc in serviceVm.document!.data!) {
if (doc.status != 3) {
isAllApproved = false;
}
}
setState(() {});
}
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.defineLicences.tr(),
isRemoveBackButton: false,
),
body: Consumer<ServiceVM>(builder: (_, model, __) {
if (model.state == ViewState.busy) {
body: Consumer<ServiceVM>(builder: (_, serviceVM, __) {
if (serviceVM.state == ViewState.busy) {
return const Center(
child: CircularProgressIndicator(),
);
@ -77,9 +62,9 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: SingleChildScrollView(child: buildContent(model)),
child: SingleChildScrollView(child: buildContent(serviceVM)),
),
if (!isAllApproved) ...[
if (!serviceVM.isAllDocsApproved) ...[
Padding(
padding: const EdgeInsets.all(12.0),
child: ShowFillButton(
@ -88,14 +73,14 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
onPressed: () async {
bool status = false;
if (AppState().getUser.data!.userInfo!.roleId == 5) {
if (validation(model)) {
status = await updateDocument(model);
if (validateDocuments(serviceVM)) {
status = await updateDocument(serviceVM);
} else {
Utils.showToast(LocaleKeys.allDocumentMandatoryDealershipProvider.tr());
return;
}
} else {
status = await updateDocument(model);
status = await updateDocument(serviceVM);
}
Future.delayed(const Duration(seconds: 1), () {
if (status) {
@ -112,9 +97,10 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
);
}
validation(ServiceVM model) {
validateDocuments(ServiceVM model) {
bool valid = true;
for (var element in model.document!.data!) {
log("documentUrl: ${element.documentUrl}");
if (element.documentUrl == null) {
valid = false;
}
@ -140,19 +126,19 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
}
}
Widget buildContent(ServiceVM model) {
return model.document!.data!.isEmpty
Widget buildContent(ServiceVM serviceVM) {
return serviceVM.document!.data!.isEmpty
? Text(LocaleKeys.somethingWrong.tr())
: ListView.separated(
separatorBuilder: (context, index) {
return 20.height;
},
itemCount: model.document!.data!.length,
itemCount: serviceVM.document!.data!.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.symmetric(horizontal: 20),
itemBuilder: (context, index) {
DocumentData? document = model.document?.data![index];
DocumentData? document = serviceVM.document?.data![index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -179,28 +165,29 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
),
],
10.height,
if (isNeedToShow(model: model, document: document)) ...[
if (isNeedToShow(model: serviceVM, document: document)) ...[
PickedFilesContainer(
isReview: document.status != 4,
pickedFiles: isLocalOrNetworkFiles(model: model, document: document),
isReview: document.status == 4,
allowAdButton: false,
pickedFiles: isLocalOrNetworkFiles(model: serviceVM, document: document),
onCrossPressedPrimary: chkIsLocalOrNetwork(document: document)
? model.removeNetworkImag
? serviceVM.removeNetworkImage
: document.documentId == 1
? model.commerceRemove
? serviceVM.commerceRemove
: document.documentId == 2
? model.commercialRemove
: model.vatRemove,
isPdf: model.document!.data![index].fileExt == "pdf",
? serviceVM.commercialRemove
: serviceVM.vatRemove,
isPdf: serviceVM.document!.data![index].fileExt == "pdf",
isFromNetwork: !(document.isLocalFile ?? false),
onAddFilePressed: () {
model.pickPdfReceiptFile(context, document.documentId!, index);
serviceVM.pickPdfReceiptFile(context, document.documentId!, index);
},
),
buildCommentContainer(document: document),
] else ...[
10.height,
DottedRectContainer(
onTap: () => model.pickPdfReceiptFile(context, document.documentId!, index) ?? "",
onTap: () => serviceVM.pickPdfReceiptFile(context, document.documentId!, index) ?? "",
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
),
@ -214,7 +201,7 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
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)];
@ -229,7 +216,8 @@ 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);
log("document.documentUrl: ${document.documentUrl}");
if (isNetworkImage) {
allow = true;
} else {
@ -247,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 {
@ -266,7 +254,7 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
bool chkIsLocalOrNetwork({required DocumentData document}) {
bool isNetworkImage = false;
if (!document.isLocalFile!) {
isNetworkImage = document.documentUrl != null && document.documentUrl.isNotEmpty ? true : false;
isNetworkImage = document.documentUrl != null && document.documentUrl!.isNotEmpty ? true : false;
}
return isNetworkImage;
}
@ -274,7 +262,7 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
Widget buildCommentContainer({required DocumentData document}) {
String comment = "";
if (document.status == 4 && document.comment != null) {
comment = document.comment;
comment = document.comment ?? "";
}
if (comment.isEmpty) {

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

@ -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),
],
],
);
}
}

@ -17,14 +17,14 @@ import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class SettingOptionsLanguage extends StatefulWidget {
const SettingOptionsLanguage({super.key});
class SettingOptionsMore extends StatefulWidget {
const SettingOptionsMore({super.key});
@override
State<SettingOptionsLanguage> createState() => _SettingOptionsLanguageState();
State<SettingOptionsMore> createState() => _SettingOptionsMoreState();
}
class _SettingOptionsLanguageState extends State<SettingOptionsLanguage> {
class _SettingOptionsMoreState extends State<SettingOptionsMore> {
@override
Widget build(BuildContext context) {
return Scaffold(
@ -65,25 +65,10 @@ class _SettingOptionsLanguageState extends State<SettingOptionsLanguage> {
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.settings, size: 20),
titleText: LocaleKeys.settings.tr(),
needBorderBelow: true,
onTap: () {},
//navigateWithName(context, AppRoutes.settingOptionsInviteFriends),
needBorderBelow: false,
onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends),
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.question_mark_outlined, size: 20),
titleText: LocaleKeys.help.tr(),
needBorderBelow: true,
onTap: () => navigateWithName(context, AppRoutes.settingOptionsHelp),
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.person, size: 20),
titleText: LocaleKeys.account.tr(),
needBorderBelow: false,
onTap: () {
navigateWithName(context, AppRoutes.profileView);
// context.read<DashboardVmCustomer>().onNavbarTapped(4);
// Navigator.pop(context);
}),
],
).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0),
10.height,

@ -6,13 +6,13 @@ import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:sizer/sizer.dart';
class CustomSettingOptionsTile extends StatelessWidget {
final Widget leadingWidget;
final String titleText;
final bool needBorderBelow;
final bool isForLanguage;
final bool showTrailingArrow;
final Function() onTap;
final String? subTitle;
@ -23,6 +23,7 @@ class CustomSettingOptionsTile extends StatelessWidget {
required this.titleText,
this.needBorderBelow = false,
this.isForLanguage = false,
this.showTrailingArrow = true,
this.subTitle,
});
@ -62,7 +63,9 @@ class CustomSettingOptionsTile extends StatelessWidget {
// )
//
: const Icon(Icons.arrow_forward, size: 18)
: showTrailingArrow
? const Icon(Icons.arrow_forward, size: 18)
: const SizedBox()
],
).onPress(onTap),
subTitle != null ? 3.height : 5.height,
@ -97,9 +100,7 @@ class CustomProfileOptionsTile extends StatelessWidget {
subtitleText.toText(fontSize: 12, color: MyColors.darkTextColor.withOpacity(0.7)),
],
),
if (needEditButton) ...[
MyAssets.icEdit.buildSvg()
],
if (needEditButton) ...[MyAssets.icEdit.buildSvg()],
],
).onPress(onTap),
5.height,

@ -375,7 +375,12 @@ extension BuildSVG on String? {
return Image.network(
this!,
errorBuilder: (BuildContext context, Object obj, StackTrace? s) {
return SizedBox(height: height, width: width, child: const Icon(Icons.signal_wifi_connected_no_internet_4_outlined));
return Container(
height: height,
width: width,
decoration: BoxDecoration(border: Border.all(color: MyColors.roundedCrossBgColor)),
child: const Icon(Icons.image_not_supported_outlined, color: MyColors.roundedCrossBgColor),
);
},
loadingBuilder: (BuildContext context, Widget? child, ImageChunkEvent? imageChunk) {
if (imageChunk == null) {

Loading…
Cancel
Save