Merge branch 'refs/heads/master' into aamir_dev

# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/generated/codegen_loader.g.dart
#	lib/generated/locale_keys.g.dart
aamir_dev
Aamir Muhammad 11 months ago
commit 9efb174e0c

@ -745,6 +745,16 @@
"active": "نشط",
"paymentType": "نوع الدفع",
"searchByCreatedDate": "البحث حسب تاريخ الإنشاء",
"cityNameMandatory": "المدينة إلزامية",
"genderMandatory": "الجنس إلزامي",
"updateCity": "تحديث المدينة",
"userGender": "جنس",
"userMale": "ذكر",
"userFemale": "أنثى",
"maxFileSelection": "يمكنك تحديد الحد الأقصى لملفات 7",
"maxFileSize": "يجب أن يكون حجم كل ملف أقل من 2 ميغابايت",
"onlyJPGandPNG": "يُسمح فقط بملفات JPG وPNG",
"expiryDate": "تاريخ انتهاء الصلاحية",
"dealCompleted": "تم إتمام الصفقة",
"theDealNotCompleted": "لم تكتمل الصفقة",
"cancelRequest": "أريد إلغاء الطلب.",
@ -765,16 +775,16 @@
"tapToSelect": "اضغط للاختيار",
"noteCopyItemsExplanation": "ملاحظة: ستتمكن من نسخ العناصر من خدمة إلى أخرى في الفئة المحددة. يجب عليك إنشاء الخدمات أولاً ويجب أن تكون معتمدة. ثم ستتمكن من الحصول على الخدمات المتاحة التي يمكنك نسخ جميع العناصر منها أو تحديد العناصر التي تريد نسخها.",
"requestCreatedOn": "تم إنشاء الطلب في",
"cityNameMandatory": "المدينة إلزامية",
"genderMandatory": "الجنس إلزامي",
"updateCity": "تحديث المدينة",
"userGender": "جنس",
"userMale": "ذكر",
"userFemale": "أنثى",
"maxFileSelection" :"يمكنك تحديد الحد الأقصى لملفات 7",
"maxFileSize": "يجب أن يكون حجم كل ملف أقل من 2 ميغابايت",
"onlyJPGandPNG": "يُسمح فقط بملفات JPG وPNG",
"expiryDate": "تاريخ انتهاء الصلاحية"
"online": "عبر الإنترنت",
"deliveryStatus": "حالة توصيلك / الشحن هي:",
"markAsCompleted": "ضع علامة كمكتمل",
"searchByServiceDelivery": "البحث حسب خدمة التوصيل",
"searchByMobileNumber": "البحث بواسطة رقم الجوال",
"enterMobileNumber": "أدخل رقم الجوال",
"enterCustomerName": "أدخل اسم العميل",
"tapToView": "انقر لعرض",
"noServicesAvailableToCopy": "لا توجد خدمات متاحة في هذا الفرع لنسخها.",
"copySelectedItems": "نسخ العناصر المحددة"
}

@ -738,7 +738,6 @@
"blockedByAdmin": "Blocked by admin",
"active": "Active",
"paymentType": "Payment Type",
"searchByCreatedDate": "Search By Created Date",
"dealCompleted": "The Deal Completed",
"theDealNotCompleted": "The Deal Not Completed",
"cancelRequest": "I want to cancel the request.",
@ -759,6 +758,32 @@
"tapToSeeItems": "Tap to see items",
"noteCopyItemsExplanation": "Note: You will be able to copy items from one service to another in a selected category. You must create the services first and they should be approved. Then you will be able to get the available services from which you can copy all or selected items.",
"requestCreatedOn": "Request created on",
"online": "Online",
"searchByCreatedDate": "Search By Created Date",
"updateUserDetails": "Update User Details",
"enterNewFirstName": "Enter First Name",
"enterNewLastName": "Enter Last Name",
"userDetailsUpdated": "User Details is Updated",
"cityNameMandatory": "City is mandatory",
"genderMandatory": "Gender is mandatory",
"updateCity": "Update City",
"userGender": "Gender",
"userMale": "Male",
"userFemale": "Female",
"maxFileSelection": "You can select a maximum of 7 files",
"maxFileSize": "Each file size must be less than 2 MB",
"onlyJPGandPNG": "Only JPG and PNG files are allowed",
"expiryDate": "Expiry Date",
"deliveryStatus": "Your Delivery / Shipping Status is:",
"markAsCompleted": "Mark as Completed",
"searchByServiceDelivery": "Search by Service Delivery",
"searchByMobileNumber": "Search by Mobile Number",
"enterMobileNumber": "Enter Mobile Number",
"enterCustomerName": "Enter Customer Name",
"tapToView": "Tap to view",
"noServicesAvailableToCopy": "There are no services available in this branch to copy.",
"copySelectedItems": "Copy Selected Items"
"requestCreatedOn": "Request created on",
"updateUserDetails": "Update User Details",
"enterNewFirstName": "Enter First Name",
"enterNewLastName": "Enter Last Name",

@ -56,7 +56,7 @@ class ApiConsts {
static String servicesGet = "${baseUrlServices}api/ServiceProviders/Services_Get";
static String serviceProviderServiceCreate = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Create";
static String serviceProviderServiceUpdate = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Update";
static String serviceProviderServiceStatusUpdate = "${baseUrlServices}api/ServiceProviders/CategoryAndServiceDeactivateBySP";
static String serviceProviderServiceStatusUpdate = "${baseUrlServices}api/ServiceProviders/CategoryAndService_ActivateDeactivateBySP";
static String getProviderServices = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Get";
static String setScheduleInactive = "${baseUrlServices}api/ServiceProviders/BranchAppointmentSchedule_IsActiveUpdate";
static String serviceProviderAppointmentGetByCategoryOrService = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_GetByCategoryOrService";
@ -257,11 +257,10 @@ class GlobalConsts {
return appInvitationMessageEn;
}
// Attachment Values
int maxFileCount = 7;
int maxFileSizeInBytes = 2 * 1024 * 1024;
static int maxFileCount = 7;
static int maxFileSizeInBytes = 2 * 1024 * 1024;
}
class MyAssets {
@ -403,6 +402,4 @@ class SignalrConsts {
// General
static String sendMessageGeneral = "SendMessageGeneral";
static String receiveMessageGeneral = "ReceiveMessageGeneral";
}

@ -998,3 +998,12 @@ extension ShippingStatusEnumToInt on ShippingRequestStatusEnum {
}
}
}
extension CapitalizeFirstLetter on String {
String capitalizeFirstLetter() {
if (isEmpty) {
return this; // Return the string as-is if it's empty
}
return this[0].toUpperCase() + substring(1).toLowerCase();
}
}

