Compare commits

...

14 Commits

Author SHA1 Message Date
Faiz Hashmi 2215bbd63b Changes 10 months ago
Faiz Hashmi f8514aaf7b Changes 10 months ago
Faiz Hashmi 054944e28a Merge branch 'aamir_dev' into faiz_development_common
# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/generated/codegen_loader.g.dart
#	lib/generated/locale_keys.g.dart
#	lib/views/advertisement/bottom_sheets/ads_damage_part_pictures_sheet.dart
10 months ago
Faiz Hashmi 656dbfd5ac Changes 10 months ago
Aamir Muhammad 7a93004fba changes 10 months ago
Faiz Hashmi 4b350dbeb6 Minor Fixes 10 months ago
Faiz Hashmi 0ada291516 Merge branch 'aamir_dev' into faiz_development_common
# Conflicts:
#	lib/widgets/dropdown/dropdow_field.dart
10 months ago
Faiz Hashmi f7356592a0 Minor Fixes 10 months ago
Aamir Muhammad 272cf1e104 fixes 10 months ago
Aamir Muhammad 03ccb1a60c Merge branch 'refs/heads/master' into aamir_dev 10 months ago
Aamir Muhammad fd684e9649 fix 10 months ago
Aamir Muhammad 2eb1f22a24 CMA-458, CMA-455, CMA-434, CMA-247, CMA-343 11 months ago
Aamir Muhammad 9efb174e0c 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
11 months ago
Aamir 1fd69dc2e4 Merge branch 'faiz_development_common' 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
11 months ago

@ -784,5 +784,18 @@
"enterCustomerName": "أدخل اسم العميل",
"tapToView": "انقر لعرض",
"noServicesAvailableToCopy": "لا توجد خدمات متاحة في هذا الفرع لنسخها.",
"copySelectedItems": "نسخ العناصر المحددة"
"copySelectedItems": "نسخ العناصر المحددة",
"noOfInvites": "عدد الدعوات",
"noSpecialServicesAvailable": "لا توجد خدمات خاصة متاحة للمنطقة المحددة.",
"customService": "الخدمة المخصصة",
"selectServiceDetails": "اختر تفاصيل الخدمة",
"addServiceDetails": "أضف تفاصيل الخدمة",
"companyNameMandatory": "اسم الشركة إلزامي",
"connectionProblem": "مشكلة في الاتصال",
"pleaseCheckConnection": "يرجى التحقق من اتصالك بالإنترنت والمحاولة مرة أخرى",
"ok": "نعم",
"continueAsGuest": "الاستمرار كضيف",
"loginToViewAppointments": "الرجاء تسجيل الدخول لعرض المواعيد",
"itemType": "غرض"
}

@ -782,5 +782,17 @@
"enterCustomerName": "Enter Customer Name",
"tapToView": "Tap to view",
"noServicesAvailableToCopy": "There are no services available in this branch to copy.",
"copySelectedItems": "Copy Selected Items"
"copySelectedItems": "Copy Selected Items",
"companyNameMandatory": "Company Name is mandatory",
"noOfInvites": "No of invites",
"noSpecialServicesAvailable": "There are no special services available for the selected region.",
"customService": "Custom Service",
"selectServiceDetails": "Select service details",
"addServiceDetails": "Add service details",
"connectionProblem": "Connection Problem",
"pleaseCheckConnection": "Please check your internet connection and try again.",
"ok": "Ok",
"continueAsGuest": "Continue as a guest",
"loginToViewAppointments": "Please Login to View Appointments",
"itemType": "Item Type"
}