@ -761,6 +761,16 @@ class CodegenLoader extends AssetLoader{
"active": "نشط",
"paymentType": "نوع الدفع",
"searchByCreatedDate": "البحث حسب تاريخ الإنشاء",
"cityNameMandatory": "المدينة إلزامية",
"genderMandatory": "الجنس إلزامي",
"updateCity": "تحديث المدينة",
"userGender": "جنس",
"userMale": "ذكر",
"userFemale": "أنثى",
"maxFileSelection": "يمكنك تحديد الحد الأقصى لملفات 7",
"maxFileSize": "يجب أن يكون حجم كل ملف أقل من 2 ميغابايت",
"onlyJPGandPNG": "يُسمح فقط بملفات JPG وPNG",
"expiryDate": "تاريخ انتهاء الصلاحية",
"dealCompleted": "تم إتمام الصفقة",
"theDealNotCompleted": "لم تكتمل الصفقة",
"cancelRequest": "أريد إلغاء الطلب.",
@ -780,6 +790,17 @@ class CodegenLoader extends AssetLoader{
"tapToSeeItems": "اضغط لرؤية العناصر",
"tapToSelect": "اضغط للاختيار",
"noteCopyItemsExplanation": "ملاحظة: ستتمكن من نسخ العناصر من خدمة إلى أخرى في الفئة المحددة. يجب عليك إنشاء الخدمات أولاً ويجب أن تكون معتمدة. ثم ستتمكن من الحصول على الخدمات المتاحة التي يمكنك نسخ جميع العناصر منها أو تحديد العناصر التي تريد نسخها.",
"requestCreatedOn": "تم إنشاء الطلب في",
"online": "عبر الإنترنت",
"deliveryStatus": "حالة توصيلك / الشحن هي:",
"markAsCompleted": "ضع علامة كمكتمل",
"searchByServiceDelivery": "البحث حسب خدمة التوصيل",
"searchByMobileNumber": "البحث بواسطة رقم الجوال",
"enterMobileNumber": "أدخل رقم الجوال",
"enterCustomerName": "أدخل اسم العميل",
"tapToView": "انقر لعرض",
"noServicesAvailableToCopy": "لا توجد خدمات متاحة في هذا الفرع لنسخها.",
"copySelectedItems": "نسخ العناصر المحددة"
"requestCreatedOn": "تم إنشاء الطلب في"
"searchByCreatedDate": "البحث حسب تاريخ الإنشاء",
"cityNameMandatory": "المدينة إلزامية",
@ -1533,7 +1554,6 @@ static const Map<String,dynamic> en_US = {
"blockedByAdmin": "Blocked by admin",
"active": "Active",
"paymentType": "Payment Type",
"searchByCreatedDate": "Search By Created Date",
"dealCompleted": "The Deal Completed",
"theDealNotCompleted": "The Deal Not Completed",
"cancelRequest": "I want to cancel the request.",
@ -1554,6 +1574,32 @@ static const Map<String,dynamic> en_US = {
"tapToSeeItems": "Tap to see items",
"noteCopyItemsExplanation": "Note: You will be able to copy items from one service to another in a selected category. You must create the services first and they should be approved. Then you will be able to get the available services from which you can copy all or selected items.",
"requestCreatedOn": "Request created on",
"online": "Online",
"searchByCreatedDate": "Search By Created Date",
"updateUserDetails": "Update User Details",
"enterNewFirstName": "Enter First Name",
"enterNewLastName": "Enter Last Name",
"userDetailsUpdated": "User Details is Updated",
"cityNameMandatory": "City is mandatory",
"genderMandatory": "Gender is mandatory",
"updateCity": "Update City",
"userGender": "Gender",
"userMale": "Male",
"userFemale": "Female",
"maxFileSelection": "You can select a maximum of 7 files",
"maxFileSize": "Each file size must be less than 2 MB",
"onlyJPGandPNG": "Only JPG and PNG files are allowed",
"expiryDate": "Expiry Date",
"deliveryStatus": "Your Delivery / Shipping Status is:",
"markAsCompleted": "Mark as Completed",
"searchByServiceDelivery": "Search by Service Delivery",
"searchByMobileNumber": "Search by Mobile Number",
"enterMobileNumber": "Enter Mobile Number",
"enterCustomerName": "Enter Customer Name",
"tapToView": "Tap to view",
"noServicesAvailableToCopy": "There are no services available in this branch to copy.",
"copySelectedItems": "Copy Selected Items"
"requestCreatedOn": "Request created on",
"updateUserDetails": "Update User Details",
"enterNewFirstName": "Enter First Name",
"enterNewLastName": "Enter Last Name",

@ -724,6 +724,16 @@ abstract class LocaleKeys {
static const active = 'active';
static const paymentType = 'paymentType';
static const searchByCreatedDate = 'searchByCreatedDate';
static const cityNameMandatory = 'cityNameMandatory';
static const genderMandatory = 'genderMandatory';
static const updateCity = 'updateCity';
static const userGender = 'userGender';
static const userMale = 'userMale';
static const userFemale = 'userFemale';
static const maxFileSelection = 'maxFileSelection';
static const maxFileSize = 'maxFileSize';
static const onlyJPGandPNG = 'onlyJPGandPNG';
static const expiryDate = 'expiryDate';
static const dealCompleted = 'dealCompleted';
static const theDealNotCompleted = 'theDealNotCompleted';
static const cancelRequest = 'cancelRequest';
@ -744,15 +754,16 @@ abstract class LocaleKeys {
static const tapToSelect = 'tapToSelect';
static const noteCopyItemsExplanation = 'noteCopyItemsExplanation';
static const requestCreatedOn = 'requestCreatedOn';
static const cityNameMandatory = 'cityNameMandatory';
static const genderMandatory = 'genderMandatory';
static const updateCity = 'updateCity';
static const userGender = 'userGender';
static const userMale = 'userMale';
static const userFemale = 'userFemale';
static const maxFileSelection = 'maxFileSelection';
static const maxFileSize = 'maxFileSize';
static const onlyJPGandPNG = 'onlyJPGandPNG';
static const expiryDate = 'expiryDate';
static const online = 'online';
static const deliveryStatus = 'deliveryStatus';
static const markAsCompleted = 'markAsCompleted';
static const searchByServiceDelivery = 'searchByServiceDelivery';
static const searchByMobileNumber = 'searchByMobileNumber';
static const enterMobileNumber = 'enterMobileNumber';
static const enterCustomerName = 'enterCustomerName';
static const tapToView = 'tapToView';
static const noServicesAvailableToCopy = 'noServicesAvailableToCopy';
static const copySelectedItems = 'copySelectedItems';
}

@ -50,6 +50,7 @@ class AdDetailsModel {
String? warrantyYears;
CreatedByRoleEnum? createdByRoleEnum;
List<ChatMessageModel>? adMessages;
int? totalItemsCount;
AdDetailsModel({
this.id,
@ -95,6 +96,7 @@ class AdDetailsModel {
this.createdByRoleEnum,
this.modifiedOn,
this.adMessages,
this.totalItemsCount,
});
int getRandomValue({required int min, required int max}) {
@ -103,7 +105,7 @@ class AdDetailsModel {
return randomNumber;
}
AdDetailsModel.fromJson(Map<String, dynamic> json, bool isMyAds) {
AdDetailsModel.fromJson(Map<String, dynamic> json, bool isMyAds, int totalItems) {
id = json['id'];
startdate = json['startdate'];
enddate = json['enddate'];
@ -150,6 +152,7 @@ class AdDetailsModel {
createdByRoleEnum = (json['createdByRole'] as int).toCreatedByRoleEnum();
isMyAd = isMyAds;
isReservedByMe = false;
totalItemsCount = totalItems;
}
}

@ -80,7 +80,7 @@ class ReqOffer {
String? offerStatusText;
String? comment;
String? serviceItemName;
int? manufacturedById;
String? manufacturedByName;
String? manufacturedOn;
double? price;
bool? isDeliveryAvailable;
@ -96,7 +96,7 @@ class ReqOffer {
this.offerStatusText,
this.comment,
this.serviceItemName,
this.manufacturedById,
this.manufacturedByName,
this.manufacturedOn,
this.price,
this.isDeliveryAvailable,
@ -113,7 +113,7 @@ class ReqOffer {
offerStatusText = json['offerStatusText'];
comment = json['comment'];
serviceItemName = json['serviceItem'];
manufacturedById = json['offeredItemCreatedBy'];
manufacturedByName = json['offeredItemCreatedBy'].toString();
manufacturedOn = json['offeredItemCreatedOn'];
price = json['price'];
isDeliveryAvailable = json['isDeliveryAvailable'];
@ -138,7 +138,7 @@ class ReqOffer {
@override
String toString() {
return 'ReqOffer{id: $id, requestID: $requestID, serviceProviderID: $serviceProviderID, offerStatus: $offerStatus, offerStatusText: $offerStatusText, comment: $comment, serviceItemName: $serviceItemName, manufacturedById: $manufacturedById, manufacturedOn: $manufacturedOn, price: $price, requestsTypeEnum: $requestsTypeEnum, requestOfferStatusEnum: $requestOfferStatusEnum}';
return 'ReqOffer{id: $id, requestID: $requestID, serviceProviderID: $serviceProviderID, offerStatus: $offerStatus, offerStatusText: $offerStatusText, comment: $comment, serviceItemName: $serviceItemName, manufacturedById: $manufacturedByName, manufacturedOn: $manufacturedOn, price: $price, requestsTypeEnum: $requestsTypeEnum, requestOfferStatusEnum: $requestOfferStatusEnum}';
}
}

@ -101,7 +101,7 @@ class RequestModel {
requestStatusName: json["requestStatusName"],
requestStatus: (json['requestStatus'] as int).toRequestStatusEnum(),
shippingStatus: json['shippingRequestStatus'],
shippingStatusEnum: json['shippingRequestStatus'] != null ? (json['shippingRequestStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.initiated,
shippingStatusEnum: json['shippingRequestStatus'] != null ? (json['shippingRequestStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.pending,
cityName: json["cityName"],
vehicleTypeName: json["vehicleTypeName"],
countryName: json["countryName"],

@ -1,10 +1,12 @@
import 'dart:developer';
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/advertisment_models/ad_details_model.dart';
import 'package:mc_common_app/models/advertisment_models/ads_bank_details_model.dart';
import 'package:mc_common_app/models/advertisment_models/ads_duration_model.dart';
@ -14,15 +16,22 @@ import 'package:mc_common_app/models/advertisment_models/special_service_model.d
import 'package:mc_common_app/models/chat_models/buyers_chat_for_ads_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
abstract class AdsRepo {
Future<List<AdsDurationModel>> getAdsDuration({required int? countryId});
Future<List<SpecialServiceModel>> getSpecialServices({required int specialServiceType});
Future<List<SpecialServiceModel>> getSpecialServices({required int specialServiceType, required int cityId, required int countryId});
Future<GenericRespModel> createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew});
Future<List<AdDetailsModel>> getAllAds({required bool isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum});
Future<List<AdDetailsModel>> getAllAds({
required bool isMyAds,
AdPostStatus? adPostStatus,
CreatedByRoleEnum? createdByRoleEnum,
int? vehicleBrandId,
int? page,
});
Future<List<AdDetailsModel>> getExploreAdsBasedOnFilters({
List<String>? cityIdsList,
@ -31,6 +40,8 @@ abstract class AdsRepo {
List<String>? createdByRolesIdsList,
List<String>? vehicleAdConditionIdsList,
List<String>? vehicleAdCreatedDateList,
int page,
bool isMyAds = false,
});
Future<List<AdDetailsModel>> getMyReservedAds();
@ -62,10 +73,10 @@ class AdsRepoImp implements AdsRepo {
@override
Future<List<AdsDurationModel>> getAdsDuration({required int? countryId}) async {
int roleID = appState.getUser!.data!.userInfo!.roleId ?? 0;
int roleID = appState.getUser.data!.userInfo!.roleId ?? 0;
var param = {
"CountryID": countryId.toString() ?? "0",
"roleID": roleID.toString() ?? "0",
"CountryID": countryId.toString(),
"roleID": roleID.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
@ -78,14 +89,34 @@ class AdsRepoImp implements AdsRepo {
}
@override
Future<List<SpecialServiceModel>> getSpecialServices({required int specialServiceType}) async {
Future<List<SpecialServiceModel>> getSpecialServices({required int specialServiceType, required int cityId, required int countryId}) async {
log("getSpecialServices cityId: $cityId");
var params = {
"SpecialServiceType": specialServiceType.toString(),
"CountryID": countryId.toString(),
};
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject(token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsSpecialServicesGet, queryParameters: params);
List<SpecialServiceModel> vehicleAdsDuration = List.generate(adsGenericModel.data.length, (index) => SpecialServiceModel.fromJson(adsGenericModel.data[index]));
return vehicleAdsDuration;
List<SpecialServiceModel> list = List.generate(adsGenericModel.data.length, (index) => SpecialServiceModel.fromJson(adsGenericModel.data[index]));
List<SpecialServiceModel> specialServicesByCity = getOfficesOnlyByCity(list, cityId);
return specialServicesByCity;
}
List<SpecialServiceModel> getOfficesOnlyByCity(List<SpecialServiceModel> allServices, int cityId) {
List<SpecialServiceModel> list = [];
for (var service in allServices) {
if (service.office != null && service.office!.isNotEmpty) {
for (var office in service.office!) {
if (office.cityID == cityId) {
list.add(service);
}
}
}
}
return list;
}
@override
@ -168,7 +199,13 @@ class AdsRepoImp implements AdsRepo {
}
@override
Future<List<AdDetailsModel>> getAllAds({required isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum}) async {
Future<List<AdDetailsModel>> getAllAds({
required isMyAds,
AdPostStatus? adPostStatus,
CreatedByRoleEnum? createdByRoleEnum,
int? vehicleBrandId,
int? page,
}) async {
Map<String, dynamic> onlyMyAdsParams = {
"userID": appState.getUser.data!.userInfo!.userId ?? "",
};
@ -176,6 +213,7 @@ class AdsRepoImp implements AdsRepo {
onlyMyAdsParams.addAll({
"AdsStatuses": ["${adPostStatus.getIdFromAdPostStatusEnum()}"],
"PageSize": "30",
"PageIndex": page != null ? page.toString() : "0",
});
}
var allAdsParams = {
@ -183,6 +221,7 @@ class AdsRepoImp implements AdsRepo {
"isActive": "true", //only Active ADS
"isExplore": "true",
"PageSize": "30",
"PageIndex": page != null ? page.toString() : "0",
};
if (!isMyAds && createdByRoleEnum != null) {
@ -190,13 +229,18 @@ class AdsRepoImp implements AdsRepo {
"CreatedByRoles": ["${createdByRoleEnum.getIdFromCreatedByRoleEnum()}"],
});
}
if (!isMyAds && vehicleBrandId != null && vehicleBrandId != 0) {
allAdsParams.addAll({
"VehicleBrandIDs": ["$vehicleBrandId"],
});
}
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),
ApiConsts.vehicleAdsGet,
queryParameters: isMyAds ? onlyMyAdsParams : allAdsParams,
);
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], isMyAds));
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], isMyAds, adsGenericModel.totalItemsCount ?? 1));
return vehicleAdsDetails;
}
@ -208,6 +252,8 @@ class AdsRepoImp implements AdsRepo {
List<String>? createdByRolesIdsList,
List<String>? vehicleAdConditionIdsList,
List<String>? vehicleAdCreatedDateList,
int? page,
bool isMyAds = false,
}) async {
var parameters = {
"CityIDs": cityIdsList ?? [],
@ -218,17 +264,29 @@ class AdsRepoImp implements AdsRepo {
"VehicleNew": vehicleAdConditionIdsList ?? [],
"CreatedOn": (vehicleAdCreatedDateList != null && vehicleAdCreatedDateList.isNotEmpty) ? vehicleAdCreatedDateList.first.toString() : "",
"isActive": "true", //only Active ADS
"isExplore": "true",
"isExplore": (!isMyAds).toString(),
"PageSize": "30",
"PageIndex": page != null ? page.toString() : "1",
};
if (isMyAds) {
parameters.addAll({
"userID": appState.getUser.data!.userInfo!.userId ?? "",
});
}
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),
ApiConsts.vehicleAdsGet,
queryParameters: parameters,
);
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], false));
if (adsGenericModel.messageStatus != 1 || adsGenericModel.data == null) {
Utils.showToast(adsGenericModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], false, adsGenericModel.totalItemsCount ?? 1));
return vehicleAdsDetails;
}
@ -244,6 +302,11 @@ class AdsRepoImp implements AdsRepo {
ApiConsts.myAdsReserveGet,
queryParameters: params,
);
if (adsGenericModel.messageStatus != 1 || adsGenericModel.data == null) {
Utils.showToast(adsGenericModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<MyReservedAdsRespModel> reservedAds = List.generate(adsGenericModel.data.length, (index) => MyReservedAdsRespModel.fromJson(adsGenericModel.data[index]));
List<String> selectedIdsString = reservedAds.map((component) => component.adsID.toString()).toList();
@ -266,7 +329,11 @@ class AdsRepoImp implements AdsRepo {
queryParameters: params,
ApiConsts.vehicleAdsGet,
);
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true));
if (adsGenericModel.messageStatus != 1 || adsGenericModel.data == null) {
Utils.showToast(adsGenericModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true, adsGenericModel.totalItemsCount ?? 1));
for (int i = 0; i < vehicleAdsDetails.length; i++) {
vehicleAdsDetails[i].adReserveStatus = (reservedAds[i].adsReserveStatus ?? 0).toAdRserveStatusEnum();
@ -288,7 +355,11 @@ class AdsRepoImp implements AdsRepo {
queryParameters: params,
ApiConsts.vehicleAdsGet,
);
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true));
if (adsGenericModel.messageStatus != 1 || adsGenericModel.data == null) {
Utils.showToast(adsGenericModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true, adsGenericModel.totalItemsCount ?? 1));
return vehicleAdsDetails;
}

@ -15,6 +15,16 @@ import 'package:mc_common_app/utils/enums.dart';
abstract class AppointmentRepo {
Future<List<AppointmentListModel>> getMyAppointmentsForProvider(Map<String, dynamic> map);
Future<List<AppointmentListModel>> getMyAppointmentsForProvidersByFilters({
required int branchID,
List<String>? serviceProviderServiceIdsList,
List<String>? customerNamesList,
List<String>? customerPhonesList,
List<String>? deliveryTypeIdsList,
String? fromDate,
String? toDate,
});
Future<List<AppointmentListModel>> getMyAppointmentsForCustomersByFilters({
List<String>? providerIdsList,
List<String>? categoryIdsList,
@ -27,7 +37,7 @@ abstract class AppointmentRepo {
Future<GenericRespModel> updateAppointmentPaymentStatus(Map<String, dynamic> map);
Future<GenericRespModel> getAppointmentSlots(Map<String, dynamic> map);
Future<GenericRespModel> getAppointmentSlots({required int branchID, required String fromDate, required String toDate});
Future<GenericRespModel> createMergeAppointment(Map<String, dynamic> map);
@ -219,6 +229,40 @@ class AppointmentRepoImp implements AppointmentRepo {
return appointmentList;
}
@override
Future<List<AppointmentListModel>> getMyAppointmentsForProvidersByFilters({
required int branchID,
List<String>? serviceProviderServiceIdsList,
List<String>? customerNamesList,
List<String>? customerPhonesList,
List<String>? deliveryTypeIdsList,
String? fromDate,
String? toDate,
}) async {
String t = appState.getUser.data!.accessToken ?? "";
String providerId = appState.getUser.data!.userInfo!.providerId.toString();
var params = {
"ServiceProviderID": providerId.toString(),
"ProviderBranchID": branchID.toString(),
"IsSearchFilter": true.toString(),
"SPServiceIDs": serviceProviderServiceIdsList ?? [],
"CustomerNames": customerNamesList ?? [],
"CustomerMobileNos": customerPhonesList ?? [],
"AppointmentTypes": deliveryTypeIdsList ?? [],
"FromDate": fromDate ?? "",
"DateTo": toDate ?? "",
};
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
token: t,
(json) => GenericRespModel.fromJson(json),
queryParameters: params,
ApiConsts.serviceProvidersAppointmentGet,
);
List<AppointmentListModel> appointmentList = List.generate(genericRespModel.data.length, (index) => AppointmentListModel.fromJson(genericRespModel.data[index]));
return appointmentList;
}
@override
Future<List<AppointmentListModel>> getMyAppointmentsForCustomersByFilters({
List<String>? providerIdsList,
@ -246,8 +290,9 @@ class AppointmentRepoImp implements AppointmentRepo {
}
@override
Future<GenericRespModel> getAppointmentSlots(Map<String, dynamic> map) async {
Future<GenericRespModel> getAppointmentSlots({required int branchID, required String fromDate, required String toDate}) async {
String t = appState.getUser.data!.accessToken ?? "";
final map = {"ProviderBranchID": branchID.toString(), "FromDate": fromDate, "ToDate": toDate};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),

@ -75,6 +75,8 @@ abstract class BranchRepo {
Future<GenericRespModel> updateServiceStatus({required int branchId, required List<int> serviceIds, required ServiceStatusEnum serviceStatusEnum});
Future<GenericRespModel> updateCategoryStatus({required int branchId, required int categoryId, required ServiceStatusEnum serviceStatusEnum});
Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required int branchId, required int serviceId});
Future<GenericRespModel> getMatchedServices(int oldBranchId, int newBranchId, int categoryId);
@ -237,7 +239,7 @@ class BranchRepoImp implements BranchRepo {
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: t,
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.serviceProviderDDLGet,
);
List<ProviderBasicDataModel> providersList = List.generate(adsGenericModel.data.length, (index) => ProviderBasicDataModel.fromJson(adsGenericModel.data[index]));
@ -269,8 +271,7 @@ class BranchRepoImp implements BranchRepo {
serviceProviderBranchImages.add(imageMap);
}
String lat = "0",
long = "0";
String lat = "0", long = "0";
try {
lat = latitude.toString().substring(0, 9);
long = longitude.toString().substring(0, 9);
@ -333,6 +334,19 @@ class BranchRepoImp implements BranchRepo {
return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceStatusUpdate, queryParameters: map, token: t);
}
@override
Future<GenericRespModel> updateCategoryStatus({required int branchId, required int categoryId, required ServiceStatusEnum serviceStatusEnum}) async {
int providerID = AppState().getUser.data!.userInfo!.providerId;
var map = {
"ServiceProviderID": providerID.toString(),
"ProviderBranchID": branchId.toString(),
"ServiceCategoryID": categoryId.toString(),
"Status": serviceStatusEnum.getIdFromServiceStatusEnum().toString(),
};
String t = AppState().getUser.data!.accessToken ?? "";
return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceStatusUpdate, queryParameters: map, token: t);
}
@override
Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required int branchId, required int serviceId}) async {
var map = {
@ -342,7 +356,7 @@ class BranchRepoImp implements BranchRepo {
String t = AppState().getUser.data!.accessToken ?? "";
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.serviceProviderAppointmentGetByCategoryOrService,
queryParameters: map,
token: t,
@ -417,7 +431,7 @@ class BranchRepoImp implements BranchRepo {
String t = AppState().getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.getAllNearBranches,
token: appState.getUser.data!.accessToken,
queryParameters: queryParameters,
@ -445,7 +459,7 @@ class BranchRepoImp implements BranchRepo {
"longitude": longitude.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.getAllNearBranches,
token: appState.getUser.data!.accessToken,
queryParameters: queryParameters,
@ -457,7 +471,7 @@ class BranchRepoImp implements BranchRepo {
@override
Future<List<BranchDetailModel>> getMyRecentBranchesWithServices() async {
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.getMyRecentBranches,
token: appState.getUser.data!.accessToken,
);
@ -472,7 +486,7 @@ class BranchRepoImp implements BranchRepo {
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.getServiceItems,
token: appState.getUser.data!.accessToken,
queryParameters: serviceId.toString() != "-1" ? queryParameters : null,
@ -492,7 +506,7 @@ class BranchRepoImp implements BranchRepo {
postParams.addAll({"Latitude": "$latitude"});
}
GenericRespModel adsGenericModel =
await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams);
await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams);
return ProviderProfileModel.fromJson(adsGenericModel.data);
}
@ -537,7 +551,7 @@ class BranchRepoImp implements BranchRepo {
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.getAllNearBranches,
token: appState.getUser.data!.accessToken,
queryParameters: postParams,
@ -551,7 +565,7 @@ class BranchRepoImp implements BranchRepo {
var postParams = {"ServiceProviderBranchID": serviceProviderBranchID.toString()};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.getBranchRatings,
token: appState.getUser.data!.accessToken,
queryParameters: postParams,
@ -565,7 +579,7 @@ class BranchRepoImp implements BranchRepo {
final customerID = appState.getUser.data!.userInfo!.customerId;
final parameters = {"title": title, "review": review, "ratNo": ratingNo, "serviceProviderBranchID": serviceProviderBranchID, "customerID": "$customerID"};
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.createBranchRatings,
parameters,
token: appState.getUser.data!.accessToken,
@ -581,7 +595,7 @@ class BranchRepoImp implements BranchRepo {
"customerID": customerID.toString(),
};
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.favouriteServiceProviderCreate,
parameters,
token: appState.getUser.data!.accessToken,
@ -593,7 +607,7 @@ class BranchRepoImp implements BranchRepo {
Future<GenericRespModel> removeProviderFromFavourite({required int providerID}) async {
final parameters = {"id": providerID.toString()};
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.unFavouriteServiceProvider,
parameters,
token: appState.getUser.data!.accessToken,
@ -608,7 +622,7 @@ class BranchRepoImp implements BranchRepo {
var postParams = {"customerID": customerID.toString()};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
(json) => GenericRespModel.fromJson(json),
ApiConsts.favouriteServiceProviderGet,
token: appState.getUser.data!.accessToken,
queryParameters: postParams,

@ -63,7 +63,7 @@ abstract class RequestRepo {
required String offerPrice,
required bool isDeliveryAvailable,
required String serviceItemName,
required int manufacturedById,
required String manufacturedByName,
required String manufacturedOn,
required List requestImages,
required RequestOfferStatusEnum requestOfferStatusEnum,
@ -354,7 +354,7 @@ class RequestRepoImp implements RequestRepo {
required String offerPrice,
required bool isDeliveryAvailable,
required String serviceItemName,
required int manufacturedById,
required String manufacturedByName,
required String manufacturedOn,
required List requestImages,
required RequestOfferStatusEnum requestOfferStatusEnum,
@ -369,7 +369,7 @@ class RequestRepoImp implements RequestRepo {
"serviceItem": serviceItemName,
"comment": message,
"price": offerPrice,
"offeredItemCreatedBy": manufacturedById.toString(),
"offeredItemCreatedBy": manufacturedByName.toString(),
"offeredItemCreatedOn": manufacturedOn.toString(),
"reqOfferImages": requestImages,
"isDeliveryAvailable": isDeliveryAvailable

@ -6,7 +6,6 @@ import 'package:local_auth/local_auth.dart';
import 'package:local_auth_android/local_auth_android.dart';
import 'package:local_auth_darwin/types/auth_messages_ios.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:easy_localization/easy_localization.dart';
abstract class CommonAuthServices {
@ -18,10 +17,11 @@ abstract class CommonAuthServices {
class CommonAuthImp implements CommonAuthServices {
final LocalAuthentication localAuth = LocalAuthentication();
@override
Future<bool> authenticate() async {
try {
final availableBiometrics = await localAuth.getAvailableBiometrics();
if (availableBiometrics == null || availableBiometrics.isEmpty) {
if (availableBiometrics.isEmpty) {
return false;
}

@ -58,8 +58,7 @@ class CommonServicesImp implements CommonAppServices {
return pickedFiles;
}
@override
Future<List<File>> pickMultipleImages() async {
final picker = ImagePicker();
List<File> imageModels = [];
@ -74,16 +73,16 @@ class CommonServicesImp implements CommonAppServices {
return [];
}
if (await element.length() > GlobalConsts().maxFileSizeInBytes) {
if (await element.length() > GlobalConsts.maxFileSizeInBytes) {
Utils.showToast(LocaleKeys.maxFileSize);
return [];
}
imageModels.add(File(element.path));
}
if (imageModels.length > GlobalConsts().maxFileCount) {
if (imageModels.length > GlobalConsts.maxFileCount) {
Utils.showToast(LocaleKeys.maxFileSelection);
imageModels = imageModels.sublist(0, GlobalConsts().maxFileCount); // Keep only the first 7 images
imageModels = imageModels.sublist(0, GlobalConsts.maxFileCount); // Keep only the first 7 images
}
pickedImages.addAll(imageModels);
@ -120,7 +119,7 @@ class CommonServicesImp implements CommonAppServices {
destLongitude,
);
return (distance / 1000) ?? 0.0;
return (distance / 1000);
} catch (e) {
logger.e(e.toString());
return 0.0;

@ -13,10 +13,11 @@ class AppEnums {
static const int requestFilterEnumId = 16; // Requests Filter Enums
static const int branchServicesEnumId = 6; // Branch Services Filter Enums
static const int myAdsFilterEnumId = 18; // My Ads Filter Enums
static const int exploreAdsFilterEnumId = 23; // Explore Ads Filter Enums
static const int adOwnersFilterEnumId = 23; // Explore Ads Filter Enums
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 serviceDeliveryTypeEnumId = 24; // to get the serviceDeliveryType
static const int shippingStatusEnumId = 30; // to get the Shipping Filter Enums
}

@ -165,9 +165,8 @@ class AdVM extends BaseVM {
notifyListeners();
}
List<EnumsModel> exploreAdsEnums = [];
List<EnumsModel> myAdsEnums = [];
List<EnumsModel> myAdsStatusEnums = [];
List<VehicleBrandsModel> vehicleBrandsForFilters = [];
List<FilterListModel> exploreAdsFilterOptions = [];
List<FilterListModel> myAdsFilterOptions = [];
@ -177,60 +176,119 @@ class AdVM extends BaseVM {
return;
}
if (myAdsEnums.isEmpty) {
myAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.myAdsFilterEnumId);
if (myAdsStatusEnums.isEmpty) {
myAdsStatusEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.myAdsFilterEnumId);
}
if (exploreAdsEnums.isEmpty) {
exploreAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.exploreAdsFilterEnumId);
if (vehicleBrandsForFilters.isEmpty) {
vehicleBrandsForFilters = await commonRepo.getVehicleBrands(vehicleTypeId: -1); // to get all the brands
}
exploreAdsFilterOptions.clear();
myAdsFilterOptions.clear();
for (int i = 0; i < myAdsEnums.length; i++) {
myAdsFilterOptions.add(FilterListModel(title: myAdsEnums[i].enumValueStr, isSelected: false, id: myAdsEnums[i].enumValue));
for (int i = 0; i < myAdsStatusEnums.length; i++) {
myAdsFilterOptions.add(FilterListModel(title: myAdsStatusEnums[i].enumValueStr, isSelected: false, id: myAdsStatusEnums[i].enumValue));
}
myAdsFilterOptions.insert(0, FilterListModel(title: "All Ads", isSelected: true, id: 0));
log("myAdsFilterOptions: ${myAdsFilterOptions.last.id}");
for (int i = 0; i < exploreAdsEnums.length; i++) {
exploreAdsFilterOptions.add(FilterListModel(title: "${exploreAdsEnums[i].enumValueStr} Ads", isSelected: false, id: exploreAdsEnums[i].enumValue));
for (int i = 0; i < vehicleBrandsForFilters.length; i++) {
exploreAdsFilterOptions.add(FilterListModel(
id: vehicleBrandsForFilters[i].id!,
title: "${vehicleBrandsForFilters[i].vehicleBrandDescription}",
iconUrl: "",
isSelected: false,
));
}
exploreAdsFilterOptions.insert(0, FilterListModel(title: "All Ads", isSelected: true, id: 0));
notifyListeners();
}
applyFilterOnExploreAds({required CreatedByRoleEnum createdByRoleFilter}) async {
applyFilterOnExploreAds({required int vehicleBrandId}) async {
pageIndexForExploreAds = 1;
hasMoreDataForExploreAds = true;
if (exploreAdsFilterOptions.isEmpty) return;
int index = exploreAdsFilterOptions.indexWhere((element) => element.id.toCreatedByRoleEnum() == createdByRoleFilter);
int index = exploreAdsFilterOptions.indexWhere((element) => element.id == vehicleBrandId);
for (var value in exploreAdsFilterOptions) {
value.isSelected = false;
}
exploreAdsFilterOptions[index].isSelected = true;
if (createdByRoleFilter == CreatedByRoleEnum.allAds) {
if (vehicleBrandId == 0) {
// all ads
getExploreAds();
return;
}
setState(ViewState.busy);
exploreAdsFilteredList = await getAdsByFilter(createdByRoleEnum: createdByRoleFilter, isMyAds: false);
exploreAdsFilteredList = await getAdsByFilter(vehicleBrandId: vehicleBrandId, isMyAds: false);
setState(ViewState.idle);
notifyListeners();
}
int currentPageForExploreAds = 1;
int currentPageForMyAds = 1;
int pageIndexForExploreAds = 1;
int pageIndexForMyAds = 1;
bool hasMoreDataForExploreAds = true;
bool hasMoreDataForMyAds = true;
bool isLoadingMore = false;
bool hasMoreData = true;
fetchMoreAds() async {}
Future<void> fetchMoreExploreAds() async {
if (isLoadingMore) return;
isLoadingMore = true;
hasMoreDataForExploreAds = true;
notifyListeners();
try {
final List<AdDetailsModel> newAds = await getAdsBasedOnFilters(pageIndex: pageIndexForExploreAds, isFromLazyLoad: true);
if (newAds.isEmpty) {
hasMoreDataForExploreAds = false;
} else {
exploreAdsFilteredList.addAll(newAds);
pageIndexForExploreAds++;
if (exploreAdsFilteredList.length < exploreAdsFilteredList.last.totalItemsCount!) {
hasMoreDataForExploreAds = true;
} else {
hasMoreDataForExploreAds = false;
}
}
} catch (error) {
isLoadingMore = false;
Utils.showToast(error.toString());
}
isLoadingMore = false;
notifyListeners();
}
Future<List<AdDetailsModel>> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum}) async {
return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum);
Future<void> fetchMoreMyAds({required AdPostStatus adsStatus}) async {
if (isLoadingMore) return;
hasMoreDataForMyAds = true;
isLoadingMore = true;
notifyListeners();
try {
final List<AdDetailsModel> newAds = await adsRepo.getAllAds(page: pageIndexForMyAds, isMyAds: true, adPostStatus: adsStatus);
if (newAds.isEmpty) {
hasMoreDataForMyAds = false;
} else {
myAdsFilteredList.addAll(newAds);
pageIndexForMyAds++;
if (myAdsFilteredList.length < myAdsFilteredList.last.totalItemsCount!) {
hasMoreDataForMyAds = true;
} else {
hasMoreDataForMyAds = false;
}
}
} catch (error) {
isLoadingMore = false;
Utils.showToast(error.toString());
}
isLoadingMore = false;
notifyListeners();
}
Future<List<AdDetailsModel>> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum, int? vehicleBrandId}) async {
return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum, vehicleBrandId: vehicleBrandId);
}
applyFilterOnMyAds({required AdPostStatus adPostStatusEnum}) async {
pageIndexForMyAds = 1;
hasMoreDataForMyAds = true;
if (myAdsFilterOptions.isEmpty) return;
int index = myAdsFilterOptions.indexWhere((element) => element.id.toAdPostEnum() == adPostStatusEnum);
@ -265,7 +323,7 @@ class AdVM extends BaseVM {
if (genericRespModel.messageStatus == null) {
Utils.hideLoading(context);
Utils.showToast(genericRespModel.message ?? "Something went wrong!");
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
return false;
}
Utils.hideLoading(context);
@ -273,6 +331,8 @@ class AdVM extends BaseVM {
}
Future<void> getExploreAds() async {
pageIndexForExploreAds = 1;
hasMoreDataForExploreAds = true;
setState(ViewState.busy);
exploreAds = await adsRepo.getAllAds(isMyAds: false);
exploreAdsFilteredList = exploreAds;
@ -281,6 +341,8 @@ class AdVM extends BaseVM {
}
Future<void> getMyAds() async {
pageIndexForMyAds = 1;
hasMoreDataForMyAds = true;
setState(ViewState.busy);
myAds = await adsRepo.getAllAds(isMyAds: true);
myAdsFilteredList = myAds;
@ -316,8 +378,8 @@ class AdVM extends BaseVM {
notifyListeners();
}
Future<void> getVehicleAdsSpecialServices() async {
vehicleAdsSpecialServices = await adsRepo.getSpecialServices(specialServiceType: 1);
Future<void> getVehicleAdsSpecialServices({required int cityId, required int countryId, required int specialServiceType}) async {
vehicleAdsSpecialServices = await adsRepo.getSpecialServices(specialServiceType: specialServiceType, countryId: countryId, cityId: cityId);
notifyListeners();
}
@ -1126,9 +1188,7 @@ class AdVM extends BaseVM {
getVehicleAdsDuration();
}
if (vehicleAdsSpecialServices.isEmpty) {
getVehicleAdsSpecialServices();
}
getVehicleAdsSpecialServices(countryId: vehicleCountryId.selectedId, cityId: vehicleCityId.selectedId, specialServiceType: 1);
notifyListeners();
}
@ -1205,8 +1265,8 @@ class AdVM extends BaseVM {
pickedPostingImages.addAll(imageModels);
// Added By Aamir
if (pickedPostingImages.length > GlobalConsts().maxFileCount) {
pickedPostingImages = pickedPostingImages.sublist(0, GlobalConsts().maxFileCount);
if (pickedPostingImages.length > GlobalConsts.maxFileCount) {
pickedPostingImages = pickedPostingImages.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
}
@ -1253,8 +1313,8 @@ class AdVM extends BaseVM {
} else {
vehicleDamageCards[index].partImages!.addAll(imageModels);
// Added By Aamir
if (vehicleDamageCards[index].partImages!.length > GlobalConsts().maxFileCount) {
vehicleDamageCards[index].partImages = vehicleDamageCards[index].partImages!.sublist(0, GlobalConsts().maxFileCount);
if (vehicleDamageCards[index].partImages!.length > GlobalConsts.maxFileCount) {
vehicleDamageCards[index].partImages = vehicleDamageCards[index].partImages!.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
}
}
@ -1309,8 +1369,8 @@ class AdVM extends BaseVM {
List<File> images = await commonServices.pickMultipleImages();
pickedDamageImages.addAll(images);
if (pickedDamageImages.length > GlobalConsts().maxFileCount) {
pickedDamageImages = pickedDamageImages.sublist(0, GlobalConsts().maxFileCount);
if (pickedDamageImages.length > GlobalConsts.maxFileCount) {
pickedDamageImages = pickedDamageImages.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
}
if (pickedDamageImages.isNotEmpty) vehicleDamageImageError = "";
@ -1596,20 +1656,32 @@ class AdVM extends BaseVM {
// ************ ADS SEARCH VIEW ****************
List<EnumsModel> vehicleConditionsEnum = [];
List<EnumsModel> adOwnerEnumsFilter = [];
Future<void> populateDataForAdFilter() async {
setState(ViewState.busy);
pageIndexForExploreAds = 1;
pageIndexForMyAds = 1;
hasMoreDataForExploreAds = true;
hasMoreDataForMyAds = true;
isLoadingMore = false;
if (adOwnerEnumsFilter.isEmpty) {
adOwnerEnumsFilter = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.adOwnersFilterEnumId);
}
if (vehicleConditionsEnum.isEmpty) {
vehicleConditionsEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.conditionEnumId);
}
if (vehicleBrands.isEmpty) {
vehicleBrands = await commonRepo.getVehicleBrands(vehicleTypeId: -1);
if (vehicleBrandsForFilters.isEmpty) {
vehicleBrandsForFilters = await commonRepo.getVehicleBrands(vehicleTypeId: -1); // to get all the brands
}
if (vehicleModelYears.isEmpty) {
vehicleModelYears = await commonRepo.getVehicleModelYears(vehicleTypeId: -1);
}
vehicleCities = await commonRepo.getVehicleCities(countryId: -1); // fetch all the cities
setState(ViewState.idle);
}
@ -1678,9 +1750,9 @@ class AdVM extends BaseVM {
SelectionModel vehicleOwnerId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateSelectionVehicleAdOwnerId(SelectionModel id, {bool isForSearch = false}) {
void updateSelectionVehicleAdOwnerId(SelectionModel id, {bool showSelectionInDropdown = true, bool isForSearch = false}) {
if (isForSearch) {
EnumsModel owner = exploreAdsEnums.firstWhere((element) => element.enumValue == id.selectedId);
EnumsModel owner = adOwnerEnumsFilter.firstWhere((element) => element.enumValue == id.selectedId);
DropValue ownerValue = DropValue(owner.enumValue, "${owner.enumValueStr} Ads", "");
if (!ifAlreadyExist(list: vehicleAdOwnerSearchHistory, value: ownerValue)) {
addToVehicleAdOwnerSearchHistory(value: ownerValue);
@ -1688,7 +1760,9 @@ class AdVM extends BaseVM {
notifyListeners();
return;
}
vehicleOwnerId = id;
if (showSelectionInDropdown) {
vehicleOwnerId = id;
}
notifyListeners();
}
@ -1819,9 +1893,14 @@ class AdVM extends BaseVM {
vehicleOwnerId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
}
Future<void> getAdsBasedOnFilters() async {
exploreAdsFilteredList.clear();
setState(ViewState.busy);
Future<List<AdDetailsModel>> getAdsBasedOnFilters({
required int pageIndex,
required bool isFromLazyLoad,
}) async {
if (!isFromLazyLoad) {
exploreAdsFilteredList.clear();
setState(ViewState.busy);
}
List<String> cityIdsList = [];
if (vehicleLocationAdSearchHistory.isNotEmpty) {
for (var element in vehicleLocationAdSearchHistory) {
@ -1863,15 +1942,25 @@ class AdVM extends BaseVM {
}
}
exploreAdsFilteredList = await adsRepo.getExploreAdsBasedOnFilters(
List<AdDetailsModel> list = await adsRepo.getExploreAdsBasedOnFilters(
cityIdsList: cityIdsList,
createdByRolesIdsList: adOwnerIdsList,
vehicleBrandIdsList: brandsIdsList,
vehicleModelYearIdsList: vehicleYearIdsList,
vehicleAdConditionIdsList: conditionsIdsList,
vehicleAdCreatedDateList: createdDatesList,
page: pageIndex,
);
setState(ViewState.idle);
if (!isFromLazyLoad) {
exploreAdsFilteredList.clear();
exploreAdsFilteredList = list;
setState(ViewState.idle);
} else {
return list;
}
return [];
}
void onEditUpdateAdPressed({required BuildContext context, required AdDetailsModel previousDetails, required bool isFromExtendAd}) {
@ -1962,7 +2051,9 @@ class AdVM extends BaseVM {
updateSelectionVehicleConditionId(SelectionModel(selectedId: previousAdDetails!.vehicle!.condition!.id!, selectedOption: previousAdDetails!.vehicle!.condition!.label ?? ""));
updateSelectionVehicleCategoryId(SelectionModel(selectedId: previousAdDetails!.vehicle!.category!.id!, selectedOption: previousAdDetails!.vehicle!.category!.label ?? ""));
updateSelectionVehicleMileageId(SelectionModel(
selectedId: previousAdDetails!.vehicle!.mileage!.id!, selectedOption: "${previousAdDetails!.vehicle!.mileage!.mileageStart} - ${previousAdDetails!.vehicle!.mileage!.mileageEnd}"));
selectedId: previousAdDetails!.vehicle!.mileage!.id!,
selectedOption: "${previousAdDetails!.vehicle!.mileage!.mileageStart} - ${previousAdDetails!.vehicle!.mileage!.mileageEnd}",
));
updateSelectionVehicleTransmissionId(SelectionModel(selectedId: previousAdDetails!.vehicle!.transmission!.id!, selectedOption: previousAdDetails!.vehicle!.transmission!.label ?? ""));
updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: previousAdDetails!.vehicle!.sellertype!.id!, selectedOption: previousAdDetails!.vehicle!.sellertype!.label ?? ""));
int indexCountry = vehicleCountries.indexWhere((element) => element.id == previousAdDetails!.vehicle!.countryID);

@ -55,20 +55,17 @@ class AppointmentsVM extends BaseVM {
bool isUpcomingEnabled = true;
bool isFetchingLists = false;
int selectedBranch = 0;
int selectedAppointmentIndex = 0;
int selectedAppointmentSubIndex = 0;
int selectedAppointmentId = 0;
int selectedBranchIdForAppointments = 0;
List<AppointmentListModel> myAppointments = [];
List<AppointmentListModel> myUpComingAppointments = [];
List<AppointmentListModel> myFilteredAppointments = [];
List<FilterListModel> appointmentsFilterOptions = [];
List<CustomerData> customersAppointments = [];
List<AppointmentListModel> myFilteredAppointments2 = [];
// List<ScheduleData> availableSchedules = [];
bool isFetchingServices = false;
List<DropValue> branchCategories = [];
@ -234,7 +231,6 @@ class AppointmentsVM extends BaseVM {
Utils.showToast(genericRespModel.message.toString());
await getMyAppointmentsForCustomer();
Utils.hideLoading(context);
getMyAppointmentsForCustomer();
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
} catch (e) {
@ -374,7 +370,7 @@ class AppointmentsVM extends BaseVM {
// appointmentStatusEnum.getIdFromAppointmentStatusEnum()]
// .isSelected = true;
if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == 0) {
if (appointmentStatusEnum == AppointmentStatusEnum.allAppointments) {
myFilteredAppointments = myAppointments;
if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers();
notifyListeners();
@ -459,10 +455,10 @@ class AppointmentsVM extends BaseVM {
AppointmentSlots? appointmentSlots;
Future<void> getAppointmentSlotsInfo({required Map<String, dynamic> map, required BuildContext context, bool isNeedToRebuild = false}) async {
Future<void> getAppointmentSlotsInfo({required int branchID, required BuildContext context, bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
try {
GenericRespModel genericRespModel = await appointmentRepo.getAppointmentSlots(map);
GenericRespModel genericRespModel = await appointmentRepo.getAppointmentSlots(branchID: branchID, fromDate: selectedDateForAppointments, toDate: selectedDateForAppointments);
if (genericRespModel.messageStatus == 1) {
appointmentSlots = AppointmentSlots.fromJson(genericRespModel.data);
} else {
@ -473,9 +469,9 @@ class AppointmentsVM extends BaseVM {
}
}
Future<void> getMyAppointmentsForProvider(Map<String, dynamic> map) async {
Future<void> getMyAppointmentsForProvider({required int branchID}) async {
setState(ViewState.busy);
myAppointments = await appointmentRepo.getMyAppointmentsForProvider(map);
myAppointments = await appointmentRepo.getMyAppointmentsForProvidersByFilters(branchID: branchID);
myFilteredAppointments = myAppointments;
myUpComingAppointments = myAppointments
.where((element) =>
@ -1272,6 +1268,21 @@ class AppointmentsVM extends BaseVM {
return false;
}
ifAlreadyExistForStringValue({required List<String> list, required String value}) {
int index = list.indexWhere((element) {
log("element: $element");
log("value: $value");
return element == value;
});
if (index != -1) {
return true;
}
return false;
}
// Rating filter
double branchFilterByRating = 4.0;
@ -1285,6 +1296,7 @@ class AppointmentsVM extends BaseVM {
List<DropValue> providersDropList = [];
List<DropValue> servicesDropList = [];
List<DropValue> itemsDropList = [];
List<DropValue> serviceDeliveryTypesDropList = [];
Future<void> fetchAllProviders() async {
if (providersDropList.isNotEmpty) return;
@ -1351,6 +1363,25 @@ class AppointmentsVM extends BaseVM {
setState(ViewState.idle);
}
Future<void> fetchAllServiceDeliveryTypes() async {
if (serviceDeliveryTypesDropList.isNotEmpty) return;
serviceDeliveryTypesDropList.clear();
setState(ViewState.busy);
List<EnumsModel> enums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.serviceDeliveryTypeEnumId);
for (EnumsModel element in enums) {
serviceDeliveryTypesDropList.add(
DropValue(
element.enumValue,
element.enumValueStr,
"",
),
);
}
setState(ViewState.idle);
}
Future<void> populateDataForBranchesFilter() async {
await fetchAllProviders();
await fetchAllCategories();
@ -1440,7 +1471,6 @@ class AppointmentsVM extends BaseVM {
branchesDropList.add(DropValue(element.id ?? 0, element.branchName ?? "N/A", ""));
}
setState(ViewState.idle);
log("branchesDropList: ${branchesDropList.length}");
}
@ -1449,6 +1479,7 @@ class AppointmentsVM extends BaseVM {
await fetchAllCategories();
await fetchAllServices();
await fetchAllItems();
await fetchAllServiceDeliveryTypes();
}
int appointmentFiltersCounter = 0;
@ -1661,15 +1692,132 @@ class AppointmentsVM extends BaseVM {
notifyListeners();
}
// Customer Name Filter for PROVIDER ONLY
List<String> appointmentFilterCustomerNameSearchHistory = [];
String currentCustomerNameFilter = '';
void onCurrentCustomerNameFilterChanged(var value) {
currentCustomerNameFilter = value;
notifyListeners();
}
void removeAppointmentFilterCustomerNameSearchHistory({bool isClear = false, required int index}) {
if (isClear) {
appointmentFilterCustomerNameSearchHistory.clear();
notifyListeners();
return;
}
appointmentFilterCustomerNameSearchHistory.removeAt(index);
if (appointmentFilterCustomerNameSearchHistory.isEmpty) {
updateAppointmentFiltersCounter(appointmentFiltersCounter - 1);
// appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
}
notifyListeners();
}
void addAppointmentFilterCustomerNameSearchHistory({required String value}) {
if (appointmentFilterCustomerNameSearchHistory.isEmpty) {
updateAppointmentFiltersCounter(appointmentFiltersCounter + 1);
}
if (!ifAlreadyExistForStringValue(list: appointmentFilterCustomerNameSearchHistory, value: value)) {
appointmentFilterCustomerNameSearchHistory.add(value);
currentCustomerNameFilter = "";
}
notifyListeners();
}
// MOBILE PHONE Filter for PROVIDER ONLY
String currentMobilePhoneFilter = '';
void onCurrentMobilePhoneFilterChanged(var value) {
currentMobilePhoneFilter = value;
notifyListeners();
}
List<String> appointmentFilterMobilePhoneSearchHistory = [];
void removeAppointmentFilterMobilePhoneSearchHistory({bool isClear = false, required int index}) {
if (isClear) {
appointmentFilterMobilePhoneSearchHistory.clear();
notifyListeners();
return;
}
appointmentFilterMobilePhoneSearchHistory.removeAt(index);
if (appointmentFilterMobilePhoneSearchHistory.isEmpty) {
updateAppointmentFiltersCounter(appointmentFiltersCounter - 1);
// appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
}
notifyListeners();
}
void addAppointmentFilterMobilePhoneSearchHistory({required String value}) {
if (appointmentFilterMobilePhoneSearchHistory.isEmpty) {
updateAppointmentFiltersCounter(appointmentFiltersCounter + 1);
}
if (!ifAlreadyExistForStringValue(list: appointmentFilterMobilePhoneSearchHistory, value: value)) {
appointmentFilterMobilePhoneSearchHistory.add(value);
currentMobilePhoneFilter = "";
}
notifyListeners();
}
// Service Delivery Types For PROVIDER ONLY
List<DropValue> appointmentFilterServiceDeliverySearchHistory = [];
void removeAppointmentFilterServiceDeliverySearchHistory({bool isClear = false, required int index}) {
if (isClear) {
appointmentFilterServiceDeliverySearchHistory.clear();
notifyListeners();
return;
}
appointmentFilterServiceDeliverySearchHistory.removeAt(index);
if (appointmentFilterServiceDeliverySearchHistory.isEmpty) {
updateAppointmentFiltersCounter(appointmentFiltersCounter - 1);
// appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
}
notifyListeners();
}
void addAppointmentFilterServiceDeliverySearchHistory({required DropValue value}) {
if (appointmentFilterServiceDeliverySearchHistory.isEmpty) {
updateAppointmentFiltersCounter(appointmentFiltersCounter + 1);
}
appointmentFilterServiceDeliverySearchHistory.add(value);
notifyListeners();
}
SelectionModel appointmentFilterSelectedServiceDeliveryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateAppointmentFilterSelectedServiceDeliveryId(SelectionModel id, {bool isForSearch = false}) async {
if (isForSearch) {
DropValue itemsDrop = serviceDeliveryTypesDropList.firstWhere((element) => element.id == id.selectedId);
if (!ifAlreadyExist(list: appointmentFilterServiceDeliverySearchHistory, value: itemsDrop)) {
addAppointmentFilterServiceDeliverySearchHistory(value: itemsDrop);
}
}
// appointmentFilterSelectedServiceId = id;
notifyListeners();
}
void clearAppointmentFilterSelections() {
appointmentFilterSelectedProviderId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
appointmentFilterSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
appointmentFilterSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
appointmentFilterSelectedItemId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
appointmentFilterSelectedServiceDeliveryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
appointmentFilterSelectedBranchId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
}
void clearAppointmentFilters() {
appointmentFilterServiceDeliverySearchHistory.clear();
appointmentFilterCustomerNameSearchHistory.clear();
appointmentFilterMobilePhoneSearchHistory.clear();
appointmentFilterItemsSearchHistory.clear();
appointmentFilterServicesSearchHistory.clear();
appointmentFilterCategorySearchHistory.clear();
@ -1677,11 +1825,16 @@ class AppointmentsVM extends BaseVM {
appointmentFilterBranchSearchHistory.clear();
appointmentFiltersCounter = 0;
clearAppointmentFilterSelections();
getMyAppointmentsForCustomer();
if (AppState().currentAppType == AppType.provider) {
getMyAppointmentsForProvider(branchID: selectedBranchIdForAppointments);
} else {
getMyAppointmentsForCustomer();
}
notifyListeners();
}
Future<void> getAppointmentsBasedOnFilters() async {
Future<void> getAppointmentsBasedOnFiltersForCustomer() async {
setState(ViewState.busy);
List<String> providersIdsList = [];
if (appointmentFilterProviderSearchHistory.isNotEmpty) {
@ -1725,6 +1878,56 @@ class AppointmentsVM extends BaseVM {
setState(ViewState.idle);
}
String selectedDateForAppointments = "";
Future<void> getAppointmentsBasedOnFiltersForProviders({required int branchID}) async {
setState(ViewState.busy);
List<String> servicesIdsList = [];
if (appointmentFilterServicesSearchHistory.isNotEmpty) {
for (var element in appointmentFilterServicesSearchHistory) {
servicesIdsList.add(element.id.toString());
}
}
List<String> customerNamesList = [];
if (appointmentFilterCustomerNameSearchHistory.isNotEmpty) {
for (var element in appointmentFilterCustomerNameSearchHistory) {
customerNamesList.add(element.toString());
}
}
List<String> customerPhonesList = [];
if (appointmentFilterMobilePhoneSearchHistory.isNotEmpty) {
for (var element in appointmentFilterMobilePhoneSearchHistory) {
customerPhonesList.add(element.toString());
}
}
List<String> customerDeliveryTypesList = [];
if (appointmentFilterServiceDeliverySearchHistory.isNotEmpty) {
for (var element in appointmentFilterServiceDeliverySearchHistory) {
customerDeliveryTypesList.add(element.id.toString());
}
}
log("customerNamesList: $customerNamesList");
log("customerPhonesList: $customerPhonesList");
log("customerDeliveryTypesList: $customerDeliveryTypesList");
log("servicesIdsList: $servicesIdsList");
myAppointments = await appointmentRepo.getMyAppointmentsForProvidersByFilters(
branchID: branchID,
customerNamesList: customerNamesList,
customerPhonesList: customerPhonesList,
deliveryTypeIdsList: customerDeliveryTypesList,
serviceProviderServiceIdsList: servicesIdsList,
fromDate: selectedDateForAppointments,
toDate: selectedDateForAppointments);
log(" myFilteredAppointments : ${myFilteredAppointments.length}");
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
setState(ViewState.idle);
}
Future<bool> addProviderToFavorite({required int serviceProviderID, required BuildContext context}) async {
Utils.showLoading(context);
try {

@ -97,8 +97,8 @@ class ChatVM extends BaseVM {
notifyListeners();
}
List<int> indexesForCancelSpecialCarOffer = [0, 1, 5, 6, 10];
List<int> indexesForCancelSparePartOffer = [2, 5, 6, 10];
List<int> indexesForCancelSpecialCarOffer = [0, 1, 6, 10];
List<int> indexesForCancelSparePartOffer = [2, 10];
List<int> indexesForRejectOffer = [3, 4, 10];
List<int> indexesForDealNotCompleted = [7, 4, 8, 9, 10];
@ -363,7 +363,7 @@ class ChatVM extends BaseVM {
required String offerPrice,
required bool isDeliveryAvailable,
required String serviceItemName,
required int manufacturedById,
required String manufacturedByName,
required String manufacturedOn,
required List<dynamic> offerImages,
required BuildContext context,
@ -387,7 +387,7 @@ class ChatVM extends BaseVM {
"IsDeliveryAvailable": isDeliveryAvailable,
"ServiceItem": serviceItemName,
"ReqOfferImages": offerImages,
"OfferedItemCreatedBy": manufacturedById, // TODO: The ID should be used from the manufacturer database
"OfferedItemCreatedBy": manufacturedByName,
// "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime
"ServiceProviderID": providerId,
"OfferStatus": RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
@ -443,8 +443,8 @@ class ChatVM extends BaseVM {
imageModels.add(ImageModel(filePath: element.path, isFromNetwork: false));
}
pickedImagesForMessage.addAll(imageModels);
if (pickedImagesForMessage.length > GlobalConsts().maxFileCount) {
pickedImagesForMessage = pickedImagesForMessage.sublist(0, GlobalConsts().maxFileCount);
if (pickedImagesForMessage.length > GlobalConsts.maxFileCount) {
pickedImagesForMessage = pickedImagesForMessage.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
}
@ -652,7 +652,7 @@ class ChatVM extends BaseVM {
required int requestOfferID,
required String offerPrice,
required String serviceItemName,
required int manufacturedById,
required String manufacturedByName,
required String manufacturedOn,
required RequestOfferStatusEnum requestOfferStatusEnum,
required BuildContext context,
@ -674,8 +674,8 @@ class ChatVM extends BaseVM {
"RequestID": requestId,
"Price": double.parse(offerPrice),
"ServiceItem": serviceItemName,
"OfferedItemCreatedBy": manufacturedById, // TODO: The ID should be used from the manufacturer database
// "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime
"OfferedItemCreatedBy": manufacturedByName,
// "OfferedItemCreatedOn": manufacturedOn,
"ServiceProviderID": serviceProviderID,
"OfferStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum(),
"Comment": comments,

@ -83,7 +83,7 @@ class DashboardVMProvider extends BaseVM {
shippingManagementVM.populateShippingRequestFilterList();
await serviceVM.getBranchAndServices();
await serviceVM.populateBranchServiceFilters();
await appointmentVM.getMyAppointmentsForProvider({"ServiceProviderID": injector.get<AppState>().getUser.data?.userInfo?.providerId.toString() ?? "0"});
await appointmentVM.getMyAppointmentsForProvider(branchID: appointmentVM.selectedBranchIdForAppointments);
adVM.populateAdsFilterList();
await subscriptionsVM.getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
if (dashboardRouteEnum != DashboardRouteEnum.fromAdsPayment && dashboardRouteEnum != DashboardRouteEnum.fromAdsSubmit) {

@ -39,7 +39,6 @@ class RequestsVM extends BaseVM {
RequestsVM({required this.commonServices, required this.commonRepo, required this.requestRepo});
List<RequestModel> myRequests = [];
List<RequestModel> myFilteredRequests = [];
List<FilterListModel> requestsTypeFilterOptions = [];
@ -92,8 +91,39 @@ class RequestsVM extends BaseVM {
setState(ViewState.idle);
}
int pageIndexForRequests = 1;
bool hasMoreDataRequests = true;
bool isLoadingMore = false;
// Future<void> fetchMoreRequests() async {
// log("isLoadingMore: $isLoadingMore");
// log("hasMoreDataRequests: $hasMoreDataRequests");
// if (isLoadingMore) return;
// isLoadingMore = true;
// hasMoreDataRequests = true;
// notifyListeners();
// try {
// final List<RequestModel> newRequests = await requestRepo.getRequestsBasedOnFilters(requestedDate: requestedDateFilter, requestTypeId: requestTypeId);
// if (newAds.isEmpty) {
// hasMoreDataForExploreAds = false;
// } else {
// exploreAdsFilteredList.addAll(newAds);
// pageIndexForExploreAds++;
// if (exploreAdsFilteredList.length < exploreAdsFilteredList.last.totalItemsCount!) {
// hasMoreDataForExploreAds = true;
// } else {
// hasMoreDataForExploreAds = false;
// }
// }
// } catch (error) {
// isLoadingMore = false;
// Utils.showToast(error.toString());
// }
// isLoadingMore = false;
// notifyListeners();
// }
Future<bool> getRequestsBasedOnFilters() async {
myFilteredRequests.clear();
setState(ViewState.busy);
int requestTypeIdApi = requestsTypeFilterOptions.firstWhere((element) => element.isSelected).id;
@ -127,7 +157,7 @@ class RequestsVM extends BaseVM {
);
setState(ViewState.idle);
return true;
} catch (e, s) {
} catch (e) {
logger.i("e: ${e.toString()}");
setState(ViewState.idle);
return false;
@ -186,7 +216,7 @@ class RequestsVM extends BaseVM {
}
overwriteChatMessagesInRequestsModel({required List<ChatMessageModel> messages, required int index, required BuildContext context}) {
myFilteredRequests[index].chatMessages.clear();
if (myFilteredRequests.isEmpty) return;
myFilteredRequests[index].chatMessages = messages;
if (myFilteredRequests[index].chatMessages.isNotEmpty) {
for (var message in myFilteredRequests[index].chatMessages) {
@ -237,8 +267,8 @@ class RequestsVM extends BaseVM {
imageModels.add(ImageModel(filePath: element.path, isFromNetwork: false));
}
pickedVehicleImages.addAll(imageModels);
if (pickedVehicleImages.length > GlobalConsts().maxFileCount) {
pickedVehicleImages = pickedVehicleImages.sublist(0, GlobalConsts().maxFileCount);
if (pickedVehicleImages.length > GlobalConsts.maxFileCount) {
pickedVehicleImages = pickedVehicleImages.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
}
@ -246,8 +276,6 @@ class RequestsVM extends BaseVM {
notifyListeners();
}
bool isFetchingRequestType = false;
bool isFetchingVehicleType = true;
bool isFetchingVehicleDetail = false;
@ -914,7 +942,7 @@ class RequestsVM extends BaseVM {
return genericRespModel.messageStatus == 1;
} catch (e) {
logger.i(e.toString());
// Utils.showToast(e.toString());
Utils.showToast(e.toString());
if (needLoading) {
Utils.hideLoading(context);
}
@ -933,7 +961,7 @@ class RequestsVM extends BaseVM {
offerPriceError = "";
}
if (offerDescription.isEmpty) {
if (offerDescription.isEmpty || offerDescription.length < 5) {
offerDescriptionError = GlobalConsts.descriptionError;
isValidated = false;
notifyListeners();
@ -1022,7 +1050,7 @@ class RequestsVM extends BaseVM {
required String offerPrice,
required bool isDeliveryAvailable,
required String serviceItemName,
required int manufacturedById,
required String manufacturedByName,
required String manufacturedOn,
required int receiverId,
required int customerRequestIndex,
@ -1044,7 +1072,7 @@ class RequestsVM extends BaseVM {
offerPrice: offerPrice,
isDeliveryAvailable: isDeliveryAvailable,
serviceItemName: serviceItemName,
manufacturedById: manufacturedById,
manufacturedByName: manufacturedByName,
manufacturedOn: manufacturedOn,
requestImages: offerImages,
requestOfferStatusEnum: RequestOfferStatusEnum.offer,
@ -1068,7 +1096,7 @@ class RequestsVM extends BaseVM {
pop(context);
}
}
} catch (e, s) {
} catch (e) {
Utils.hideLoading(context);
log(e.toString());
Utils.showToast(e.toString());
@ -1082,7 +1110,7 @@ class RequestsVM extends BaseVM {
required int requestId,
required String offerPrice,
required String serviceItemName,
required int manufacturedById,
required String manufacturedByName,
required String manufacturedOn,
required RequestModel requestModel,
required bool isDeliveryAvailable,
@ -1105,7 +1133,7 @@ class RequestsVM extends BaseVM {
requestId: requestId,
offerPrice: offerPrice,
isDeliveryAvailable: isDeliveryAvailable,
manufacturedById: manufacturedById,
manufacturedByName: manufacturedByName,
manufacturedOn: manufacturedOn,
serviceItemName: serviceItemName,
offerImages: offerImages,
@ -1139,7 +1167,7 @@ class RequestsVM extends BaseVM {
requestID: requestModel.id,
price: double.parse(offerPrice),
isDeliveryAvailable: isDeliveryAvailable,
manufacturedById: manufacturedById,
manufacturedByName: manufacturedByName,
manufacturedOn: manufacturedOn,
serviceItemName: serviceItemName,
requestOfferStatusEnum: RequestOfferStatusEnum.offer,

@ -140,8 +140,8 @@ class ServiceVM extends BaseVM {
imageModels.add(ImageModel(filePath: element.path, isFromNetwork: false));
}
pickedBranchImages.addAll(imageModels);
if (pickedBranchImages.length > GlobalConsts().maxFileCount) {
pickedBranchImages = pickedBranchImages.sublist(0, GlobalConsts().maxFileCount);
if (pickedBranchImages.length > GlobalConsts.maxFileCount) {
pickedBranchImages = pickedBranchImages.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
}
if (pickedBranchImages.isNotEmpty) branchImageError = "";
@ -168,7 +168,6 @@ class ServiceVM extends BaseVM {
final BranchDetailModel currentBranch = branches!.data!.serviceProviderBranch!.firstWhere((element) => element.id == selectedBranchId);
for (var element in currentBranch.branchServices!) {
// TODO: Here , we need to add the category deactivated status.
categories.add(
CategoryData(
id: element.categoryId,
@ -567,6 +566,25 @@ class ServiceVM extends BaseVM {
}
}
Future<bool> updateCategoryStatus({required BuildContext context, required ServiceStatusEnum serviceStatusEnum, required int branchId, required int categoryId}) async {
try {
Utils.showLoading(context);
GenericRespModel genericRespModel = await branchRepo.updateCategoryStatus(
branchId: branchId,
categoryId: categoryId,
serviceStatusEnum: serviceStatusEnum,
);
Utils.hideLoading(context);
Utils.showToast(genericRespModel.message ?? "");
return genericRespModel.messageStatus == 1;
} catch (e) {
Utils.hideLoading(context);
log(e.toString());
Utils.showToast(e.toString());
return false;
}
}
Future buildDealNotCompletedBottomSheetOptions({required BuildContext mainContext, required List<AppointmentBasicDetailsModel> appointments, required String branchName}) async {
return actionConfirmationBottomSheet(
isOnlyOneButton: true,
@ -628,6 +646,7 @@ class ServiceVM extends BaseVM {
}
List<ServiceModel>? matchedServices;
bool isAllSelected = false;
Future<void> getAllMatchedServices({required int oldBranchId, required int newBranchId, required int categoryId}) async {

@ -294,7 +294,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
adVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: adVM.photoSSSchedulesByOffices.isEmpty
? LocaleKeys.noAvailableOfficesInCity.tr().toText(fontSize: 14, height: 1.2)
? LocaleKeys.noAvailableOfficesInCity.tr().toText(fontSize: 14, height: 1.2, color: MyColors.lightTextColor)
: Builder(
builder: (context) {
List<DropValue> vehicleCitiesDrop = [];

@ -78,13 +78,13 @@ class _AdsFilterViewState extends State<AdsFilterView> {
SearchEntityWidget(
title: LocaleKeys.searchByCity.tr(),
actionWidget: Builder(builder: (context) {
List<DropValue> vehicleBrandsDrop = [];
List<DropValue> vehicleCitiesDrop = [];
for (var element in adVM.vehicleCities) {
vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", ""));
vehicleCitiesDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", ""));
}
return DropdownField(
(DropValue value) => adVM.updateSelectionVehicleCityId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: vehicleBrandsDrop,
list: vehicleCitiesDrop,
dropdownValue: adVM.vehicleCityId.selectedId != -1 ? DropValue(adVM.vehicleCityId.selectedId, adVM.vehicleCityId.selectedOption, "") : null,
hint: LocaleKeys.selectCity.tr(),
errorValue: adVM.vehicleCityId.errorValue,
@ -99,7 +99,7 @@ class _AdsFilterViewState extends State<AdsFilterView> {
title: LocaleKeys.searchByBrandName.tr(),
actionWidget: Builder(builder: (context) {
List<DropValue> vehicleBrandsDrop = [];
for (var element in adVM.vehicleBrands) {
for (var element in adVM.vehicleBrandsForFilters) {
vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleBrandDescription ?? "", ""));
}
return DropdownField(
@ -160,6 +160,29 @@ class _AdsFilterViewState extends State<AdsFilterView> {
onHistoryItemTapped: (DropValue value) => null,
),
const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7),
SearchEntityWidget(
title: LocaleKeys.searchByAdOwner.tr(),
actionWidget: Builder(
builder: (context) {
List<DropValue> vehicleOwnerDrop = [];
for (var element in adVM.adOwnerEnumsFilter) {
vehicleOwnerDrop.add(DropValue(element.enumValue.toInt(), element.enumValueStr, ""));
}
return DropdownField(
(DropValue value) =>
adVM.updateSelectionVehicleAdOwnerId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true, showSelectionInDropdown: false),
list: vehicleOwnerDrop,
dropdownValue: adVM.vehicleOwnerId.selectedId != -1 ? DropValue(adVM.vehicleOwnerId.selectedId, adVM.vehicleOwnerId.selectedOption, "") : null,
hint: LocaleKeys.selectOwner.tr(),
errorValue: adVM.vehicleOwnerId.errorValue,
);
},
),
historyContent: adVM.vehicleAdOwnerSearchHistory,
onHistoryItemDeleted: (index) => adVM.removeVehicleAdOwnerSearchHistory(index: index),
onHistoryItemTapped: (DropValue value) => null,
),
const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7),
SearchEntityWidget(
title: LocaleKeys.searchByCreatedDate.tr(),
actionWidget: TxtField(
@ -197,7 +220,7 @@ class _AdsFilterViewState extends State<AdsFilterView> {
title: LocaleKeys.search.tr(),
onPressed: () {
Navigator.pop(context);
adVM.getAdsBasedOnFilters();
adVM.getAdsBasedOnFilters(isFromLazyLoad: false, pageIndex: 0);
},
backgroundColor: MyColors.darkPrimaryColor,
txtColor: MyColors.white,

@ -10,6 +10,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/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';
@ -21,12 +22,14 @@ class AdsListWidget extends StatelessWidget {
final ScrollPhysics? scrollPhysics;
final bool isAdsFragment;
final bool shouldShowAdStatus;
final bool hasMoreData;
final Function()? onFetchMoreAds;
const AdsListWidget({
super.key,
required this.adsList,
required this.shouldShowAdStatus,
required this.hasMoreData,
this.onFetchMoreAds,
this.scrollPhysics,
this.isAdsFragment = false,
@ -44,7 +47,8 @@ class AdsListWidget extends StatelessWidget {
}
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && context.read<AdVM>().hasMoreData) {
log("hasMoreData: $hasMoreData");
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && hasMoreData) {
if (onFetchMoreAds != null) {
onFetchMoreAds!();
}
@ -104,7 +108,9 @@ class AdCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (isAdsFragment && !context.read<AdVM>().isExploreAdsTapped) ...[
if (isAdsFragment && adDetails.adPostStatus! == AdPostStatus.sold) ...[
Utils.statusContainerChip(text: adDetails.statuslabel!, chipColor: Utils.getChipColorByAdStatus(adDetails.adPostStatus!)),
] else if (isAdsFragment && !context.read<AdVM>().isExploreAdsTapped) ...[
Utils.statusContainerChip(text: adDetails.statuslabel!, chipColor: Utils.getChipColorByAdStatus(adDetails.adPostStatus!)),
],
(adDetails.vehicle!.vehicleTitle ?? "").toText(

@ -1,7 +1,9 @@
import 'dart:async';
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
@ -16,6 +18,7 @@ import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/search_entity_widget.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.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:sizer/sizer.dart';
@ -33,6 +36,7 @@ class _AppointmentsFilterViewState extends State<AppointmentsFilterView> {
void initState() {
appointmentsVM = context.read<AppointmentsVM>();
scheduleMicrotask(() async {
log('herre called');
await appointmentsVM.populateDataForAppointmentsFilter();
});
super.initState();
@ -44,6 +48,21 @@ class _AppointmentsFilterViewState extends State<AppointmentsFilterView> {
super.dispose();
}
// Provider filters
// * Service
// * Customer name
// * Mobile number
// * Type of service Delivery
// Customer filters
// * Provider
// * Branch
// * Category
// * Service
// * Item
@override
Widget build(BuildContext context) {
return Scaffold(
@ -59,96 +78,154 @@ class _AppointmentsFilterViewState extends State<AppointmentsFilterView> {
),
body: Consumer<AppointmentsVM>(
builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return WillPopScope(
onWillPop: () async {
context.read<AdVM>().resetValues();
return true;
return PopScope(
onPopInvokedWithResult: (bool result, dynamic) {
if (result) {
context.read<AdVM>().resetValues();
}
},
child: Column(
children: [
ListView(
children: [
20.height,
SearchEntityWidget(
title: LocaleKeys.searchByProvider.tr(),
actionWidget: Builder(
builder: (context) {
return DropdownField(
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedProviderId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.providersDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedProviderId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedProviderId.selectedId, appointmentsVM.appointmentFilterSelectedProviderId.selectedOption, "")
: null,
hint: LocaleKeys.selectProvider.tr(),
);
if (AppState().currentAppType == AppType.provider) ...[
SearchEntityWidget(
title: LocaleKeys.searchByCustomerName.tr(),
isForString: true,
actionWidget: TxtField(
value: appointmentsVM.currentCustomerNameFilter,
onChanged: (value) => appointmentsVM.onCurrentCustomerNameFilterChanged(value),
hint: LocaleKeys.enterCustomerName.tr(),
postfixWidget: Consumer(
builder: (BuildContext context, AppointmentsVM appointmentVm, Widget? child) {
if (appointmentsVM.currentCustomerNameFilter.isEmpty) {
return const SizedBox();
}
return IconButton(
onPressed: () => appointmentsVM.addAppointmentFilterCustomerNameSearchHistory(value: appointmentsVM.currentCustomerNameFilter),
icon: const Icon(Icons.done, color: MyColors.lightIconColor),
);
},
),
),
historyContentString: appointmentsVM.appointmentFilterCustomerNameSearchHistory,
onHistoryItemDeleted: (index) {
appointmentsVM.removeAppointmentFilterCustomerNameSearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) => null,
historyContent: const [], // ignore in the case of TEXT FIELD
),
historyContent: appointmentsVM.appointmentFilterProviderSearchHistory,
onHistoryItemDeleted: (index) {
appointmentsVM.removeAppointmentFilterProviderSearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) =>
appointmentsVM.updateAppointmentFilterSelectedProviderId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
),
if (appointmentsVM.state == ViewState.busy) ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 5.h,
width: 5.h,
child: const CircularProgressIndicator(),
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
SearchEntityWidget(
title: LocaleKeys.searchByMobileNumber.tr(),
isForString: true,
actionWidget: TxtField(
onChanged: (value) => appointmentsVM.onCurrentMobilePhoneFilterChanged(value),
value: appointmentsVM.currentMobilePhoneFilter,
hint: LocaleKeys.enterMobileNumber.tr(),
postfixWidget: Consumer(
builder: (BuildContext context, AppointmentsVM appointmentVm, Widget? child) {
if (appointmentsVM.currentMobilePhoneFilter.isEmpty) {
return const SizedBox();
}
return IconButton(
onPressed: () => appointmentsVM.addAppointmentFilterMobilePhoneSearchHistory(value: appointmentsVM.currentMobilePhoneFilter),
icon: const Icon(Icons.done, color: MyColors.lightIconColor),
);
},
),
],
),
historyContentString: appointmentsVM.appointmentFilterMobilePhoneSearchHistory,
onHistoryItemDeleted: (index) {
appointmentsVM.removeAppointmentFilterMobilePhoneSearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) => null,
historyContent: const [], // ignore in the case of TEXT FIELD
),
],
if (appointmentsVM.appointmentFilterProviderSearchHistory.isNotEmpty) ...[
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
] else ...[
SearchEntityWidget(
title: LocaleKeys.searchByBranch.tr(),
title: LocaleKeys.searchByProvider.tr(),
actionWidget: Builder(
builder: (context) {
return DropdownField(
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedBranchId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.branchesDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedBranchId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedBranchId.selectedId, appointmentsVM.appointmentFilterSelectedBranchId.selectedOption, "")
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedProviderId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.providersDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedProviderId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedProviderId.selectedId, appointmentsVM.appointmentFilterSelectedProviderId.selectedOption, "")
: null,
hint: LocaleKeys.selectBranch.tr(),
hint: LocaleKeys.selectProvider.tr(),
);
},
),
historyContent: appointmentsVM.appointmentFilterBranchSearchHistory,
historyContent: appointmentsVM.appointmentFilterProviderSearchHistory,
onHistoryItemDeleted: (index) {
appointmentsVM.removeAppointmentFilterBranchSearchHistory(index: index);
appointmentsVM.removeAppointmentFilterProviderSearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) =>
appointmentsVM.updateAppointmentFilterSelectedBranchId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
appointmentsVM.updateAppointmentFilterSelectedProviderId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
),
],
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
SearchEntityWidget(
title: LocaleKeys.searchByCategory.tr(),
actionWidget: Builder(
builder: (context) {
return DropdownField(
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.categoryDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedCategoryId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedCategoryId.selectedId, appointmentsVM.appointmentFilterSelectedCategoryId.selectedOption, "")
: null,
hint: LocaleKeys.selectCategory.tr(),
);
if (appointmentsVM.state == ViewState.busy) ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 5.h,
width: 5.h,
child: const CircularProgressIndicator(),
),
],
),
],
if (appointmentsVM.appointmentFilterProviderSearchHistory.isNotEmpty) ...[
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
SearchEntityWidget(
title: LocaleKeys.searchByBranch.tr(),
actionWidget: Builder(
builder: (context) {
return DropdownField(
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedBranchId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.branchesDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedBranchId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedBranchId.selectedId, appointmentsVM.appointmentFilterSelectedBranchId.selectedOption, "")
: null,
hint: LocaleKeys.selectBranch.tr(),
);
},
),
historyContent: appointmentsVM.appointmentFilterBranchSearchHistory,
onHistoryItemDeleted: (index) {
appointmentsVM.removeAppointmentFilterBranchSearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) =>
appointmentsVM.updateAppointmentFilterSelectedBranchId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
),
],
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
SearchEntityWidget(
title: LocaleKeys.searchByCategory.tr(),
actionWidget: Builder(
builder: (context) {
return DropdownField(
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.categoryDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedCategoryId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedCategoryId.selectedId, appointmentsVM.appointmentFilterSelectedCategoryId.selectedOption, "")
: null,
hint: LocaleKeys.selectCategory.tr(),
);
},
),
historyContent: appointmentsVM.appointmentFilterCategorySearchHistory,
onHistoryItemDeleted: (index) {
appointmentsVM.removeAppointmentFilterCategorySearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) =>
appointmentsVM.updateAppointmentFilterSelectedCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
),
historyContent: appointmentsVM.appointmentFilterCategorySearchHistory,
onHistoryItemDeleted: (index) {
appointmentsVM.removeAppointmentFilterCategorySearchHistory(index: index);
},
onHistoryItemTapped: (DropValue value) =>
appointmentsVM.updateAppointmentFilterSelectedCategoryId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
),
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
],
SearchEntityWidget(
title: LocaleKeys.searchByService.tr(),
actionWidget: Builder(builder: (context) {
@ -166,22 +243,41 @@ class _AppointmentsFilterViewState extends State<AppointmentsFilterView> {
onHistoryItemTapped: (DropValue value) => null,
),
const Divider(thickness: 1.2).paddingOnly(top: 5, bottom: 5),
SearchEntityWidget(
title: LocaleKeys.searchByItem.tr(),
actionWidget: Builder(builder: (context) {
return DropdownField(
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedItemId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.itemsDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedItemId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedItemId.selectedId, appointmentsVM.appointmentFilterSelectedItemId.selectedOption, "")
: null,
hint: LocaleKeys.selectItems.tr(),
);
}),
historyContent: appointmentsVM.appointmentFilterItemsSearchHistory,
onHistoryItemDeleted: (index) => appointmentsVM.removeAppointmentFilterItemsSearchHistory(index: index),
onHistoryItemTapped: (DropValue value) => null,
),
if (AppState().currentAppType == AppType.customer) ...[
SearchEntityWidget(
title: LocaleKeys.searchByItem.tr(),
actionWidget: Builder(builder: (context) {
return DropdownField(
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedItemId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.itemsDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedItemId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedItemId.selectedId, appointmentsVM.appointmentFilterSelectedItemId.selectedOption, "")
: null,
hint: LocaleKeys.selectItems.tr(),
);
}),
historyContent: appointmentsVM.appointmentFilterItemsSearchHistory,
onHistoryItemDeleted: (index) => appointmentsVM.removeAppointmentFilterItemsSearchHistory(index: index),
onHistoryItemTapped: (DropValue value) => null,
),
] else ...[
SearchEntityWidget(
title: LocaleKeys.searchByServiceDelivery.tr(),
actionWidget: Builder(builder: (context) {
return DropdownField(
(DropValue value) => appointmentsVM.updateAppointmentFilterSelectedServiceDeliveryId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true),
list: appointmentsVM.serviceDeliveryTypesDropList,
dropdownValue: appointmentsVM.appointmentFilterSelectedServiceDeliveryId.selectedId != -1
? DropValue(appointmentsVM.appointmentFilterSelectedServiceDeliveryId.selectedId, appointmentsVM.appointmentFilterSelectedServiceDeliveryId.selectedOption, "")
: null,
hint: LocaleKeys.selectDeliveryType.tr(),
);
}),
historyContent: appointmentsVM.appointmentFilterServiceDeliverySearchHistory,
onHistoryItemDeleted: (index) => appointmentsVM.removeAppointmentFilterServiceDeliverySearchHistory(index: index),
onHistoryItemTapped: (DropValue value) => null,
),
],
],
).expand(),
Container(
@ -197,7 +293,12 @@ class _AppointmentsFilterViewState extends State<AppointmentsFilterView> {
title: LocaleKeys.search.tr(),
onPressed: () {
Navigator.pop(context);
appointmentsVM.getAppointmentsBasedOnFilters();
if (AppState().currentAppType == AppType.provider) {
appointmentsVM.getAppointmentsBasedOnFiltersForProviders(branchID: appointmentsVM.selectedBranchIdForAppointments);
} else {
appointmentsVM.getAppointmentsBasedOnFiltersForCustomer();
}
},
backgroundColor: MyColors.darkPrimaryColor,
txtColor: MyColors.white,
@ -208,7 +309,9 @@ class _AppointmentsFilterViewState extends State<AppointmentsFilterView> {
),
8.height,
InkWell(
onTap: () => appointmentsVM.clearAppointmentFilters(),
onTap: () {
appointmentsVM.clearAppointmentFilters();
},
child: LocaleKeys.clearFilters.tr().toText(
fontSize: 14,
isBold: true,

@ -13,6 +13,7 @@ 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/payment_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/views/advertisement/ads_buyer_chats_view.dart';
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart';
import 'package:mc_common_app/views/chat/widgets/chat_message_widget.dart';
@ -111,16 +112,37 @@ class _ChatViewState extends State<ChatView> {
padding: const EdgeInsets.symmetric(horizontal: 21, vertical: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
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,
Expanded(
flex: 4,
child: Row(
children: [
Flexible(
child: "${LocaleKeys.deliveryStatus.tr()} ${Utils.getNameByShippingRequestStatusEnum(requestVM.currentSelectedRequest?.shippingStatusEnum ?? ShippingRequestStatusEnum.pending)}"
.toText(
fontSize: 12,
color: Colors.white,
decorationColor: MyColors.white,
),
),
],
),
),
10.width,
if ((requestVM.currentSelectedRequest?.shippingStatusEnum ?? ShippingRequestStatusEnum.pending) == ShippingRequestStatusEnum.delivered) ...[
Expanded(
flex: 2,
child: "${LocaleKeys.markAsCompleted.tr()} ".toText(isUnderLine: true, fontSize: 12, color: Colors.white, decorationColor: MyColors.white).onPress(() {
return dealCompletedConsentBottomSheet(
mainContext: context,
requestStatusEnum: RequestStatusEnum.completed,
requestId: requestVM.currentSelectedRequest!.id,
showAcknowledgement: false,
);
}),
),
],
],
),
);
@ -138,7 +160,9 @@ class _ChatViewState extends State<ChatView> {
if (AppState().currentAppType == AppType.customer && chatVM.serviceProviderOffersList.isNotEmpty) {
chatMessages = chatVM.serviceProviderOffersList[chatViewArgumentsForRequest!.providerIndex].chatMessages ?? [];
} else {
chatMessages = requestVM.myFilteredRequests[chatViewArgumentsForRequest!.requestIndex].chatMessages;
if (requestVM.myFilteredRequests.isNotEmpty) {
chatMessages = requestVM.myFilteredRequests[chatViewArgumentsForRequest!.requestIndex].chatMessages;
}
}
}
return Column(
@ -151,20 +175,20 @@ class _ChatViewState extends State<ChatView> {
child: chatMessages.isEmpty
? 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,
requestModel: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel! : null,
requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus,
chatTypeEnum: chatTypeEnum,
requestsTypeEnum: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel!.requestType.toRequestTypeEnum() : RequestsTypeEnum.specialCarRequest,
);
},
).horPaddingMain(),
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,
requestModel: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel! : null,
requestStatusEnum: requestVM.currentSelectedRequest?.requestStatus,
chatTypeEnum: chatTypeEnum,
requestsTypeEnum: chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArgumentsForRequest!.requestModel!.requestType.toRequestTypeEnum() : RequestsTypeEnum.specialCarRequest,
);
},
).horPaddingMain(),
),
10.height,
Row(
@ -208,88 +232,90 @@ class _ChatViewState extends State<ChatView> {
// ),
// ]
//
else ...[
if (AppState().currentAppType == AppType.provider &&
chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted &&
chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.local_offer_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() {
requestVM.resetSendOfferBottomSheet();
RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(
requestIndex: chatViewArgumentsForRequest!.requestIndex,
requestModel: chatViewArgumentsForRequest!.requestModel!,
);
buildSendOfferBottomSheet(
context: context,
requestDetailPageArguments: requestDetailArguments,
isFromChatScreen: true,
offerId: null, // null means creating new offer
);
},
else
...[
if (AppState().currentAppType == AppType.provider &&
chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted &&
chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.local_offer_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() {
requestVM.resetSendOfferBottomSheet();
RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(
requestIndex: chatViewArgumentsForRequest!.requestIndex,
requestModel: chatViewArgumentsForRequest!.requestModel!,
);
buildSendOfferBottomSheet(
context: context,
requestDetailPageArguments: requestDetailArguments,
isFromChatScreen: true,
offerId: null, // null means creating new offer
);
},
),
),
),
],
if (chatVM.pickedImagesForMessage.isNotEmpty) ...[
Expanded(
flex: 8,
child: PickedFilesContainer(
pickedFiles: chatVM.pickedImagesForMessage,
onCrossPressedPrimary: chatVM.removeImageFromList,
onAddFilePressed: () => chatVM.pickMultipleImages(),
],
if (chatVM.pickedImagesForMessage.isNotEmpty) ...[
Expanded(
flex: 8,
child: PickedFilesContainer(
pickedFiles: chatVM.pickedImagesForMessage,
onCrossPressedPrimary: chatVM.removeImageFromList,
onAddFilePressed: () => chatVM.pickMultipleImages(),
),
),
),
] else if (chatTypeEnum == ChatTypeEnum.requestOffer) ...[
] else
if (chatTypeEnum == ChatTypeEnum.requestOffer) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.photo_library_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(() => chatVM.pickMultipleImages()),
),
],
if (chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 8,
child: TxtField(
value: chatVM.chatMessageText,
hint: LocaleKeys.typeMessageHere.tr(),
keyboardType: TextInputType.text,
isNeedBorder: false,
onChanged: (v) => chatVM.updateChatMessageText(v),
),
),
],
Expanded(
flex: 1,
child: const Icon(
Icons.photo_library_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(() => chatVM.pickMultipleImages()),
),
],
if (chatVM.pickedImagesForMessage.isEmpty) ...[
Expanded(
flex: 8,
child: TxtField(
value: chatVM.chatMessageText,
hint: LocaleKeys.typeMessageHere.tr(),
keyboardType: TextInputType.text,
isNeedBorder: false,
onChanged: (v) => chatVM.updateChatMessageText(v),
),
),
],
Expanded(
flex: 1,
child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress(
() async {
ChatMessageTypeEnum chatMessageTypeEnum = ChatMessageTypeEnum.freeText;
child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress(
() async {
ChatMessageTypeEnum chatMessageTypeEnum = ChatMessageTypeEnum.freeText;
if (chatVM.pickedImagesForMessage.isNotEmpty) {
chatMessageTypeEnum = ChatMessageTypeEnum.image;
}
final status = await onMessageSend(chatMessageType: chatMessageTypeEnum);
if (chatVM.pickedImagesForMessage.isNotEmpty) {
chatMessageTypeEnum = ChatMessageTypeEnum.image;
}
final status = await onMessageSend(chatMessageType: chatMessageTypeEnum);
if (status) {
chatVM.scrollChatDown();
if (chatMessageTypeEnum == ChatMessageTypeEnum.freeText) {
chatVM.clearChatMessageText();
} else if (chatMessageTypeEnum == ChatMessageTypeEnum.image) {
chatVM.clearPickedImagesForMessage();
if (status) {
chatVM.scrollChatDown();
if (chatMessageTypeEnum == ChatMessageTypeEnum.freeText) {
chatVM.clearChatMessageText();
} else if (chatMessageTypeEnum == ChatMessageTypeEnum.image) {
chatVM.clearPickedImagesForMessage();
}
}
}
},
},
),
),
),
],
],
],
).toContainer(isShadowEnabled: true)
],

@ -18,14 +18,20 @@ import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:provider/provider.dart';
void dealCompletedConsentBottomSheet({required BuildContext mainContext, required RequestStatusEnum requestStatusEnum, required int requestId, Function()? acceptRequestOffer}) {
void dealCompletedConsentBottomSheet({
required BuildContext mainContext,
required RequestStatusEnum requestStatusEnum,
required int requestId,
required bool showAcknowledgement,
Function()? acceptRequestOffer,
}) {
final requestVM = mainContext.read<RequestsVM>();
return actionConfirmationBottomSheet(
isOnlyOneButton: AppState().currentAppType == AppType.customer,
isOnlyOneButton: showAcknowledgement,
context: mainContext,
title: LocaleKeys.doYouWantToCompleteThisDeal.tr().toText(fontSize: 26, isBold: true, letterSpacing: -1.44),
subtitle: AppState().currentAppType == AppType.provider ? LocaleKeys.providerCompletingDealMeansThat.tr() : LocaleKeys.customerCompletingDealMeansThat.tr(),
checkBoxConfirmationWidget: AppState().currentAppType == AppType.customer
checkBoxConfirmationWidget: showAcknowledgement
? Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return Row(
children: [
@ -47,14 +53,13 @@ void dealCompletedConsentBottomSheet({required BuildContext mainContext, require
return Expanded(
child: ShowFillButton(
maxHeight: 55,
isDisabled: !chatVM.acknowledgePaymentToMowaterStatus,
title: AppState().currentAppType == AppType.customer ? LocaleKeys.submit.tr() : LocaleKeys.yes.tr(),
isDisabled: showAcknowledgement == false ? false : !chatVM.acknowledgePaymentToMowaterStatus,
title: showAcknowledgement ? LocaleKeys.submit.tr() : LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () async {
pop(context);
bool statusForReqAccept = false;
if (acceptRequestOffer != null) {
if (acceptRequestOffer != null && showAcknowledgement) {
statusForReqAccept = await acceptRequestOffer();
if (statusForReqAccept) {
bool status = await requestVM.onActionRequestTapped(
@ -79,7 +84,7 @@ void dealCompletedConsentBottomSheet({required BuildContext mainContext, require
context: mainContext,
requestStatusEnum: requestStatusEnum,
requestId: requestId,
needLoading: false,
needLoading: showAcknowledgement ? true : false,
);
log("status: $status");
if (status) {

@ -129,7 +129,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
offerPrice: chatMessageModel.reqOffer!.price.toString(),
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
requestOfferStatusEnum: RequestOfferStatusEnum.rejected,
context: context,
);
@ -208,6 +208,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
mainContext: context,
requestStatusEnum: RequestStatusEnum.completed,
requestId: requestVM.currentSelectedRequest!.id,
showAcknowledgement: AppState().currentAppType == AppType.customer,
);
} else {
final requestVM = context.read<RequestsVM>();
@ -215,6 +216,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
mainContext: context,
requestStatusEnum: RequestStatusEnum.completed,
requestId: requestVM.currentSelectedRequest!.id,
showAcknowledgement: AppState().currentAppType == AppType.customer,
acceptRequestOffer: () async {
bool status = await chatVM.onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.senderUserID ?? "",
@ -226,7 +228,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
offerPrice: chatMessageModel.reqOffer!.price.toString(),
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
context: context,
);
@ -373,7 +375,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
offerPrice: chatMessageModel.reqOffer!.price.toString(),
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
requestOfferStatusEnum: requestOfferStatusEnum,
context: context,
);
@ -427,7 +429,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
offerPrice: chatMessageModel.reqOffer!.price.toString(),
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
context: context,
);
@ -736,7 +738,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
ReqOffer offer = widget.chatMessageModel.reqOffer!;
requestVM.updateOfferPrice((offer.price ?? "").toString());
requestVM.updateServiceItem((offer.serviceItemName ?? "").toString());
requestVM.updateItemManufacturer((offer.manufacturedById ?? "").toString());
requestVM.updateItemManufacturer((offer.manufacturedByName ?? "").toString());
requestVM.updateServiceItemCreatedOn((offer.manufacturedOn ?? "").toString());
requestVM.updateOfferDescription((widget.chatMessageModel.chatText ?? "").toString());
requestVM.updateIsDeliveryAvailableStatus((offer.isDeliveryAvailable ?? false));
@ -747,8 +749,6 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
id: element.id,
filePath: element.imageStr != null && element.imageStr!.isNotEmpty ? element.imageStr : element.imageUrl,
isFromNetwork: element.imageStr != null && element.imageStr!.isNotEmpty ? false : true);
log("imageModelimageModel: ${imageModel.toString()}");
requestVM.addImageToPickedVehicleImages(imageModel);
}
}
@ -771,7 +771,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
] else if (AppState().currentAppType == AppType.provider &&
chatMessageTypeEnum == ChatMessageTypeEnum.offer &&
widget.chatMessageModel.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.offer &&
(widget.chatMessageModel.isMyMessage == true)) ...[
(widget.chatMessageModel.isMyMessage == true && widget.chatMessageModel.reqOffer != null && widget.chatMessageModel.reqOffer!.id != null)) ...[
MyAssets.icEdit.buildSvg(color: MyColors.white, height: 15).onPress(() => onOfferEditIconPressed()),
],
Expanded(

@ -79,7 +79,9 @@ class AdsFragment extends StatelessWidget {
title: LocaleKeys.exploreAds.tr(),
txtColor: adVM.isExploreAdsTapped ? MyColors.white : MyColors.darkTextColor,
onPressed: () {
print("accessToken: ${AppState().getUser.data!.accessToken}");
log("accessToken: ${AppState().getUser.data!.accessToken}");
log("AppState().getUser.data!.userInfo!.userId;: ${AppState().getUser.data!.userInfo!.userId}");
if (adVM.myAds.isEmpty) {
adVM.getMyAds();
}
@ -107,7 +109,9 @@ class AdsFragment extends StatelessWidget {
16.height,
FiltersList(
filterList: adVM.exploreAdsFilterOptions,
onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnExploreAds(createdByRoleFilter: selectedFilterId.toCreatedByRoleEnum()),
onFilterTapped: (index, selectedFilterId) {
adVM.applyFilterOnExploreAds(vehicleBrandId: selectedFilterId);
},
needLeftPadding: false,
).paddingOnly(left: 21),
]
@ -115,7 +119,9 @@ class AdsFragment extends StatelessWidget {
16.height,
FiltersList(
filterList: adVM.myAdsFilterOptions,
onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnMyAds(adPostStatusEnum: selectedFilterId.toAdPostEnum()),
onFilterTapped: (index, selectedFilterId) {
adVM.applyFilterOnMyAds(adPostStatusEnum: selectedFilterId.toAdPostEnum());
},
needLeftPadding: false,
).paddingOnly(left: 21),
],
@ -128,8 +134,8 @@ class AdsFragment extends StatelessWidget {
child: RefreshIndicator(
onRefresh: () async {
if (adVM.isExploreAdsTapped) {
CreatedByRoleEnum createdByRoleEnum = adVM.exploreAdsFilterOptions.firstWhere((element) => element.isSelected).id.toCreatedByRoleEnum();
adVM.applyFilterOnExploreAds(createdByRoleFilter: createdByRoleEnum);
int vehicleBrandId = adVM.exploreAdsFilterOptions.firstWhere((element) => element.isSelected).id;
adVM.applyFilterOnExploreAds(vehicleBrandId: vehicleBrandId);
} else {
AdPostStatus adPostStatusEnum = adVM.myAdsFilterOptions.firstWhere((element) => element.isSelected).id.toAdPostEnum();
adVM.applyFilterOnMyAds(adPostStatusEnum: adPostStatusEnum);
@ -141,12 +147,26 @@ class AdsFragment extends StatelessWidget {
isAdsFragment: true,
shouldShowAdStatus: !adVM.isExploreAdsTapped,
adsList: getAdsList(adVM),
onFetchMoreAds: () {
log("fetch more ads");
hasMoreData: adVM.isExploreAdsTapped ? adVM.hasMoreDataForExploreAds : adVM.hasMoreDataForMyAds,
onFetchMoreAds: () async {
AdPostStatus adPostStatusEnum = adVM.myAdsFilterOptions.firstWhere((element) => element.isSelected).id.toAdPostEnum();
if (adVM.isExploreAdsTapped) {
await adVM.fetchMoreExploreAds();
} else {
await adVM.fetchMoreMyAds(adsStatus: adPostStatusEnum);
}
},
),
),
)
),
if (adVM.isLoadingMore) ...[
const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
))
]
],
),
),

@ -158,7 +158,7 @@ class MyRequestsFragment extends StatelessWidget {
},
child: RequestItem(
request: requestsVM.myFilteredRequests[index],
shouldShowStatuses: AppState().currentAppType == AppType.customer,
shouldShowStatuses: true,
onTap: () async {
RequestModel request = requestsVM.myFilteredRequests[index];
requestsVM.updateCurrentSelectedRequest(request);

@ -56,14 +56,14 @@ Future buildSendOfferBottomSheet({
numbersOnly: true,
onChanged: (v) => requestsVM.updateOfferPrice(v),
),
12.height,
TxtField(
value: requestsVM.serviceItem,
errorValue: requestsVM.offerPriceError,
hint: LocaleKeys.serviceItem.tr(),
onChanged: (v) => requestsVM.updateServiceItem(v),
),
if (requestDetail.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeEnum()) ...[
12.height,
TxtField(
value: requestsVM.serviceItem,
errorValue: requestsVM.offerPriceError,
hint: LocaleKeys.serviceItem.tr(),
onChanged: (v) => requestsVM.updateServiceItem(v),
),
12.height,
TxtField(
value: requestsVM.itemManufacturer,
@ -157,8 +157,7 @@ Future buildSendOfferBottomSheet({
isDeliveryAvailable: requestsVM.isDeliveryAvailableStatus,
requestIndex: requestDetailPageArguments.requestIndex,
isFromChatScreen: isFromChatScreen,
manufacturedById: 1,
// Todo: It should be the ID of the manufacturer
manufacturedByName: requestsVM.itemManufacturer,
manufacturedOn: requestsVM.serviceItemCreatedOn,
serviceItemName: requestsVM.serviceItem,
);
@ -170,8 +169,7 @@ Future buildSendOfferBottomSheet({
offerPrice: requestsVM.offerPrice,
isDeliveryAvailable: requestsVM.isDeliveryAvailableStatus,
serviceItemName: requestsVM.serviceItem,
manufacturedById: 1,
// Todo: It should be the ID of the manufacturer
manufacturedByName: requestsVM.itemManufacturer,
manufacturedOn: requestsVM.serviceItemCreatedOn,
receiverId: requestDetail.customerId,
customerRequestIndex: requestIndex,

@ -29,6 +29,7 @@ class RequestDetailPage extends StatelessWidget {
required int requestId,
required RequestStatusEnum requestStatus,
required RequestsTypeEnum requestTypeEnum,
required ShippingRequestStatusEnum shippingRequestStatusEnum,
required String statusText,
required BuildContext context,
}) {
@ -47,6 +48,7 @@ class RequestDetailPage extends StatelessWidget {
if (requestTypeEnum == RequestsTypeEnum.serviceRequest) {
return Utils.buildStatusContainer(LocaleKeys.awaitingPaymentFromCustomer.tr());
} else {
return Utils.buildStatusContainer(LocaleKeys.awaitingResponseFromCustomer.tr());
}
@ -68,7 +70,28 @@ class RequestDetailPage extends StatelessWidget {
case RequestStatusEnum.shipping:
case RequestStatusEnum.delivery:
if (AppState().currentAppType == AppType.provider) {
if (AppState().currentAppType == AppType.provider && shippingRequestStatusEnum == ShippingRequestStatusEnum.delivered) {
return Column(
children: [
Utils.buildStatusContainer("${LocaleKeys.deliveryStatus.tr()} ${Utils.getNameByShippingRequestStatusEnum(shippingRequestStatusEnum).capitalizeFirstLetter()}")
.paddingOnly(left: 8, right: 8),
ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: LocaleKeys.markAsCompleted.tr(),
onPressed: () {
return dealCompletedConsentBottomSheet(
mainContext: context,
requestStatusEnum: RequestStatusEnum.completed,
requestId: requestId,
showAcknowledgement: AppState().currentAppType == AppType.customer,
);
},
)
],
);
} else if (AppState().currentAppType == AppType.provider) {
return Utils.buildStatusContainer(LocaleKeys.shippingManagementInstruction.tr());
} else {
return ShowFillButton(
@ -81,7 +104,7 @@ class RequestDetailPage extends StatelessWidget {
mainContext: context,
requestStatusEnum: RequestStatusEnum.completed,
requestId: requestId,
acceptRequestOffer: () {},
showAcknowledgement: AppState().currentAppType == AppType.customer,
);
},
);
@ -176,8 +199,9 @@ class RequestDetailPage extends StatelessWidget {
requestId: requestDetailPageArguments.requestModel.id,
requestStatus: requestDetailPageArguments.requestModel.requestStatus,
statusText: "Offer ${requestDetailPageArguments.requestModel.requestStatusName}",
context: context,
requestTypeEnum: requestDetailPageArguments.requestModel.requestType.toRequestTypeEnum(),
shippingRequestStatusEnum: requestDetailPageArguments.requestModel.shippingStatusEnum ?? ShippingRequestStatusEnum.pending,
context: context,
),
],
],

@ -153,15 +153,16 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
SingleDetailWidget(text: requestVM.currentSelectedRequest!.vehicleTypeName, type: LocaleKeys.vehicleType.tr()),
16.height,
SingleDetailWidget(text: '${requestVM.currentSelectedRequest!.model} ${requestVM.currentSelectedRequest!.year}', type: LocaleKeys.model.tr()),
if (requestVM.acceptedRequestOffer!.manufacturedById != null) ...[
if (requestVM.acceptedRequestOffer!.manufacturedByName != null) ...[
16.height,
SingleDetailWidget(text: (requestVM.acceptedRequestOffer!.manufacturedById ?? "").toString(), type: LocaleKeys.manufacturedBy.tr()),
SingleDetailWidget(text: (requestVM.acceptedRequestOffer!.manufacturedByName ?? "").toString(), type: LocaleKeys.manufacturedBy.tr()),
],
// 16.height,
// SingleDetailWidget(text: "${requestVM.acceptedRequestOffer!.price.toString()} ${LocaleKeys.sar.tr()}", type: LocaleKeys.offerPrice.tr()),
16.height,
SingleDetailWidget(text: requestVM.acceptedRequestOfferProviderName ?? "", type: LocaleKeys.providerName.tr()),
16.height,
SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()),
],
),
),
@ -186,12 +187,14 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
16.height,
SingleDetailWidget(text: requestCreatedOn, type: LocaleKeys.requestCreatedOn.tr()),
],
16.height,
SingleDetailWidget(text: LocaleKeys.online.tr(), type: LocaleKeys.paymentType.tr()),
],
),
),
],
),
SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()),
],
);
}

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/gestures.dart';
import 'package:mc_common_app/classes/app_state.dart';
@ -63,20 +64,6 @@ class _ProviderAcceptedRequestsViewState extends State<ProviderAcceptedRequestsV
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.noRequeststoShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor),
if (requestsVM.requestsFiltersCounter > 0) ...[
8.height,
InkWell(
onTap: () async {
requestsVM.clearRequestsFilters();
await requestsVM.getRequestsBasedOnFilters();
},
child: LocaleKeys.clearFilters.tr().toText(
fontSize: 14,
isBold: true,
color: MyColors.darkPrimaryColor,
),
),
],
],
)
: ListView.separated(
@ -84,7 +71,7 @@ class _ProviderAcceptedRequestsViewState extends State<ProviderAcceptedRequestsV
return RequestItem(
request: requestsVM.providersAcceptedRequestsList[index],
onTap: () async {
RequestModel request = requestsVM.myFilteredRequests[index];
RequestModel request = requestsVM.providersAcceptedRequestsList[index];
requestsVM.updateCurrentSelectedRequest(request);
if (request.requestStatus == RequestStatusEnum.pending ||
request.requestStatus == RequestStatusEnum.cancelled ||

@ -1,15 +1,51 @@
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/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:provider/provider.dart';
class SplashPage extends StatelessWidget {
const SplashPage({Key? key}) : super(key: key);
class SplashPage extends StatefulWidget {
const SplashPage({super.key});
@override
Widget build(BuildContext context) {
SplashPageState createState() => SplashPageState();
}
class SplashPageState extends State<SplashPage> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
performTimer(context);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
final chatVM = context.read<ChatVM>();
chatVM.buildHubConnection(context);
}
}
// Timer function to navigate after a delay
performTimer(BuildContext context) {
Utils.delay(3).whenComplete(() {
navigateReplaceWithName(context, AppRoutes.registerSelection);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: double.infinity,
@ -17,37 +53,36 @@ class SplashPage extends StatelessWidget {
color: Colors.black,
child: Column(
children: [
Utils. mExp(1),
// Spacer widget for flexible spacing
Utils.mExp(1),
// Logo Image
Expanded(
child: Image.asset(
MyAssets.icLogoWhitePng,
),
),
// Car Image (flex value adjusted to make it more responsive)
Expanded(
flex: 10,
child: Image.asset(
MyAssets.bnCar,
fit: BoxFit.cover,
width: double.infinity,
height: 00,
height: double.infinity, // Use full height in this expanded section
),
),
// Engine Image
Expanded(
flex: 3,
child: Image.asset(
MyAssets.icEnginePng,
),
),
// Spacer widget again for spacing
Utils.mExp(1),
],
),
),
);
}
performTimer(BuildContext context) {
Utils.delay(3).whenComplete(() {
navigateReplaceWithName(context, AppRoutes.registerSelection);
});
}
}

@ -46,7 +46,6 @@ class _CompleteProfilePageState extends State<CompleteProfilePage> {
@override
Widget build(BuildContext context) {
return Consumer(builder: (BuildContext context, UserVM userVM, Widget? child) {
print("Country ID = " + AppState().getUserRegisterCountrySelection.id.toString());
userVM.getAllCitiesForUser(AppState().getUserRegisterCountrySelection.id);
return Scaffold(
appBar: CustomAppBar(

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
void showMyBottomSheet(BuildContext context, {bool isDismissible = true, required Widget child, VoidCallback? callBackFunc}) {
void showMyBottomSheet(BuildContext context, {bool isDismissible = true,bool isScrollControlled = true, required Widget child, VoidCallback? callBackFunc}) {
showModalBottomSheet<String>(
context: context,
isScrollControlled: true,

@ -2,20 +2,21 @@ import 'package:flutter/material.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/widgets/extensions/extensions_widget.dart';
class FiltersList extends StatelessWidget {
final List<FilterListModel> filterList;
final Function(int, int) onFilterTapped;
final bool needLeftPadding;
EdgeInsets? padding;
final EdgeInsets? padding;
FiltersList({
Key? key,
const FiltersList({
super.key,
this.padding,
required this.filterList,
this.needLeftPadding = true,
required this.onFilterTapped,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {
@ -23,38 +24,50 @@ class FiltersList extends StatelessWidget {
height: 37,
width: double.infinity,
child: ListView.builder(
padding: padding ?? EdgeInsets.only(left: needLeftPadding ? 12 : 0),
itemCount: filterList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
onFilterTapped(index, filterList[index].id);
},
padding: padding ?? EdgeInsets.only(left: needLeftPadding ? 12 : 0),
itemCount: filterList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
onFilterTapped(index, filterList[index].id);
},
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 80), // Set a minimum width for each filter chip
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 8),
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: filterList[index].isSelected
? MyColors.darkIconColor
: null,
color: filterList[index].isSelected ? MyColors.darkIconColor : null,
border: Border.all(
color: filterList[index].isSelected
? MyColors.darkIconColor
: MyColors.primaryColor,
color: filterList[index].isSelected ? MyColors.darkIconColor : MyColors.primaryColor,
width: 2,
),
),
child: filterList[index].title.toText(
fontSize: 12,
letterSpacing: -0.14,
color:
filterList[index].isSelected ? MyColors.white : null,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Icon before the text
// if (index != 0 || filterList[index].iconUrl.isNotEmpty) ...[
// filterList[index].iconUrl.buildNetworkImage(height: 16, width: 16, fit: BoxFit.cover),
// const SizedBox(width: 4), // Space between icon and text
// ],
// Text displayed in the chip
filterList[index].title.toText(
fontSize: 13,
letterSpacing: -0.14,
color: filterList[index].isSelected ? MyColors.white : null,
),
],
),
),
);
}),
),
);
},
),
);
}
}

@ -9,16 +9,20 @@ class SearchEntityWidget extends StatelessWidget {
final String title;
final Widget actionWidget;
final List<DropValue> historyContent;
final List<String>? historyContentString;
final Function(int) onHistoryItemDeleted;
final Function(DropValue) onHistoryItemTapped;
final bool isForString;
const SearchEntityWidget({
super.key,
required this.title,
required this.actionWidget,
required this.historyContent,
this.historyContentString,
required this.onHistoryItemDeleted,
required this.onHistoryItemTapped,
this.isForString = false,
});
@override
@ -30,26 +34,45 @@ class SearchEntityWidget extends StatelessWidget {
8.height,
actionWidget,
10.height,
historyContent.isNotEmpty
? SizedBox(
height: 33,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: historyContent.length,
itemBuilder: (BuildContext context, int index) {
return CustomRectangleChip(
text: historyContent[index].value,
onHistoryItemDeleted: () {
onHistoryItemDeleted(index);
},
onHistoryItemTapped: () {
onHistoryItemTapped(historyContent[index]);
},
).paddingOnly(right: 10);
}),
)
: const SizedBox.shrink(),
if (isForString && historyContentString != null && historyContentString!.isNotEmpty) ...[
SizedBox(
height: 33,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: historyContentString!.length,
itemBuilder: (BuildContext context, int index) {
return CustomRectangleChip(
text: historyContentString![index],
onHistoryItemDeleted: () {
onHistoryItemDeleted(index);
},
onHistoryItemTapped: () {},
).paddingOnly(right: 10);
}),
)
] else ...[
historyContent.isNotEmpty
? SizedBox(
height: 33,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: historyContent.length,
itemBuilder: (BuildContext context, int index) {
return CustomRectangleChip(
text: historyContent[index].value,
onHistoryItemDeleted: () {
onHistoryItemDeleted(index);
},
onHistoryItemTapped: () {
onHistoryItemTapped(historyContent[index]);
},
).paddingOnly(right: 10);
}),
)
: const SizedBox.shrink(),
]
],
);
}

@ -56,7 +56,7 @@ class MediaViewerScreenState extends State<MediaViewerScreen> {
borderRadius: BorderRadius.circular(16),
child: (widget.images[index].isFromNetwork ?? false)
? widget.images[index].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
fit: BoxFit.contain,
width: double.infinity,
height: double.infinity,
)

@ -39,7 +39,7 @@ class TxtField extends StatelessWidget {
bool allowOnlyOneDigit;
TxtField({
Key? key,
super.key,
this.value,
this.lable,
this.hint,
@ -67,7 +67,7 @@ class TxtField extends StatelessWidget {
this.onPostFixPressed,
this.numbersOnly = false,
this.allowOnlyOneDigit = false,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

Loading…
Cancel
Save