@ -4,15 +4,18 @@ import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:http/io_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/routes.dart';
import 'package:mc_common_app/exceptions/api_exception.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/user_models/refresh_token.dart';
import 'package:mc_common_app/models/user_models/user.dart';
import 'package:mc_common_app/utils/shared_prefrence.dart';
import 'package:mc_common_app/utils/utils.dart';
typedef FactoryConstructor<U> = U Function(dynamic);
@ -56,7 +59,8 @@ class APIError {
}
abstract class ApiClient {
Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<Response> postJsonForResponse<T>(String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
@ -67,7 +71,8 @@ abstract class ApiClient {
class ApiClientImp implements ApiClient {
@override
Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
var headers0 = {'Accept': 'application/json'};
if (headers != null && headers.isNotEmpty) {
headers0.addAll(headers);
@ -163,6 +168,10 @@ class ApiClientImp implements ApiClient {
throw APIException(APIException.OTHER, arguments: e);
}
} on TimeoutException catch (e) {
BuildContext? context = navigatorKey.currentContext;
if (context != null) {
NoInternetDialog.show(context);
}
throw APIException(APIException.TIMEOUT, arguments: e);
} on ClientException catch (e) {
if (retryTimes > 0) {
@ -193,7 +202,8 @@ class ApiClientImp implements ApiClient {
Future<Response> _post(url, {Map<String, String>? headers, body, Encoding? encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding));
@override
Future<U> getJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
Future<U> getJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
var headers0 = {'Accept': 'application/json'};
if (headers != null && headers.isNotEmpty) {
headers0.addAll(headers);
@ -272,6 +282,10 @@ class ApiClientImp implements ApiClient {
throw APIException(APIException.OTHER, arguments: e);
}
} on TimeoutException catch (e) {
final context = navigatorKey.currentContext;
if (context != null) {
NoInternetDialog.show(context);
}
throw APIException(APIException.TIMEOUT, arguments: e);
} on ClientException catch (e) {
if (retryTimes > 0) {

@ -72,12 +72,21 @@ class AppState {
_providerSubscription = value;
}
DropValue? _userRegisterCountrySelection ;
DropValue? _userRegisterCountrySelection;
DropValue get getUserRegisterCountrySelection => _userRegisterCountrySelection!;
set setUserRegisterCountrySelection(DropValue value) {
_userRegisterCountrySelection = value;
}
bool _isViewOnly = false;
bool get getIsViewOnly => _isViewOnly;
set setIsViewOnly(bool value) {
_isViewOnly = value;
}
}

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/generated/codegen_loader.g.dart';
import 'package:mc_common_app/models/user_models/user.dart';
import 'package:mc_common_app/utils/enums.dart';
class ApiConsts {
@ -41,6 +42,7 @@ class ApiConsts {
static String getUserImage = "${baseUrlServices}api/ProfileImage";
static String userUpdate = "${baseUrlServices}api/User_Update";
static String providerComplaintCreate = "${baseUrlServices}api/ServiceProviders/ProviderComplaint_Create";
static String provideContactInfoGet = "${baseUrlServices}api/ServiceProviders/ServiceProviderContact_Get";
//Profile
static String fetProviderDocument = "${baseUrlServices}api/ServiceProviders/ServiceProviderDocument_Get";
@ -191,6 +193,7 @@ class ApiConsts {
static String getContactInfo = "${baseUrlServices}api/Master/ContactInfo_Get";
static String getAppInfo = "${baseUrlServices}api/Master/AppInfo_Get";
static String getTermsAndConditions = "${baseUrlServices}api/Master/TermAndCondition_Get";
static String getAppInvitationHistory = "${baseUrlServices}api/Common/AppInvitation_Get";
static List<String> closingUrls = ["PayFortResponse"];
}
@ -234,6 +237,9 @@ class GlobalConsts {
static String homeLocationEmptyError = "Home location cannot be empty";
static String fillAllFields = "Please fill out all the fields.";
static String requestTypeCannotBeEmpty = "Request type cannot be empty.";
static String serviceNameError = "Service name cannot be empty.";
static String priceError = "Price cannot be empty.";
static String taxError = "Tax cannot be empty.";
static String reserveAdPriceInfo =
"Some dummy description to explain the following concept. This price will be for 24 hours and if a user cancels the reservations before 24 hours then the amount will be automatically refunded to the buyer.";
static String appInvitationMessageEn = "🚗 Hey , check out MOWATER to book car services, buy/sell vehicles, and more—join here! [Invite Link]";
@ -261,6 +267,8 @@ class GlobalConsts {
static int maxFileCount = 7;
static int maxFileSizeInBytes = 2 * 1024 * 1024;
static String allowedFileExtensions = "jpg,jpeg,png - 2MB/file";
static String allowedFileExtensionsPDF = "PDF 2MB/file";
static int providerDealerRoleTypeId = 6;
static int providerIndividualRoleTypeId = 5;
@ -406,3 +414,46 @@ class SignalrConsts {
static String sendMessageGeneral = "SendMessageGeneral";
static String receiveMessageGeneral = "ReceiveMessageGeneral";
}
class GuestConsts {
UserInfo userInfo = UserInfo.fromJson({
"id": -1,
"userID": null,
"firstName": "Guest",
"lastName": "User",
"companyName": null,
"accountStatus": "2",
"activityStatus": "Offline",
"accountStatusText": null,
"subscriptionDate": null,
"mobileNo": "966123456789",
"email": "mowatter@gmail.com",
"userImageUrl": "https://ms.hmg.com/api/ProfileImage?imageName=User_Default.png",
"roleID": 4,
"roleName": "Customer",
"isEmailVerified": false,
"serviceProviderBranch": [],
"isVerified": true,
"userRoles": [],
"isProviderDealership": false,
"isProviderIndividual": false,
"isProvider": false,
"providerID": null,
"isCustomer": true,
"customerID": 25,
"countryID": 1,
"countryName": "Saudi Arabia",
"cityID": 1,
"cityName": "Riyadh",
"dealershipUserID": null,
"serviceProviderBranchID": null,
"createdOn": "2024-12-24T09:20:47.6733333",
"genderID": 1,
"genderName": "Male",
"serviceProviderPayment": [],
"deviceType": "1",
"deviceToken": null,
});
}

@ -50,6 +50,8 @@ import 'package:mc_common_app/views/user/vertify_password_page.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/widgets/image_viewer/image_viewer_screen.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class AppRoutes {
//User
static const String splash = "/splash";
@ -183,7 +185,7 @@ class AppRoutes {
registerProvider: (context) => const RegisterProviderPage(),
forgetPassword: (context) => const ForgetPasswordPage(),
loginVerification: (context) => const LoginVerificationPage(),
loginWithPassword: (context) => const LoginWithPassword(),
loginWithPassword: (context) => LoginWithPassword(isRemoveBackButton: (ModalRoute.of(context)!.settings.arguments ?? true) as bool),
loginMethodSelection: (context) => LoginMethodSelectionPage(ModalRoute.of(context)!.settings.arguments as String),
completeProfile: (context) => CompleteProfilePage(ModalRoute.of(context)!.settings.arguments as RegisterUserRespModel),
verifyPassword: (context) => VerifyPasswordPage(),

@ -975,7 +975,7 @@ extension ShippingStatusEnumExt on int {
return ShippingRequestStatusEnum.inTransit;
} else if (this == 3) {
return ShippingRequestStatusEnum.outForDelivery;
} else if (this == 4) {
} else if (this == 4) {
return ShippingRequestStatusEnum.delivered;
}
return ShippingRequestStatusEnum.pending;
@ -995,8 +995,10 @@ extension ShippingStatusEnumToInt on ShippingRequestStatusEnum {
return 3;
case ShippingRequestStatusEnum.delivered:
return 4;
case ShippingRequestStatusEnum.allRequests:
return -1;
default:
return 0;
return -1;
}
}
}
@ -1009,3 +1011,33 @@ extension CapitalizeFirstLetter on String {
return this[0].toUpperCase() + substring(1).toLowerCase();
}
}
// Extension to convert InviteType to corresponding integer
extension InviteTypeToInt on InviteTypeEnum {
int toIntFromEnum() {
switch (this) {
case InviteTypeEnum.whatsapp:
return 1;
case InviteTypeEnum.sms:
return 2;
case InviteTypeEnum.email:
return 3;
}
}
}
// Extension to convert integer to corresponding InviteType
extension IntToInviteType on int {
InviteTypeEnum get toInviteTypeFromInt {
switch (this) {
case 1:
return InviteTypeEnum.whatsapp;
case 2:
return InviteTypeEnum.sms;
case 3:
return InviteTypeEnum.email;
default:
throw ArgumentError('Invalid integer value: $this');
}
}
}

@ -800,7 +800,19 @@ class CodegenLoader extends AssetLoader{
"enterCustomerName": "أدخل اسم العميل",
"tapToView": "انقر لعرض",
"noServicesAvailableToCopy": "لا توجد خدمات متاحة في هذا الفرع لنسخها.",
"copySelectedItems": "نسخ العناصر المحددة"
"copySelectedItems": "نسخ العناصر المحددة",
"noOfInvites": "عدد الدعوات",
"noSpecialServicesAvailable": "لا توجد خدمات خاصة متاحة للمنطقة المحددة.",
"customService": "الخدمة المخصصة",
"selectServiceDetails": "اختر تفاصيل الخدمة",
"addServiceDetails": "أضف تفاصيل الخدمة",
"companyNameMandatory": "اسم الشركة إلزامي",
"connectionProblem": "مشكلة في الاتصال",
"pleaseCheckConnection": "يرجى التحقق من اتصالك بالإنترنت والمحاولة مرة أخرى",
"ok": "نعم",
"continueAsGuest": "الاستمرار كضيف",
"loginToViewAppointments": "الرجاء تسجيل الدخول لعرض المواعيد",
"itemType": "غرض"
};
static const Map<String,dynamic> en_US = {
"firstTimeLogIn": "First Time Log In",
@ -1586,7 +1598,19 @@ static const Map<String,dynamic> en_US = {
"enterCustomerName": "Enter Customer Name",
"tapToView": "Tap to view",
"noServicesAvailableToCopy": "There are no services available in this branch to copy.",
"copySelectedItems": "Copy Selected Items"
"copySelectedItems": "Copy Selected Items",
"companyNameMandatory": "Company Name is mandatory",
"noOfInvites": "No of invites",
"noSpecialServicesAvailable": "There are no special services available for the selected region.",
"customService": "Custom Service",
"selectServiceDetails": "Select service details",
"addServiceDetails": "Add service details",
"connectionProblem": "Connection Problem",
"pleaseCheckConnection": "Please check your internet connection and try again.",
"ok": "Ok",
"continueAsGuest": "Continue as a guest",
"loginToViewAppointments": "Please Login to View Appointments",
"itemType": "Item Type"
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": ar_SA, "en_US": en_US};
}

@ -764,5 +764,17 @@ abstract class LocaleKeys {
static const tapToView = 'tapToView';
static const noServicesAvailableToCopy = 'noServicesAvailableToCopy';
static const copySelectedItems = 'copySelectedItems';
static const noOfInvites = 'noOfInvites';
static const noSpecialServicesAvailable = 'noSpecialServicesAvailable';
static const customService = 'customService';
static const selectServiceDetails = 'selectServiceDetails';
static const addServiceDetails = 'addServiceDetails';
static const companyNameMandatory = 'companyNameMandatory';
static const connectionProblem = 'connectionProblem';
static const pleaseCheckConnection = 'pleaseCheckConnection';
static const ok = 'ok';
static const continueAsGuest = 'continueAsGuest';
static const loginToViewAppointments = 'loginToViewAppointments';
static const itemType = 'itemType';
}

@ -37,6 +37,8 @@ class AdDetailsModel {
double? priceExcludingDiscount;
double? reservePrice;
bool? isMCHandled;
bool? showContactDetail;
bool? isOnWhatsApp;
String? modifiedOn;
AdPostStatus? adPostStatus;
AdReserveStatus? adReserveStatus;
@ -83,6 +85,8 @@ class AdDetailsModel {
this.priceExcludingDiscount,
this.reservePrice,
this.isMCHandled,
this.showContactDetail,
this.isOnWhatsApp,
this.adPostStatus,
this.adReserveStatus,
this.isMyAd,
@ -140,6 +144,8 @@ class AdDetailsModel {
priceExcludingDiscount = json['priceExcludingDiscount'];
reservePrice = json['reservePrice'];
isMCHandled = json['isMCHandled'];
showContactDetail = json['showContactDetail'];
isOnWhatsApp = json['isOnWhatsApp'];
modifiedOn = json['modifiedOn'];
phoneNo = json['vehicle'] != null ? (json['vehicle']['mobileNo'] ?? "") : "";
whatsAppNo = json['vehicle'] != null ? (json['vehicle']['whatsAppNo'] ?? "") : "";

@ -451,10 +451,11 @@ class VehicleBrandsModel {
String? vehicleTypeVal;
String? vehicleBrandDescription;
String? vehicleBrandDescriptionN;
String? imageUrl;
bool? isActive;
bool? isSelected;
VehicleBrandsModel({this.id, this.vehicleType, this.vehicleTypeVal, this.vehicleBrandDescription, this.vehicleBrandDescriptionN, this.isActive, this.isSelected});
VehicleBrandsModel({this.id, this.vehicleType, this.vehicleTypeVal, this.vehicleBrandDescription, this.vehicleBrandDescriptionN, this.isActive, this.isSelected, this.imageUrl});
VehicleBrandsModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -463,6 +464,7 @@ class VehicleBrandsModel {
vehicleBrandDescription = json['vehicleBrand_Description'];
vehicleBrandDescriptionN = json['vehicleBrand_DescriptionN'];
isActive = json['isActive'];
imageUrl = json['imageUrl'];
isSelected = false;
}
@ -473,6 +475,7 @@ class VehicleBrandsModel {
data['vehicleTypeVal'] = vehicleTypeVal;
data['vehicleBrand_Description'] = vehicleBrandDescription;
data['vehicleBrand_DescriptionN'] = vehicleBrandDescriptionN;
data['imageUrl'] = imageUrl;
data['isActive'] = isActive;
return data;
}

@ -113,8 +113,8 @@ class ReqOffer {
offerStatusText = json['offerStatusText'];
comment = json['comment'];
serviceItemName = json['serviceItem'];
manufacturedByName = json['offeredItemCreatedBy'].toString();
manufacturedOn = json['offeredItemCreatedOn'];
manufacturedByName = json['offeredItemCreatedByName'].toString();
manufacturedOn = json['offeredItemCreatedOn'] ?? json["createdOn"];
price = json['price'];
isDeliveryAvailable = json['isDeliveryAvailable'];
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();

@ -2,6 +2,7 @@ class EnumsModel {
int id;
int enumTypeId;
String enumValueStr;
String enumValueStrDes;
int enumValue;
bool isActive;
@ -9,6 +10,7 @@ class EnumsModel {
required this.id,
required this.enumTypeId,
required this.enumValueStr,
required this.enumValueStrDes,
required this.enumValue,
required this.isActive,
});
@ -17,20 +19,13 @@ class EnumsModel {
id: json["id"],
enumTypeId: json["enumTypeID"],
enumValueStr: json["enumValueStr"],
enumValueStrDes: json["enumValueStrDes"] ?? json["enumValueStr"],
enumValue: json["enumValue"],
isActive: json["isActive"],
);
@override
String toString() {
return 'EnumsModel{id: $id, enumTypeId: $enumTypeId, enumValueStr: $enumValueStr, enumValue: $enumValue, isActive: $isActive}';
return 'EnumsModel{id: $id, enumTypeId: $enumTypeId, enumValueStr: $enumValueStr,enumValueStrDes: $enumValueStrDes, enumValue: $enumValue, isActive: $isActive}';
}
Map<String, dynamic> toJson() => {
"id": id,
"enumTypeID": enumTypeId,
"enumValueStr": enumValueStr,
"enumValue": enumValue,
"isActive": isActive,
};
}

@ -47,17 +47,6 @@ class AdsCreationPayloadModel {
ads = json['ads'] != null ? Ads.fromJson(json['ads']) : null;
vehiclePosting = json['vehiclePosting'] != null ? VehiclePosting.fromJson(json['vehiclePosting']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (ads != null) {
data['ads'] = ads!.toJson();
}
if (vehiclePosting != null) {
data['vehiclePosting'] = vehiclePosting!.toJson();
}
return data;
}
}
class Ads {
@ -67,8 +56,19 @@ class Ads {
int? countryId;
List<int>? specialServiceIDs;
bool? isMCHandled;
Ads({this.id, this.adsDurationID, this.startDate, this.countryId, this.specialServiceIDs, this.isMCHandled});
bool? showContactDetail;
bool? isOnWhatsApp;
Ads({
this.id,
this.adsDurationID,
this.startDate,
this.countryId,
this.specialServiceIDs,
this.isMCHandled,
this.showContactDetail,
this.isOnWhatsApp,
});
Ads.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -77,6 +77,8 @@ class Ads {
countryId = json['countryId'];
specialServiceIDs = json['specialServiceIDs'].cast<int>();
isMCHandled = json['isMCHandled'];
showContactDetail = json['showContactDetail'];
isOnWhatsApp = json['isOnWhatsApp'];
}
Map<String, dynamic> toJson() {
@ -123,32 +125,33 @@ class VehiclePosting {
String? phoneNo;
String? whatsAppNo;
VehiclePosting(
{this.id,
this.userID,
this.vehicleType,
this.vehicleModelID,
this.vehicleModelYearID,
this.vehicleColorID,
this.vehicleCategoryID,
this.vehicleConditionID,
this.vehicleMileageID,
this.vehicleTransmissionID,
this.vehicleSellerTypeID,
this.cityID,
this.price,
this.vehicleVIN,
this.vehicleDescription,
this.vehicleTitle,
this.vehicleDescriptionN,
this.isFinanceAvailable,
this.warantyYears,
this.demandAmount,
this.adStatus,
this.phoneNo,
this.whatsAppNo,
this.vehiclePostingImages,
this.vehiclePostingDamageParts});
VehiclePosting({
this.id,
this.userID,
this.vehicleType,
this.vehicleModelID,
this.vehicleModelYearID,
this.vehicleColorID,
this.vehicleCategoryID,
this.vehicleConditionID,
this.vehicleMileageID,
this.vehicleTransmissionID,
this.vehicleSellerTypeID,
this.cityID,
this.price,
this.vehicleVIN,
this.vehicleDescription,
this.vehicleTitle,
this.vehicleDescriptionN,
this.isFinanceAvailable,
this.warantyYears,
this.demandAmount,
this.adStatus,
this.phoneNo,
this.whatsAppNo,
this.vehiclePostingImages,
this.vehiclePostingDamageParts,
});
VehiclePosting.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -188,40 +191,6 @@ class VehiclePosting {
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['userID'] = userID;
data['vehicleType'] = vehicleType;
data['vehicleModelID'] = vehicleModelID;
data['vehicleModelYearID'] = vehicleModelYearID;
data['vehicleColorID'] = vehicleColorID;
data['vehicleCategoryID'] = vehicleCategoryID;
data['vehicleConditionID'] = vehicleConditionID;
data['vehicleMileageID'] = vehicleMileageID;
data['vehicleTransmissionID'] = vehicleTransmissionID;
data['vehicleSellerTypeID'] = vehicleSellerTypeID;
data['cityID'] = cityID;
data['price'] = price;
data['vehicleVIN'] = vehicleVIN;
data['vehicleDescription'] = vehicleDescription;
data['vehicleTitle'] = vehicleTitle;
data['vehicleDescriptionN'] = vehicleDescriptionN;
data['isFinanceAvailable'] = isFinanceAvailable;
data['warantyYears'] = warantyYears;
data['demandAmount'] = demandAmount;
data['adStatus'] = adStatus;
data['phoneNo'] = phoneNo;
data['whatsAppNo'] = whatsAppNo;
if (vehiclePostingImages != null) {
data['vehiclePostingImages'] = vehiclePostingImages!.map((v) => v.toJson()).toList();
}
if (vehiclePostingDamageParts != null) {
data['vehiclePostingDamageParts'] = vehiclePostingDamageParts!.map((v) => v.toJson()).toList();
}
return data;
}
@override
String toString() {
return 'VehiclePosting{id: $id, userID: $userID, vehicleType: $vehicleType, vehicleModelID: $vehicleModelID, vehicleModelYearID: $vehicleModelYearID, vehicleColorID: $vehicleColorID, vehicleCategoryID: $vehicleCategoryID, vehicleConditionID: $vehicleConditionID, vehicleMileageID: $vehicleMileageID, vehicleTransmissionID: $vehicleTransmissionID, vehicleSellerTypeID: $vehicleSellerTypeID, cityID: $cityID, price: $price, vehicleVIN: $vehicleVIN, vehicleDescription: $vehicleDescription, vehicleTitle: $vehicleTitle, vehicleDescriptionN: $vehicleDescriptionN, isFinanceAvailable: $isFinanceAvailable, warantyYears: $warantyYears, demandAmount: $demandAmount, adStatus: $adStatus, vehiclePostingImages: $vehiclePostingImages, vehiclePostingDamageParts: $vehiclePostingDamageParts, phoneNo: $phoneNo, whatsAppNo: $whatsAppNo}';

@ -0,0 +1,47 @@
class ProviderContactInfoModel {
int? id;
int? providerID;
String? name;
int? allDocStatus;
String? companyName;
String? companyDescription;
int? countryID;
String? countryName;
String? userID;
String? memberSince;
String? mobile;
String? whatsApp;
String? email;
ProviderContactInfoModel({
this.id,
this.providerID,
this.name,
this.allDocStatus,
this.companyName,
this.companyDescription,
this.countryID,
this.countryName,
this.userID,
this.memberSince,
this.mobile,
this.whatsApp,
this.email,
});
ProviderContactInfoModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
providerID = json['providerID'];
name = json['name'];
allDocStatus = json['allDocStatus'];
companyName = json['companyName'];
companyDescription = json['companyDescription'];
countryID = json['countryID'];
countryName = json['countryName'];
userID = json['userID'];
memberSince = json['memberSince'];
mobile = json['mobile'];
whatsApp = json['whatsApp'];
email = json['email'];
}
}

@ -4,6 +4,7 @@ class ProviderProfileModel {
ProviderProfileModel({
this.id,
this.companyName,
this.profileImage,
this.name,
this.countryName,
this.companyDescription,
@ -20,6 +21,7 @@ class ProviderProfileModel {
});
final int? id;
final String? profileImage;
final String? companyName;
final String? companyDescription;
int? countryID;
@ -38,6 +40,7 @@ class ProviderProfileModel {
factory ProviderProfileModel.fromJson(Map<String, dynamic> json) => ProviderProfileModel(
id: json["id"],
companyName: json["companyName"],
profileImage: json["profileImage"] ?? "",
name: json["name"],
countryName: json["countryName"],
countryID: json["countryID"],

@ -0,0 +1,26 @@
class AppInvitationHistoryModel {
int? id;
int? customerID;
int? channelID;
int? appInvitationEnumID;
int? noOfInvites;
String? comments;
AppInvitationHistoryModel({
this.id,
this.customerID,
this.channelID,
this.appInvitationEnumID,
this.noOfInvites,
this.comments,
});
AppInvitationHistoryModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
customerID = json['customerID'];
channelID = json['channelID'];
appInvitationEnumID = json['appInvitationEnumID'];
noOfInvites = json['noofInvites'];
comments = json['comments'];
}
}

@ -12,8 +12,22 @@ class ShippingRequestModel {
String? createdOn;
int? customerID;
String? customerName;
int? vehicleTypeID;
String? vehicleType;
ShippingRequestModel({this.id, this.requestID, this.request, this.shippingStatus, this.deliveredOn, this.comment, this.createdOn, this.customerID, this.customerName});
ShippingRequestModel({
this.id,
this.requestID,
this.request,
this.shippingStatus,
this.deliveredOn,
this.comment,
this.createdOn,
this.customerID,
this.customerName,
this.vehicleTypeID,
this.vehicleType,
});
ShippingRequestModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -26,22 +40,8 @@ class ShippingRequestModel {
createdOn = json['createdOn'];
customerID = json['customerID'];
customerName = json['customerName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['requestID'] = requestID;
if (request != null) {
data['request'] = request!.toJson();
}
data['shippingStatus'] = shippingStatus;
data['deliveredOn'] = deliveredOn;
data['comment'] = comment;
data['createdOn'] = createdOn;
data['customerID'] = customerID;
data['customerName'] = customerName;
return data;
vehicleTypeID = json['vehicleTypeID'];
vehicleType = json['vehicleType'];
}
}

@ -22,17 +22,17 @@ class Role {
String? message;
factory Role.fromJson(Map<String, dynamic> json) => Role(
totalItemsCount: json["totalItemsCount"] == null ? null : json["totalItemsCount"],
totalItemsCount: json["totalItemsCount"],
data: json["data"] == null ? null : List<RoleData>.from(json["data"].map((x) => RoleData.fromJson(x))),
messageStatus: json["messageStatus"] == null ? null : json["messageStatus"],
message: json["message"] == null ? null : json["message"],
messageStatus: json["messageStatus"],
message: json["message"],
);
Map<String, dynamic> toJson() => {
"totalItemsCount": totalItemsCount == null ? null : totalItemsCount,
"totalItemsCount": totalItemsCount,
"data": data == null ? null : List<dynamic>.from(data!.map((x) => x.toJson())),
"messageStatus": messageStatus == null ? null : messageStatus,
"message": message == null ? null : message,
"messageStatus": messageStatus,
"message": message,
};
}
@ -50,16 +50,16 @@ class RoleData {
bool? isActive;
factory RoleData.fromJson(Map<String, dynamic> json) => RoleData(
id: json["id"] == null ? null : json["id"],
roleName: json["roleName"] == null ? null : json["roleName"],
roleNameN: json["roleNameN"] == null ? null : json["roleNameN"],
isActive: json["isActive"] == null ? null : json["isActive"],
id: json["id"],
roleName: json["roleName"],
roleNameN: json["roleNameN"],
isActive: json["isActive"],
);
Map<String, dynamic> toJson() => {
"id": id == null ? null : id,
"roleName": roleName == null ? null : roleName,
"roleNameN": roleNameN == null ? null : roleNameN,
"isActive": isActive == null ? null : isActive,
"id": id,
"roleName": roleName,
"roleNameN": roleNameN,
"isActive": isActive,
};
}

@ -137,6 +137,9 @@ class AdsRepoImp implements AdsRepo {
});
List vehiclePostingDamageParts = [];
int sellerTypeID = AppState().getUser.data!.userInfo!.roleId ?? 0;
adsCreationPayloadModel.vehiclePosting!.vehiclePostingDamageParts?.forEach((element) {
log("comment: ${element.comment}");
var imageMap = {
@ -156,7 +159,9 @@ class AdsRepoImp implements AdsRepo {
"startDate": adsCreationPayloadModel.ads!.startDate,
"countryId": adsCreationPayloadModel.ads!.countryId,
"specialServiceIDs": adsCreationPayloadModel.ads!.specialServiceIDs,
"isMCHandled": false
"isMCHandled": false,
"showContactDetail": adsCreationPayloadModel.ads!.showContactDetail ?? false,
"isOnWhatsApp": adsCreationPayloadModel.ads!.isOnWhatsApp ?? false,
},
"vehiclePosting": {
"id": adsCreationPayloadModel.vehiclePosting!.id ?? 0,
@ -169,7 +174,7 @@ class AdsRepoImp implements AdsRepo {
"vehicleConditionID": adsCreationPayloadModel.vehiclePosting!.vehicleConditionID,
"vehicleMileageID": adsCreationPayloadModel.vehiclePosting!.vehicleMileageID,
"vehicleTransmissionID": adsCreationPayloadModel.vehiclePosting!.vehicleTransmissionID,
"vehicleSellerTypeID": adsCreationPayloadModel.vehiclePosting!.vehicleSellerTypeID,
"vehicleSellerTypeID": sellerTypeID,
"cityID": adsCreationPayloadModel.vehiclePosting!.cityID,
"price": adsCreationPayloadModel.vehiclePosting!.price,
"vehicleVIN": adsCreationPayloadModel.vehiclePosting!.vehicleVIN,
@ -534,7 +539,7 @@ class AdsRepoImp implements AdsRepo {
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.adsPhotoOfficeAppointmentCreate,
"",
postParams,
token: token,
);

@ -21,6 +21,7 @@ abstract class AppointmentRepo {
List<String>? customerNamesList,
List<String>? customerPhonesList,
List<String>? deliveryTypeIdsList,
AppointmentStatusEnum? appointmentStatusEnum,
String? fromDate,
String? toDate,
});
@ -31,6 +32,7 @@ abstract class AppointmentRepo {
List<String>? serviceIdsList,
List<String>? itemIdsList,
List<String>? branchIdsList,
AppointmentStatusEnum? appointmentStatusEnum,
});
Future<GenericRespModel> updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum});
@ -236,6 +238,7 @@ class AppointmentRepoImp implements AppointmentRepo {
List<String>? customerNamesList,
List<String>? customerPhonesList,
List<String>? deliveryTypeIdsList,
AppointmentStatusEnum? appointmentStatusEnum,
String? fromDate,
String? toDate,
}) async {
@ -253,6 +256,9 @@ class AppointmentRepoImp implements AppointmentRepo {
"DateTo": toDate ?? "",
};
if (appointmentStatusEnum != null) {
params.addAll({"AppointmentStatusID": appointmentStatusEnum.getIdFromAppointmentStatusEnum().toString()});
}
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
token: t,
(json) => GenericRespModel.fromJson(json),
@ -270,6 +276,7 @@ class AppointmentRepoImp implements AppointmentRepo {
List<String>? serviceIdsList,
List<String>? itemIdsList,
List<String>? branchIdsList,
AppointmentStatusEnum? appointmentStatusEnum,
}) async {
var params = {
"customerID": appState.getUser.data!.userInfo!.customerId.toString(),
@ -279,6 +286,10 @@ class AppointmentRepoImp implements AppointmentRepo {
"ServiceItemIDs": itemIdsList ?? [],
"CategoryIDs": categoryIdsList ?? [],
};
if (appointmentStatusEnum != null) {
params.addAll({"AppointmentStatusID": appointmentStatusEnum.getIdFromAppointmentStatusEnum().toString()});
}
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),

@ -77,7 +77,7 @@ abstract class BranchRepo {
Future<GenericRespModel> updateCategoryStatus({required int branchId, required int categoryId, required ServiceStatusEnum serviceStatusEnum});
Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required int branchId, required int serviceId});
Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required int branchId, required int categoryId, required int serviceId});
Future<GenericRespModel> getMatchedServices(int oldBranchId, int newBranchId, int categoryId);
@ -217,7 +217,7 @@ class BranchRepoImp implements BranchRepo {
"documentExt": documents[i].fileExt,
"documentImage": documents[i].document,
"isActive": true,
"dateExpire":documents[i].dateExpire,
"dateExpire": documents[i].dateExpire,
};
map.add(postParams);
}
@ -348,12 +348,22 @@ class BranchRepoImp implements BranchRepo {
}
@override
Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required int branchId, required int serviceId}) async {
Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required int branchId, required int categoryId, required int serviceId}) async {
var map = {
"ProviderBranchID": branchId.toString(),
"ServiceProviderServiceID": serviceId.toString(),
};
if (serviceId != -1) {
map.addAll({
"ServiceProviderServiceID": serviceId.toString(),
});
}
if (categoryId != -1) {
map.addAll({
"ServiceProviderServiceID": serviceId.toString(),
});
}
String t = AppState().getUser.data!.accessToken ?? "";
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),

@ -8,6 +8,7 @@ import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/models/general_models/enums_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_contact_info_model.dart';
import 'package:mc_common_app/models/user_models/cities.dart';
import 'package:mc_common_app/models/user_models/country.dart';
import 'package:mc_common_app/models/user_models/role.dart';
@ -56,6 +57,8 @@ abstract class CommonRepo {
Future<List<EnumsModel>> getEnumTypeValues({int? enumTypeID, String? enumTypeName});
Future<GenericRespModel> createComplainFromProvider({required int customerId, required String comment, required int complainType});
Future<ProviderContactInfoModel> getProvidersContactInfo({required int providerId});
}
class CommonRepoImp implements CommonRepo {
@ -363,4 +366,19 @@ class CommonRepoImp implements CommonRepo {
);
return adsGenericModel;
}
@override
Future<ProviderContactInfoModel> getProvidersContactInfo({required int providerId}) async {
final parameters = {
"ProviderID": providerId.toString(),
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),
ApiConsts.provideContactInfoGet,
queryParameters: parameters,
);
ProviderContactInfoModel contactInfoModel = ProviderContactInfoModel.fromJson(adsGenericModel.data);
return contactInfoModel;
}
}

@ -369,7 +369,7 @@ class RequestRepoImp implements RequestRepo {
"serviceItem": serviceItemName,
"comment": message,
"price": offerPrice,
"offeredItemCreatedBy": manufacturedByName.toString(),
"offeredItemCreatedByName": manufacturedByName.toString(),
"offeredItemCreatedOn": manufacturedOn.toString(),
"reqOfferImages": requestImages,
"isDeliveryAvailable": isDeliveryAvailable

@ -6,6 +6,7 @@ import 'package:mc_common_app/config/dependency_injection.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart';
import 'package:mc_common_app/models/setting_utils_models/app_invitation_history_model.dart';
import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart';
import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart';
import 'package:mc_common_app/utils/enums.dart';
@ -20,7 +21,9 @@ abstract class SettingOptionsRepo {
Future<List<AppInfoModel>> getTermsAndConditions();
Future<void> appInvitationCreate({required int channelId});
Future<GenericRespModel> appInvitationCreate({required int channelId, required String message});
Future<List<AppInvitationHistoryModel>> getAppInvitationsHistory();
}
class SettingOptionsRepoImp extends SettingOptionsRepo {
@ -154,21 +157,23 @@ class SettingOptionsRepoImp extends SettingOptionsRepo {
}
@override
Future<void> appInvitationCreate({required int channelId}) async {
Future<GenericRespModel> appInvitationCreate({required int channelId, required String message}) async {
String token = appState.getUser.data!.accessToken ?? "";
final params = {
"channelID": channelId.toString(),
"comments": "string",
"appInvitationEnumID": channelId.toString(),
"comments": message,
};
if (AppState().currentAppType == AppType.provider) {
if (AppState().currentAppType == AppType.customer) {
params.addAll({
"customerID": (appState.getUser.data!.userInfo!.customerId ?? "0").toString(),
"channelID": "3",
});
} else {
params.addAll({
"providerID": (appState.getUser.data!.userInfo!.providerId ?? "0").toString(),
"channelID": "2",
});
}
@ -178,5 +183,32 @@ class SettingOptionsRepoImp extends SettingOptionsRepo {
token: token,
params,
);
return genericRespModel;
}
@override
Future<List<AppInvitationHistoryModel>> getAppInvitationsHistory() async {
final params = {
"CustomerID": (appState.getUser.data!.userInfo!.customerId ?? "0").toString(),
"ChannelID": "3",
};
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.getAppInvitationHistory,
queryParameters: params,
token: token,
);
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
return [];
}
List<AppInvitationHistoryModel> list = List.generate(genericRespModel.data.length, (index) => AppInvitationHistoryModel.fromJson(genericRespModel.data[index]));
return list;
}
}

@ -31,7 +31,7 @@ abstract class UserRepo {
Future<RegisterUserRespModel> basicVerify(String phoneNo, String otp, String userToken, {bool isNeedToPassToken = false});
Future<RegisterUserRespModel> basicComplete(String userId, String firstName, String lastName, String email, String password, String cityID, String genderID, {bool isNeedToPassToken = false});
Future<RegisterUserRespModel> basicComplete(String userId, String firstName, String lastName, String compannyNname, String email, String password, String cityID, String genderID, {bool isNeedToPassToken = false});
Future<Response> loginV1(String phoneNo, String password);
@ -106,12 +106,12 @@ class UserRepoImp implements UserRepo {
}
@override
Future<RegisterUserRespModel> basicComplete(String userId, String firstName, String lastName, String email, String password, String cityID, String genderID, {bool isNeedToPassToken = false}) async {
Future<RegisterUserRespModel> basicComplete(String userId, String firstName, String lastName, String companyName, String email, String password, String cityID, String genderID, {bool isNeedToPassToken = false}) async {
Map<String, Object> postParams;
if (email.isEmpty) {
postParams = {"userID": userId, "firstName": firstName, "lastName": lastName, "companyName": "string", "isEmailVerified": true, "password": password, "cityID": cityID, "genderID": genderID};
postParams = {"userID": userId, "firstName": firstName, "lastName": lastName, "companyName": companyName, "isEmailVerified": true, "password": password, "cityID": cityID, "genderID": genderID};
} else {
postParams = {"userID": userId, "firstName": firstName, "lastName": lastName, "email": email, "companyName": "string", "isEmailVerified": true, "password": password, "cityID": cityID, "genderID": genderID};
postParams = {"userID": userId, "firstName": firstName, "lastName": lastName, "email": email, "companyName": companyName, "isEmailVerified": true, "password": password, "cityID": cityID, "genderID": genderID};
}
String? t;
if (isNeedToPassToken) {

@ -27,13 +27,13 @@ abstract class PaymentService {
class PaymentServiceImp implements PaymentService {
MyInAppBrowser? myInAppBrowser;
var inAppBrowserOptions = InAppBrowserClassSettings(
webViewSettings: InAppWebViewSettings(
useShouldOverrideUrlLoading: false,
transparentBackground: false,
isInspectable: false,
applePayAPIEnabled: true,
cacheEnabled: false,
allowsBackForwardNavigationGestures: false,
),
browserSettings: InAppBrowserSettings(
hideUrlBar: true,
@ -42,7 +42,7 @@ class PaymentServiceImp implements PaymentService {
hideToolbarBottom: true,
hideToolbarTop: false,
hideCloseButton: false,
allowGoBackWithBackButton: true,
allowGoBackWithBackButton: false,
toolbarBottomBackgroundColor: Colors.black,
closeButtonColor: Colors.white,
presentationStyle: ModalPresentationStyle.FULL_SCREEN,

@ -233,3 +233,11 @@ enum ShippingRequestStatusEnum {
outForDelivery,
delivered,
}
enum InviteTypeEnum {
whatsapp,
sms,
email,
}

@ -4,6 +4,8 @@ import 'dart:typed_data';
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/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/utils/date_helper.dart';
@ -640,6 +642,37 @@ class Utils {
return result;
}
static Future<void> inviteFriend({required InviteTypeEnum inviteTypeEnum, required String message}) async {
String url;
switch (inviteTypeEnum) {
case InviteTypeEnum.whatsapp:
// WhatsApp requires a phone number and message, format like this:
url = 'https://wa.me/?text=${Uri.encodeComponent(message)}';
break;
case InviteTypeEnum.sms:
// SMS: 'sms:<phone_number>?body=<message>'
// You can add a phone number, or keep it empty to allow the user to input one.
url = 'sms:?body=${Uri.encodeComponent(message)}';
break;
case InviteTypeEnum.email:
// Email: 'mailto:<email>?subject=<subject>&body=<message>'
url = 'mailto:?subject=MowaterInvitation&body=${Uri.encodeComponent(message)}';
break;
}
// Try to launch the URL and catch any exceptions.
if (await canLaunchUrl(Uri.parse(url))) {
try {
await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
} catch (e) {
await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
}
}
}
static buildProviderContactInfoBottomSheet({required BuildContext context, required String? email, required String? mobileNo}) {
return showModalBottomSheet(
context: context,
@ -693,3 +726,36 @@ class Utils {
);
}
}
class NoInternetDialog {
static void show(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text(
LocaleKeys.connectionProblem,
textAlign: TextAlign.center,
),
content: const Text(
LocaleKeys.pleaseCheckConnection,
textAlign: TextAlign.center,
),
actions: [
ElevatedButton(
onPressed: () {
// // if (AppState().currentAppType == AppType.provider) {
// Navigator.of(context).pop();
Navigator.of(context).pushNamedAndRemoveUntil(AppRoutes.registerSelection, (Route<dynamic> route) => true);
},
child: const Text(
LocaleKeys.ok,
style: TextStyle(color: Colors.white),
),
),
],
);
},
);
}
}

@ -194,7 +194,7 @@ class AdVM extends BaseVM {
exploreAdsFilterOptions.add(FilterListModel(
id: vehicleBrandsForFilters[i].id!,
title: "${vehicleBrandsForFilters[i].vehicleBrandDescription}",
iconUrl: "",
iconUrl: vehicleBrandsForFilters[i].imageUrl ?? "",
isSelected: false,
));
}
@ -344,7 +344,9 @@ class AdVM extends BaseVM {
pageIndexForMyAds = 1;
hasMoreDataForMyAds = true;
setState(ViewState.busy);
myAds = await adsRepo.getAllAds(isMyAds: true);
if (!AppState().getIsViewOnly) {
myAds = await adsRepo.getAllAds(isMyAds: true);
}
myAdsFilteredList = myAds;
final myActiveAds = myAds.where((element) => element.adPostStatus == AdPostStatus.active).toList();
myActiveAdsForHome = myActiveAds.length >= 3 ? myActiveAds.take(3).toList() : myActiveAds;
@ -379,7 +381,14 @@ class AdVM extends BaseVM {
}
Future<void> getVehicleAdsSpecialServices({required int cityId, required int countryId, required int specialServiceType}) async {
log("cityId: $cityId");
vehicleAdsSpecialServices = await adsRepo.getSpecialServices(specialServiceType: specialServiceType, countryId: countryId, cityId: cityId);
vehicleAdsSpecialServices = vehicleAdsSpecialServices.where((ad) {
bool matchesCityInDetails = ad.details?.any((detail) => detail.fromcity == cityId || detail.tocity == cityId) ?? false;
bool matchesCityInOffice = ad.office?.any((office) => office.cityID == cityId) ?? false;
return matchesCityInDetails || matchesCityInOffice;
}).toList();
notifyListeners();
}
@ -580,6 +589,7 @@ class AdVM extends BaseVM {
return;
}
vehicleBrandId = id;
vehicleModelId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
await getVehicleDetailsByVehicleBrandId();
notifyListeners();
@ -1471,12 +1481,11 @@ class AdVM extends BaseVM {
List<SSPhotoOfficeScheduleModel> photoSSSchedulesByOffices = [];
SSPhotoOfficeScheduleModel selectedPhotoSSSchedulesByOffice = SSPhotoOfficeScheduleModel();
Future<void> getPhotographyServiceScheduleListByOffices({required double latitude, required double longitude, bool isNeedToRebuild = false}) async {
Future<void> getPhotographyServiceScheduleListByOffices({required int cityId, required double latitude, required double longitude, bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
int cityID = AppState().getUser.data!.userInfo!.cityId ?? 1;
try {
photoSSSchedulesByOffices = await commonRepo.getPhotographyServiceScheduleListByOffices(lat: latitude, long: longitude, cityID: cityID);
photoSSSchedulesByOffices = await commonRepo.getPhotographyServiceScheduleListByOffices(lat: latitude, long: longitude, cityID: cityId);
if (isNeedToRebuild) setState(ViewState.idle);
} catch (e) {
if (isNeedToRebuild) setState(ViewState.idle);
@ -1496,8 +1505,12 @@ class AdVM extends BaseVM {
// }
// }
Future<void> onAdSSBookAppointmentPressed(BuildContext context, {required AdDetailsModel adDetailsModel, required int adsSpecialServiceID}) async {
Future<void> onAdSSBookAppointmentPressed(BuildContext context, {required AdDetailsModel adDetailsModel}) async {
bool isValidated = false;
int specialSServideId = 1;
if (adDetailsModel.specialservice != null && adDetailsModel.specialservice!.isNotEmpty) {
specialSServideId = adDetailsModel.specialservice!.first.specialServiceID ?? 1;
}
if (selectedPhotoSSSchedulesByOffice.photoOfficeID == null) {
isValidated = false;
@ -1523,7 +1536,7 @@ class AdVM extends BaseVM {
adId: adDetailsModel.id ?? 0,
photoOfficeID: photoOfficeSelectedId.selectedId,
photoOfficeSlotID: selectedPhotoOfficeSlotDateTime ?? 0,
adsSpecialServiceID: adsSpecialServiceID,
adsSpecialServiceID: specialSServideId,
);
Utils.hideLoading(context);
@ -1546,12 +1559,26 @@ class AdVM extends BaseVM {
for (var value in specialServiceCards) {
adsSelectedServices.add(value.serviceSelectedId!.selectedId);
}
if (selectionDurationStartDate.isNotEmpty) {
// Define the format of the input string
DateTime startDate = DateTime.parse(
"${selectionDurationStartDate.split('-')[2]}-${selectionDurationStartDate.split('-')[1]}-${selectionDurationStartDate.split('-')[0]}",
);
DateTime currentDate = DateTime.now();
if (startDate.isBefore(currentDate)) {
selectionDurationStartDate = DateHelper.formatAsYearMonthDay(currentDate);
}
}
Ads ads = Ads(
id: isAdEditEnabled ? previousAdDetails!.id : null,
adsDurationID: vehicleAdDurationId.selectedId == -1 ? 0 : vehicleAdDurationId.selectedId,
startDate: selectionDurationStartDate,
countryId: vehicleCountryId.selectedId,
specialServiceIDs: adsSelectedServices,
showContactDetail: isPhoneNumberShown,
isOnWhatsApp: isNumberOnWhatsApp,
);
List<VehiclePostingImages> vehicleImages = [];
@ -2041,6 +2068,7 @@ class AdVM extends BaseVM {
void autoFillSelectedVehicleAdsDetails() async {
int index = vehicleBrands.indexWhere((element) => element.id == previousAdDetails!.vehicle!.model!.vehicleBrandID);
if (index != -1) {
await updateSelectionVehicleBrandId(SelectionModel(selectedId: vehicleBrands[index].id!, selectedOption: vehicleBrands[index].vehicleBrandDescription ?? ""));
}
@ -2060,8 +2088,8 @@ class AdVM extends BaseVM {
if (indexCountry != -1) {
updateSelectionVehicleCountryId(SelectionModel(selectedId: vehicleCountries[indexCountry].id!, selectedOption: vehicleCountries[indexCountry].countryName ?? ""));
}
updateSelectionVehicleCityId(SelectionModel(selectedId: previousAdDetails!.vehicle!.cityID!, selectedOption: previousAdDetails!.vehicle!.cityName ?? ""));
updateSelectionVehicleCityId(SelectionModel(selectedId: previousAdDetails!.vehicle!.cityID!, selectedOption: previousAdDetails!.vehicle!.cityName ?? ""));
vehicleDemandAmount = previousAdDetails!.vehicle!.demandAmount!.toInt().toString();
vehicleVin = previousAdDetails!.vehicle!.vehicleVIN.toString();
vehicleTitle = previousAdDetails!.vehicle!.vehicleTitle.toString();
@ -2117,12 +2145,10 @@ class AdVM extends BaseVM {
}
}
selectionDurationStartDate = DateHelper.formatDateT(previousAdDetails!.startdate ?? "");
isPhoneNumberShown = previousAdDetails!.phoneNo != null;
if (isPhoneNumberShown) {
adPhoneNumber = previousAdDetails!.phoneNo ?? "";
adPhoneNumberDialCode = "+966";
}
// selectionDurationStartDate = DateHelper.formatDateT(previousAdDetails!.startdate ?? "");
selectionDurationStartDate = "";
isPhoneNumberShown = previousAdDetails!.showContactDetail ?? false;
adPhoneNumber = previousAdDetails!.adOwnerDetails!.mobileNo ?? "";
isNumberOnWhatsApp = previousAdDetails!.whatsAppNo != null;
warrantyDuration = previousAdDetails!.warrantyYears ?? "";

@ -20,6 +20,7 @@ import 'package:mc_common_app/models/provider_branches_models/branch_detail_mode
import 'package:mc_common_app/models/provider_branches_models/branch_review_model.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/categroy.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/services.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_contact_info_model.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_model.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
@ -62,9 +63,9 @@ class AppointmentsVM extends BaseVM {
List<AppointmentListModel> myAppointments = [];
List<AppointmentListModel> myUpComingAppointments = [];
List<AppointmentListModel> myFilteredAppointments = [];
List<AppointmentListModel> myFilteredAppointmentsForCustomers = [];
List<FilterListModel> appointmentsFilterOptions = [];
List<AppointmentListModel> myFilteredAppointments2 = [];
List<AppointmentListModel> myFilteredAppointmentsForProvider = [];
bool isFetchingServices = false;
@ -140,7 +141,7 @@ class AppointmentsVM extends BaseVM {
}
context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.booked);
applyFilterOnAppointmentsVMForCustomers(appointmentStatusEnum: AppointmentStatusEnum.booked);
Utils.hideLoading(context);
resetAfterBookingAppointment();
if (isSuccess) {
@ -149,7 +150,6 @@ class AppointmentsVM extends BaseVM {
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
} else {
Utils.showToast(LocaleKeys.appointmentBookedSuccessfully.tr());
getMyAppointmentsForCustomer();
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
}
@ -227,9 +227,8 @@ class AppointmentsVM extends BaseVM {
}
if (genericRespModel.messageStatus == 1 && AppState().currentAppType == AppType.customer) {
context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
applyFilterOnAppointmentsVMForCustomers(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast(genericRespModel.message.toString());
await getMyAppointmentsForCustomer();
Utils.hideLoading(context);
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
@ -353,46 +352,69 @@ class AppointmentsVM extends BaseVM {
notifyListeners();
}
applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) {
log("appointmentStatusEnum: $appointmentStatusEnum");
// isNeedCustomerFilter IS ONLY FOR THE PROVIDER APP
bool isAppointmentLoading = false;
applyFilterOnAppointmentsVMForProviders({int? branchID, required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false, bool shouldPopulateUpcoming = false}) async {
if (appointmentsFilterOptions.isEmpty) return;
for (var value in appointmentsFilterOptions) {
value.isSelected = false;
}
int index = appointmentsFilterOptions.indexWhere((element) => element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum());
appointmentsFilterOptions[index].isSelected = true;
for (var element in appointmentsFilterOptions) {
if (element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum()) {
element.isSelected = true;
}
isAppointmentLoading = true;
notifyListeners();
myFilteredAppointmentsForProvider = await getAppointmentsBasedOnFiltersForProviders(branchID: branchID ?? selectedBranchIdForAppointments, appointmentStatus: appointmentStatusEnum);
isAppointmentLoading = false;
if (shouldPopulateUpcoming) {
myUpComingAppointments = myFilteredAppointmentsForProvider
.where((element) =>
(element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.toList();
}
// appointmentsFilterOptions[
// appointmentStatusEnum.getIdFromAppointmentStatusEnum()]
// .isSelected = true;
if (appointmentStatusEnum == AppointmentStatusEnum.allAppointments) {
myFilteredAppointments = myAppointments;
if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(myFilteredAppointments);
notifyListeners();
return;
log("myUpComingAppointments: ${myUpComingAppointments.length}");
if (isNeedCustomerFilter) {
myFilteredAppointmentsForProvider = findAppointmentsBasedOnCustomers(myFilteredAppointmentsForProvider);
}
notifyListeners();
}
applyFilterOnAppointmentsVMForCustomers({required AppointmentStatusEnum appointmentStatusEnum, bool shouldPopulateUpcoming = false}) async {
log("appointmentsFilterOptions: ${appointmentsFilterOptions.isEmpty}");
if (appointmentsFilterOptions.isEmpty) return;
for (var value in appointmentsFilterOptions) {
value.isSelected = false;
}
int index = appointmentsFilterOptions.indexWhere((element) => element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum());
appointmentsFilterOptions[index].isSelected = true;
isAppointmentLoading = true;
notifyListeners();
myFilteredAppointmentsForCustomers = await getAppointmentsBasedOnFiltersForCustomer();
isAppointmentLoading = false;
if (shouldPopulateUpcoming) {
myUpComingAppointments = myFilteredAppointmentsForCustomers
.where((element) =>
(element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.toList();
}
myFilteredAppointments = myAppointments.where((element) => element.appointmentStatusID! == appointmentStatusEnum.getIdFromAppointmentStatusEnum()).toList();
if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(myFilteredAppointments);
notifyListeners();
}
findAppointmentsBasedOnCustomers(List<AppointmentListModel> appointments) {
// Use a Set to ensure uniqueness of customerIDs
List<AppointmentListModel> findAppointmentsBasedOnCustomers(List<AppointmentListModel> appointments) {
Set<int> uniqueCustomerIDs = <int>{};
// Extract unique customerIDs
for (var item in appointments) {
uniqueCustomerIDs.add(item.customerID ?? 0);
}
// Create a list of CustomerData instances
myFilteredAppointments2 = uniqueCustomerIDs.map((id) {
List<AppointmentListModel> groupedList = uniqueCustomerIDs.map((id) {
List<AppointmentListModel> list = appointments.where((item) => item.customerID == id).toList();
list.sort((a, b) => DateHelper.parseStringToDate(DateHelper.formatDateT(b.appointmentDate!))
.millisecondsSinceEpoch
@ -402,61 +424,16 @@ class AppointmentsVM extends BaseVM {
return model;
}).toList();
// customersAppointments = uniqueCustomerIDs.map((id) {
// List<AppointmentListModel> list = myFilteredAppointments
// .where((item) => item.customerID == id)
// .toList();
// var customerItem =
// myFilteredAppointments.firstWhere((item) => item.customerID == id);
//
// return CustomerData(
// customerID: id,
// customerName: customerItem.customerName ?? "",
// appointmentList: list,
// );
// }).toList();
}
// Future<void> getMyAppointments() async {
// setState(ViewState.busy);
//
// myAppointments =
// await appointmentRepo.getMyAppointmentsForCustomersByFilters();
// myFilteredAppointments = myAppointments;
// myUpComingAppointments = myAppointments
// .where((element) =>
// element.appointmentStatusEnum == AppointmentStatusEnum.booked)
// .toList();
// applyFilterOnAppointmentsVM(
// appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
// if (myUpComingAppointments.isEmpty) {
// isShowEmptyMessage = true;
// }
// setState(ViewState.idle);
// notifyListeners();
// }
Future<void> getMyAppointmentsForCustomer({bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters();
// myFilteredAppointments = myAppointments;
setState(ViewState.idle);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
myUpComingAppointments = myAppointments
.where((element) =>
(element.appointmentStatusEnum == AppointmentStatusEnum.booked || element.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.toList();
notifyListeners();
return groupedList;
}
AppointmentSlots? appointmentSlots;
Future<void> getAppointmentSlotsInfo({required int branchID, required BuildContext context, bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
bool isSlotsLoading = true;
Future<void> getAppointmentSlotsInfo({required int branchID}) async {
isSlotsLoading = true;
notifyListeners();
try {
GenericRespModel genericRespModel = await appointmentRepo.getAppointmentSlots(branchID: branchID, fromDate: selectedDateForAppointments, toDate: selectedDateForAppointments);
if (genericRespModel.messageStatus == 1) {
@ -464,25 +441,15 @@ class AppointmentsVM extends BaseVM {
} else {
Utils.showToast(genericRespModel.message.toString());
}
isSlotsLoading = false;
notifyListeners();
} catch (e) {
isSlotsLoading = false;
notifyListeners();
Utils.showToast(e.toString());
}
}
Future<void> getMyAppointmentsForProvider({required int branchID}) async {
setState(ViewState.busy);
myAppointments = await appointmentRepo.getMyAppointmentsForProvidersByFilters(branchID: branchID);
myFilteredAppointments = myAppointments;
myUpComingAppointments = myAppointments
.where((element) =>
(element.appointmentStatusEnum == AppointmentStatusEnum.confirmed || element.appointmentStatusEnum == AppointmentStatusEnum.booked) &&
(DateHelper.parseStringToDate(element.appointmentDate!).isAfter(DateTime.now())))
.toList();
notifyListeners();
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
setState(ViewState.idle);
}
Future<bool> updateAppointmentStatus({required int appointmentId, required AppointmentStatusEnum appointmentStatusEnum, bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy);
try {
@ -520,8 +487,8 @@ class AppointmentsVM extends BaseVM {
}
bool isShowMergeButton() {
return myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList!.every((appointment) => appointment.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList!.length > 1;
return myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList!.every((appointment) => appointment.appointmentStatusEnum == AppointmentStatusEnum.confirmed) &&
myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList!.length > 1;
}
Future<GenericRespModel> createMergeAppointment(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
@ -534,10 +501,10 @@ class AppointmentsVM extends BaseVM {
bool inNeedToEnableMergeButton = false;
void updateCheckBoxInMergeRequest(int currentIndex) {
myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected =
!(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false);
myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected =
!(myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false);
int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList ?? []);
int count = countSelected(myFilteredAppointmentsForProvider[selectedAppointmentIndex].customerAppointmentList ?? []);
if (count > 1) {
inNeedToEnableMergeButton = true;
} else {
@ -573,9 +540,9 @@ class AppointmentsVM extends BaseVM {
element.isSelected = false;
}
}
int index = serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!;
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots![slotIndex].isSelected = true;
serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel!.availableSlots = serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots!;
int dateIndex = serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!;
serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].availableSlots![slotIndex].isSelected = true;
serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel!.availableSlots = serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].availableSlots!;
notifyListeners();
}
@ -633,7 +600,7 @@ class AppointmentsVM extends BaseVM {
}
branchesCategoriesFilterOptions[index].isSelected = true;
await getBranchesBasedOnCategoryFilters(categoryId: branchesCategoriesFilterOptions[index].id);
await getBranchesBasedOnCategoryFilters(serviceId: branchesCategoriesFilterOptions[index].id);
notifyListeners();
}
@ -917,9 +884,8 @@ class AppointmentsVM extends BaseVM {
}
if (genericRespModel.messageStatus == 1) {
context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast(genericRespModel.message.toString());
getMyAppointmentsForCustomer();
applyFilterOnAppointmentsVMForCustomers(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.hideLoading(context);
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
}
@ -1011,6 +977,20 @@ class AppointmentsVM extends BaseVM {
}
}
Future<ProviderContactInfoModel?> getProvidersContactInfo({required int providerId, required BuildContext context}) async {
Utils.showLoading(context);
try {
ProviderContactInfoModel contactInfoModel = await commonRepo.getProvidersContactInfo(providerId: providerId);
Utils.hideLoading(context);
return contactInfoModel;
} catch (e, s) {
logger.e(s);
Utils.hideLoading(context);
Utils.showToast(e.toString());
return null;
}
}
// BRANCHES RELATED
List<BranchDetailModel> nearbyBranches = [];
@ -1023,9 +1003,9 @@ class AppointmentsVM extends BaseVM {
if (branchesCategoriesFilterOptions.isNotEmpty) return;
branchesCategoriesFilterOptions.clear();
setOnlyState(ViewState.busy);
Category category = await branchRepo.fetchBranchCategory();
category.data?.forEach((element) {
branchesCategoriesFilterOptions.add(FilterListModel(id: element.id ?? 0, isSelected: false, iconUrl: element.serviceCategoryIconUrl ?? "", title: element.categoryName ?? "N/A"));
Services services = await branchRepo.fetchServicesByCategoryId(serviceCategoryId: -1);
services.data?.forEach((element) {
branchesCategoriesFilterOptions.add(FilterListModel(id: element.id ?? 0, isSelected: false, iconUrl: element.serviceIconUrl ?? "", title: element.description ?? ""));
});
branchesCategoriesFilterOptions.insert(0, FilterListModel(id: 0, isSelected: true, title: "All Branches"));
notifyListeners();
@ -1440,15 +1420,15 @@ class AppointmentsVM extends BaseVM {
setState(ViewState.idle);
}
Future<void> getBranchesBasedOnCategoryFilters({required int categoryId}) async {
Future<void> getBranchesBasedOnCategoryFilters({required int serviceId}) async {
setState(ViewState.busy);
if (categoryId == 0) {
if (serviceId == 0) {
await getAllNearBranches();
return;
}
nearbyBranches.clear();
nearbyBranches = await branchRepo.getBranchesByFilters(
categoryIdsList: [categoryId],
serviceIdsList: [serviceId],
latitude: AppState().currentLocation.latitude,
longitude: AppState().currentLocation.longitude,
);
@ -1827,105 +1807,120 @@ class AppointmentsVM extends BaseVM {
clearAppointmentFilterSelections();
if (AppState().currentAppType == AppType.provider) {
getMyAppointmentsForProvider(branchID: selectedBranchIdForAppointments);
applyFilterOnAppointmentsVMForProviders(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
} else {
getMyAppointmentsForCustomer();
applyFilterOnAppointmentsVMForCustomers(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
}
notifyListeners();
}
Future<void> getAppointmentsBasedOnFiltersForCustomer() async {
setState(ViewState.busy);
List<String> providersIdsList = [];
if (appointmentFilterProviderSearchHistory.isNotEmpty) {
for (var element in appointmentFilterProviderSearchHistory) {
providersIdsList.add(element.id.toString());
Future<List<AppointmentListModel>> getAppointmentsBasedOnFiltersForCustomer() async {
try {
setState(ViewState.busy);
List<String> providersIdsList = [];
if (appointmentFilterProviderSearchHistory.isNotEmpty) {
for (var element in appointmentFilterProviderSearchHistory) {
providersIdsList.add(element.id.toString());
}
}
}
List<String> categoryIdsList = [];
if (appointmentFilterCategorySearchHistory.isNotEmpty) {
for (var element in appointmentFilterCategorySearchHistory) {
categoryIdsList.add(element.id.toString());
List<String> categoryIdsList = [];
if (appointmentFilterCategorySearchHistory.isNotEmpty) {
for (var element in appointmentFilterCategorySearchHistory) {
categoryIdsList.add(element.id.toString());
}
}
}
List<String> servicesIdsList = [];
if (appointmentFilterServicesSearchHistory.isNotEmpty) {
for (var element in appointmentFilterServicesSearchHistory) {
servicesIdsList.add(element.id.toString());
List<String> servicesIdsList = [];
if (appointmentFilterServicesSearchHistory.isNotEmpty) {
for (var element in appointmentFilterServicesSearchHistory) {
servicesIdsList.add(element.id.toString());
}
}
}
List<String> itemIdsList = [];
if (appointmentFilterItemsSearchHistory.isNotEmpty) {
for (var element in appointmentFilterItemsSearchHistory) {
itemIdsList.add(element.id.toString());
List<String> itemIdsList = [];
if (appointmentFilterItemsSearchHistory.isNotEmpty) {
for (var element in appointmentFilterItemsSearchHistory) {
itemIdsList.add(element.id.toString());
}
}
}
List<String> branchesIdsList = [];
if (appointmentFilterBranchSearchHistory.isNotEmpty) {
for (var element in appointmentFilterBranchSearchHistory) {
branchesIdsList.add(element.id.toString());
List<String> branchesIdsList = [];
if (appointmentFilterBranchSearchHistory.isNotEmpty) {
for (var element in appointmentFilterBranchSearchHistory) {
branchesIdsList.add(element.id.toString());
}
}
}
myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters(
providerIdsList: providersIdsList,
categoryIdsList: categoryIdsList,
serviceIdsList: servicesIdsList,
itemIdsList: itemIdsList,
branchIdsList: branchesIdsList,
);
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
setState(ViewState.idle);
List<AppointmentListModel> myAppointments = await appointmentRepo.getMyAppointmentsForCustomersByFilters(
providerIdsList: providersIdsList,
categoryIdsList: categoryIdsList,
serviceIdsList: servicesIdsList,
itemIdsList: itemIdsList,
branchIdsList: branchesIdsList,
);
setState(ViewState.idle);
return myAppointments;
} catch (e) {
logger.e(e);
setState(ViewState.idle);
Utils.showToast(e.toString());
return [];
}
}
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());
Future<List<AppointmentListModel>> getAppointmentsBasedOnFiltersForProviders({required int branchID, AppointmentStatusEnum? appointmentStatus}) async {
try {
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> 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> 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());
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: '',
toDate: '');
log(" myFilteredAppointments : ${myFilteredAppointments.length}");
applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
setState(ViewState.idle);
log("customerNamesList: $customerNamesList");
log("customerPhonesList: $customerPhonesList");
log("customerDeliveryTypesList: $customerDeliveryTypesList");
log("servicesIdsList: $servicesIdsList");
List<AppointmentListModel> myAppointments = await appointmentRepo.getMyAppointmentsForProvidersByFilters(
branchID: branchID,
customerNamesList: customerNamesList,
customerPhonesList: customerPhonesList,
deliveryTypeIdsList: customerDeliveryTypesList,
serviceProviderServiceIdsList: servicesIdsList,
appointmentStatusEnum: appointmentStatus,
fromDate: '',
toDate: '');
setState(ViewState.idle);
return myAppointments ?? [];
} catch (e) {
logger.e(e);
setState(ViewState.idle);
Utils.showToast(e.toString());
return [];
}
}
Future<bool> addProviderToFavorite({required int serviceProviderID, required BuildContext context}) async {

@ -387,8 +387,8 @@ class ChatVM extends BaseVM {
"IsDeliveryAvailable": isDeliveryAvailable,
"ServiceItem": serviceItemName,
"ReqOfferImages": offerImages,
"OfferedItemCreatedBy": manufacturedByName,
// "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime
"OfferedItemCreatedByName": manufacturedByName,
"OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime
"ServiceProviderID": providerId,
"OfferStatus": RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
"Comment": message,
@ -610,7 +610,7 @@ class ChatVM extends BaseVM {
int providerId = AppState().getUser.data!.userInfo!.providerId!;
Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessagesForRequests(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
context.read<RequestsVM>().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex, context: context);
await context.read<RequestsVM>().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex, context: context);
List<int> unreadMessageIds = [];
for (var msg in chatMessages) {
if (!msg.isRead! && !msg.isMyMessage!) {
@ -674,7 +674,7 @@ class ChatVM extends BaseVM {
"RequestID": requestId,
"Price": double.parse(offerPrice),
"ServiceItem": serviceItemName,
"OfferedItemCreatedBy": manufacturedByName,
"OfferedItemCreatedByName": manufacturedByName,
// "OfferedItemCreatedOn": manufacturedOn,
"ServiceProviderID": serviceProviderID,
"OfferStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum(),

@ -63,11 +63,12 @@ class DashboardVmCustomer extends BaseVM {
AppointmentsVM appointmentsVM = Provider.of<AppointmentsVM>(context, listen: false);
RequestsVM requestsVM = Provider.of<RequestsVM>(context, listen: false);
ChatVM chatVM = Provider.of<ChatVM>(context, listen: false);
appointmentsVM.populateBranchesFilterList();
appointmentsVM.populateAppointmentsFilterList();
adVM.populateAdsFilterList();
requestsVM.populateDataForRequestsFilter();
await appointmentsVM.getMyAppointmentsForCustomer();
await appointmentsVM.applyFilterOnAppointmentsVMForCustomers(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, shouldPopulateUpcoming: true);
await appointmentsVM.applyFilterOnBranches(index: 0); // to get all branches!
await appointmentsVM.getMyRecentBranches(); // to get my recent branches

@ -37,6 +37,7 @@ class DashboardVMProvider extends BaseVM {
void onNavbarTapped(int index) async {
logger.i(AppState().getDeviceToken);
selectedNavbarBarIndex = index;
notifyListeners();
}
@ -81,18 +82,19 @@ class DashboardVMProvider extends BaseVM {
requestsVM.populateDataForRequestsFilter();
appointmentVM.populateAppointmentsFilterList();
shippingManagementVM.populateShippingRequestFilterList();
await serviceVM.getBranchAndServices();
await serviceVM.populateBranchServiceFilters();
await appointmentVM.getMyAppointmentsForProvider(branchID: appointmentVM.selectedBranchIdForAppointments);
adVM.populateAdsFilterList();
await subscriptionsVM.getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
await serviceVM.getBranchAndServices();
if (dashboardRouteEnum != DashboardRouteEnum.fromAdsPayment && dashboardRouteEnum != DashboardRouteEnum.fromAdsSubmit) {
await adVM.getMyAds();
await adVM.getExploreAds();
}
await appointmentVM.applyFilterOnAppointmentsVMForProviders(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, shouldPopulateUpcoming: true);
adVM.populateAdsFilterList();
await requestsVM.getRequests();
await subscriptionsVM.getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
await adVM.getVehicleTypes();
await adVM.getVehicleAdsDuration();
await requestsVM.getRequests();
await chatVM.buildHubConnection(context);
}
@ -128,7 +130,7 @@ class DashboardVMProvider extends BaseVM {
void performCheckOnUsers(BuildContext context, Function() callBack) async {
if (AppState().getproviderSubscription.isNotEmpty && AppState().getproviderSubscription.first.subUsersRemaining! > 0) {
showMyBottomSheet(context, isDismissible: false, child: const AddPhoneNumWidget(), callBackFunc: callBack);
showMyBottomSheet(context, isDismissible: false, isScrollControlled: true, child: AddPhoneNumWidget(), callBackFunc: callBack);
} else {
Utils.showToast(LocaleKeys.upgradeSubUsers.tr());
}

@ -136,7 +136,7 @@ class PaymentVM extends ChangeNotifier {
}
},
onSuccess: () async {
Utils.showToast("Verifying Payment..");
// Utils.showToast("Verifying Payment..");
switch (paymentTypeEnum) {
case PaymentTypes.subscription:

@ -215,13 +215,13 @@ class RequestsVM extends BaseVM {
notifyListeners();
}
overwriteChatMessagesInRequestsModel({required List<ChatMessageModel> messages, required int index, required BuildContext context}) {
overwriteChatMessagesInRequestsModel({required List<ChatMessageModel> messages, required int index, required BuildContext context}) async {
if (myFilteredRequests.isEmpty) return;
myFilteredRequests[index].chatMessages = messages;
if (myFilteredRequests[index].chatMessages.isNotEmpty) {
for (var message in myFilteredRequests[index].chatMessages) {
if (message.chatMessageTypeEnum == ChatMessageTypeEnum.offer) {
context.read<ChatVM>().updateLatestOfferId(message.reqOfferID ?? 0);
await context.read<ChatVM>().updateLatestOfferId(message.reqOfferID ?? 0);
if (message.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted) {
updateAcceptedReqOffer(message.reqOffer!);
updateAcceptedRequestOfferProviderName(message.senderName ?? "");
@ -783,7 +783,7 @@ class RequestsVM extends BaseVM {
requestImages.add(element.toJson());
});
// try {
try {
GenericRespModel respModel = await requestRepo.createRequest(
requestTypeId: requestTypeId.selectedId,
vehicleTypeId: vehicleTypeId.selectedId,
@ -802,6 +802,7 @@ class RequestsVM extends BaseVM {
);
Utils.hideLoading(context);
if (respModel.messageStatus == 1) {
log("requestTypeId.selectedId.toRequestTypeEnum(): ${requestTypeId.selectedId.toRequestTypeEnum()}");
Utils.showToast(LocaleKeys.requestSuccessfullyCreated.tr());
Navigator.pop(context);
await applyFilterOnRequestsVM(requestsTypeEnum: requestTypeId.selectedId.toRequestTypeEnum());
@ -809,11 +810,11 @@ class RequestsVM extends BaseVM {
} else {
Utils.showToast(respModel.message.toString());
}
// } catch (e, s) {
// Utils.hideLoading(context);
// log(e.toString());
// Utils.showToast(e.toString());
// }
} catch (e, s) {
Utils.hideLoading(context);
log(e.toString());
Utils.showToast(e.toString());
}
}
}

@ -275,7 +275,11 @@ class ServiceVM extends BaseVM {
context,
allowMultiple: false,
);
if (files != null && files.any((element) => element.path.split('.').last.toLowerCase() != 'pdf')) {
if (files != null && files.any((element) =>
element.path
.split('.')
.last
.toLowerCase() != 'pdf')) {
Utils.showToast("Only PDF Files are allowed");
return;
}
@ -339,6 +343,67 @@ class ServiceVM extends BaseVM {
return await branchRepo.serviceProviderDocumentsUpdate(data);
}
bool customServiceEnabledStatus = false;
void updateCustomServiceEnabledStatus(bool status) {
customServiceEnabledStatus = status;
notifyListeners();
}
String customServiceTitle = "";
void updateCustomServiceTitle(String title) {
customServiceTitle = title;
}
String customServicePrice = "";
void updateCustomServicePrice(String title) {
customServicePrice = title;
}
String customerServiceTax = "15";
void updateCustomerServiceTax(String title) {
customerServiceTax = title;
}
resetCustomServiceForm() {
customServiceTitle = "";
customServicePrice = "";
customerServiceTax = "15";
}
String customServiceTitleError = "";
String customServicePriceError = "";
String customerServiceTaxError = "";
bool isCustomServiceDetailValidated() {
bool isValidated = true;
if (customServiceTitle.isEmpty) {
customServiceTitleError = GlobalConsts.serviceNameError;
isValidated = false;
notifyListeners();
return isValidated;
} else {
customServiceTitleError = "";
}
if (customServicePrice.isEmpty) {
customServicePriceError = GlobalConsts.priceError;
isValidated = false;
} else {
customServicePriceError = "";
}
if (customerServiceTax.isEmpty) {
customerServiceTaxError = GlobalConsts.taxError;
isValidated = false;
} else {
customerServiceTaxError = "";
}
notifyListeners();
return isValidated;
}
// Create new branch
Future<void> getBranchAndServices() async {
setState(ViewState.busy);
@ -466,10 +531,10 @@ class ServiceVM extends BaseVM {
DropValue(
element.id ?? 0,
((element.categoryName!.isEmpty
? "N/A"
: countryCode == "SA"
? element.categoryNameN
: element.categoryName) ??
? "N/A"
: countryCode == "SA"
? element.categoryNameN
: element.categoryName) ??
"N/A"),
"",
),
@ -527,18 +592,23 @@ class ServiceVM extends BaseVM {
Future<GenericRespModel> createService(List<Map<String, dynamic>> map) async {
setState(ViewState.busy);
return await branchRepo.createService(map);
GenericRespModel response = await branchRepo.createService(map);
setState(ViewState.idle);
return response;
}
Future<GenericRespModel> updateServices(List<Map<String, dynamic>> map) async {
return await branchRepo.updateService(map);
}
Future<List<AppointmentBasicDetailsModel>> getAppointmentsByServiceID({required BuildContext context, required int branchId, required int serviceId}) async {
Future<List<AppointmentBasicDetailsModel>> getAppointmentsByCategoryOrService({required BuildContext context, required int branchId, required int categoryId, required int serviceId}) async {
try {
Utils.showLoading(context);
List<AppointmentBasicDetailsModel> appointmentList = await branchRepo.getAppointmentsByCategoryOrService(branchId: branchId, serviceId: serviceId);
List<AppointmentBasicDetailsModel> appointmentList = await branchRepo.getAppointmentsByCategoryOrService(
branchId: branchId,
serviceId: serviceId,
categoryId: categoryId,
);
// log("list: ${mResponse.}");
Utils.hideLoading(context);
@ -585,7 +655,8 @@ class ServiceVM extends BaseVM {
}
}
Future buildDealNotCompletedBottomSheetOptions({required BuildContext mainContext, required List<AppointmentBasicDetailsModel> appointments, required String branchName}) async {
Future cannotDeactivateServiceOrCategoryBottomSheet(
{required BuildContext mainContext, required List<AppointmentBasicDetailsModel> appointments, required String branchName, required bool isService}) async {
return actionConfirmationBottomSheet(
isOnlyOneButton: true,
context: mainContext,
@ -770,7 +841,9 @@ class ServiceVM extends BaseVM {
File file = File(imageModel.filePath!);
List<int> imageBytes = await file.readAsBytes();
String image = base64Encode(imageBytes);
String fileName = file.path.split('/').last;
String fileName = file.path
.split('/')
.last;
branchPostingImages = BranchPostingImages(
imageName: fileName,
imageStr: image,

@ -1,6 +1,10 @@
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart';
import 'package:mc_common_app/models/setting_utils_models/app_invitation_history_model.dart';
import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart';
import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart';
import 'package:mc_common_app/repositories/setting_options_repo.dart';
@ -74,20 +78,25 @@ class SettingOptionsVM extends BaseVM {
}
}
Future<void> appInvitationCreate({required BuildContext context, required int channelId}) async {
ShareResult result = await Utils.shareAppInvitation();
if (result.status != ShareResultStatus.success) {
return;
}
Future<void> appInvitationCreate({required BuildContext context, required InviteTypeEnum inviteTypeEnum}) async {
Utils.showLoading(context);
try {
await settingOptionsRepo.appInvitationCreate(channelId: channelId);
GenericRespModel respModel = await settingOptionsRepo.appInvitationCreate(channelId: inviteTypeEnum.toIntFromEnum(), message: GlobalConsts.getAppInvitationLink());
await Utils.inviteFriend(inviteTypeEnum: inviteTypeEnum, message: GlobalConsts.getAppInvitationLink());
Utils.hideLoading(context);
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
}
}
List<AppInvitationHistoryModel> appInvitationHistoryList = [];
Future<void> getAppInvitationHistoryList() async {
try {
appInvitationHistoryList = await settingOptionsRepo.getAppInvitationsHistory();
} catch (e) {
logger.i(e.toString());
}
}
}

@ -48,8 +48,8 @@ class ShippingManagementVM extends BaseVM {
shippingRequestFilterOptions.clear();
shippingRequestStatusesList.clear();
for (int i = 0; i < shippingStatusEnums.length; i++) {
shippingRequestFilterOptions.add(FilterListModel(title: shippingStatusEnums[i].enumValueStr, isSelected: false, id: shippingStatusEnums[i].enumValue));
shippingRequestStatusesList.add(FilterListModel(title: shippingStatusEnums[i].enumValueStr, isSelected: false, id: shippingStatusEnums[i].enumValue));
shippingRequestFilterOptions.add(FilterListModel(title: shippingStatusEnums[i].enumValueStrDes, isSelected: false, id: shippingStatusEnums[i].enumValue));
shippingRequestStatusesList.add(FilterListModel(title: shippingStatusEnums[i].enumValueStrDes, isSelected: false, id: shippingStatusEnums[i].enumValue));
}
int index = shippingRequestFilterOptions.indexWhere((element) => element.id == 0);
@ -62,6 +62,7 @@ class ShippingManagementVM extends BaseVM {
}
Future<void> applyFiltersOnShippingRequests({required ShippingRequestStatusEnum shippingRequestStatusEnum}) async {
log("ship: $shippingRequestStatusEnum");
for (var value in shippingRequestFilterOptions) {
value.isSelected = false;
}
@ -71,7 +72,8 @@ class ShippingManagementVM extends BaseVM {
notifyListeners();
return;
}
shippingRequestFilterOptions[shippingRequestStatusEnum.getIdFromShippingStatusEnum() + 1].isSelected = true; // +1 to match with the index 0 index has all requests
int index = shippingRequestFilterOptions.indexWhere((element) => element.id == shippingRequestStatusEnum.getIdFromShippingStatusEnum());
shippingRequestFilterOptions[index].isSelected = true; // +1 to match with the index 0 index has all requests
await getShippingRequestsListByFilters(shippingStatusEnum: shippingRequestStatusEnum);
notifyListeners();
}

@ -65,6 +65,15 @@ class UserVM extends BaseVM {
_loginOtherAccount = value;
}
bool isImagepUploading = false;
bool get getIsImagepUploading => isImagepUploading;
set setIsImagepUploading(bool value) {
isImagepUploading = value;
notifyListeners();
}
Country? userCountries;
Cities? userCities;
@ -198,11 +207,13 @@ class UserVM extends BaseVM {
notifyListeners();
}
Future<void> performCompleteProfile(BuildContext context, {
Future<void> performCompleteProfile(
BuildContext context, {
required String password,
required String confirmPassword,
required String firstName,
required String lastName,
required String companyName,
required String email,
required String? userId,
bool isNeedToPassToken = false,
@ -212,15 +223,7 @@ class UserVM extends BaseVM {
if (Utils.passwordValidateStructure(password)) {
if (password == confirmPassword) {
Utils.showLoading(context);
RegisterUserRespModel user = await userRepo.basicComplete(
userId ?? "",
firstName,
lastName,
email,
password,
cityID,
genderID,
isNeedToPassToken: isNeedToPassToken);
RegisterUserRespModel user = await userRepo.basicComplete(userId ?? "", firstName, lastName, companyName, email, password, cityID, genderID, isNeedToPassToken: isNeedToPassToken);
Utils.hideLoading(context);
if (user.messageStatus == 1) {
Utils.showToast(LocaleKeys.successfullyRegistered.tr());
@ -242,6 +245,7 @@ class UserVM extends BaseVM {
required String password,
required String? firstName,
required String? lastName,
required String? companyName,
required String? email,
required DropValue? city,
required DropValue? gender,
@ -255,6 +259,11 @@ class UserVM extends BaseVM {
Utils.showToast(LocaleKeys.surnameNameMandatory.tr());
//("Surname is mandatory");
isValid = false;
} else if (companyName!.isEmpty) {
if (AppState().currentAppType == AppType.provider) {
Utils.showToast(LocaleKeys.companyNameMandatory.tr());
isValid = false;
}
} else if (email!.isNotEmpty) {
if (!Utils.isEmailValid(email)) {
Utils.showToast(LocaleKeys.enterValidEmail.tr());
@ -395,6 +404,7 @@ class UserVM extends BaseVM {
}
Future<void> performBasicOtpLoginSelectionPage(BuildContext context, {required String userToken, required AppType appType, String? loginType}) async {
AppState().setIsViewOnly = false;
log("loginType: $loginType");
if (loginType == "3" || loginType == "4") {
//Utils.showLoading(context);
@ -582,17 +592,18 @@ class UserVM extends BaseVM {
type == ClassType.NUMBER && countryCode != null
? countryCode + phoneNum
: type == ClassType.NUMBER && countryCode == null
? phoneNum
: phoneNum,
? phoneNum
: phoneNum,
password);
Utils.hideLoading(context);
LoginPasswordRespModel user = LoginPasswordRespModel.fromJson(jsonDecode(response.body));
if (user.messageStatus == 1) {
SharedPrefManager.setPhoneOrEmail(type == ClassType.NUMBER && countryCode != null
? countryCode + phoneNum
: type == ClassType.NUMBER && countryCode == null
? phoneNum
: phoneNum);
? phoneNum
: phoneNum);
SharedPrefManager.setUserPassword(password);
navigateReplaceWithName(context, AppRoutes.loginMethodSelection, arguments: user.data!.userToken);
} else {
@ -683,9 +694,22 @@ class UserVM extends BaseVM {
Future<void> updateUserImage(BuildContext context) async {
File? myPick = await commanServices.pickFile(context, fileType: FileType.image);
if (myPick != null) {
await userRepo.updateUserImage(encodeBase64Image(myPick));
AppState().getUser.data!.userInfo!.userLocalImage = myPick;
notifyListeners();
setIsImagepUploading = true;
ImageResponse image = await userRepo.updateUserImage(encodeBase64Image(myPick));
print(image.message);
print(image.messageStatus);
if (image.messageStatus == 1) {
UserInfo user = AppState().getUser.data!.userInfo!;
user.userLocalImage = myPick;
AppState().setUser = User(data: UserData(userInfo: user));
setIsImagepUploading = false;
notifyListeners();
} else {
Utils.showToast(image.message!);
setIsImagepUploading = false;
notifyListeners();
}
}
notifyListeners();
}
@ -718,13 +742,8 @@ class UserVM extends BaseVM {
}
void changeLanguage(BuildContext context) {
print("${EasyLocalization
.of(context)
?.currentLocale}");
if (EasyLocalization
.of(context)
?.currentLocale
?.countryCode == "SA") {
print("${EasyLocalization.of(context)?.currentLocale}");
if (EasyLocalization.of(context)?.currentLocale?.countryCode == "SA") {
context.setLocale(const Locale("en", "US"));
} else {
context.setLocale(const Locale('ar', 'SA'));
@ -749,13 +768,8 @@ class UserVM extends BaseVM {
AppState().setUser = null;
if (AppState().currentAppType == AppType.provider) {
AppState().setproviderSubscription = null;
context
.read<SubscriptionsVM>()
.mySubscriptionsBySp
.clear();
context
.read<SubscriptionsVM>()
.allSubscriptions = SubscriptionModel();
context.read<SubscriptionsVM>().mySubscriptionsBySp.clear();
context.read<SubscriptionsVM>().allSubscriptions = SubscriptionModel();
}
navigateReplaceWithNameUntilRoute(context, AppRoutes.registerSelection);

@ -1,14 +1,16 @@
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/common_widgets/dotted_rect.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class DottedRoundedCard extends StatelessWidget {
final Function() onTap;
final String text;
final Widget icon;
const DottedRoundedCard({Key? key, required this.onTap, required this.text, required this.icon}) : super(key: key);
const DottedRoundedCard({super.key, required this.onTap, required this.text, required this.icon});
@override
Widget build(BuildContext context) {
@ -26,7 +28,7 @@ class DottedRoundedCard extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
icon,
SizedBox(width: 8),
const SizedBox(width: 8),
text.toText(
fontSize: 15,
isBold: true,
@ -44,33 +46,48 @@ class DottedRectContainer extends StatelessWidget {
final Function() onTap;
final String text;
final Widget icon;
final String extensions;
const DottedRectContainer({Key? key, required this.onTap, required this.text, required this.icon}) : super(key: key);
const DottedRectContainer({super.key, required this.onTap, required this.text, required this.icon, required this.extensions});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
height: 46,
width: double.infinity,
color: MyColors.white,
child: DashedRect(
color: MyColors.lightIconColor,
strokeWidth: 2.0,
gap: 4.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
icon,
SizedBox(width: 8),
text.toText(
fontSize: 15,
isBold: true,
color: MyColors.darkPrimaryColor,
constraints: const BoxConstraints(minHeight: 60),
child: Column(
children: [
DashedRect(
color: MyColors.lightIconColor,
strokeWidth: 2.0,
gap: 4.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
icon,
const SizedBox(width: 8),
text
.toText(
fontSize: 15,
isBold: true,
color: MyColors.darkPrimaryColor,
)
.paddingAll(10),
],
),
],
),
),
8.height,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"( $extensions )".toText(
fontSize: 8,
color: MyColors.lightTextColor,
),
],
)
],
),
),
);

@ -6,6 +6,7 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
@ -22,6 +23,7 @@ class AdDuration extends StatelessWidget {
const AdDuration({super.key});
void onAddSpecialServiceButtonTapped(BuildContext context, AdVM adVM) {
adVM.updateSelectedVehicleAdsSpecialServicesId(SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""));
showModalBottomSheet(
context: context,
isScrollControlled: true,
@ -72,10 +74,7 @@ class AdDuration extends StatelessWidget {
value: adVM.vehicleAdDurationId.selectedOption,
isNeedClickAll: true,
postFixDataColor: MyColors.darkTextColor,
onTap: () async {
final formattedDate = await Utils.pickDateFromDatePicker(context);
adVM.updateSelectionDurationStartDate(formattedDate);
},
onTap: () {},
),
],
// Builder(builder: (context) {

@ -26,10 +26,31 @@ class VehicleDetails extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) {
String roleAppState = "";
String roleName = "";
if (AppState().getUser.data!.userInfo != null) {
roleAppState = AppState().getUser.data!.userInfo!.roleName ?? "";
List list = roleAppState.split('_');
if (list.length > 1) {
roleName = list[1];
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.vehicleDetail.tr().toText(fontSize: 18, isBold: true),
if (AppState().currentAppType == AppType.provider && roleName.isNotEmpty) ...[
8.height,
TxtField(
value: roleName,
isBackgroundEnabled: true,
isNeedClickAll: true,
hint: '',
onTap: () {},
),
],
8.height,
Builder(
builder: (context) {
@ -56,40 +77,32 @@ class VehicleDetails extends StatelessWidget {
).paddingAll(10),
] else ...[
8.height,
if (AppState().currentAppType == AppType.provider) ...[
Builder(builder: (context) {
List<DropValue> vehicleSellerTypesDrop = [];
for (var element in adVM.vehicleSellerTypes) {
log("${element.id} : ${element.sellerType}");
vehicleSellerTypesDrop.add(DropValue(element.id?.toInt() ?? 0, element.sellerType ?? "", ""));
}
DropValue? model;
int index = -1;
if (AppState().userType == UserType.providerDealer) {
index = vehicleSellerTypesDrop.indexWhere((element) => element.id == GlobalConsts.providerDealerRoleTypeId);
} else {
index = vehicleSellerTypesDrop.indexWhere((element) => element.id == GlobalConsts.providerIndividualRoleTypeId);
}
if (index == -1) {
model = vehicleSellerTypesDrop.first;
} else {
model = vehicleSellerTypesDrop[index];
}
adVM.updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: model.id, selectedOption: model.value), needRefresh: false);
return DropdownField(
(DropValue value) => adVM.updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
isSelectAble: false,
list: vehicleSellerTypesDrop,
dropdownValue: adVM.vehicleSellerTypeId.selectedId != -1 ? DropValue(adVM.vehicleSellerTypeId.selectedId, adVM.vehicleSellerTypeId.selectedOption, "") : null,
hint: LocaleKeys.vehicleSellerType.tr(),
errorValue: adVM.vehicleSellerTypeId.errorValue,
);
}),
8.height,
],
// if (AppState().currentAppType == AppType.provider) ...[
// Builder(builder: (context) {
// List<DropValue> vehicleSellerTypesDrop = [];
// for (var element in adVM.vehicleSellerTypes) {
// log("${element.id} : ${element.sellerType}");
// vehicleSellerTypesDrop.add(DropValue(element.id?.toInt() ?? 0, element.sellerType ?? "", ""));
// }
//
// adVM.updateSelectionVehicleSellerTypeId(
// SelectionModel(
// selectedId: AppState().getUser.data!.userInfo!.roleId ?? GlobalConsts.providerIndividualRoleTypeId,
// selectedOption: AppState().getUser.data!.userInfo!.roleName ?? "",
// ),
// needRefresh: false);
//
// return DropdownField(
// (DropValue value) => adVM.updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)),
// isSelectAble: false,
// list: vehicleSellerTypesDrop,
// dropdownValue: adVM.vehicleSellerTypeId.selectedId != -1 ? DropValue(adVM.vehicleSellerTypeId.selectedId, adVM.vehicleSellerTypeId.selectedOption, "") : null,
// hint: LocaleKeys.vehicleSellerType.tr(),
// errorValue: adVM.vehicleSellerTypeId.errorValue,
// );
// }),
// 8.height,
// ],
Builder(builder: (context) {
List<DropValue> vehicleModelsDrop = [];
for (var element in adVM.vehicleModels) {
@ -300,6 +313,7 @@ class VehicleDetails extends StatelessWidget {
onTap: () => context.read<AdVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
extensions: GlobalConsts.allowedFileExtensions,
),
],
if (adVM.vehicleImageError != "") ...[

@ -364,7 +364,9 @@ class _AdsDetailViewState extends State<AdsDetailView> {
endDate.isNotEmpty ? endDate.toText(fontSize: 10) : const SizedBox(),
],
),
(LocaleKeys.extendAd.tr()).toText(fontSize: 12, color: MyColors.darkPrimaryColor, isUnderLine: true).onPress(() async => await onExtendAdPressed()),
if (widget.adDetails.adPostStatus == AdPostStatus.active || widget.adDetails.adPostStatus == AdPostStatus.expired) ...[
(LocaleKeys.extendAd.tr()).toText(fontSize: 12, color: MyColors.darkPrimaryColor, isUnderLine: true).onPress(() async => await onExtendAdPressed()),
],
],
),
],
@ -420,7 +422,7 @@ class _AdsDetailViewState extends State<AdsDetailView> {
if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) ...[
buildDamagePartDetails(),
],
if (!(widget.adDetails.isMyAd ?? false)) ...[
if ((!(widget.adDetails.isMyAd ?? false) && (widget.adDetails.showContactDetail ?? false))) ...[
12.height,
buildPersonalInformationCard(context: context),
],

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
@ -275,8 +276,13 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
void onBookPhotographyServiceClicked(BuildContext context, {required AdDetailsModel adDetailsModel}) async {
AdVM adVM = context.read<AdVM>();
log("adDetailsModel.vehicle!.cityID: ${adDetailsModel.vehicle!.cityID}");
if (adVM.photoOfficeSelectedId.selectedId == -1) {
adVM.getPhotographyServiceScheduleListByOffices(latitude: AppState().currentLocation.latitude, longitude: AppState().currentLocation.longitude, isNeedToRebuild: true);
adVM.getPhotographyServiceScheduleListByOffices(
cityId: adDetailsModel.vehicle!.cityID ?? AppState().getUser.data!.userInfo!.cityId ?? 0,
latitude: AppState().currentLocation.latitude,
longitude: AppState().currentLocation.longitude,
isNeedToRebuild: true);
}
return showModalBottomSheet(
@ -302,6 +308,8 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
var element = adVM.photoSSSchedulesByOffices[i];
vehicleCitiesDrop.add(DropValue(element.photoOfficeID?.toInt() ?? 0, element.photoOfficeName ?? "", i.toString()));
}
print(adVM.photoOfficeSelectedId.selectedId);
return DropdownField(
(DropValue value) => adVM.updatePhotoOfficeSelectedId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: vehicleCitiesDrop,
@ -345,7 +353,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
title: LocaleKeys.bookAndPay.tr(),
fontSize: 15,
onPressed: () {
adVM.onAdSSBookAppointmentPressed(context, adDetailsModel: adDetailsModel, adsSpecialServiceID: 1); //1 for photography Service
adVM.onAdSSBookAppointmentPressed(context, adDetailsModel: adDetailsModel);
},
),
),
@ -716,7 +724,11 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () {
adVM.deactivateTheAd(context, adId: adDetailsModel.id!, comment: comments);
if (adVM.selectedDeActivateAdCommentModel.index == 0) {
adVM.markAdAsSold(context, adId: adDetailsModel.id!);
} else {
adVM.deactivateTheAd(context, adId: adDetailsModel.id!, comment: comments);
}
},
),
),

@ -1,4 +1,7 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
@ -10,6 +13,7 @@ import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/time_slots.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
import 'package:mc_common_app/widgets/empty_widget.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';
@ -177,170 +181,186 @@ class BottomSheetAdSpecialServiceContent extends StatelessWidget {
builder: (BuildContext context, AdVM adVM, Widget? child) {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
child: Column(
children: [
Container(
margin: const EdgeInsets.all(8),
height: 8,
width: 60,
decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))),
),
12.height,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
child: adVM.vehicleAdsSpecialServices.isEmpty
? Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.addSpecialService.tr().toText(fontSize: 24, isBold: true),
8.height,
Builder(
builder: (context) {
List<DropValue> vehicleAdsSpecialServices = [];
for (var element in adVM.vehicleAdsSpecialServices) {
if (!element.isSelected!) {
vehicleAdsSpecialServices.add(DropValue(element.id?.toInt() ?? 0, element.name ?? "", element.price == null ? "" : element.price!.toInt().toString()));
}
}
return DropdownField(
(DropValue value) => adVM.updateSelectedVehicleAdsSpecialServicesId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: vehicleAdsSpecialServices,
hint: LocaleKeys.selectService.tr(),
dropdownValue: adVM.selectedVehicleAdsSpecialServicesId.selectedId != -1 ? DropValue(adVM.selectedVehicleAdsSpecialServicesId.selectedId, adVM.selectedVehicleAdsSpecialServicesId.selectedOption, "") : null,
);
},
Container(
margin: const EdgeInsets.all(8),
height: 8,
width: 60,
decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))),
),
if (adVM.selectedVehicleAdsSpecialServicesId.selectedId != -1) ...[
if (adVM.isFetchingLists) ...[
Center(child: const CircularProgressIndicator().paddingAll(20)),
] else ...[
8.height,
if ((adVM.selectedVehicleAdsSpecialServicesId.selectedId == 1 && adVM.ssPhotoScheduleModel != null)) ...[
// Builder(
// builder: (context) {
// List<DropValue> vehicleAdsSpecialServiceDates = adVM.populateScheduleDatesForAdPhotoService();
//
// return DropdownField(
// (DropValue value) => adVM.updateVehicleVehicleAdsPhotoServiceDate(SelectionModel(
// selectedId: value.id,
// selectedOption: value.value,
// )),
// list: vehicleAdsSpecialServiceDates,
// hint: "Select Date",
// dropdownValue: adVM.vehicleAdsPhotoServiceDate.selectedId != -1
// ? DropValue(
// adVM.vehicleAdsPhotoServiceDate.selectedId,
// adVM.vehicleAdsPhotoServiceDate.selectedOption,
// "",
// )
// : null,
// );
// },
// ),
// 22.height,
] else if ((adVM.selectedVehicleAdsSpecialServicesId.selectedId == 3 && adVM.ssCarCheckScheduleModel != null)) ...[
// Builder(
// builder: (context) {
// List<DropValue> vehicleAdsSpecialServiceDates = adVM.populateScheduleDatesForAdCarCheckService();
//
// return DropdownField(
// (DropValue value) => adVM.updateVehicleAdsCarCheckServicesDate(SelectionModel(
// selectedId: value.id,
// selectedOption: value.value,
// )),
// list: vehicleAdsSpecialServiceDates,
// hint: "Select Date",
// dropdownValue: adVM.vehicleAdsCarCheckServicesDate.selectedId != -1
// ? DropValue(
// adVM.vehicleAdsCarCheckServicesDate.selectedId,
// adVM.vehicleAdsCarCheckServicesDate.selectedOption,
// "",
// )
// : null,
// );
// },
// ),
// 22.height,
],
if (adVM.selectedVehicleAdsSpecialServicesId.selectedId != 3 && adVM.selectedVehicleAdsSpecialServicesId.selectedId != 1) ...[
descriptionCard(
description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.selectedVehicleAdsSpecialServicesId.selectedId).description ?? "",
),
],
if (adVM.adSSTimeSlots.isNotEmpty) ...[
LocaleKeys.availableSlots.tr().toText(fontSize: 15, isBold: true),
Center(child: LocaleKeys.noSpecialServicesAvailable.tr().toText(fontSize: 18, color: MyColors.lightTextColor, fontWeight: MyFonts.Medium)).paddingAll(30),
const SizedBox(),
],
)
: Column(
children: [
Container(
margin: const EdgeInsets.all(8),
height: 8,
width: 60,
decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))),
),
12.height,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.addSpecialService.tr().toText(fontSize: 24, isBold: true),
8.height,
BuildTimeSlots(
timeSlots: adVM.adSSTimeSlots,
onPressed: (index) => adVM.updateIsSelectedInSlots(index),
Builder(
builder: (context) {
List<DropValue> vehicleAdsSpecialServices = [];
for (var element in adVM.vehicleAdsSpecialServices) {
if (!element.isSelected!) {
vehicleAdsSpecialServices.add(DropValue(element.id?.toInt() ?? 0, element.name ?? "", element.price == null ? "" : element.price!.toInt().toString()));
}
}
return DropdownField(
(DropValue value) => adVM.updateSelectedVehicleAdsSpecialServicesId(SelectionModel(selectedId: value.id, selectedOption: value.value, itemPrice: value.subValue)),
list: vehicleAdsSpecialServices,
hint: LocaleKeys.selectService.tr(),
dropdownValue: adVM.selectedVehicleAdsSpecialServicesId.selectedId != -1
? DropValue(adVM.selectedVehicleAdsSpecialServicesId.selectedId, adVM.selectedVehicleAdsSpecialServicesId.selectedOption, "")
: null,
);
},
),
],
22.height,
LocaleKeys.serviceAmount.tr().toText(fontSize: 16, isBold: true, color: MyColors.lightTextColor),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
adVM.selectedVehicleAdsSpecialServicesId.itemPrice.toText(fontSize: 20, isBold: true),
SizedBox(width: 1.w),
LocaleKeys.sar.tr().toText(fontSize: 12, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 2),
if (adVM.selectedVehicleAdsSpecialServicesId.selectedId != -1) ...[
if (adVM.isFetchingLists) ...[
Center(child: const CircularProgressIndicator().paddingAll(20)),
] else ...[
8.height,
if ((adVM.selectedVehicleAdsSpecialServicesId.selectedId == 1 && adVM.ssPhotoScheduleModel != null)) ...[
// Builder(
// builder: (context) {
// List<DropValue> vehicleAdsSpecialServiceDates = adVM.populateScheduleDatesForAdPhotoService();
//
// return DropdownField(
// (DropValue value) => adVM.updateVehicleVehicleAdsPhotoServiceDate(SelectionModel(
// selectedId: value.id,
// selectedOption: value.value,
// )),
// list: vehicleAdsSpecialServiceDates,
// hint: "Select Date",
// dropdownValue: adVM.vehicleAdsPhotoServiceDate.selectedId != -1
// ? DropValue(
// adVM.vehicleAdsPhotoServiceDate.selectedId,
// adVM.vehicleAdsPhotoServiceDate.selectedOption,
// "",
// )
// : null,
// );
// },
// ),
// 22.height,
] else if ((adVM.selectedVehicleAdsSpecialServicesId.selectedId == 3 && adVM.ssCarCheckScheduleModel != null)) ...[
// Builder(
// builder: (context) {
// List<DropValue> vehicleAdsSpecialServiceDates = adVM.populateScheduleDatesForAdCarCheckService();
//
// return DropdownField(
// (DropValue value) => adVM.updateVehicleAdsCarCheckServicesDate(SelectionModel(
// selectedId: value.id,
// selectedOption: value.value,
// )),
// list: vehicleAdsSpecialServiceDates,
// hint: "Select Date",
// dropdownValue: adVM.vehicleAdsCarCheckServicesDate.selectedId != -1
// ? DropValue(
// adVM.vehicleAdsCarCheckServicesDate.selectedId,
// adVM.vehicleAdsCarCheckServicesDate.selectedOption,
// "",
// )
// : null,
// );
// },
// ),
// 22.height,
],
if (adVM.selectedVehicleAdsSpecialServicesId.selectedId != 3 && adVM.selectedVehicleAdsSpecialServicesId.selectedId != 1) ...[
descriptionCard(
description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.selectedVehicleAdsSpecialServicesId.selectedId).description ?? "",
),
],
if (adVM.adSSTimeSlots.isNotEmpty) ...[
LocaleKeys.availableSlots.tr().toText(fontSize: 15, isBold: true),
8.height,
BuildTimeSlots(
timeSlots: adVM.adSSTimeSlots,
onPressed: (index) => adVM.updateIsSelectedInSlots(index),
),
],
22.height,
LocaleKeys.serviceAmount.tr().toText(fontSize: 16, isBold: true, color: MyColors.lightTextColor),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
adVM.selectedVehicleAdsSpecialServicesId.itemPrice.toText(fontSize: 20, isBold: true),
SizedBox(width: 1.w),
LocaleKeys.sar.tr().toText(fontSize: 12, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 2),
],
),
],
],
),
],
],
],
),
),
SizedBox(
width: double.infinity,
child: ShowFillButton(
backgroundColor: !isButtonTappable(adVM) ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.darkPrimaryColor,
title: LocaleKeys.addService.tr(),
onPressed: () {
if (!isButtonTappable(adVM)) {
return;
}
Navigator.pop(context);
adVM.addNewSpecialServiceCard(
specialServiceCard: SpecialServiceCard(
serviceSelectedId: adVM.selectedVehicleAdsSpecialServicesId,
description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.selectedVehicleAdsSpecialServicesId.selectedId).description ?? "",
duration: "",
serviceDate: "",
serviceDateError: "",
serviceTimeError: "",
address: "",
serviceTime: "",
],
),
);
),
SizedBox(
width: double.infinity,
child: ShowFillButton(
backgroundColor: !isButtonTappable(adVM) ? MyColors.lightTextColor.withOpacity(0.6) : MyColors.darkPrimaryColor,
title: LocaleKeys.addService.tr(),
onPressed: () {
if (!isButtonTappable(adVM)) {
return;
}
Navigator.pop(context);
// adVM.addNewSpecialServiceCard(
// specialServiceCard: SpecialServiceCard(
// serviceDate: adVM.vehicleAdsSpecialServicesId.selectedId == 1
// ? adVM.vehicleAdsPhotoServiceDate.selectedOption
// : adVM.vehicleAdsSpecialServicesId.selectedId == 3
// ? adVM.vehicleAdsCarCheckServicesDate.selectedOption
// : "",
// serviceDateError: "",
// serviceSelectedId: adVM.vehicleAdsSpecialServicesId,
// serviceTimeError: "",
// description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.vehicleAdsSpecialServicesId.selectedId).description ?? "",
// duration: adVM.vehicleAdsSpecialServicesId.selectedId == 1
// ? adVM.ssPhotoScheduleModel!.distanceKM.toString()
// : adVM.vehicleAdsSpecialServicesId.selectedId == 3
// ? adVM.ssCarCheckScheduleModel!.distanceKM.toString()
// : "",
// address: adVM.vehicleAdsSpecialServicesId.selectedId == 1
// ? "${adVM.ssPhotoScheduleModel!.photoOfficeName} - ${adVM.ssPhotoScheduleModel!.areaName}"
// : adVM.vehicleAdsSpecialServicesId.selectedId == 3
// ? adVM.ssCarCheckScheduleModel!.address
// : "",
// serviceTime: adVM.slotSelectedIndex == null || adVM.adSSTimeSlots.isEmpty ? "" : adVM.adSSTimeSlots[adVM.slotSelectedIndex!].slot,
// ),
// );
},
).paddingOnly(bottom: 10),
),
],
).horPaddingMain(),
adVM.addNewSpecialServiceCard(
specialServiceCard: SpecialServiceCard(
serviceSelectedId: adVM.selectedVehicleAdsSpecialServicesId,
description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.selectedVehicleAdsSpecialServicesId.selectedId).description ?? "",
duration: "",
serviceDate: "",
serviceDateError: "",
serviceTimeError: "",
address: "",
serviceTime: "",
),
);
// adVM.addNewSpecialServiceCard(
// specialServiceCard: SpecialServiceCard(
// serviceDate: adVM.vehicleAdsSpecialServicesId.selectedId == 1
// ? adVM.vehicleAdsPhotoServiceDate.selectedOption
// : adVM.vehicleAdsSpecialServicesId.selectedId == 3
// ? adVM.vehicleAdsCarCheckServicesDate.selectedOption
// : "",
// serviceDateError: "",
// serviceSelectedId: adVM.vehicleAdsSpecialServicesId,
// serviceTimeError: "",
// description: adVM.vehicleAdsSpecialServices.firstWhere((element) => element.id == adVM.vehicleAdsSpecialServicesId.selectedId).description ?? "",
// duration: adVM.vehicleAdsSpecialServicesId.selectedId == 1
// ? adVM.ssPhotoScheduleModel!.distanceKM.toString()
// : adVM.vehicleAdsSpecialServicesId.selectedId == 3
// ? adVM.ssCarCheckScheduleModel!.distanceKM.toString()
// : "",
// address: adVM.vehicleAdsSpecialServicesId.selectedId == 1
// ? "${adVM.ssPhotoScheduleModel!.photoOfficeName} - ${adVM.ssPhotoScheduleModel!.areaName}"
// : adVM.vehicleAdsSpecialServicesId.selectedId == 3
// ? adVM.ssCarCheckScheduleModel!.address
// : "",
// serviceTime: adVM.slotSelectedIndex == null || adVM.adSSTimeSlots.isEmpty ? "" : adVM.adSSTimeSlots[adVM.slotSelectedIndex!].slot,
// ),
// );
},
).paddingOnly(bottom: 10),
),
],
).horPaddingMain(),
);
},
);

@ -35,19 +35,26 @@ class _AdDamagePartPicturesSheetState extends State<AdDamagePartPicturesSheet> {
}
void populateDamagePartPictures() {
for (var element in widget.adDamageReportList) {
ImageModel imageModel = ImageModel(id: element.id!, filePath: element.imageUrl!, isFromNetwork: true);
List<dynamic> adDamageReportList = widget.adDamageReportList; // Assuming this is your data source.
Map<String, VehicleDamageCard> groupedDamageCards = {};
VehicleDamageCard vehicleDamageCard = VehicleDamageCard(
partSelectedId: SelectionModel(
selectedId: element.vehicleDamagePartID!,
selectedOption: element.partName ?? "",
),
damagePartDescription: element.comment ?? "",
partImages: [imageModel],
);
vehicleDamageCards.add(vehicleDamageCard);
for (var element in adDamageReportList) {
String partName = element.partName ?? "Unknown";
ImageModel imageModel = ImageModel(id: element.id!, filePath: element.imageUrl!, isFromNetwork: true);
if (groupedDamageCards.containsKey(partName)) {
groupedDamageCards[partName]!.partImages!.add(imageModel);
} else {
groupedDamageCards[partName] = VehicleDamageCard(
partSelectedId: SelectionModel(
selectedId: element.vehicleDamagePartID!,
selectedOption: partName,
),
damagePartDescription: element.comment ?? "",
partImages: [imageModel]);
}
}
vehicleDamageCards = groupedDamageCards.values.toList();
setState(() {});
}
@ -56,7 +63,6 @@ class _AdDamagePartPicturesSheetState extends State<AdDamagePartPicturesSheet> {
return SizedBox(
height: MediaQuery.of(context).size.height / 1.2,
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: vehicleDamageCards.length,
itemBuilder: (BuildContext context, int index) {

@ -64,7 +64,9 @@ class AdsListWidget extends StatelessWidget {
adDetails: adsList[index],
isAdsFragment: isAdsFragment,
shouldShowAdStatus: shouldShowAdStatus,
).onPress(() => navigateWithName(context, AppRoutes.adsDetailView, arguments: adsList[index]));
).onPress(() => navigateWithName(context, AppRoutes.adsDetailView, arguments: adsList[index])).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
});
},
separatorBuilder: (BuildContext context, int index) {
return 12.height;

@ -80,7 +80,7 @@ class SelectAdTypeView extends StatelessWidget {
if (adVM.isAdEditEnabled) {
adVM.autoFillSelectedVehicleAdsDetails();
}
if (AppState().currentAppType == AppType.provider && AppState().userType == UserType.providerDealer) {
if (AppState().currentAppType == AppType.provider) {
if (isFromExtendAd && !adVM.isAdEditEnabled) {
bool status = await adVM.createAdExtensionOrder(context, adId: adsId, adsDurationId: adVM.vehicleAdDurationId.selectedId);
log("hereStatus: $status");

@ -293,11 +293,10 @@ class _AppointmentsFilterViewState extends State<AppointmentsFilterView> {
title: LocaleKeys.search.tr(),
onPressed: () {
Navigator.pop(context);
if (AppState().currentAppType == AppType.provider) {
appointmentsVM.getAppointmentsBasedOnFiltersForProviders(branchID: appointmentsVM.selectedBranchIdForAppointments);
appointmentsVM.applyFilterOnAppointmentsVMForProviders(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
} else {
appointmentsVM.getAppointmentsBasedOnFiltersForCustomer();
appointmentsVM.applyFilterOnAppointmentsVMForCustomers(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
}
},
backgroundColor: MyColors.darkPrimaryColor,

@ -5,9 +5,11 @@ import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
@ -53,6 +55,7 @@ class BookAppointmentSchedulesView extends StatelessWidget {
return ExpansionTile(
tilePadding: const EdgeInsets.symmetric(horizontal: 21, vertical: 10),
childrenPadding: const EdgeInsets.only(left: 16, bottom: 10, right: 16),
initiallyExpanded: true,
title: Column(
children: [
Row(
@ -120,6 +123,7 @@ class BookAppointmentSchedulesView extends StatelessWidget {
.customTimeDateSlotList![appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!].availableSlots ??
[],
onPressed: (slotIndex) {
log("slotIndex: $slotIndex");
appointmentsVM.updateSelectedAppointmentSlotByDate(scheduleIndex: scheduleIndex, slotIndex: slotIndex);
},
),
@ -151,10 +155,26 @@ class BookAppointmentSchedulesView extends StatelessWidget {
if (screenArgumentsForAppointmentDetailPage.routeFlag == 1) {
appointmentsVM.onReviewButtonPressed(context);
} else {
int selectedSlotId = 0;
int index = 0;
List<TimeSlotModel> timeSlots = appointmentsVM.serviceAppointmentScheduleList.first.selectedCustomTimeDateSlotModel!.availableSlots ?? [];
if (timeSlots.isNotEmpty) {
index = timeSlots.indexWhere((element) => element.isSelected);
}
if (index != -1) {
selectedSlotId = timeSlots[index].slotId;
} else {
Utils.showToast("Please select correct date and time.");
return;
}
log("selectedSlotId: $selectedSlotId");
appointmentsVM.onRescheduleAppointmentConfirmPressed(
context: context,
appointmentId: screenArgumentsForAppointmentDetailPage.appointmentId,
selectedSlotId: appointmentsVM.serviceAppointmentScheduleList.first.selectedCustomTimeDateSlotModel!.date!.slotId,
selectedSlotId: selectedSlotId,
);
}
},

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
@ -6,6 +8,7 @@ import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_contact_info_model.dart';
import 'package:mc_common_app/models/services_models/item_model.dart';
import 'package:mc_common_app/models/services_models/service_model.dart';
import 'package:mc_common_app/theme/colors.dart';
@ -372,6 +375,7 @@ class ReviewAppointment extends StatelessWidget {
@override
Widget build(BuildContext context) {
log("state: ${AppState().getUser.data!.userInfo!.customerId}");
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.reviewAppointment.tr(),
@ -380,8 +384,12 @@ class ReviewAppointment extends StatelessWidget {
onBackButtonTapped: () => Navigator.pop(context),
actions: [
IconButton(
onPressed: () {
// Utils.buildProviderContactInfoBottomSheet(context: context, email:, mobileNo:);
onPressed: () async {
final appointmentsVM = context.read<AppointmentsVM>();
ProviderContactInfoModel? contact = await appointmentsVM.getProvidersContactInfo(providerId: appointmentsVM.selectedBranchModel!.serviceProviderId ?? 0, context: context);
if (contact != null) {
Utils.buildProviderContactInfoBottomSheet(context: context, email: contact.email, mobileNo: contact.mobile);
}
},
icon: const Icon(Icons.help_outline_outlined).paddingOnly(right: 21),
),

@ -175,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(
@ -197,6 +197,7 @@ class _ChatViewState extends State<ChatView> {
if (chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestType.toRequestTypeEnum() == RequestsTypeEnum.serviceRequest &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress &&
requestVM.currentSelectedOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted &&
AppState().currentAppType == AppType.customer) ...[
Expanded(
child: ShowFillButton(
@ -232,90 +233,88 @@ 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
);
},
),
),
],
if (chatVM.pickedImagesForMessage.isNotEmpty) ...[
Expanded(
flex: 8,
child: PickedFilesContainer(
pickedFiles: chatVM.pickedImagesForMessage,
onCrossPressedPrimary: chatVM.removeImageFromList,
onAddFilePressed: () => chatVM.pickMultipleImages(),
),
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 (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),
),
),
],
if (chatVM.pickedImagesForMessage.isNotEmpty) ...[
Expanded(
flex: 8,
child: PickedFilesContainer(
pickedFiles: chatVM.pickedImagesForMessage,
onCrossPressedPrimary: chatVM.removeImageFromList,
onAddFilePressed: () => chatVM.pickMultipleImages(),
),
],
),
] else if (chatTypeEnum == ChatTypeEnum.requestOffer) ...[
Expanded(
flex: 1,
child: const Icon(Icons.send_rounded, color: MyColors.darkPrimaryColor, size: 30).onPress(
() async {
ChatMessageTypeEnum chatMessageTypeEnum = ChatMessageTypeEnum.freeText;
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;
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)
],

@ -12,6 +12,7 @@ import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
@ -739,7 +740,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
requestVM.updateOfferPrice((offer.price ?? "").toString());
requestVM.updateServiceItem((offer.serviceItemName ?? "").toString());
requestVM.updateItemManufacturer((offer.manufacturedByName ?? "").toString());
requestVM.updateServiceItemCreatedOn((offer.manufacturedOn ?? "").toString());
requestVM.updateServiceItemCreatedOn(DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(offer.manufacturedOn.toString() ?? ""))));
requestVM.updateOfferDescription((widget.chatMessageModel.chatText ?? "").toString());
requestVM.updateIsDeliveryAvailableStatus((offer.isDeliveryAvailable ?? false));
if (offer.reqOfferImages != null && offer.reqOfferImages!.isNotEmpty) {

@ -18,7 +18,7 @@ import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/views/advertisement/components/ads_list_widget.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:mc_common_app/widgets/common_widgets/filters_list.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
@ -58,7 +58,9 @@ class AdsFragment extends StatelessWidget {
navigateWithName(context, AppRoutes.adsFilterView);
})
],
),
).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
}),
body: SizedBox(
width: double.infinity,
height: double.infinity,
@ -100,7 +102,9 @@ class AdsFragment extends StatelessWidget {
onPressed: () {
adVM.updateIsExploreAds(false);
},
),
).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
}),
),
],
).horPaddingMain(),
@ -108,6 +112,7 @@ class AdsFragment extends StatelessWidget {
if (adVM.adsFiltersCounter == 0) ...[
16.height,
FiltersList(
showImages: true,
filterList: adVM.exploreAdsFilterOptions,
onFilterTapped: (index, selectedFilterId) {
adVM.applyFilterOnExploreAds(vehicleBrandId: selectedFilterId);
@ -189,7 +194,9 @@ class AdsFragment extends StatelessWidget {
},
backgroundColor: MyColors.darkPrimaryColor,
child: const Icon(Icons.add, color: MyColors.white),
),
).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
}),
);
},
);

@ -15,7 +15,7 @@ import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/views/requests/widget/request_item.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:mc_common_app/widgets/common_widgets/filters_list.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
@ -94,7 +94,9 @@ class MyRequestsFragment extends StatelessWidget {
navigateWithName(context, AppRoutes.requestsFilterView);
})
],
),
).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
}),
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
@ -175,7 +177,9 @@ class MyRequestsFragment extends StatelessWidget {
requestsVM.notifyListeners();
navigateWithName(context, AppRoutes.offersListPage, arguments: request.id);
}
}),
}).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
}),
);
},
separatorBuilder: (context, index) {
@ -196,7 +200,9 @@ class MyRequestsFragment extends StatelessWidget {
Icons.add,
color: MyColors.white,
),
)
).toViewOnly(context, onTap: () {
navigateWithName(context, AppRoutes.loginWithPassword, arguments: false);
})
: null,
);
});

@ -48,9 +48,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
String freeTrialName = "";
if (AppState().currentAppType == AppType.provider && AppState().getproviderSubscription.isNotEmpty) {
mySubscription = AppState().getproviderSubscription.first;
log("mySubscription!.dateStart.toString(): ${mySubscription!.dateStart}");
log("mySubscription!.dateEnd.toString(): ${mySubscription!.dateEnd}");
if (mySubscription!.id == 1) {
freeTrialName = mySubscription!.name ?? "";
} else {
@ -176,32 +173,40 @@ class _ProfileScreenState extends State<ProfileScreen> {
height: 100,
alignment: Alignment.centerLeft,
child: ClipOval(
child: AppState().getUser.data!.userInfo!.userLocalImage != null
? Image.file(
AppState().getUser.data!.userInfo!.userLocalImage!,
child: model.isImagepUploading
? SizedBox(
width: 100,
height: 100,
fit: BoxFit.fill,
child: Center(
child: CircularProgressIndicator(),
).toCircle(borderRadius: 100),
)
: CachedNetworkImage(
imageUrl: "${AppState().getUser.data!.userInfo!.userImageUrl}",
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
: AppState().getUser.data!.userInfo!.userLocalImage != null
? Image.file(
AppState().getUser.data!.userInfo!.userLocalImage!,
width: 100,
height: 100,
fit: BoxFit.fill,
)
: CachedNetworkImage(
imageUrl: "${AppState().getUser.data!.userInfo!.userImageUrl}",
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(Icons.error),
width: 100,
height: 100,
fit: BoxFit.fill,
fadeInCurve: Curves.easeIn,
fadeInDuration: const Duration(milliseconds: 1000),
useOldImageOnUrlChange: false,
),
),
placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(Icons.error),
width: 100,
height: 100,
fit: BoxFit.fill,
fadeInCurve: Curves.easeIn,
fadeInDuration: const Duration(milliseconds: 1000),
useOldImageOnUrlChange: false,
),
),
).horPaddingMain(),
),

@ -239,6 +239,7 @@ class CreateRequestPage extends StatelessWidget {
onTap: () => context.read<RequestsVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
extensions: GlobalConsts.allowedFileExtensions,
),
],
if (requestsVM.vehicleImageError != "") ...[

@ -2,6 +2,7 @@ import 'dart:developer';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
@ -9,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/chat_models/chat_message_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
@ -66,20 +68,27 @@ Future buildSendOfferBottomSheet({
if (requestDetail.requestType == RequestsTypeEnum.serviceRequest.getIdFromRequestTypeEnum()) ...[
12.height,
TxtField(
value: requestsVM.itemManufacturer,
onTap: () {},
isNeedClickAll: true,
isButtonEnable: false,
isBackgroundEnabled: true,
value: "${AppState().getUser.data!.userInfo!.firstName} ${AppState().getUser.data!.userInfo!.lastName}",
errorValue: requestsVM.offerPriceError,
hint: LocaleKeys.createdBy.tr(),
onChanged: (v) => requestsVM.updateItemManufacturer(v),
),
12.height,
TxtField(
isButtonEnable: false,
isBackgroundEnabled: true,
errorValue: "",
hint: LocaleKeys.createdOn.tr(),
value: requestsVM.serviceItemCreatedOn,
value: DateHelper.formatAsYearMonthDay(DateTime.now()),
isNeedClickAll: true,
postfixData: Icons.calendar_month_rounded,
postFixDataColor: MyColors.darkTextColor,
onTap: () async {
return;
final formattedDate = await Utils.pickDateFromDatePicker(context, firstDate: DateTime(2020), lastDate: DateTime.now());
requestsVM.updateServiceItemCreatedOn(formattedDate);
},
@ -126,6 +135,7 @@ Future buildSendOfferBottomSheet({
onTap: () => context.read<RequestsVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
extensions: GlobalConsts.allowedFileExtensions,
),
],
if (requestsVM.pickedVehicleImages.isNotEmpty) ...[
@ -145,6 +155,10 @@ Future buildSendOfferBottomSheet({
title: offerId == null ? LocaleKeys.submit.tr() : LocaleKeys.update.tr(),
maxHeight: 55,
onPressed: () {
requestsVM.updateItemManufacturer(
"${AppState().getUser.data!.userInfo!.firstName} ${AppState().getUser.data!.userInfo!.lastName}",
);
requestsVM.updateServiceItemCreatedOn(DateHelper.formatAsYearMonthDay(DateTime.now()));
if (offerId == null) {
requestsVM.onSendOfferPressed(
context: context,

@ -287,6 +287,7 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
IconButton(
onPressed: () {
final requestVM = context.read<RequestsVM>();
Utils.buildProviderContactInfoBottomSheet(
context: context,
email: requestVM.currentSelectedOffer!.email,

@ -17,7 +17,7 @@ import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/views/requests/widget/request_item.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:mc_common_app/widgets/common_widgets/filters_list.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';

@ -235,6 +235,7 @@ class _ProviderLicensePageState extends State<ProviderLicensePage> {
onTap: () => serviceVM.pickPdfReceiptFile(context, document.documentId!, index) ?? "",
text: LocaleKeys.attachPDF.tr(),
icon: MyAssets.attachmentIcon.buildSvg(),
extensions: GlobalConsts.allowedFileExtensionsPDF,
),
]
],

@ -5,6 +5,7 @@ import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/setting_utils_models/app_invitation_history_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';
@ -25,52 +26,82 @@ class SettingOptionsInviteFriends extends StatelessWidget {
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return InfoBottomSheet(
title: LocaleKeys.inviteFriends.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomSettingOptionsTile(
leadingWidget: MyAssets.whatsAppIcon.buildSvg(
height: 20,
width: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.inviteFriendsByWhatsApp.tr(),
needBorderBelow: true,
showTrailingArrow: false,
onTap: () async {
await context.read<SettingOptionsVM>().appInvitationCreate(context: context, channelId: 1);
},
),
CustomSettingOptionsTile(
leadingWidget: const Icon(
Icons.sms_outlined,
size: 20,
color: MyColors.greyColor,
return Consumer(
builder: (BuildContext context, SettingOptionsVM settingsOptionsVM, Widget? child) {
List<AppInvitationHistoryModel> list = settingsOptionsVM.appInvitationHistoryList;
int whatsAppInvites = 0;
int smsAppInvites = 0;
int emailAppInvites = 0;
for (var invite in list) {
switch (invite.appInvitationEnumID) {
case 1: // Assume 1 is WhatsApp channel
whatsAppInvites += invite.noOfInvites ?? 0;
break;
case 2: // Assume 2 is SMS channel
smsAppInvites += invite.noOfInvites ?? 0;
break;
case 3: // Assume 3 is Email channel
emailAppInvites += invite.noOfInvites ?? 0;
break;
default:
break;
}
}
return InfoBottomSheet(
title: LocaleKeys.inviteFriends.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomSettingOptionsTile(
leadingWidget: MyAssets.whatsAppIcon.buildSvg(
height: 20,
width: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.inviteFriendsByWhatsApp.tr(),
subTitle: " ${LocaleKeys.noOfInvites.tr()}: $whatsAppInvites",
subtitleTextColor: MyColors.lightTextColor,
needBorderBelow: true,
showTrailingArrow: false,
onTap: () async {
await settingsOptionsVM.appInvitationCreate(context: context, inviteTypeEnum: InviteTypeEnum.whatsapp);
},
),
titleText: LocaleKeys.inviteFriendsBySMS.tr(),
needBorderBelow: true,
showTrailingArrow: false,
onTap: () async {
await context.read<SettingOptionsVM>().appInvitationCreate(context: context, channelId: 2);
}),
CustomSettingOptionsTile(
leadingWidget: const Icon(
Icons.email_outlined,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.inviteFriendsByEmail.tr(),
needBorderBelow: false,
showTrailingArrow: false,
onTap: () async {
await context.read<SettingOptionsVM>().appInvitationCreate(context: context, channelId: 3);
},
CustomSettingOptionsTile(
leadingWidget: const Icon(
Icons.sms_outlined,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.inviteFriendsBySMS.tr(),
subTitle: " ${LocaleKeys.noOfInvites.tr()}: $smsAppInvites",
subtitleTextColor: MyColors.lightTextColor,
needBorderBelow: true,
showTrailingArrow: false,
onTap: () async {
await settingsOptionsVM.appInvitationCreate(context: context, inviteTypeEnum: InviteTypeEnum.sms);
}),
CustomSettingOptionsTile(
leadingWidget: const Icon(
Icons.email_outlined,
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.inviteFriendsByEmail.tr(),
subTitle: " ${LocaleKeys.noOfInvites.tr()}: $emailAppInvites",
subtitleTextColor: MyColors.lightTextColor,
needBorderBelow: false,
showTrailingArrow: false,
onTap: () async {
await settingsOptionsVM.appInvitationCreate(context: context, inviteTypeEnum: InviteTypeEnum.email);
},
),
30.height,
],
),
30.height,
],
),
);
},
);
},
);

@ -12,6 +12,7 @@ import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/setting_options_view_model.dart';
import 'package:mc_common_app/view_models/user_view_model.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
@ -110,11 +111,13 @@ class _SettingOptionsMoreState extends State<SettingOptionsMore> {
),
],
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.settings, size: 20),
titleText: LocaleKeys.settings.tr(),
needBorderBelow: false,
onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends),
),
leadingWidget: const Icon(Icons.settings, size: 20),
titleText: LocaleKeys.settings.tr(),
needBorderBelow: false,
onTap: () {
context.read<SettingOptionsVM>().getAppInvitationHistoryList();
navigateWithName(context, AppRoutes.settingOptionsInviteFriends);
}),
],
).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.all(12), borderRadius: 0),
10.height,

@ -15,7 +15,7 @@ import 'package:mc_common_app/view_models/shipping_management_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:mc_common_app/widgets/common_widgets/filters_list.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
@ -184,8 +184,11 @@ class _ShippingManagementViewState extends State<ShippingManagementView> {
text: Utils.getNameByShippingRequestStatusEnum(shippingRequest.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated),
chipColor: Utils.getChipColorByShippingRequestStatusEnum(shippingRequest.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated),
),
("${shippingRequest.request!.brand} ${shippingRequest.request!.model} | ${shippingRequest.id.toString()}").toText(fontSize: 16),
("${shippingRequest.request!.brand} ${shippingRequest.request!.model} | ${shippingRequest.requestID.toString()}").toText(fontSize: 16),
(shippingRequest.request!.description ?? "").toText(color: MyColors.lightTextColor, fontSize: 12, fontWeight: MyFonts.Medium),
if (shippingRequest.vehicleType != null && shippingRequest.vehicleType!.isNotEmpty) ...[
("${LocaleKeys.itemType.tr()}: " "${shippingRequest.vehicleType}" ?? "").toText(color: MyColors.lightTextColor, fontSize: 12, fontWeight: MyFonts.Medium),
],
if (shippingRequest.comment != null && shippingRequest.comment!.isNotEmpty) ...[
("${LocaleKeys.comment.tr()}: ${shippingRequest.comment ?? ""}").toText(color: MyColors.lightTextColor, fontSize: 12, fontWeight: MyFonts.Medium),
],

@ -6,6 +6,7 @@ import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/user_models/register_user.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/user_view_model.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
@ -27,10 +28,7 @@ class CompleteProfilePage extends StatefulWidget {
}
class _CompleteProfilePageState extends State<CompleteProfilePage> {
String? firstName = "",
lastName = "",
email = "",
confirmPassword = "";
String? firstName = "", lastName = "", email = "", confirmPassword = "", companyName = "";
late String password = "";
bool isChecked = false;
DropValue? city;
@ -41,231 +39,234 @@ class _CompleteProfilePageState extends State<CompleteProfilePage> {
super.initState();
}
@override
Widget build(BuildContext context) {
context.read<UserVM>().getAllCitiesForUser(AppState().getUserRegisterCountrySelection.id);
return Consumer(builder: (BuildContext context, UserVM userVM, Widget? child) {
userVM.getAllCitiesForUser(AppState().getUserRegisterCountrySelection.id);
return Scaffold(
appBar: CustomAppBar(
isRemoveBackButton: widget.user.data!.roleId == 7 ? false : true,
title: widget.user.data!.roleId == 7 ? "" : LocaleKeys.signUp.tr(),
),
body: SizedBox(
width: double.infinity,
height: double.infinity,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
6.height,
LocaleKeys.completeProfile.tr().toText(
height: 23 / 24,
fontSize: 24,
letterSpacing: -1.44,
),
12.height,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: LocaleKeys.profileMsg.tr().toText(
color: MyColors.lightTextColor,
textAlign: TextAlign.center,
fontSize: 14,
appBar: CustomAppBar(
isRemoveBackButton: widget.user.data!.roleId == 7 ? false : true,
title: widget.user.data!.roleId == 7 ? "" : LocaleKeys.signUp.tr(),
),
body: SizedBox(
width: double.infinity,
height: double.infinity,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
6.height,
LocaleKeys.completeProfile.tr().toText(
height: 23 / 24,
letterSpacing: -0.48,
fontSize: 24,
letterSpacing: -1.44,
),
),
12.height,
TxtField(
hint: LocaleKeys.firstName.tr(),
value: firstName,
onChanged: (v) {
firstName = v;
},
),
12.height,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: LocaleKeys.profileMsg.tr().toText(
color: MyColors.lightTextColor,
textAlign: TextAlign.center,
fontSize: 14,
height: 23 / 24,
letterSpacing: -0.48,
),
),
12.height,
TxtField(
hint: LocaleKeys.firstName.tr(),
value: firstName,
onChanged: (v) {
firstName = v;
},
),
12.height,
TxtField(
hint: LocaleKeys.surname.tr(),
value: lastName,
onChanged: (v) {
lastName = v;
},
),
if (AppState().currentAppType == AppType.provider) ...[
12.height,
TxtField(
hint: LocaleKeys.surname.tr(),
value: lastName,
hint: LocaleKeys.companyName.tr(),
value: companyName,
onChanged: (v) {
lastName = v;
companyName = v;
},
),
12.height,
Container(
padding: const EdgeInsets.only(right: 0, left: 0, top: 0, bottom: 0),
child: Builder(builder: (context) {
List<DropValue> userGender = [];
userGender.add(DropValue(1.toInt(), "${LocaleKeys.userMale.tr()}", "", isEnabled: true));
userGender.add(DropValue(2.toInt(), "${LocaleKeys.userFemale.tr()}", "", isEnabled: true));
// for (var element in userVM.userCities!.data!) {
// if (AppState().getUser.data != null) {
// if (AppState().getUser.data!.userInfo!.cityId == element.id) {
// city = DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "");
// }
// }
//
// }
return DropdownField(
(DropValue value) {
gender = value;
setState(() {});
},
list: userGender,
dropdownValue: gender != null && gender != -1 ? DropValue(gender!.id, gender!.value, "") : null,
hint: gender != null && gender != -1 ? gender!.value : "${LocaleKeys.userGender.tr()} *",
// errorValue: adVM.vehicleCountryId.errorValue,
);
})),
12.height,
TxtField(
hint: LocaleKeys.email.tr(),
value: email,
// isButtonEnable: email!.length > 0 ? true : false,
buttonTitle: LocaleKeys.verify.tr(),
onChanged: (v) {
email = v;
},
),
12.height,
userVM.userCities != null
? Container(
padding: const EdgeInsets.only(right: 0, left: 0, top: 0, bottom: 0),
child: Builder(builder: (context) {
List<DropValue> userCityDrop = [];
for (var element in userVM.userCities!.data!) {
if (AppState().getUser.data != null) {
if (AppState().getUser.data!.userInfo!.cityId == element.id) {
city = DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "");
],
12.height,
Container(
padding: const EdgeInsets.only(right: 0, left: 0, top: 0, bottom: 0),
child: Builder(builder: (context) {
List<DropValue> userGender = [];
userGender.add(DropValue(1.toInt(), "${LocaleKeys.userMale.tr()}", "", isEnabled: true));
userGender.add(DropValue(2.toInt(), "${LocaleKeys.userFemale.tr()}", "", isEnabled: true));
// for (var element in userVM.userCities!.data!) {
// if (AppState().getUser.data != null) {
// if (AppState().getUser.data!.userInfo!.cityId == element.id) {
// city = DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "");
// }
// }
//
// }
return DropdownField(
(DropValue value) {
gender = value;
setState(() {});
},
list: userGender,
dropdownValue: gender != null && gender != -1 ? DropValue(gender!.id, gender!.value, "") : null,
hint: gender != null && gender != -1 ? gender!.value : "${LocaleKeys.userGender.tr()} *",
// errorValue: adVM.vehicleCountryId.errorValue,
);
})),
12.height,
TxtField(
hint: LocaleKeys.email.tr(),
value: email,
// isButtonEnable: email!.length > 0 ? true : false,
buttonTitle: LocaleKeys.verify.tr(),
onChanged: (v) {
email = v;
},
),
12.height,
userVM.userCities != null
? Container(
padding: const EdgeInsets.only(right: 0, left: 0, top: 0, bottom: 0),
child: Builder(builder: (context) {
List<DropValue> userCityDrop = [];
for (var element in userVM.userCities!.data!) {
if (AppState().getUser.data != null) {
if (AppState().getUser.data!.userInfo!.cityId == element.id) {
city = DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "");
}
}
userCityDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", ""));
}
userCityDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", ""));
}
return DropdownField(
(DropValue value) {
city = value;
setState(() {});
},
list: userCityDrop,
dropdownValue: city != null && city != -1 ? DropValue(city!.id, city!.value, "") : null,
hint: city != null && city != -1 ? city!.value : "${LocaleKeys.city.tr()} *",
// errorValue: adVM.vehicleCountryId.errorValue,
);
}))
: SizedBox(),
12.height,
TxtField(
hint: LocaleKeys.createPass.tr(),
isPasswordEnabled: true,
maxLines: 1,
value: password,
onChanged: (v) {
password = v;
},
),
12.height,
TxtField(
hint: LocaleKeys.confirmPass.tr(),
isPasswordEnabled: true,
maxLines: 1,
value: confirmPassword,
onChanged: (v) {
confirmPassword = v;
},
),
50.height,
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Consumer(builder: (BuildContext context, UserVM userVM, Widget? child) {
return Checkbox(
value: userVM.completeProfilePageCheckbox,
activeColor: MyColors.darkPrimaryColor,
onChanged: (value) {
userVM.updateCompleteProfilePageCheckbox(value!);
},
);
return DropdownField(
(DropValue value) {
city = value;
setState(() {});
},
list: userCityDrop,
dropdownValue: city != null && city != -1 ? DropValue(city!.id, city!.value, "") : null,
hint: city != null && city != -1 ? city!.value : "${LocaleKeys.city.tr()} *",
// errorValue: adVM.vehicleCountryId.errorValue,
);
}))
: SizedBox(),
12.height,
TxtField(
hint: LocaleKeys.createPass.tr(),
isPasswordEnabled: true,
maxLines: 1,
value: password,
onChanged: (v) {
password = v;
},
),
12.height,
TxtField(
hint: LocaleKeys.confirmPass.tr(),
isPasswordEnabled: true,
maxLines: 1,
value: confirmPassword,
onChanged: (v) {
confirmPassword = v;
},
),
50.height,
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Consumer(builder: (BuildContext context, UserVM userVM, Widget? child) {
return Checkbox(
value: userVM.completeProfilePageCheckbox,
activeColor: MyColors.darkPrimaryColor,
onChanged: (value) {
userVM.updateCompleteProfilePageCheckbox(value!);
},
);
}),
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: LocaleKeys.termsOfService.tr(),
style: const TextStyle(fontSize: 12, fontWeight: MyFonts.Medium),
),
TextSpan(
text: " ${LocaleKeys.terms.tr()}",
style: const TextStyle(
decoration: TextDecoration.underline,
fontSize: 12,
color: MyColors.darkPrimaryColor,
fontWeight: MyFonts.Bold,
))
],
),
).onPress(() {
navigateWithName(context, AppRoutes.settingOptionsTermsAndConditions);
}),
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: LocaleKeys.termsOfService.tr(),
style: const TextStyle(fontSize: 12, fontWeight: MyFonts.Medium),
),
TextSpan(
text: " ${LocaleKeys.terms.tr()}",
style: const TextStyle(
decoration: TextDecoration.underline,
fontSize: 12,
color: MyColors.darkPrimaryColor,
fontWeight: MyFonts.Bold,
))
],
),
).onPress(() {
navigateWithName(context, AppRoutes.settingOptionsTermsAndConditions);
}),
)
// Column(
// children: [
// LocaleKeys.termsOfService.tr().toText(fontSize: 12),
// LocaleKeys.terms.tr().toText(fontSize: 12, color: MyColors.darkPrimaryColor),
// ],
// ),
// Theme(
// data: ThemeData(unselectedWidgetColor: Colors.transparent),
// child: Checkbox(
// value: false,
// onChanged: (_) {},
// ),
// )
],
),
16.height,
Consumer(builder: (BuildContext context, UserVM userVM, Widget? child) {
return ShowFillButton(
title: LocaleKeys.save.tr(),
maxWidth: double.infinity,
isDisabled: !userVM.completeProfilePageCheckbox,
onPressed: () {
if (!userVM.completeProfilePageCheckbox) {
return;
}
bool validateStatus = userVM.dataValidation(password: password,
firstName: firstName,
lastName: lastName,
email: email,
city: city,
gender: gender);
if (validateStatus) {
userVM.performCompleteProfile(
context,
password: password,
confirmPassword: confirmPassword!,
firstName: firstName!,
lastName: lastName!,
email: email!,
userId: widget.user.data!.userId ?? "",
isNeedToPassToken: widget.user.data!.isNeedToPassToken,
cityID: city!.id.toString(),
genderID: gender!.id.toString(),
);
}
});
}),
16.height,
],
),
)
// Column(
// children: [
// LocaleKeys.termsOfService.tr().toText(fontSize: 12),
// LocaleKeys.terms.tr().toText(fontSize: 12, color: MyColors.darkPrimaryColor),
// ],
// ),
// Theme(
// data: ThemeData(unselectedWidgetColor: Colors.transparent),
// child: Checkbox(
// value: false,
// onChanged: (_) {},
// ),
// )
],
),
16.height,
Consumer(builder: (BuildContext context, UserVM userVM, Widget? child) {
return ShowFillButton(
title: LocaleKeys.save.tr(),
maxWidth: double.infinity,
isDisabled: !userVM.completeProfilePageCheckbox,
onPressed: () {
if (!userVM.completeProfilePageCheckbox) {
return;
}
bool validateStatus = userVM.dataValidation(password: password, firstName: firstName, lastName: lastName, companyName: companyName, email: email, city: city, gender: gender);
if (validateStatus) {
userVM.performCompleteProfile(
context,
password: password,
confirmPassword: confirmPassword!,
firstName: firstName!,
lastName: lastName!,
companyName: companyName!,
email: email!,
userId: widget.user.data!.userId ?? "",
isNeedToPassToken: widget.user.data!.isNeedToPassToken,
cityID: city!.id.toString(),
genderID: gender!.id.toString(),
);
}
});
}),
16.height,
],
),
),
),
);
}
);
),
);
});
}
}

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -21,7 +22,9 @@ import 'package:provider/provider.dart';
import '../../utils/enums.dart';
class LoginWithPassword extends StatefulWidget {
const LoginWithPassword({Key? key}) : super(key: key);
final bool isRemoveBackButton;
const LoginWithPassword({super.key, this.isRemoveBackButton = true});
@override
State<LoginWithPassword> createState() => _LoginWithPasswordState();
@ -84,7 +87,7 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const CustomAppBar(isRemoveBackButton: true, title: ""),
appBar: CustomAppBar(isRemoveBackButton: widget.isRemoveBackButton, title: ""),
body: Container(
width: double.infinity,
height: double.infinity,

@ -7,6 +7,7 @@ import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/user_models/user.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';
@ -21,7 +22,7 @@ import 'package:sizer/sizer.dart';
import '../../theme/colors.dart';
class RegisterSelectionPage extends StatefulWidget {
const RegisterSelectionPage({Key? key}) : super(key: key);
const RegisterSelectionPage({super.key});
@override
State<RegisterSelectionPage> createState() => _RegisterSelectionPageState();
@ -58,14 +59,14 @@ class _RegisterSelectionPageState extends State<RegisterSelectionPage> {
SvgPicture.asset(MyAssets.logo),
Utils.mFlex(4),
LocaleKeys.welcomeMessage.tr().toText(
fontSize: 20,
letterSpacing: -1.44,
fontSize: 22,
letterSpacing: -1,
),
10.height,
LocaleKeys.welcomeDes.tr().toText(
color: MyColors.lightTextColor,
fontSize: 14,
fontSize: 16,
height: 23 / 24,
letterSpacing: -0.48,
),
Utils.mFlex(1),
ShowFillButton(
@ -90,20 +91,24 @@ class _RegisterSelectionPageState extends State<RegisterSelectionPage> {
}
},
),
10.height,
20.height,
Utils.mFlex(3),
// TextButton(
// onPressed: () {},
// child: const Text(
// "Continue as Guest",
// style: TextStyle(
// color: MyColors.darkPrimaryColor,
// fontWeight: FontWeight.bold,
// decoration: TextDecoration.underline,
// ),
// ),
// ),
// 10.height,
if (AppState().currentAppType == AppType.customer) ...[
TextButton(
onPressed: () {
appState.setIsViewOnly = true;
AppState().setUser = User(data: UserData(userInfo: GuestConsts().userInfo, accessToken: null, expiryDate: DateTime.now()));
navigateReplaceWithName(context, AppRoutes.dashboard);
},
child: LocaleKeys.continueAsGuest.tr().toText(color: MyColors.darkTextColor, fontSize: 18),
),
10.height,
],
TextButton(
onPressed: () {},
child: Text(

@ -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,bool isScrollControlled = 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,

@ -1,7 +1,10 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.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';
import 'package:mc_common_app/generated/locale_keys.g.dart';
@ -12,6 +15,7 @@ import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/user_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.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';
@ -24,13 +28,13 @@ class AddPhoneNumWidget extends StatefulWidget {
class _AddPhoneNumWidgetState extends State<AddPhoneNumWidget> {
String phoneNum = "", countryCode = "";
DropValue? countryCodeVal;
late UserVM userVM;
Country? _country;
@override
void initState() {
super.initState();
scheduleMicrotask(() {
userVM = Provider.of(context, listen: false);
getCountryList();
@ -56,7 +60,25 @@ class _AddPhoneNumWidgetState extends State<AddPhoneNumWidget> {
children: [
"Enter Phone Number".toText(fontSize: 16, isBold: true),
8.height,
getCountry(context),
Container(
decoration: Utils.containerColorRadiusBorderWidth(MyColors.white, 0, MyColors.darkPrimaryColor, 2),
margin: const EdgeInsets.all(0),
padding: const EdgeInsets.only(left: 8, right: 8),
width: null,
child: Row(
children: [
if (countryCode.isEmpty) ...[
LocaleKeys.selectCountryCode.tr().toText(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium).paddingOnly(top: 12, bottom: 12),
] else ...[
countryCode.toText(fontSize: 15, fontWeight: MyFonts.Medium).paddingOnly(top: 12, bottom: 12),
]
],
),
).onPress(() {
if (_country != null) {
getCountry(context);
}
}),
8.height,
TxtField(
keyboardType: TextInputType.phone,
@ -103,24 +125,20 @@ class _AddPhoneNumWidgetState extends State<AddPhoneNumWidget> {
);
}
Widget getCountry(BuildContext context) {
if (_country != null) {
List<DropValue> dropList = [];
_country!.data?.forEach((element) {
dropList.add(DropValue(element.id ?? 0, "${element.countryName ?? ""} ${element.countryCode ?? ""}", element.countryCode ?? ""));
});
return DropdownField(
(DropValue value) {
void getCountry(BuildContext context) {
List<DropValue> dropList = [];
_country!.data?.forEach((element) {
dropList.add(DropValue(element.id ?? 0, "${element.countryName ?? ""} ${element.countryCode ?? ""}", element.countryCode ?? ""));
});
return showCustomBottomPicker(
items: dropList,
onItemSelected: (value) {
AppState().setUserRegisterCountrySelection = value;
countryCode = value.subValue;
countryCodeVal = value;
setState(() {});
},
list: dropList,
hint: countryCode.isNotEmpty ? countryCode.toString() : LocaleKeys.selectCountryCode.tr(),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
context: context);
}
bool validation() {
@ -137,3 +155,73 @@ class _AddPhoneNumWidgetState extends State<AddPhoneNumWidget> {
return isValid;
}
}
class CustomBottomPicker extends StatelessWidget {
final List<DropValue> items;
final void Function(DropValue selectedItem) onItemSelected;
const CustomBottomPicker({
Key? key,
required this.items,
required this.onItemSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Colors.black.withOpacity(0.4),
child: DraggableScrollableSheet(
initialChildSize: 0.2,
maxChildSize: 0.4,
minChildSize: 0.2,
expand: true,
builder: (context, scrollController) {
return Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: ListView.builder(
controller: scrollController,
itemCount: items.length,
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].value),
onTap: () {
onItemSelected(items[index]);
Navigator.of(context).pop();
},
);
},
),
);
},
),
),
);
}
}
// Function to show the bottom picker
void showCustomBottomPicker({
required BuildContext context,
required List<DropValue> items,
required void Function(DropValue selectedItem) onItemSelected,
}) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: false,
backgroundColor: Colors.transparent,
builder: (context) {
return CustomBottomPicker(
items: items,
onItemSelected: onItemSelected,
);
},
);
}

@ -8,7 +8,9 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/view_models/user_view_model.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final Color? backgroundColor;
@ -58,81 +60,79 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
toolbarHeight: toolbarHeight,
leading: isDrawerEnabled
? InkWell(
onTap: onTap,
child: Row(
children: [
profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userLocalImage != null
? Image.file(
AppState().getUser.data!.userInfo!.userLocalImage!,
width: 34,
height: 34,
fit: BoxFit.fill,
).toCircle(borderRadius: 100)
: profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userImageUrl != null
? CachedNetworkImage(
imageUrl: AppState().getUser.data!.userInfo!.userImageUrl,
imageBuilder: (context, imageProvider) =>
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(Icons.supervised_user_circle_outlined),
fadeInCurve: Curves.easeIn,
width: 34,
height: 34,
fit: BoxFit.fill,
fadeInDuration: const Duration(milliseconds: 1000),
useOldImageOnUrlChange: false)
.toCircle(borderRadius: 100)
: Image.asset(
MyAssets.carBanner,
width: 34,
height: 34,
fit: BoxFit.fill,
).toCircle(borderRadius: 100),
10.width,
SvgPicture.asset(MyAssets.dashboardDrawerIcon),
],
).paddingOnly(left: 21),
)
onTap: onTap,
child: Row(
children: [
Consumer(builder: (BuildContext context, UserVM uvm, Widget? child) {
return profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userLocalImage != null
? Image.file(
AppState().getUser.data!.userInfo!.userLocalImage!,
width: 34,
height: 34,
fit: BoxFit.fill,
).toCircle(borderRadius: 100)
: profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userImageUrl != null
? CachedNetworkImage(
imageUrl: AppState().getUser.data!.userInfo!.userImageUrl,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(Icons.supervised_user_circle_outlined),
fadeInCurve: Curves.easeIn,
width: 34,
height: 34,
fit: BoxFit.fill,
fadeInDuration: const Duration(milliseconds: 1000),
useOldImageOnUrlChange: false)
.toCircle(borderRadius: 100)
: Image.asset(
MyAssets.carBanner,
width: 34,
height: 34,
fit: BoxFit.fill,
).toCircle(borderRadius: 100);
}),
10.width,
SvgPicture.asset(MyAssets.dashboardDrawerIcon),
],
).paddingOnly(left: 21),
)
: isRemoveBackButton
? null
: Row(
children: [
21.width,
const Icon(Icons.arrow_back_ios, color: Colors.black, size: 16)
.toContainer(
padding: const EdgeInsets.only(left: 5),
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
height: 40,
width: 40,
)
.onPress(() {
if (onBackButtonTapped != null) {
onBackButtonTapped!();
} else {
Navigator.pop(context);
}
}),
],
),
? null
: Row(
children: [
21.width,
const Icon(Icons.arrow_back_ios, color: Colors.black, size: 16)
.toContainer(
padding: const EdgeInsets.only(left: 5),
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
height: 40,
width: 40,
)
.onPress(() {
if (onBackButtonTapped != null) {
onBackButtonTapped!();
} else {
Navigator.pop(context);
}
}),
],
),
iconTheme: IconThemeData(
color: backIconColor ?? Colors.black, //change your color here
),
actions: actions,
title: (title ?? "").toText(fontSize: 20),
),
if (backgroundColor == null)
const Divider(
thickness: 1,
height: 1)
if (backgroundColor == null) const Divider(thickness: 1, height: 1)
],
);
}

@ -9,9 +9,9 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class CustomBottomNavbar extends StatelessWidget {
final int selectedNavbarBarIndex;
final Function(int) onSelected;
bool isProvider;
final bool isProvider;
CustomBottomNavbar({Key? key, required this.onSelected, required this.selectedNavbarBarIndex, this.isProvider = false}) : super(key: key);
const CustomBottomNavbar({super.key, required this.onSelected, required this.selectedNavbarBarIndex, this.isProvider = false});
@override
Widget build(BuildContext context) {

@ -68,10 +68,11 @@ class BranchDetailCard extends StatelessWidget {
],
if (providerName != null)
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
("${LocaleKeys.provider.tr()}:").toText(color: MyColors.lightTextColor, fontSize: 12),
4.width,
providerName!.toText(fontSize: 12, isBold: true),
Flexible(child: providerName!.toText(fontSize: 12, isBold: true),)
],
),
],

@ -1,3 +1,5 @@
import 'dart:developer';
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';
@ -8,6 +10,7 @@ class FiltersList extends StatelessWidget {
final List<FilterListModel> filterList;
final Function(int, int) onFilterTapped;
final bool needLeftPadding;
final bool showImages;
final EdgeInsets? padding;
const FiltersList({
@ -15,6 +18,7 @@ class FiltersList extends StatelessWidget {
this.padding,
required this.filterList,
this.needLeftPadding = true,
this.showImages = false,
required this.onFilterTapped,
});
@ -47,14 +51,16 @@ class FiltersList extends StatelessWidget {
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
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
// ],
if (showImages) ...[
if (index != 0 && filterList[index].iconUrl.isNotEmpty) ...[
filterList[index].iconUrl.buildNetworkImage(height: 25, width: 25, fit: BoxFit.contain),
const SizedBox(width: 4), // Space between icon and text
],
],
// Text displayed in the chip
filterList[index].title.toText(
fontSize: 13,

@ -33,27 +33,14 @@ class ProviderDetailCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
String nameInitials = "";
if (title.isNotEmpty) {
nameInitials = title[0];
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isFromFavoriteList) ...[
CircleAvatar(
radius: 22,
backgroundColor: MyColors.darkPrimaryColor,
child: nameInitials.toText(fontSize: 25, isBold: true),
),
] else ...[
Image.asset(
providerImageUrl,
width: 80,
height: 85,
fit: BoxFit.cover,
),
],
providerImageUrl.buildNetworkImage(
width: 80,
height: 85,
fit: BoxFit.cover,
),
12.width,
Expanded(
child: Column(

@ -1,6 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/utils.dart';
@ -28,11 +29,13 @@ class DropdownField extends StatefulWidget {
final List<DropValue>? list;
final DropValue? dropdownValue;
final Function(DropValue) onSelect;
final Function()? onTap;
final bool showAppointmentPickerVariant;
final TextStyle? textStyle;
final bool isSelectAble;
const DropdownField(this.onSelect, {Key? key, this.hint, this.list, this.dropdownValue, this.errorValue = "", this.showAppointmentPickerVariant = false, this.textStyle, this.isSelectAble = true}) : super(key: key);
const DropdownField(this.onSelect,
{super.key, this.hint, this.list, this.dropdownValue, this.onTap, this.errorValue = "", this.showAppointmentPickerVariant = false, this.textStyle, this.isSelectAble = true});
@override
State<DropdownField> createState() => _DropdownFieldState();
@ -55,16 +58,34 @@ class _DropdownFieldState extends State<DropdownField> {
dropdownValue = widget.dropdownValue;
return Column(
children: [
if (widget.hint != null) ...[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
(widget.hint ?? "").toText(
color: borderColor,
fontSize: 13,
fontWeight: MyFonts.Medium,
),
],
),
4.height,
],
IgnorePointer(
ignoring: !widget.isSelectAble,
child: Container(
decoration: widget.showAppointmentPickerVariant ? null : Utils.containerColorRadiusBorderWidth(MyColors.white, 0, widget.isSelectAble ? MyColors.darkPrimaryColor : MyColors.greyACColor, 2),
decoration:
widget.showAppointmentPickerVariant ? null : Utils.containerColorRadiusBorderWidth(MyColors.white, 0, widget.isSelectAble ? MyColors.darkPrimaryColor : MyColors.greyACColor, 2),
margin: const EdgeInsets.all(0),
padding: const EdgeInsets.only(left: 8, right: 8),
width: widget.showAppointmentPickerVariant ? 170 : null,
child: DropdownButton<DropValue>(
value: dropdownValue,
icon: Icon(Icons.keyboard_arrow_down_sharp, color: !widget.isSelectAble ? Colors.transparent : null, size: 21,),
icon: Icon(
Icons.keyboard_arrow_down_sharp,
color: !widget.isSelectAble ? Colors.transparent : null,
size: 21,
),
elevation: 16,
iconSize: widget.showAppointmentPickerVariant ? 26 : 16,
iconEnabledColor: borderColor,
@ -74,7 +95,7 @@ class _DropdownFieldState extends State<DropdownField> {
color: borderColor,
fontSize: 15,
),
hint: (widget.hint ?? "").toText(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium),
// hint: (widget.hint ?? "").toText(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium),
underline: Container(height: 0),
onChanged: (DropValue? newValue) {
setState(() {
@ -82,6 +103,7 @@ class _DropdownFieldState extends State<DropdownField> {
widget.onSelect(newValue);
});
},
onTap: widget.onTap,
items: (widget.list ?? defaultV).map<DropdownMenuItem<DropValue>>(
(DropValue value) {
return DropdownMenuItem<DropValue>(

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/theme/colors.dart';
@ -25,7 +26,8 @@ extension ExtendedText on Widget {
}
extension ContainerExt on Widget {
Widget toWhiteContainer({required double width, double? allPading, EdgeInsets? pading, EdgeInsets? margin, VoidCallback? onTap, Color? backgroundColor, bool isBorderRequired = false, double borderRadius = 0}) {
Widget toWhiteContainer(
{required double width, double? allPading, EdgeInsets? pading, EdgeInsets? margin, VoidCallback? onTap, Color? backgroundColor, bool isBorderRequired = false, double borderRadius = 0}) {
return InkWell(
onTap: onTap,
child: Container(
@ -234,7 +236,8 @@ extension WidgetExt on Widget {
);
}
Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this);
Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) =>
Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this);
// Widget onTap(VoidCallback onTap, {double corners = 0}) {
// return Clickable.widget(child: this, corners: corners, onTap: onTap);
@ -407,10 +410,49 @@ extension BuildSVG on String? {
extension LocaleSetup on MultiProvider {
Widget setupLocale() {
return EasyLocalization(supportedLocales: MyLocales.supportedLocales, fallbackLocale: MyLocales.fallBackLocale, startLocale: MyLocales.startLocale, assetLoader: MyLocales.assetLoader, path: MyLocales.localesAssetPath, child: this);
return EasyLocalization(
supportedLocales: MyLocales.supportedLocales,
fallbackLocale: MyLocales.fallBackLocale,
startLocale: MyLocales.startLocale,
assetLoader: MyLocales.assetLoader,
path: MyLocales.localesAssetPath,
child: this);
}
}
extension WidgetExtensions on Widget {
Widget onPress(VoidCallback onTap) => InkWell(onTap: onTap, child: this);
}
extension ViewOnlyExtension on Widget {
Widget toViewOnly(BuildContext context, {required Function() onTap}) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (AppState().getIsViewOnly) {
onTap();
}
},
child: AbsorbPointer(absorbing: AppState().getIsViewOnly, child: this));
}
}
extension PreferredSizeWidgetViewOnlyExtension on PreferredSizeWidget {
PreferredSizeWidget toViewOnly(BuildContext context, {required Function() onTap}) {
return PreferredSize(
preferredSize: preferredSize,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (AppState().getIsViewOnly) {
onTap();
}
},
child: AbsorbPointer(
absorbing: AppState().getIsViewOnly,
child: this,
),
),
);
}
}

@ -9,7 +9,6 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:sizer/sizer.dart';
class TxtField extends StatelessWidget {
TextEditingController controller = TextEditingController();
String? value;
String? hint;
String? lable;
@ -69,6 +68,8 @@ class TxtField extends StatelessWidget {
this.allowOnlyOneDigit = false,
});
TextEditingController controller = TextEditingController();
@override
Widget build(BuildContext context) {
controller.text = value ?? "";
@ -76,6 +77,19 @@ class TxtField extends StatelessWidget {
return Column(
children: [
if (hint != null) ...[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
(hint ?? "").toText(
color: borderColor,
fontSize: 13,
fontWeight: MyFonts.Medium,
),
],
),
4.height,
],
InkWell(
onTap: isNeedClickAll == false
? null
@ -133,9 +147,9 @@ class TxtField extends StatelessWidget {
)
: postfixWidget,
prefixIcon: prefixData != null ? Icon(prefixData, color: borderColor) : preFixWidget,
labelStyle: const TextStyle(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium),
hintStyle: const TextStyle(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium),
hintText: hint ?? "",
labelStyle: const TextStyle(color: borderColor, fontSize: 13, fontWeight: MyFonts.Medium),
hintStyle: const TextStyle(color: borderColor, fontSize: 13, fontWeight: MyFonts.Medium),
// hintText: hint ?? "",
contentPadding: prefixData == null
? EdgeInsets.only(
left: 12,

Loading…
Cancel
Save