Merge branch 'master' into mirza_development

# Conflicts:
#	pubspec.lock
mirza_development
Mirza.Shafique@cloudsolutions.com.sa 2 years ago
commit 20125d81ae

@ -61,6 +61,11 @@ class ApiConsts {
static String ServiceProviderAppointmentRescheduleCancelAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_RescheduleCancelAppointment"; static String ServiceProviderAppointmentRescheduleCancelAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_RescheduleCancelAppointment";
static String AddNewServicesInAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_ServiceItemAdd"; static String AddNewServicesInAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_ServiceItemAdd";
static String getAppointmentSlots = "${baseUrlServices}api/ServiceProviders/ScheduleSlotsInfo_Get";
static String updateAppointmentStatus = "${baseUrlServices}api/ServiceProviders/ServiceProvidersAppointmentStatus_Update";
static String updateAppointmentPaymentStatus = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointmentServiceItemPaymentStatus_Update";
static String createMergeAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderMergeAppointment_Create";
//ServiceProvidersServiceID as params //ServiceProvidersServiceID as params
// static String servicesGet = "${baseUrlServices}api/ServiceProviders/Services_Get"; // static String servicesGet = "${baseUrlServices}api/ServiceProviders/Services_Get";
@ -105,10 +110,7 @@ class ApiConsts {
static String adsPhotoOfficeAppointmentScheduleSlotGet = "${baseUrlServices}api/Advertisement/PhotoOfficeAppointmentScheduleSlot_Get"; static String adsPhotoOfficeAppointmentScheduleSlotGet = "${baseUrlServices}api/Advertisement/PhotoOfficeAppointmentScheduleSlot_Get";
static String adsPhotoOfficeAppointmentCreate = "${baseUrlServices}api/Advertisement/PhotoOfficeAppointment_Create"; static String adsPhotoOfficeAppointmentCreate = "${baseUrlServices}api/Advertisement/PhotoOfficeAppointment_Create";
static String adsMCBankAccountAdGet = "${baseUrlServices}api/Advertisement/MCBankAccountAd_Get"; static String adsMCBankAccountAdGet = "${baseUrlServices}api/Advertisement/MCBankAccountAd_Get";
static String getAppointmentSlots = "${baseUrlServices}api/ServiceProviders/ScheduleSlotsInfo_Get"; static String adsReserveCreate = "${baseUrlServices}api/Advertisement/AdsReserve_Create";
static String updateAppointmentStatus = "${baseUrlServices}api/ServiceProviders/ServiceProvidersAppointmentStatus_Update";
static String updateAppointmentPaymentStatus = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointmentServiceItemPaymentStatus_Update";
static String createMergeAppointment = "${baseUrlServices}api/ServiceProviders/ServiceProviderMergeAppointment_Create";
//Subscription //Subscription
static String getAllSubscriptions = "${baseUrlServices}api/Common/Subscription_Get"; static String getAllSubscriptions = "${baseUrlServices}api/Common/Subscription_Get";
@ -158,6 +160,7 @@ class GlobalConsts {
static String welcomeVideoUrl = "welcomeVideoUrl"; static String welcomeVideoUrl = "welcomeVideoUrl";
static String doNotShowWelcomeVideo = "doNotShowWelcomeVideo"; static String doNotShowWelcomeVideo = "doNotShowWelcomeVideo";
static String demandAmountError = "Amount Cannot be Empty"; static String demandAmountError = "Amount Cannot be Empty";
static String reservationCancelError = "Cancellation Reason Cannot be Empty";
static String descriptionError = "Description Cannot be Empty"; static String descriptionError = "Description Cannot be Empty";
static String vehicleVinError = "Vehicle VIN Cannot be Empty"; static String vehicleVinError = "Vehicle VIN Cannot be Empty";
static String vehicleTitleError = "Vehicle Title Cannot be Empty"; static String vehicleTitleError = "Vehicle Title Cannot be Empty";
@ -262,8 +265,6 @@ class MyAssets {
static String tamaraEngPng = "${assetPath}icons/payments/tamara_en.png"; static String tamaraEngPng = "${assetPath}icons/payments/tamara_en.png";
static String visaPng = "${assetPath}icons/payments/visa.png"; static String visaPng = "${assetPath}icons/payments/visa.png";
static String whatsAppIcon = "${assetPath}icons/whatsapp_icon.svg"; static String whatsAppIcon = "${assetPath}icons/whatsapp_icon.svg";
} }

@ -3,6 +3,7 @@ import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'
import 'package:mc_common_app/models/requests_models/request_model.dart'; import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/models/user_models/register_user.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/enums.dart';
import 'package:mc_common_app/views/profile/profile_view.dart';
import 'package:mc_common_app/views/user/change_email_page.dart'; import 'package:mc_common_app/views/user/change_email_page.dart';
import 'package:mc_common_app/views/user/change_mobile_page.dart'; import 'package:mc_common_app/views/user/change_mobile_page.dart';
@ -83,6 +84,10 @@ class AppRoutes {
static const String settingOptionsLanguages = "/settingOptionsLanguages"; static const String settingOptionsLanguages = "/settingOptionsLanguages";
static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends"; static const String settingOptionsInviteFriends = "/settingOptionsInviteFriends";
//Profile Screen
static const String profileView = "/profileView";
//Chat //Chat
static const String chatView = "/chatView"; static const String chatView = "/chatView";
@ -107,6 +112,7 @@ class AppRoutes {
changeMobilePage: (context) => ChangeMobilePage(), changeMobilePage: (context) => ChangeMobilePage(),
changeEmailPage: (context) => const ChangeEmailPage(), changeEmailPage: (context) => const ChangeEmailPage(),
editAccountPage: (context) => const EditAccountPage(), editAccountPage: (context) => const EditAccountPage(),
profileView: (context) => const ProfileScreen(),
}; };
} }

@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/enums.dart';
extension EmailValidator on String { extension EmailValidator on String {
Widget toText( Widget toText(
@ -25,10 +24,7 @@ extension EmailValidator on String {
style: TextStyle( style: TextStyle(
fontStyle: isItalic ? FontStyle.italic : null, fontStyle: isItalic ? FontStyle.italic : null,
height: height, height: height,
decoration: isUnderLine decoration: isUnderLine ? TextDecoration.underline : textDecoration ?? TextDecoration.none,
? TextDecoration.underline
: textDecoration ?? TextDecoration.none,
fontSize: fontSize ?? 10, fontSize: fontSize ?? 10,
fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.w600, fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.w600,
color: color ?? MyColors.darkTextColor, color: color ?? MyColors.darkTextColor,
@ -37,9 +33,7 @@ extension EmailValidator on String {
); );
bool isValidEmail() { bool isValidEmail() {
return RegExp( return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this);
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
.hasMatch(this);
} }
bool isNum() { bool isNum() {
@ -428,6 +422,10 @@ extension PaymentTypesToInt on PaymentTypes {
case PaymentTypes.request: case PaymentTypes.request:
return 5; return 5;
case PaymentTypes.extendAds:
return 6;
case PaymentTypes.partialAppointment:
return 7;
default: default:
return 0; return 0;
@ -462,6 +460,12 @@ extension AdReserveStatusEnum on int {
return AdReserveStatus.cancelledByOwner; return AdReserveStatus.cancelledByOwner;
} else if (this == 3) { } else if (this == 3) {
return AdReserveStatus.cancelledByAdmin; return AdReserveStatus.cancelledByAdmin;
} else if (this == 4) {
return AdReserveStatus.timeOver;
} else if (this == 5) {
return AdReserveStatus.dealDone;
} else if (this == 6) {
return AdReserveStatus.fullPaymentVerified;
} else { } else {
return AdReserveStatus.defaultStatus; return AdReserveStatus.defaultStatus;
} }

@ -3,18 +3,30 @@ class MyReservedAdsRespModel {
int? adsID; int? adsID;
int? customerID; int? customerID;
int? paymentStatus; int? paymentStatus;
int? adsReserveStatus;
int? reservationTimeID; int? reservationTimeID;
double? reservationBasePrice; double? reservationBasePrice;
double? refundAmount; double? refundAmount;
String? refundDate; String? refundDate;
MyReservedAdsRespModel({this.id, this.adsID, this.customerID, this.paymentStatus, this.reservationTimeID, this.reservationBasePrice, this.refundAmount, this.refundDate}); MyReservedAdsRespModel({
this.id,
this.adsID,
this.customerID,
this.paymentStatus,
this.adsReserveStatus,
this.reservationTimeID,
this.reservationBasePrice,
this.refundAmount,
this.refundDate,
});
MyReservedAdsRespModel.fromJson(Map<String, dynamic> json) { MyReservedAdsRespModel.fromJson(Map<String, dynamic> json) {
id = json['id']; id = json['id'];
adsID = json['adsID']; adsID = json['adsID'];
customerID = json['customerID']; customerID = json['customerID'];
paymentStatus = json['paymentStatus']; paymentStatus = json['paymentStatus'];
paymentStatus = json['adsReserveStatus'];
reservationTimeID = json['reservationTimeID']; reservationTimeID = json['reservationTimeID'];
reservationBasePrice = json['reservationBasePrice']; reservationBasePrice = json['reservationBasePrice'];
refundAmount = json['refundAmount']; refundAmount = json['refundAmount'];
@ -27,6 +39,7 @@ class MyReservedAdsRespModel {
data['adsID'] = adsID; data['adsID'] = adsID;
data['customerID'] = customerID; data['customerID'] = customerID;
data['paymentStatus'] = paymentStatus; data['paymentStatus'] = paymentStatus;
data['adsReserveStatus'] = adsReserveStatus;
data['reservationTimeID'] = reservationTimeID; data['reservationTimeID'] = reservationTimeID;
data['reservationBasePrice'] = reservationBasePrice; data['reservationBasePrice'] = reservationBasePrice;
data['refundAmount'] = refundAmount; data['refundAmount'] = refundAmount;

@ -7,6 +7,7 @@ import 'package:mc_common_app/utils/enums.dart';
class ChatMessageModel { class ChatMessageModel {
int? id; int? id;
String? senderUserID; String? senderUserID;
String? receiverUserID;
String? senderName; String? senderName;
int? messageType; int? messageType;
ChatMessageTypeEnum? chatMessageTypeEnum; ChatMessageTypeEnum? chatMessageTypeEnum;
@ -23,6 +24,7 @@ class ChatMessageModel {
ChatMessageModel({ ChatMessageModel({
this.id, this.id,
this.senderUserID, this.senderUserID,
this.receiverUserID,
this.senderName, this.senderName,
this.messageType, this.messageType,
this.chatMessageTypeEnum, this.chatMessageTypeEnum,
@ -46,6 +48,7 @@ class ChatMessageModel {
final myUserId = AppState().getUser.data!.userInfo!.userId.toString().toUpperCase(); final myUserId = AppState().getUser.data!.userInfo!.userId.toString().toUpperCase();
id = json['id']; id = json['id'];
senderUserID = json['senderUserID']; senderUserID = json['senderUserID'];
receiverUserID = json['receiverUserID'] ?? "";
senderName = json['senderName']; senderName = json['senderName'];
messageType = json['messageType']; messageType = json['messageType'];
chatMessageTypeEnum = (json['messageType'] as int).toChatMessageTypeEnum(); chatMessageTypeEnum = (json['messageType'] as int).toChatMessageTypeEnum();
@ -57,8 +60,7 @@ class ChatMessageModel {
reqOffer = json['reqOffer'] != null ? ReqOffer.fromJson(json['reqOffer']) : null; reqOffer = json['reqOffer'] != null ? ReqOffer.fromJson(json['reqOffer']) : null;
isRead = json['isRead']; isRead = json['isRead'];
readOn = json['readOn']; readOn = json['readOn'];
isMyMessage = (json['senderUserID']).toString().toUpperCase()== myUserId; isMyMessage = (json['senderUserID']).toString().toUpperCase() == myUserId;
} }
} }
@ -84,7 +86,6 @@ class ReqOffer {
}); });
ReqOffer.fromJson(Map<String, dynamic> json) { ReqOffer.fromJson(Map<String, dynamic> json) {
log("the json: $json");
id = json['id']; id = json['id'];
requestID = json['requestID']; requestID = json['requestID'];
serviceProviderID = json['serviceProviderID']; serviceProviderID = json['serviceProviderID'];

@ -11,17 +11,14 @@ class GenericRespModel {
int? totalItemsCount; int? totalItemsCount;
String? message; String? message;
factory GenericRespModel.fromJson(Map<String, dynamic> json) => GenericRespModel(
factory GenericRespModel.fromJson(Map<String, dynamic> json) =>
GenericRespModel(
data: json["data"], data: json["data"],
messageStatus: json["messageStatus"], messageStatus: json["messageStatus"],
totalItemsCount: json["totalItemsCount"], totalItemsCount: json["totalItemsCount"],
message: json["message"], message: json["message"],
); );
Map<String, dynamic> toJson() => Map<String, dynamic> toJson() => {
{
"data": data, "data": data,
"messageStatus": messageStatus, "messageStatus": messageStatus,
"totalItemsCount": totalItemsCount, "totalItemsCount": totalItemsCount,
@ -29,39 +26,6 @@ class GenericRespModel {
}; };
} }
var json = {
"ads": {"id": 0, "adsDurationID": 1, "startDate": "2023-04-12T10:10:20.905Z", "countryId": 1, "specialServiceIDs": [], "isMCHandled": false},
"vehiclePosting": {
"id": 0,
"userID": "1A1597B3-D5A0-433A-098B-08DB189E51EC",
"vehicleType": 1,
"vehicleModelID": 1,
"vehicleModelYearID": 1,
"vehicleColorID": 2,
"vehicleCategoryID": 1,
"vehicleConditionID": 1,
"vehicleMileageID": 1,
"vehicleTransmissionID": 1,
"vehicleSellerTypeID": 1,
"cityID": 1,
"price": 33,
"vehicleVIN": "fdfd",
"vehicleDescription": "dsd",
"vehicleTitle": "fsfs",
"vehicleDescriptionN": "dsdds",
"isFinanceAvailable": true,
"warantyYears": 2,
"demandAmount": 34,
"adStatus": 1,
"vehiclePostingImages": [
{"id": 0, "imageName": "onon", "imageUrl": "string", "imageStr": null, "vehiclePostingID": 0, "vehiclePosting": null}
],
"vehiclePostingDamageParts": [
{"id": 0, "comment": "hhsa", "vehicleImageBase64": null, "vehicleDamagePartID": 1, "vehiclePostingID": 0, "isActive": true}
]
}
};
class AdsCreationPayloadModel { class AdsCreationPayloadModel {
Ads? ads; Ads? ads;
VehiclePosting? vehiclePosting; VehiclePosting? vehiclePosting;
@ -114,6 +78,11 @@ class Ads {
data['isMCHandled'] = isMCHandled; data['isMCHandled'] = isMCHandled;
return data; return data;
} }
@override
String toString() {
return 'Ads{id: $id, adsDurationID: $adsDurationID, startDate: $startDate, countryId: $countryId, specialServiceIDs: $specialServiceIDs, isMCHandled: $isMCHandled}';
}
} }
class VehiclePosting { class VehiclePosting {
@ -277,6 +246,11 @@ class VehiclePostingImages {
data['vehiclePosting'] = vehiclePosting; data['vehiclePosting'] = vehiclePosting;
return data; return data;
} }
// @override
// String toString() {
// return 'VehiclePostingImages{id: $id, imageName: $imageName, imageUrl: $imageUrl, imageStr: $imageStr, vehiclePostingID: $vehiclePostingID, vehiclePosting: $vehiclePosting}';
// }
} }
class RequestPostingImages { class RequestPostingImages {
@ -299,8 +273,12 @@ class RequestPostingImages {
data['requestID'] = requestID; data['requestID'] = requestID;
return data; return data;
} }
}
@override
String toString() {
return 'RequestPostingImages{id: $id, requestImage: $requestImage, requestID: $requestID}';
}
}
class VehiclePostingDamageParts { class VehiclePostingDamageParts {
int? id; int? id;
@ -331,4 +309,9 @@ class VehiclePostingDamageParts {
data['isActive'] = isActive; data['isActive'] = isActive;
return data; return data;
} }
// @override
// String toString() {
// return 'VehiclePostingDamageParts{id: $id, comment: $comment, vehicleImageBase64: $vehicleImageBase64, vehicleDamagePartID: $vehicleDamagePartID, vehiclePostingID: $vehiclePostingID, isActive: $isActive}';
// }
} }

@ -51,7 +51,7 @@ abstract class AdsRepo {
Future<List<AdDetailsModel>> getAllAds({required bool isMyAds}); Future<List<AdDetailsModel>> getAllAds({required bool isMyAds});
Future<List<MyReservedAdsRespModel>> getMyReservedAds(); Future<List<AdDetailsModel>> getMyReservedAds();
Future<List<AdDetailsModel>> getMyAds(); Future<List<AdDetailsModel>> getMyAds();
@ -63,7 +63,9 @@ abstract class AdsRepo {
Future<GenericRespModel> deleteAd({required int adId}); Future<GenericRespModel> deleteAd({required int adId});
Future<GenericRespModel> cancelMyAdReservation({required int adId}); Future<GenericRespModel> cancelMyAdReservation({required int adId, required int adsReserveStatus, required String reason});
Future<GenericRespModel> createReserveAd({required int adId});
} }
class AdsRepoImp implements AdsRepo { class AdsRepoImp implements AdsRepo {
@ -344,6 +346,7 @@ class AdsRepoImp implements AdsRepo {
} }
}; };
log("posting this ads: ${postParams.toString()}");
String token = appState.getUser.data!.accessToken ?? ""; String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await apiClient.postJsonForObject( GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json), (json) => GenericRespModel.fromJson(json),
@ -376,7 +379,7 @@ class AdsRepoImp implements AdsRepo {
} }
@override @override
Future<List<MyReservedAdsRespModel>> getMyReservedAds() async { Future<List<AdDetailsModel>> getMyReservedAds() async {
var params = { var params = {
"userID": appState.getUser.data!.userInfo!.userId ?? "", "userID": appState.getUser.data!.userInfo!.userId ?? "",
}; };
@ -387,7 +390,31 @@ class AdsRepoImp implements AdsRepo {
ApiConsts.myAdsReserveGet, ApiConsts.myAdsReserveGet,
queryParameters: params, queryParameters: params,
); );
List<MyReservedAdsRespModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => MyReservedAdsRespModel.fromJson(adsGenericModel.data[index])); List<MyReservedAdsRespModel> reservedAds = List.generate(adsGenericModel.data.length, (index) => MyReservedAdsRespModel.fromJson(adsGenericModel.data[index]));
List<String> selectedIdsString = reservedAds.map((component) => component.adsID.toString()).toList();
if (selectedIdsString.isEmpty) {
return [];
}
return await getAdsPerSpecificIds(ids: selectedIdsString, reservedAds: reservedAds);
}
Future<List<AdDetailsModel>> getAdsPerSpecificIds({required List<String> ids, required List<MyReservedAdsRespModel> reservedAds}) async {
var params = {
"AdsIDs": ids,
};
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,
(json) => GenericRespModel.fromJson(json),
queryParameters: params,
ApiConsts.vehicleAdsGet,
);
List<AdDetailsModel> vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true));
for (int i = 0; i < vehicleAdsDetails.length; i++) {
vehicleAdsDetails[i].adReserveStatus = (reservedAds[i].adsReserveStatus ?? 0).toAdRserveStatusEnum();
}
return vehicleAdsDetails; return vehicleAdsDetails;
} }
@ -463,9 +490,14 @@ class AdsRepoImp implements AdsRepo {
} }
@override @override
Future<GenericRespModel> cancelMyAdReservation({required int adId}) async { Future<GenericRespModel> cancelMyAdReservation({required int adId, required int adsReserveStatus, required String reason}) async {
int customerID = AppState().getUser.data!.userInfo!.customerId ?? 0;
var postParams = { var postParams = {
"adID": adId, "adsID": adId,
"customerID": customerID,
"adsReserveStatus": adsReserveStatus,
"comment": reason,
}; };
String token = appState.getUser.data!.accessToken ?? ""; String token = appState.getUser.data!.accessToken ?? "";
@ -479,6 +511,27 @@ class AdsRepoImp implements AdsRepo {
return Future.value(adsGenericModel); return Future.value(adsGenericModel);
} }
@override
Future<GenericRespModel> createReserveAd({required int adId}) async {
int customerID = AppState().getUser.data!.userInfo!.customerId ?? 0;
var postParams = {
"adsID": adId,
"customerID": customerID,
"adsReserveStatus": 0,
};
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.adsReserveCreate,
postParams,
token: token,
);
return Future.value(adsGenericModel);
}
@override @override
Future<GenericRespModel> createAppointmentForAdSpecialService({ Future<GenericRespModel> createAppointmentForAdSpecialService({
required int adId, required int adId,

@ -4,18 +4,16 @@ import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependencies.dart'; import 'package:mc_common_app/config/dependencies.dart';
import 'package:mc_common_app/models/appointments_models/schedule_model.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/general_models/m_response.dart'; import 'package:mc_common_app/models/general_models/m_response.dart';
import 'package:mc_common_app/models/provider_branches_models/profile/services.dart'; import 'package:mc_common_app/models/provider_branches_models/profile/services.dart';
import 'package:mc_common_app/models/appointments_models/schedule_model.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/utils/enums.dart';
import '../models/appointments_models/appointment_list_model.dart'; import '../models/appointments_models/appointment_list_model.dart';
abstract class AppointmentRepo { abstract class AppointmentRepo {
Future<List<AppointmentListModel>> getMyAppointments( Future<List<AppointmentListModel>> getMyAppointments(Map<String, dynamic> map);
Map<String, dynamic> map);
Future<MResponse> updateAppointmentStatus(Map<String, dynamic> map); Future<MResponse> updateAppointmentStatus(Map<String, dynamic> map);
@ -37,20 +35,14 @@ abstract class AppointmentRepo {
Future<MResponse> updateServicesInSchedule(Map map); Future<MResponse> updateServicesInSchedule(Map map);
Future<List<ServiceAppointmentScheduleModel>> Future<List<ServiceAppointmentScheduleModel>> mergeServiceIntoAvailableSchedules({
mergeServiceIntoAvailableSchedules({
required List<String> serviceItemIdsForHome, required List<String> serviceItemIdsForHome,
required List<String> serviceItemIdsForWorkshop, required List<String> serviceItemIdsForWorkshop,
}); });
Future<GenericRespModel> createServiceAppointment( Future<GenericRespModel> createServiceAppointment({required List<ServiceAppointmentScheduleModel> schedules, required int serviceProviderID});
{required List<ServiceAppointmentScheduleModel> schedules,
required int serviceProviderID});
Future<GenericRespModel> cancelOrRescheduleServiceAppointment( Future<GenericRespModel> cancelOrRescheduleServiceAppointment({required int serviceAppointmentID, required int serviceSlotID, required int appointmentScheduleAction});
{required int serviceAppointmentID,
required int serviceSlotID,
required int appointmentScheduleAction});
} }
class AppointmentRepoImp implements AppointmentRepo { class AppointmentRepoImp implements AppointmentRepo {
@ -58,25 +50,19 @@ class AppointmentRepoImp implements AppointmentRepo {
Future<Services> getAllServices(String branchId) async { Future<Services> getAllServices(String branchId) async {
Map<String, dynamic> map = {"ProviderBranchID": branchId}; Map<String, dynamic> map = {"ProviderBranchID": branchId};
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().getJsonForObject( return await injector.get<ApiClient>().getJsonForObject((json) => Services.fromJson(json), ApiConsts.getServicesOfBranch, token: t, queryParameters: map);
(json) => Services.fromJson(json), ApiConsts.getServicesOfBranch,
token: t, queryParameters: map);
} }
@override @override
Future<MResponse> createSchedule(Map map) async { Future<MResponse> createSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject( return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createSchedule, map, token: t);
(json) => MResponse.fromJson(json), ApiConsts.createSchedule, map,
token: t);
} }
@override @override
Future<MResponse> addServicesInSchedule(Map map) async { Future<MResponse> addServicesInSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject( return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createGroup, map, token: t);
(json) => MResponse.fromJson(json), ApiConsts.createGroup, map,
token: t);
} }
@override @override
@ -84,36 +70,29 @@ class AppointmentRepoImp implements AppointmentRepo {
Map<String, dynamic> map = {"ServiceProviderBranchID": branchId}; Map<String, dynamic> map = {"ServiceProviderBranchID": branchId};
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = GenericRespModel adsGenericModel = await injector.get<ApiClient>().getJsonForObject(
await injector.get<ApiClient>().getJsonForObject(
(json) => GenericRespModel.fromJson(json), (json) => GenericRespModel.fromJson(json),
ApiConsts.getSchedule, ApiConsts.getSchedule,
token: t, token: t,
queryParameters: map, queryParameters: map,
); );
return List.generate(adsGenericModel.data.length, return List.generate(adsGenericModel.data.length, (index) => ScheduleData.fromJson(adsGenericModel.data[index]));
(index) => ScheduleData.fromJson(adsGenericModel.data[index]));
} }
@override @override
Future<MResponse> updateSchedule(Map map) async { Future<MResponse> updateSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject( return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateSchedule, map, token: t);
(json) => MResponse.fromJson(json), ApiConsts.updateSchedule, map,
token: t);
} }
@override @override
Future<MResponse> updateServicesInSchedule(Map map) async { Future<MResponse> updateServicesInSchedule(Map map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject( return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateGroup, map, token: t);
(json) => MResponse.fromJson(json), ApiConsts.updateGroup, map,
token: t);
} }
Future<List<ServiceAppointmentScheduleModel>> Future<List<ServiceAppointmentScheduleModel>> mergeServiceIntoAvailableSchedules({
mergeServiceIntoAvailableSchedules({
required List<String> serviceItemIdsForHome, required List<String> serviceItemIdsForHome,
required List<String> serviceItemIdsForWorkshop, required List<String> serviceItemIdsForWorkshop,
}) async { }) async {
@ -128,29 +107,21 @@ class AppointmentRepoImp implements AppointmentRepo {
"ServiceItemIDs": serviceItemIdsForWorkshop, "ServiceItemIDs": serviceItemIdsForWorkshop,
} }
]; ];
GenericRespModel adsGenericModel = GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
await injector.get<ApiClient>().postJsonForObject(
(json) => GenericRespModel.fromJson(json), (json) => GenericRespModel.fromJson(json),
ApiConsts.GetServiceItemAppointmentScheduleSlots, ApiConsts.GetServiceItemAppointmentScheduleSlots,
queryParameters, queryParameters,
token: t, token: t,
); );
if (adsGenericModel.data == null) { if (adsGenericModel.data == null) {
return []; return [];
} }
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleModel = List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleModel =
List.generate( List.generate(adsGenericModel.data.length, (index) => ServiceAppointmentScheduleModel.fromJson(adsGenericModel.data[index], isForAppointment: true));
adsGenericModel.data.length,
(index) =>
ServiceAppointmentScheduleModel.fromJson(
adsGenericModel.data[index],
isForAppointment: true));
return serviceAppointmentScheduleModel; return serviceAppointmentScheduleModel;
} }
Future<GenericRespModel> createServiceAppointment( Future<GenericRespModel> createServiceAppointment({required List<ServiceAppointmentScheduleModel> schedules, required int serviceProviderID}) async {
{required List<ServiceAppointmentScheduleModel> schedules,
required int serviceProviderID}) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
int customerId = AppState().getUser.data!.userInfo!.customerId ?? 0; int customerId = AppState().getUser.data!.userInfo!.customerId ?? 0;
@ -169,23 +140,20 @@ class AppointmentRepoImp implements AppointmentRepo {
"serviceItemID": serviceItemIds, "serviceItemID": serviceItemIds,
}); });
}); });
log("maplist: ${mapList.toString() }");
GenericRespModel adsGenericModel = GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
await injector.get<ApiClient>().postJsonForObject(
(json) => GenericRespModel.fromJson(json), (json) => GenericRespModel.fromJson(json),
ApiConsts.ServiceProvidersAppointmentCreate, ApiConsts.ServiceProvidersAppointmentCreate,
mapList, mapList,
token: t, token: t,
); );
return adsGenericModel; return adsGenericModel;
} }
@override @override
Future<GenericRespModel> cancelOrRescheduleServiceAppointment( Future<GenericRespModel> cancelOrRescheduleServiceAppointment({required int serviceAppointmentID, required int serviceSlotID, required int appointmentScheduleAction}) async {
{required int serviceAppointmentID,
required int serviceSlotID,
required int appointmentScheduleAction}) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
final payload = { final payload = {
@ -194,33 +162,27 @@ class AppointmentRepoImp implements AppointmentRepo {
"appointmentScheduleAction": appointmentScheduleAction, "appointmentScheduleAction": appointmentScheduleAction,
}; };
GenericRespModel adsGenericModel = GenericRespModel adsGenericModel = await injector.get<ApiClient>().postJsonForObject(
await injector.get<ApiClient>().postJsonForObject(
(json) => GenericRespModel.fromJson(json), (json) => GenericRespModel.fromJson(json),
ApiConsts.ServiceProviderAppointmentRescheduleCancelAppointment, ApiConsts.ServiceProviderAppointmentRescheduleCancelAppointment,
payload, payload,
token: t, token: t,
); );
return adsGenericModel; return adsGenericModel;
} }
@override @override
Future<List<AppointmentListModel>> getMyAppointments( Future<List<AppointmentListModel>> getMyAppointments(Map<String, dynamic> map) async {
Map<String, dynamic> map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
GenericRespModel genericRespModel = GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
await injector.get<ApiClient>().getJsonForObject( token: t,
token: t,
(json) => GenericRespModel.fromJson(json), (json) => GenericRespModel.fromJson(json),
queryParameters: map, queryParameters: map,
ApiConsts.serviceProvidersAppointmentGet, ApiConsts.serviceProvidersAppointmentGet,
); );
List<AppointmentListModel> appointmentList = List.generate( List<AppointmentListModel> appointmentList = List.generate(genericRespModel.data.length, (index) => AppointmentListModel.fromJson(genericRespModel.data[index]));
genericRespModel.data.length,
(index) =>
AppointmentListModel.fromJson(genericRespModel.data[index]));
return appointmentList; return appointmentList;
} }
@ -228,42 +190,31 @@ class AppointmentRepoImp implements AppointmentRepo {
Future<MResponse> getAppointmentSlots(Map<String, dynamic> map) async { Future<MResponse> getAppointmentSlots(Map<String, dynamic> map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
MResponse adsGenericModel = MResponse adsGenericModel = await injector.get<ApiClient>().getJsonForObject(
await injector.get<ApiClient>().getJsonForObject(
(json) => MResponse.fromJson(json), (json) => MResponse.fromJson(json),
ApiConsts.getAppointmentSlots, ApiConsts.getAppointmentSlots,
token: t, token: t,
queryParameters: map, queryParameters: map,
); );
return adsGenericModel; return adsGenericModel;
} }
@override @override
Future<MResponse> updateAppointmentPaymentStatus( Future<MResponse> updateAppointmentPaymentStatus(Map<String, dynamic> map) async {
Map<String, dynamic> map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject( return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateAppointmentPaymentStatus, map, token: t);
(json) => MResponse.fromJson(json),
ApiConsts.updateAppointmentPaymentStatus, map,
token: t);
} }
@override @override
Future<MResponse> updateAppointmentStatus(Map<String, dynamic> map) async { Future<MResponse> updateAppointmentStatus(Map<String, dynamic> map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject( return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateAppointmentStatus, map, token: t);
(json) => MResponse.fromJson(json),
ApiConsts.updateAppointmentStatus, map,
token: t);
} }
@override @override
Future<MResponse> createMergeAppointment(Map<String, dynamic> map) async { Future<MResponse> createMergeAppointment(Map<String, dynamic> map) async {
String t = AppState().getUser.data!.accessToken ?? ""; String t = AppState().getUser.data!.accessToken ?? "";
return await injector.get<ApiClient>().postJsonForObject( return await injector.get<ApiClient>().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createMergeAppointment, map, token: t);
(json) => MResponse.fromJson(json),
ApiConsts.createMergeAppointment, map,
token: t);
} }
} }

@ -1,15 +1,19 @@
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:mc_common_app/utils/app_permission_handler.dart';
import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/utils/utils.dart';
import 'package:permission_handler/permission_handler.dart';
abstract class CommonAppServices { abstract class CommonAppServices {
Future<List<File>> pickMultipleImages(); Future<List<File>> pickMultipleImages();
Future<File?> pickImageFromPhone(int sourceFlag); Future<File?> pickImageFromPhone(int sourceFlag);
Future<File?> pickFile({FileType fileType = FileType.custom, List<String?>? allowedExtensions}); Future<List<File>?> pickMultipleFiles(BuildContext context);
} }
class CommonServicesImp implements CommonAppServices { class CommonServicesImp implements CommonAppServices {
@ -24,23 +28,23 @@ class CommonServicesImp implements CommonAppServices {
} }
@override @override
Future<File?> pickFile({FileType fileType = FileType.custom, List<String?>? allowedExtensions}) async { Future<List<File>?> pickMultipleFiles(BuildContext context) async {
FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['png', 'pdf', 'jpeg']); FilePickerResult? result;
final status = await AppPermissions.checkStoragePermissions(context);
if (status) {
result = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: ['pdf']);
}
List<File> pickedFiles = [];
if (result != null) { if (result != null) {
File file = File(result.files.single.path ?? ""); for (var element in result.files) {
int sizeInBytes = file.lengthSync(); if (element.path != null) {
// double sizeInMb = sizeInBytes / (1024 * 1024); pickedFiles.add(File(element.path!));
if (sizeInBytes > 1000) { }
Utils.showToast("File is larger then 1KB");
} else {
return file;
} }
} else {
// User canceled the picker
return null;
} }
return null; return pickedFiles;
} }
@override @override

@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -8,7 +7,6 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/services/my_in_app_browser.dart'; import 'package:mc_common_app/services/my_in_app_browser.dart';
import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
abstract class PaymentService { abstract class PaymentService {
Future<void> placePayment({ Future<void> placePayment({
@ -30,11 +28,11 @@ class PaymentServiceImp implements PaymentService {
MyInAppBrowser? myInAppBrowser; MyInAppBrowser? myInAppBrowser;
var inAppBrowserOptions = InAppBrowserClassOptions( var inAppBrowserOptions = InAppBrowserClassOptions(
inAppWebViewGroupOptions: inAppWebViewGroupOptions:
InAppWebViewGroupOptions(crossPlatform: InAppWebViewOptions(useShouldOverrideUrlLoading: true, transparentBackground: false), ios: IOSInAppWebViewOptions(applePayAPIEnabled: true)), InAppWebViewGroupOptions(crossPlatform: InAppWebViewOptions(useShouldOverrideUrlLoading: true, transparentBackground: false), ios: IOSInAppWebViewOptions(applePayAPIEnabled: true)),
crossPlatform: InAppBrowserOptions(hideUrlBar: true, toolbarTopBackgroundColor: Colors.black), crossPlatform: InAppBrowserOptions(hideUrlBar: true, toolbarTopBackgroundColor: Colors.black),
android: AndroidInAppBrowserOptions(), android: AndroidInAppBrowserOptions(),
ios: ios:
IOSInAppBrowserOptions(hideToolbarBottom: true, toolbarBottomBackgroundColor: Colors.white, closeButtonColor: Colors.white, presentationStyle: IOSUIModalPresentationStyle.OVER_FULL_SCREEN)); IOSInAppBrowserOptions(hideToolbarBottom: true, toolbarBottomBackgroundColor: Colors.white, closeButtonColor: Colors.white, presentationStyle: IOSUIModalPresentationStyle.OVER_FULL_SCREEN));
@override @override
Future<void> placePayment({ Future<void> placePayment({
@ -51,6 +49,7 @@ class PaymentServiceImp implements PaymentService {
urlRequest = "${ApiConsts.paymentWebViewUrl}?PaymentType=${paymentType.getIdFromPaymentTypesEnum()}&OrderProviderSubscriptionID=$id"; urlRequest = "${ApiConsts.paymentWebViewUrl}?PaymentType=${paymentType.getIdFromPaymentTypesEnum()}&OrderProviderSubscriptionID=$id";
break; break;
case PaymentTypes.appointment: case PaymentTypes.appointment:
case PaymentTypes.partialAppointment:
String appointIds = ''; String appointIds = '';
for (int i = 0; i < appointmentIds!.length; i++) { for (int i = 0; i < appointmentIds!.length; i++) {
var element = appointmentIds[i]; var element = appointmentIds[i];

@ -1,67 +0,0 @@
import 'package:permission_handler/permission_handler.dart';
import 'dialogs_and_bottomsheets.dart';
enum ConfirmAction { CANCEL, ACCEPT }
Future<bool> requestPermissionGranted(
context, Permission requestPermissions) async {
var result = await requestPermissions.request();
switch (result) {
case PermissionStatus.granted:
// Application has been given permission to use the feature.
return true;
case PermissionStatus.denied:
// Application has been denied permission to use the feature.
return false;
case PermissionStatus.permanentlyDenied:
ConfirmAction? res = await showConfirmDialogs(
context,
'You was denied Permission. You have give manual permission from app setting. ',
'Open App Setting',
'Cancel');
if (res == ConfirmAction.ACCEPT) {
return false;
} else if (res == ConfirmAction.CANCEL) {
return false;
}
return false;
case PermissionStatus.restricted:
// iOS has restricted access to a specific feature.
return false;
default:
return false;
}
}
class AppPermissions{
static void location(Function(bool) completion) {
Permission.location.isGranted.then((isGranted){
if(!isGranted){
Permission.location.request().then((granted){
completion(granted == PermissionStatus.granted);
});
}
completion(isGranted);
});
}
static void checkAll(Function(bool) completion){
[
Permission.location
].request().then((value){
bool allGranted = false;
value.values.forEach((element) {
allGranted = allGranted && element == PermissionStatus.granted;
});
completion(allGranted);
});
}
}

@ -0,0 +1,97 @@
import 'dart:developer';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/widgets/common_widgets/confirm_dialog.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dialogs_and_bottomsheets.dart';
enum ConfirmAction { CANCEL, ACCEPT }
Future<bool> requestPermissionGranted(context, Permission requestPermissions) async {
var result = await requestPermissions.request();
log("result: $result");
switch (result) {
case PermissionStatus.granted:
// Application has been given permission to use the feature.
return true;
case PermissionStatus.denied:
// Application has been denied permission to use the feature.
return false;
case PermissionStatus.permanentlyDenied:
ConfirmAction? res = await showConfirmDialogs(context, 'This permission was denied permanently, Please go to settings and allow. ', 'Open App Setting', 'Cancel');
if (res == ConfirmAction.ACCEPT) {
return false;
} else if (res == ConfirmAction.CANCEL) {
return false;
}
return false;
case PermissionStatus.restricted:
// iOS has restricted access to a specific feature.
return false;
default:
return false;
}
}
class AppPermissions {
static void location(Function(bool) completion) {
Permission.location.isGranted.then((isGranted) {
if (!isGranted) {
Permission.location.request().then((granted) {
completion(granted == PermissionStatus.granted);
});
}
completion(isGranted);
});
}
static void checkAll(Function(bool) completion) {
[Permission.location].request().then((value) {
bool allGranted = false;
for (var element in value.values) {
allGranted = allGranted && element == PermissionStatus.granted;
}
completion(allGranted);
});
}
static getDialog(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to give storage permission to select files.",
onTap: () {
Navigator.pop(context);
},
),
);
}
static Future<bool> checkStoragePermissions(BuildContext context) async {
bool permissionStatus;
final deviceInfo = await DeviceInfoPlugin().androidInfo;
if (deviceInfo.version.sdkInt! > 32) {
permissionStatus = await Permission.photos.request().isGranted;
if (permissionStatus) {
return true;
} else {
getDialog(context);
return false;
}
} else {
permissionStatus = await Permission.storage.request().isGranted;
if (permissionStatus) {
return true;
} else {
getDialog(context);
return false;
}
}
}
}

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/utils/AppPermissionHandler.dart'; import 'package:mc_common_app/utils/app_permission_handler.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
Future<ConfirmAction?> showConfirmDialogs(context, msg, positiveText, negativeText) async { Future<ConfirmAction?> showConfirmDialogs(context, msg, positiveText, negativeText) async {

@ -17,6 +17,9 @@ enum AdReserveStatus {
reserved, reserved,
cancelledByOwner, cancelledByOwner,
cancelledByAdmin, cancelledByAdmin,
timeOver,
dealDone,
fullPaymentVerified,
} }
enum CreatedByRoleEnum { customer, provider, admin, allAds } enum CreatedByRoleEnum { customer, provider, admin, allAds }
@ -82,6 +85,7 @@ enum PaymentTypes {
ads, ads,
request, request,
extendAds, extendAds,
partialAppointment
} }
enum AdCreationSteps { enum AdCreationSteps {

@ -7,7 +7,7 @@ import 'package:flutter/rendering.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:mc_common_app/utils/AppPermissionHandler.dart'; import 'package:mc_common_app/utils/app_permission_handler.dart';
import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/utils/utils.dart';

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -15,9 +16,9 @@ import 'package:mc_common_app/models/advertisment_models/special_service_model.d
import 'package:mc_common_app/models/advertisment_models/ss_car_check_schedule_model.dart'; import 'package:mc_common_app/models/advertisment_models/ss_car_check_schedule_model.dart';
import 'package:mc_common_app/models/advertisment_models/ss_photo_schedule_model.dart'; import 'package:mc_common_app/models/advertisment_models/ss_photo_schedule_model.dart';
import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart'; import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/general_models/enums_model.dart'; import 'package:mc_common_app/models/general_models/enums_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.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/general_models/widgets_models.dart';
import 'package:mc_common_app/repositories/ads_repo.dart'; import 'package:mc_common_app/repositories/ads_repo.dart';
import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/repositories/common_repo.dart';
@ -75,7 +76,7 @@ class AdVM extends BaseVM {
List<AdDetailsModel> exploreAdsFilteredList = []; List<AdDetailsModel> exploreAdsFilteredList = [];
List<AdDetailsModel> myAdsFilteredList = []; List<AdDetailsModel> myAdsFilteredList = [];
List<AdDetailsModel> myAds = []; List<AdDetailsModel> myAds = [];
List<MyReservedAdsRespModel> myReservedAdsRespModel = []; List<AdDetailsModel> myReservedAds = [];
List<AdDetailsModel> myActiveAdsForHome = []; List<AdDetailsModel> myActiveAdsForHome = [];
List<VehicleDamageCard> vehicleDamageCards = []; List<VehicleDamageCard> vehicleDamageCards = [];
@ -161,11 +162,12 @@ class AdVM extends BaseVM {
List<FilterListModel> myAdsFilterOptions = []; List<FilterListModel> myAdsFilterOptions = [];
populateAdsFilterList() async { populateAdsFilterList() async {
if (myAdsFilterOptions.isNotEmpty && exploreAdsFilterOptions.isNotEmpty) return;
if (myAdsEnums.isEmpty) { if (myAdsEnums.isEmpty) {
myAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: 18); //TODO: 18 is to get My Ad Filter Enums myAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: 18); //18 is to get My Ad Filter Enums
} }
if (exploreAdsEnums.isEmpty) { if (exploreAdsEnums.isEmpty) {
exploreAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: 23); //TODO: 23 is to get Explore Ad Filter Enums exploreAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: 23); // 23 is to get Explore Ad Filter Enums
} }
exploreAdsFilterOptions.clear(); exploreAdsFilterOptions.clear();
@ -217,10 +219,8 @@ class AdVM extends BaseVM {
return; return;
} }
// this means if the filter is reserved ads // this means if the filter is reserved ads
dynamic selectedIds = []; if (adPostStatusEnum.getIdFromAdPostStatusEnum() == 9) {
if (index == 3 && adPostStatusEnum.getIdFromAdPostStatusEnum() == 9) { myAdsFilteredList = myReservedAds;
selectedIds = myReservedAdsRespModel.map((component) => component.adsID).toList();
myAdsFilteredList = myAds.where((element) => selectedIds.contains(element.id)).toList();
for (var ad in myAdsFilteredList) { for (var ad in myAdsFilteredList) {
ad.isReservedByMe = true; ad.isReservedByMe = true;
} }
@ -241,12 +241,25 @@ class AdVM extends BaseVM {
setState(ViewState.idle); setState(ViewState.idle);
} }
Future<void> getMyReservedAds() async { Future<List<AdDetailsModel>> getMyReservedAds() async {
setState(ViewState.busy); setState(ViewState.busy);
//TODO: BREAKING myReservedAds = await adsRepo.getMyReservedAds();
// myReservedAdsRespModel = await adsRepo.getMyReservedAds();
isFetchingLists = false; isFetchingLists = false;
setState(ViewState.idle); setState(ViewState.idle);
return myReservedAds;
}
Future<bool> createReserveAd({required int adId, required BuildContext context}) async {
Utils.showLoading(context);
GenericRespModel genericRespModel = await adsRepo.createReserveAd(adId: adId);
if (genericRespModel.messageStatus == null) {
Utils.hideLoading(context);
Utils.showToast(genericRespModel.message ?? "Something went wrong!");
return false;
}
Utils.hideLoading(context);
return true;
} }
Future<void> getExploreAds() async { Future<void> getExploreAds() async {
@ -316,9 +329,9 @@ class AdVM extends BaseVM {
navigateReplaceWithName(context, AppRoutes.dashboard); navigateReplaceWithName(context, AppRoutes.dashboard);
} }
Future<void> cancelMyAdReservation(BuildContext context, {required int adId}) async { Future<void> cancelMyAdReservation(BuildContext context, {required int adId, required String reason}) async {
Utils.showLoading(context); Utils.showLoading(context);
GenericRespModel respModel = await adsRepo.cancelMyAdReservation(adId: adId); GenericRespModel respModel = await adsRepo.cancelMyAdReservation(adId: adId, adsReserveStatus: 11, reason: reason); // 11 is to Cancel Reservation
if (respModel.messageStatus != 1) { if (respModel.messageStatus != 1) {
Utils.hideLoading(context); Utils.hideLoading(context);
@ -532,6 +545,22 @@ class AdVM extends BaseVM {
vehicleDemandAmount = amount; vehicleDemandAmount = amount;
} }
String reservationCancelReason = "";
String reservationCancelError = "";
void updateReservationCancelReason(String value) {
if (value.isNotEmpty) {
reservationCancelError = "";
}
reservationCancelReason = value;
}
String completeDealNotesForAdmin = "";
void updateCompleteDealNotesForAdmin(String value) {
completeDealNotesForAdmin = value;
}
String vehicleTitle = ""; String vehicleTitle = "";
void updateVehicleTitle(String title) { void updateVehicleTitle(String title) {
@ -854,6 +883,17 @@ class AdVM extends BaseVM {
return isValidated; return isValidated;
} }
bool validateReservationCancelReason() {
bool isValidated = true;
if (reservationCancelReason.isEmpty) {
reservationCancelError = GlobalConsts.reservationCancelError;
isValidated = false;
notifyListeners();
}
return isValidated;
}
bool isDamagePartsValidated() { bool isDamagePartsValidated() {
bool isValidated = true; bool isValidated = true;
@ -863,6 +903,7 @@ class AdVM extends BaseVM {
isValidated = false; isValidated = false;
} else { } else {
element.partSelectedId!.errorValue = ""; element.partSelectedId!.errorValue = "";
element.partSelectedId!.errorValue = "";
} }
} }
@ -977,11 +1018,9 @@ class AdVM extends BaseVM {
int status = await createNewAd(); int status = await createNewAd();
if (status != 1) { if (status != 1) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast("Something went wrong!");
return; return;
} }
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast("A new ads has been created.");
currentProgressStep = AdCreationSteps.vehicleDetails; currentProgressStep = AdCreationSteps.vehicleDetails;
resetValues(); resetValues();
updateIsExploreAds(false); updateIsExploreAds(false);
@ -1035,6 +1074,26 @@ class AdVM extends BaseVM {
notifyListeners(); notifyListeners();
} }
List<File> pickedReceiptPdfFiles = [];
String receiptPdfFileError = "";
void removePdfFileFromList(String filePath) {
int index = pickedReceiptPdfFiles.indexWhere((element) => element.path == filePath);
if (index == -1) {
return;
}
pickedReceiptPdfFiles.removeAt(index);
notifyListeners();
}
void pickPdfReceiptFile(BuildContext context) async {
List<File>? files = await commonServices.pickMultipleFiles(context);
pickedReceiptPdfFiles.addAll(files!);
if (pickedReceiptPdfFiles.isNotEmpty) receiptPdfFileError = "";
notifyListeners();
}
// sourceFlag for Camera = 0 // sourceFlag for Camera = 0
// sourceFlag for Gallery = 1 // sourceFlag for Gallery = 1
void pickDamagePartImage(int index) async { void pickDamagePartImage(int index) async {
@ -1313,6 +1372,8 @@ class AdVM extends BaseVM {
AdsCreationPayloadModel adsCreationPayloadModel = AdsCreationPayloadModel(ads: ads, vehiclePosting: vehiclePosting); AdsCreationPayloadModel adsCreationPayloadModel = AdsCreationPayloadModel(ads: ads, vehiclePosting: vehiclePosting);
GenericRespModel respModel = await adsRepo.createNewAd(adsCreationPayloadModel: adsCreationPayloadModel); GenericRespModel respModel = await adsRepo.createNewAd(adsCreationPayloadModel: adsCreationPayloadModel);
Utils.showToast(respModel.message.toString());
return Future.value(respModel.messageStatus); return Future.value(respModel.messageStatus);
} }

@ -4,15 +4,15 @@ import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/general_models/enums_model.dart'; import 'package:mc_common_app/models/general_models/enums_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/general_models/m_response.dart'; import 'package:mc_common_app/models/general_models/m_response.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart';
import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.dart'; import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.dart';
import 'package:mc_common_app/models/appointments_models/service_schedule_model.dart';
import 'package:mc_common_app/models/services_models/item_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/models/services_models/service_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/repositories/appointment_repo.dart'; import 'package:mc_common_app/repositories/appointment_repo.dart';
import 'package:mc_common_app/repositories/common_repo.dart'; import 'package:mc_common_app/repositories/common_repo.dart';
import 'package:mc_common_app/repositories/provider_repo.dart'; import 'package:mc_common_app/repositories/provider_repo.dart';
@ -39,10 +39,7 @@ class AppointmentsVM extends BaseVM {
final ProviderRepo providerRepo; final ProviderRepo providerRepo;
final AppointmentRepo scheduleRepo; final AppointmentRepo scheduleRepo;
AppointmentsVM({required this.commonServices, AppointmentsVM({required this.commonServices, required this.scheduleRepo, required this.providerRepo, required this.commonRepo});
required this.scheduleRepo,
required this.providerRepo,
required this.commonRepo});
bool isUpcommingEnabled = true; bool isUpcommingEnabled = true;
bool isFetchingLists = false; bool isFetchingLists = false;
@ -69,8 +66,7 @@ class AppointmentsVM extends BaseVM {
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleList = []; List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleList = [];
bool ifItemAlreadySelected(int id) { bool ifItemAlreadySelected(int id) {
int indexFound = allSelectedItemsInAppointments int indexFound = allSelectedItemsInAppointments.indexWhere((element) => element.id == id);
.indexWhere((element) => element.id == id);
if (indexFound != -1) { if (indexFound != -1) {
return true; return true;
} }
@ -81,25 +77,17 @@ class AppointmentsVM extends BaseVM {
setupProviderAppointmentFilter() { setupProviderAppointmentFilter() {
appointmentsFilterOptions.clear(); appointmentsFilterOptions.clear();
appointmentsFilterOptions.add( appointmentsFilterOptions.add(FilterListModel(id: 0, title: "All Appointments", isSelected: true));
FilterListModel(id: 0, title: "All Appointments", isSelected: true)); appointmentsFilterOptions.add(FilterListModel(id: 2, title: "Confirmed", isSelected: false));
appointmentsFilterOptions appointmentsFilterOptions.add(FilterListModel(id: 3, title: "Arrived", isSelected: false));
.add(FilterListModel(id: 2, title: "Confirmed", isSelected: false)); appointmentsFilterOptions.add(FilterListModel(id: 7, title: "Work In Progress", isSelected: false));
appointmentsFilterOptions appointmentsFilterOptions.add(FilterListModel(id: 8, title: "Completed", isSelected: false));
.add(FilterListModel(id: 3, title: "Arrived", isSelected: false)); appointmentsFilterOptions.add(FilterListModel(id: 4, title: "Canceled", isSelected: false));
appointmentsFilterOptions
.add(
FilterListModel(id: 7, title: "Work In Progress", isSelected: false));
appointmentsFilterOptions
.add(FilterListModel(id: 8, title: "Completed", isSelected: false));
appointmentsFilterOptions
.add(FilterListModel(id: 4, title: "Canceled", isSelected: false));
} }
Future<void> onItemsSelectedInService() async { Future<void> onItemsSelectedInService() async {
if (currentServiceSelection != null) { if (currentServiceSelection != null) {
int index = servicesInCurrentAppointment.indexWhere((element) => int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
element.serviceId == currentServiceSelection!.serviceId!);
if (index == -1) { if (index == -1) {
double totalPrice = 0.0; double totalPrice = 0.0;
@ -115,47 +103,49 @@ class AppointmentsVM extends BaseVM {
} }
} }
Future<void> onPayNowPressedForAppointment({required BuildContext context, required int appointmentID}) async {
context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: [appointmentID]);
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.partialAppointment);
}
Future<void> onBookAppointmentPressed(BuildContext context) async { Future<void> onBookAppointmentPressed(BuildContext context) async {
Utils.showLoading(context); Utils.showLoading(context);
bool isSuccess = false; bool isSuccess = false;
List<int> appointmentIdsList = []; List<int> appointmentIdsList = [];
try { try {
GenericRespModel genericRespModel = GenericRespModel genericRespModel = await scheduleRepo.createServiceAppointment(
await scheduleRepo.createServiceAppointment(
schedules: serviceAppointmentScheduleList, schedules: serviceAppointmentScheduleList,
serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0, serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0,
); );
if (genericRespModel.messageStatus == 2 || if (genericRespModel.data.isEmpty) {
genericRespModel.data == null) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
return; return;
} }
if (genericRespModel.data != null) {
if (genericRespModel.data != null && genericRespModel.data.isNotEmpty) {
genericRespModel.data.forEach((element) { genericRespModel.data.forEach((element) {
if (element['appointmentID'] != 0) { if (element['appointmentID'] != 0) {
appointmentIdsList.add(element['appointmentID']); appointmentIdsList.add(element['appointmentID']);
isSuccess = true; isSuccess = true;
} else { } else {
isSuccess = false; isSuccess = false;
Utils.showToast(element['message']);
return; return;
} }
}); });
} }
context.read<DashboardVmCustomer>().onNavbarTapped(1); context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.booked);
appointmentStatusEnum: AppointmentStatusEnum.booked);
Utils.hideLoading(context); Utils.hideLoading(context);
resetAfterBookingAppointment(); resetAfterBookingAppointment();
if (isSuccess) { if (isSuccess) {
if (amountToPayForAppointment > 0) { if (amountToPayForAppointment > 0) {
context context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: appointmentIdsList);
.read<PaymentVM>() navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
.updateAppointmentIdsForPayment(ids: appointmentIdsList);
navigateWithName(context, AppRoutes.paymentMethodsView,
arguments: PaymentTypes.appointment);
} else { } else {
Utils.showToast("Your appointment has been booked successfully!"); Utils.showToast("Your appointment has been booked successfully!");
getMyAppointments(); getMyAppointments();
@ -167,36 +157,28 @@ class AppointmentsVM extends BaseVM {
} }
} }
Future<void> onConfirmAppointmentPressed( Future<void> onConfirmAppointmentPressed({required BuildContext context, required appointmentId}) async {
{required BuildContext context, required appointmentId}) async { context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: [appointmentId]);
context navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
.read<PaymentVM>()
.updateAppointmentIdsForPayment(ids: [appointmentId]);
navigateWithName(context, AppRoutes.paymentMethodsView,
arguments: PaymentTypes.appointment);
} }
Future<void> onCancelAppointmentPressed({required BuildContext context, Future<void> onCancelAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
required AppointmentListModel appointmentListModel}) async {
Utils.showLoading(context); Utils.showLoading(context);
try { try {
GenericRespModel genericRespModel = GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment(
await scheduleRepo.cancelOrRescheduleServiceAppointment(
serviceAppointmentID: appointmentListModel.id ?? 0, serviceAppointmentID: appointmentListModel.id ?? 0,
serviceSlotID: appointmentListModel.serviceSlotID ?? 0, serviceSlotID: appointmentListModel.serviceSlotID ?? 0,
appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel
); );
if (genericRespModel.messageStatus == 2 || if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
genericRespModel.data == null) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
return; return;
} }
if (genericRespModel.data == 1) { if (genericRespModel.messageStatus == 1) {
context.read<DashboardVmCustomer>().onNavbarTapped(1); context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
await getMyAppointments(); await getMyAppointments();
Utils.hideLoading(context); Utils.hideLoading(context);
@ -227,8 +209,7 @@ class AppointmentsVM extends BaseVM {
notifyListeners(); notifyListeners();
} }
SelectionModel branchSelectedCategoryId = SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateProviderCategoryId(SelectionModel id) { void updateProviderCategoryId(SelectionModel id) {
branchSelectedCategoryId = id; branchSelectedCategoryId = id;
@ -247,70 +228,59 @@ class AppointmentsVM extends BaseVM {
void updateBranchServiceId(SelectionModel id) async { void updateBranchServiceId(SelectionModel id) async {
branchSelectedServiceId = id; branchSelectedServiceId = id;
currentServiceSelection = branchServices.firstWhere( currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId);
(element) => element.serviceProviderServiceId == id.selectedId);
notifyListeners(); notifyListeners();
} }
void removeServiceInCurrentAppointment(int index) { void removeServiceInCurrentAppointment(int index) {
int serviceId = servicesInCurrentAppointment int serviceId = servicesInCurrentAppointment.elementAt(index).serviceProviderServiceId ?? -1;
.elementAt(index) allSelectedItemsInAppointments.removeWhere((element) => element.serviceProviderServiceId == serviceId);
.serviceProviderServiceId ??
-1;
allSelectedItemsInAppointments.removeWhere(
(element) => element.serviceProviderServiceId == serviceId);
servicesInCurrentAppointment.removeAt(index); servicesInCurrentAppointment.removeAt(index);
notifyListeners(); notifyListeners();
} }
resetCategorySelectionBottomSheet() { resetCategorySelectionBottomSheet() {
selectedSubServicesCounter = 0; selectedSubServicesCounter = 0;
branchSelectedCategoryId = branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
isHomeTapped = false; isHomeTapped = false;
branchSelectedServiceId = branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
currentServiceSelection = null; currentServiceSelection = null;
} }
resetAfterBookingAppointment() { resetAfterBookingAppointment() {
allSelectedItemsInAppointments.clear(); // allSelectedItemsInAppointments.clear();
// servicesInCurrentAppointment.clear(); servicesInCurrentAppointment.clear();
serviceAppointmentScheduleList.clear(); // serviceAppointmentScheduleList.clear();
} }
List<EnumsModel> myAppointmentsEnum = []; List<EnumsModel> myAppointmentsEnum = [];
populateAppointmentsFilterList() async { populateAppointmentsFilterList() async {
appointmentsFilterOptions.clear(); if (appointmentsFilterOptions.isNotEmpty) return;
myAppointmentsEnum = await commonRepo.getEnumTypeValues( myAppointmentsEnum = await commonRepo.getEnumTypeValues(enumTypeID: 13); //TODO: 13 is to get Appointments Filter Enums
enumTypeID: 13); //TODO: 13 is to get Appointments Filter Enums
for (int i = 0; i < myAppointmentsEnum.length; i++) { for (int i = 0; i < myAppointmentsEnum.length; i++) {
appointmentsFilterOptions.add(FilterListModel( appointmentsFilterOptions.add(FilterListModel(title: myAppointmentsEnum[i].enumValueStr, isSelected: false, id: myAppointmentsEnum[i].enumValue));
title: myAppointmentsEnum[i].enumValueStr,
isSelected: false,
id: myAppointmentsEnum[i].enumValue));
} }
appointmentsFilterOptions.insert( appointmentsFilterOptions.insert(0, FilterListModel(title: "All Appointments", isSelected: true, id: 0));
0, FilterListModel(title: "All Appointments", isSelected: true, id: 0));
// TODO: THIS SHOULD REMOVED AND ADDED IN THE ENUMS API
appointmentsFilterOptions.add(FilterListModel(title: "Work In Progress", isSelected: false, id: 7));
appointmentsFilterOptions.add(FilterListModel(title: "Visit Completed", isSelected: false, id: 8));
notifyListeners(); notifyListeners();
} }
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) {
{required AppointmentStatusEnum appointmentStatusEnum,
bool isNeedCustomerFilter = false}) {
if (appointmentsFilterOptions.isEmpty) return; if (appointmentsFilterOptions.isEmpty) return;
for (var value in appointmentsFilterOptions) { for (var value in appointmentsFilterOptions) {
value.isSelected = false; value.isSelected = false;
} }
appointmentsFilterOptions.forEach((element) { appointmentsFilterOptions.forEach((element) {
if (element.id == if (element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum()) {
appointmentStatusEnum.getIdFromAppointmentStatusEnum()) {
element.isSelected = true; element.isSelected = true;
} }
}); });
@ -325,16 +295,11 @@ class AppointmentsVM extends BaseVM {
return; return;
} }
myFilteredAppointments = myAppointments myFilteredAppointments = myAppointments.where((element) => element.appointmentStatusID! == appointmentStatusEnum.getIdFromAppointmentStatusEnum()).toList();
.where((element) =>
element.appointmentStatusID! ==
appointmentStatusEnum.getIdFromAppointmentStatusEnum())
.toList();
if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(); if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers();
notifyListeners(); notifyListeners();
} }
findAppointmentsBasedOnCustomers() { findAppointmentsBasedOnCustomers() {
// Use a Set to ensure uniqueness of customerIDs // Use a Set to ensure uniqueness of customerIDs
Set<int> uniqueCustomerIDs = Set<int>(); Set<int> uniqueCustomerIDs = Set<int>();
@ -346,9 +311,7 @@ class AppointmentsVM extends BaseVM {
// Create a list of CustomerData instances // Create a list of CustomerData instances
myFilteredAppointments2 = uniqueCustomerIDs.map((id) { myFilteredAppointments2 = uniqueCustomerIDs.map((id) {
List<AppointmentListModel> list = myFilteredAppointments List<AppointmentListModel> list = myFilteredAppointments.where((item) => item.customerID == id).toList();
.where((item) => item.customerID == id)
.toList();
AppointmentListModel model = list.first; AppointmentListModel model = list.first;
model.customerAppointmentList = list; model.customerAppointmentList = list;
return model; return model;
@ -373,10 +336,7 @@ class AppointmentsVM extends BaseVM {
myAppointments = await commonRepo.getMyAppointments(); myAppointments = await commonRepo.getMyAppointments();
myFilteredAppointments = myAppointments; myFilteredAppointments = myAppointments;
myUpComingAppointments = myAppointments myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.confirmed).toList();
.where((element) =>
element.appointmentStatusEnum == AppointmentStatusEnum.confirmed)
.toList();
setState(ViewState.idle); setState(ViewState.idle);
// applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); // applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
notifyListeners(); notifyListeners();
@ -384,9 +344,7 @@ class AppointmentsVM extends BaseVM {
AppointmentSlots? appointmentSlots; AppointmentSlots? appointmentSlots;
Future<void> getAppointmentSlotsInfo({required Map<String, dynamic> map, Future<void> getAppointmentSlotsInfo({required Map<String, dynamic> map, required BuildContext context, bool isNeedToRebuild = false}) async {
required BuildContext context,
bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
try { try {
MResponse genericRespModel = await scheduleRepo.getAppointmentSlots(map); MResponse genericRespModel = await scheduleRepo.getAppointmentSlots(map);
@ -400,20 +358,14 @@ class AppointmentsVM extends BaseVM {
} }
} }
Future<void> getProviderMyAppointments(Map<String, dynamic> map, Future<void> getProviderMyAppointments(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
{bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
myAppointments = await scheduleRepo.getMyAppointments(map); myAppointments = await scheduleRepo.getMyAppointments(map);
myFilteredAppointments = myAppointments; myFilteredAppointments = myAppointments;
myUpComingAppointments = myAppointments myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked).toList();
.where((element) =>
element.appointmentStatusEnum == AppointmentStatusEnum.booked) applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
.toList();
applyFilterOnAppointmentsVM(
appointmentStatusEnum: AppointmentStatusEnum.allAppointments,
isNeedCustomerFilter: true);
setState(ViewState.idle); setState(ViewState.idle);
} }
@ -423,12 +375,10 @@ class AppointmentsVM extends BaseVM {
notifyListeners(); notifyListeners();
} }
updateAppointmentStatus(Map<String, dynamic> map, updateAppointmentStatus(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
{bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
try { try {
MResponse genericRespModel = MResponse genericRespModel = await scheduleRepo.updateAppointmentStatus(map);
await scheduleRepo.updateAppointmentStatus(map);
if (genericRespModel.messageStatus == 1) { if (genericRespModel.messageStatus == 1) {
Utils.showToast("appointment status updated"); Utils.showToast("appointment status updated");
@ -440,12 +390,10 @@ class AppointmentsVM extends BaseVM {
} }
} }
updateAppointmentPaymentStatus(Map<String, dynamic> map, updateAppointmentPaymentStatus(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
{bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
try { try {
MResponse genericRespModel = MResponse genericRespModel = await scheduleRepo.updateAppointmentPaymentStatus(map);
await scheduleRepo.updateAppointmentPaymentStatus(map);
if (genericRespModel.messageStatus == 1) { if (genericRespModel.messageStatus == 1) {
Utils.showToast("payment status updated"); Utils.showToast("payment status updated");
@ -457,11 +405,9 @@ class AppointmentsVM extends BaseVM {
} }
} }
Future<MResponse> createMergeAppointment(Map<String, dynamic> map, Future<MResponse> createMergeAppointment(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
{bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
MResponse genericRespModel = MResponse genericRespModel = await scheduleRepo.createMergeAppointment(map);
await scheduleRepo.createMergeAppointment(map);
return genericRespModel; return genericRespModel;
} }
@ -469,16 +415,10 @@ class AppointmentsVM extends BaseVM {
bool inNeedToEnableMergeButton = false; bool inNeedToEnableMergeButton = false;
updateCheckBoxInMergeRequest(int currentIndex) { updateCheckBoxInMergeRequest(int currentIndex) {
myFilteredAppointments2[selectedAppointmentIndex] myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList![currentIndex].isSelected =
.customerAppointmentList![currentIndex] !(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList?[currentIndex].isSelected ?? false);
.isSelected = !(myFilteredAppointments2[selectedAppointmentIndex]
.customerAppointmentList?[currentIndex] int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex].customerAppointmentList ?? []);
.isSelected ??
false);
int count = countSelected(myFilteredAppointments2[selectedAppointmentIndex]
.customerAppointmentList ??
[]);
if (count > 1) if (count > 1)
inNeedToEnableMergeButton = true; inNeedToEnableMergeButton = true;
else else
@ -487,61 +427,35 @@ class AppointmentsVM extends BaseVM {
} }
int countSelected(List<AppointmentListModel> appointments) { int countSelected(List<AppointmentListModel> appointments) {
return appointments return appointments.where((appointment) => appointment.isSelected == true).toList().length;
.where((appointment) => appointment.isSelected == true)
.toList()
.length;
} }
updateSelectedAppointmentDate( updateSelectedAppointmentDate({required int dateIndex, required int scheduleIndex}) {
{required int dateIndex, required int scheduleIndex}) { for (var element in serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!) {
for (var element in serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList!) {
element.date!.isSelected = false; element.date!.isSelected = false;
} }
serviceAppointmentScheduleList[scheduleIndex] serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.isSelected = true;
.customTimeDateSlotList![dateIndex]
.date!
.isSelected = true;
serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex = dateIndex; serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex = dateIndex;
final date = TimeSlotModel( final date = TimeSlotModel(
date: serviceAppointmentScheduleList[scheduleIndex] date: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.date,
.customTimeDateSlotList![dateIndex] slotId: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.slotId,
.date!
.date,
slotId: serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList![dateIndex]
.date!
.slotId,
isSelected: true, isSelected: true,
slot: "", slot: "",
); );
serviceAppointmentScheduleList[scheduleIndex] serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date);
.selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date);
notifyListeners(); notifyListeners();
} }
updateSelectedAppointmentSlotByDate( updateSelectedAppointmentSlotByDate({required int scheduleIndex, required int slotIndex}) {
{required int scheduleIndex, required int slotIndex}) { for (var element in serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!) {
for (var element in serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList!) {
for (var element in element.availableSlots!) { for (var element in element.availableSlots!) {
element.isSelected = false; element.isSelected = false;
} }
} }
int index = int index = serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!;
serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!; serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots![slotIndex].isSelected = true;
serviceAppointmentScheduleList[scheduleIndex] serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel!.availableSlots = serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots!;
.customTimeDateSlotList![index]
.availableSlots![slotIndex]
.isSelected = true;
serviceAppointmentScheduleList[scheduleIndex]
.selectedCustomTimeDateSlotModel!
.availableSlots =
serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList![index]
.availableSlots!;
notifyListeners(); notifyListeners();
} }
@ -555,9 +469,7 @@ class AppointmentsVM extends BaseVM {
int selectedSubServicesCounter = 0; int selectedSubServicesCounter = 0;
onItemUpdateOrSelected(int index, bool selected, int itemId) { onItemUpdateOrSelected(int index, bool selected, int itemId) {
int serviceIndex = servicesInCurrentAppointment.indexWhere( int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
(element) =>
element.serviceId == currentServiceSelection!.serviceId!);
// print("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); // print("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}");
// if (serviceIndex == -1) { // if (serviceIndex == -1) {
// return; // return;
@ -572,34 +484,25 @@ class AppointmentsVM extends BaseVM {
allSelectedItemsInAppointments.add(serviceItemsFromApi[index]); allSelectedItemsInAppointments.add(serviceItemsFromApi[index]);
for (var element in allSelectedItemsInAppointments) { for (var element in allSelectedItemsInAppointments) {
if (!ifItemAlreadySelected(element.id!)) { if (!ifItemAlreadySelected(element.id!)) {
servicesInCurrentAppointment[serviceIndex] servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]);
.serviceItems!
.add(serviceItemsFromApi[index]);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex] servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
.currentTotalServicePrice +
double.parse((serviceItemsFromApi[index].price) ?? "0.0");
} }
} }
} }
if (!selected) { if (!selected) {
selectedSubServicesCounter = selectedSubServicesCounter - 1; selectedSubServicesCounter = selectedSubServicesCounter - 1;
currentServiceSelection!.serviceItems! currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId);
.removeWhere((element) => element.id == itemId); allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId);
allSelectedItemsInAppointments
.removeWhere((element) => element.id == itemId);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0");
double.parse((serviceItemsFromApi[index].price) ?? "0.0"); servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId);
servicesInCurrentAppointment[serviceIndex]
.serviceItems!
.removeWhere((element) => element.id == itemId);
} }
notifyListeners(); notifyListeners();
} }
populateBranchesFilterList() { populateBranchesFilterList() {
providersFilterOptions.clear(); providersFilterOptions.clear(); // TODO: THIS SHOULD BE DYNAMIC AND FILTERS SHOULD COME FORM API
providersFilterOptions = [ providersFilterOptions = [
FilterListModel(title: "All Providers", isSelected: true, id: 0), FilterListModel(title: "All Providers", isSelected: true, id: 0),
FilterListModel(title: "Maintenance", isSelected: false, id: 1), FilterListModel(title: "Maintenance", isSelected: false, id: 1),
@ -649,8 +552,7 @@ class AppointmentsVM extends BaseVM {
String pickHomeLocationError = ""; String pickHomeLocationError = "";
String selectSubServicesError = ""; String selectSubServicesError = "";
SelectionModel branchSelectedServiceId = SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
bool isCategoryAlreadyPresent(int id) { bool isCategoryAlreadyPresent(int id) {
final contain = branchCategories.where((element) => element.id == id); final contain = branchCategories.where((element) => element.id == id);
@ -663,16 +565,14 @@ class AppointmentsVM extends BaseVM {
void getBranchCategories() async { void getBranchCategories() async {
for (var value in selectedBranchModel!.branchServices!) { for (var value in selectedBranchModel!.branchServices!) {
if (!isCategoryAlreadyPresent(value.categoryId!)) { if (!isCategoryAlreadyPresent(value.categoryId!)) {
branchCategories branchCategories.add(DropValue(value.categoryId!, value.categoryName!, ""));
.add(DropValue(value.categoryId!, value.categoryName!, ""));
} }
} }
notifyListeners(); notifyListeners();
} }
getBranchServices({required int categoryId}) async { getBranchServices({required int categoryId}) async {
branchSelectedServiceId = branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
isHomeTapped = false; isHomeTapped = false;
pickedHomeLocation = ""; pickedHomeLocation = "";
pickHomeLocationError = ""; pickHomeLocationError = "";
@ -685,9 +585,7 @@ class AppointmentsVM extends BaseVM {
} }
List<ServiceModel> getFilteredBranchServices({required int categoryId}) { List<ServiceModel> getFilteredBranchServices({required int categoryId}) {
List<ServiceModel> filteredServices = selectedBranchModel!.branchServices! List<ServiceModel> filteredServices = selectedBranchModel!.branchServices!.where((element) => element.categoryId == categoryId).toList();
.where((element) => element.categoryId == categoryId)
.toList();
return filteredServices; return filteredServices;
} }
@ -729,8 +627,7 @@ class AppointmentsVM extends BaseVM {
return totalPrice.toString(); return totalPrice.toString();
} }
void openTheAddServiceBottomSheet(BuildContext context, void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) {
AppointmentsVM appointmentsVM) {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@ -741,8 +638,7 @@ class AppointmentsVM extends BaseVM {
); );
} }
void priceBreakDownClicked(BuildContext context, void priceBreakDownClicked(BuildContext context, ServiceModel selectedService) {
ServiceModel selectedService) {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@ -758,27 +654,19 @@ class AppointmentsVM extends BaseVM {
Column( Column(
children: List.generate( children: List.generate(
selectedService.serviceItems!.length, selectedService.serviceItems!.length,
(index) => (index) => Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ "${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"${selectedService.serviceItems![index].name}" "${selectedService.serviceItems![index].price} SAR".toText(fontSize: 12, isBold: true),
.toText( ],
fontSize: 12, ),
color: MyColors.lightTextColor,
isBold: true),
"${selectedService.serviceItems![index]
.price} SAR"
.toText(fontSize: 12, isBold: true),
],
),
), ),
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
"${selectedService.currentTotalServicePrice} SAR" "${selectedService.currentTotalServicePrice} SAR".toText(fontSize: 16, isBold: true),
.toText(fontSize: 16, isBold: true),
], ],
), ),
if (selectedService.isHomeSelected) ...[ if (selectedService.isHomeSelected) ...[
@ -787,20 +675,15 @@ class AppointmentsVM extends BaseVM {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
"${totalKms}km ".toText( "${totalKms}km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
fontSize: 12, "${selectedService.rangePricePerKm} x $totalKms".toText(fontSize: 12, isBold: true),
color: MyColors.lightTextColor,
isBold: true),
"${selectedService.rangePricePerKm} x $totalKms"
.toText(fontSize: 12, isBold: true),
], ],
), ),
8.height, 8.height,
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
"${selectedService.rangePricePerKm ?? 0 * totalKms} SAR" "${selectedService.rangePricePerKm ?? 0 * totalKms} SAR".toText(fontSize: 16, isBold: true),
.toText(fontSize: 16, isBold: true),
], ],
), ),
], ],
@ -813,18 +696,11 @@ class AppointmentsVM extends BaseVM {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
(selectedService.isHomeSelected (selectedService.isHomeSelected
? "${(selectedService.currentTotalServicePrice) + ? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}"
(double.parse((selectedService.rangePricePerKm ?? : "${selectedService.currentTotalServicePrice}")
"0.0")) * totalKms)}"
: "${selectedService.currentTotalServicePrice}")
.toText(fontSize: 29, isBold: true), .toText(fontSize: 29, isBold: true),
2.width, 2.width,
"SAR" "SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
.toText(
color: MyColors.lightTextColor,
fontSize: 16,
isBold: true)
.paddingOnly(bottom: 5),
], ],
) )
], ],
@ -844,8 +720,7 @@ class AppointmentsVM extends BaseVM {
isValidated = false; isValidated = false;
break; break;
} }
if (schedule.selectedCustomTimeDateSlotModel!.date == null || if (schedule.selectedCustomTimeDateSlotModel!.date == null || !schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) {
!schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) {
isValidated = false; isValidated = false;
break; break;
} else { } else {
@ -853,9 +728,7 @@ class AppointmentsVM extends BaseVM {
isValidated = false; isValidated = false;
break; break;
} else { } else {
TimeSlotModel slot = schedule TimeSlotModel slot = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected);
.selectedCustomTimeDateSlotModel!.availableSlots!
.firstWhere((element) => element.isSelected);
if (slot.date.isNotEmpty) { if (slot.date.isNotEmpty) {
isValidated = true; isValidated = true;
break; break;
@ -864,8 +737,7 @@ class AppointmentsVM extends BaseVM {
} }
} }
if (!isValidated) { if (!isValidated) {
Utils.showToast( Utils.showToast("You must select appointment time for each schedule's appointment.");
"You must select appointment time for each schedule's appointment.");
return; return;
} }
navigateWithName(context, AppRoutes.reviewAppointmentView); navigateWithName(context, AppRoutes.reviewAppointmentView);
@ -884,36 +756,30 @@ class AppointmentsVM extends BaseVM {
} }
} }
serviceAppointmentScheduleList = serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(
await scheduleRepo.mergeServiceIntoAvailableSchedules(
serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForHome: serviceItemIdsForHome,
serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
); );
if (serviceAppointmentScheduleList.isEmpty) { if (serviceAppointmentScheduleList.isEmpty) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast( Utils.showToast("There are no available appointments for selected Items.");
"There are no available appointments for selected Items.");
return; return;
} }
totalAmount = 0.0; totalAmount = 0.0;
amountToPayForAppointment = 0.0; amountToPayForAppointment = 0.0;
for (var schedule in serviceAppointmentScheduleList) { for (var schedule in serviceAppointmentScheduleList) {
amountToPayForAppointment = amountToPayForAppointment = amountToPayForAppointment + (schedule.amountToPay ?? 0.0);
amountToPayForAppointment + (schedule.amountToPay ?? 0.0);
totalAmount = totalAmount + (schedule.amountTotal ?? 0.0); totalAmount = totalAmount + (schedule.amountTotal ?? 0.0);
} }
Utils.hideLoading(context); Utils.hideLoading(context);
navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment
arguments: ScreenArgumentsForAppointmentDetailPage(
routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment
notifyListeners(); notifyListeners();
} }
Future<void> onRescheduleAppointmentPressed({required BuildContext context, Future<void> onRescheduleAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
required AppointmentListModel appointmentListModel}) async {
Utils.showLoading(context); Utils.showLoading(context);
List<String> serviceItemIdsForHome = []; List<String> serviceItemIdsForHome = [];
@ -930,16 +796,14 @@ class AppointmentsVM extends BaseVM {
} }
} }
serviceAppointmentScheduleList = serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(
await scheduleRepo.mergeServiceIntoAvailableSchedules(
serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForHome: serviceItemIdsForHome,
serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
); );
if (serviceAppointmentScheduleList.isEmpty) { if (serviceAppointmentScheduleList.isEmpty) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast( Utils.showToast("There are no available appointments for selected Items.");
"There are no available appointments for selected Items.");
return; return;
} }
Utils.hideLoading(context); Utils.hideLoading(context);
@ -947,36 +811,29 @@ class AppointmentsVM extends BaseVM {
navigateWithName( navigateWithName(
context, context,
AppRoutes.bookAppointmenSchedulesView, AppRoutes.bookAppointmenSchedulesView,
arguments: ScreenArgumentsForAppointmentDetailPage( arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 2, appointmentId: appointmentListModel.id ?? 0),
routeFlag: 2, appointmentId: appointmentListModel.id ?? 0),
); // 2 For Rescheduling an Appointment ); // 2 For Rescheduling an Appointment
notifyListeners(); notifyListeners();
} }
Future<void> onRescheduleAppointmentConfirmPressed( Future<void> onRescheduleAppointmentConfirmPressed({required BuildContext context, required int appointmentId, required int selectedSlotId}) async {
{required BuildContext context,
required int appointmentId,
required int selectedSlotId}) async {
Utils.showLoading(context); Utils.showLoading(context);
try { try {
GenericRespModel genericRespModel = GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment(
await scheduleRepo.cancelOrRescheduleServiceAppointment(
serviceAppointmentID: appointmentId, serviceAppointmentID: appointmentId,
serviceSlotID: selectedSlotId, serviceSlotID: selectedSlotId,
appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel
); );
if (genericRespModel.messageStatus == 2 || if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
genericRespModel.data == null) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
return; return;
} }
if (genericRespModel.data == 1) { if (genericRespModel.messageStatus == 1) {
context.read<DashboardVmCustomer>().onNavbarTapped(1); context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
getMyAppointments(); getMyAppointments();
Utils.hideLoading(context); Utils.hideLoading(context);

@ -37,6 +37,13 @@ class ChatVM extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
int latestOfferId = 0;
updateLatestOfferId(var value) {
latestOfferId = value;
notifyListeners();
}
List<OfferRequestCommentModel> offerRejectModelList = [ List<OfferRequestCommentModel> offerRejectModelList = [
OfferRequestCommentModel( OfferRequestCommentModel(
index: 0, index: 0,
@ -93,11 +100,10 @@ class ChatVM extends ChangeNotifier {
return isValidated; return isValidated;
} }
Future<void> onNewMessageReceived({required List<ChatMessageModel> messages, required BuildContext context}) async { Future<void> onNewMessageReceived({required List<ChatMessageModel> messages, required BuildContext context, bool isMyOwnOffer = false}) async {
// log("message I received in onNewMessageReceived ${messages.first.toString()}");
if (AppState().currentAppType == AppType.customer) { if (AppState().currentAppType == AppType.customer) {
for (var msg in messages) { for (var msg in messages) {
log("printing senderUserID: ${msg.senderUserID}");
log("printing providerUserId: ${serviceProviderOffersList.first.providerUserId}");
int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == msg.senderUserID); int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == msg.senderUserID);
if (providerIndex != -1) { if (providerIndex != -1) {
serviceProviderOffersList[providerIndex].chatMessages!.add(msg); serviceProviderOffersList[providerIndex].chatMessages!.add(msg);
@ -105,9 +111,20 @@ class ChatVM extends ChangeNotifier {
} }
} else { } else {
for (var msg in messages) { for (var msg in messages) {
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerUserID == msg.senderUserID); // Where we need to call this function for saving a message in chat we will use receiverUserID and in those cases where received ID is null , it means it is a signal R call
log("here is it: ${msg.senderUserID}"); int providerIndex = -1;
log("here is it: ${context.read<RequestsVM>().myFilteredRequests.first.customerUserID.toString()}"); if (isMyOwnOffer) {
providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere(
(element) => element.customerUserID == msg.receiverUserID,
);
} else {
providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere(
(element) => element.customerUserID == msg.senderUserID,
);
}
// log("here is it: $providerIndex");
// log("here is it: ${msg.senderUserID}");
// log("here is it: ${context.read<RequestsVM>().myFilteredRequests.first.customerUserID.toString()}");
if (providerIndex != -1) { if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: msg, index: providerIndex); context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: msg, index: providerIndex);
} }
@ -197,6 +214,14 @@ class ChatVM extends ChangeNotifier {
final userId = AppState().getUser.data!.userInfo!.userId.toString(); final userId = AppState().getUser.data!.userInfo!.userId.toString();
final name = AppState().getUser.data!.userInfo!.firstName.toString(); final name = AppState().getUser.data!.userInfo!.firstName.toString();
log("I have saved this: ${(<String, dynamic>{
"ReceiverUserID": receiverId,
"SenderUserID": userId,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"ChatText": message,
"RequestID": requestId,
"ReqOfferID": latestOfferId,
}).toString()}");
hubConnection!.invoke( hubConnection!.invoke(
"SendMessageRequestOffer", "SendMessageRequestOffer",
args: <Object>[ args: <Object>[
@ -206,7 +231,7 @@ class ChatVM extends ChangeNotifier {
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"ChatText": message, "ChatText": message,
"RequestID": requestId, "RequestID": requestId,
"ReqOfferID": 0, "ReqOfferID": latestOfferId,
} }
], ],
).catchError((e) { ).catchError((e) {
@ -221,6 +246,8 @@ class ChatVM extends ChangeNotifier {
requestID: requestId, requestID: requestId,
senderName: name, senderName: name,
senderUserID: userId, senderUserID: userId,
chatMessageTypeEnum: ChatMessageTypeEnum.freeText,
receiverUserID: receiverId,
); );
if (AppState().currentAppType == AppType.customer) { if (AppState().currentAppType == AppType.customer) {
@ -229,7 +256,7 @@ class ChatVM extends ChangeNotifier {
serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel); serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel);
} }
} else { } else {
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerID == receiverId); int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerUserID == receiverId);
if (providerIndex != -1) { if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex); context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex);
} }
@ -268,6 +295,14 @@ class ChatVM extends ChangeNotifier {
Utils.showLoading(context); Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId); List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
serviceProviderOffersList[providerOfferIndex].chatMessages = chatMessages; serviceProviderOffersList[providerOfferIndex].chatMessages = chatMessages;
if (serviceProviderOffersList[providerOfferIndex].chatMessages != null) {
for (var message in serviceProviderOffersList[providerOfferIndex].chatMessages!) {
if (message.chatMessageTypeEnum == ChatMessageTypeEnum.offer) {
updateLatestOfferId(message.reqOfferID ?? 0);
log("latestOfferId: $latestOfferId");
}
}
}
Utils.hideLoading(context); Utils.hideLoading(context);
notifyListeners(); notifyListeners();
} catch (e) { } catch (e) {

@ -76,7 +76,7 @@ class PaymentVM extends ChangeNotifier {
await Future.delayed(const Duration(seconds: 2)); await Future.delayed(const Duration(seconds: 2));
Utils.hideLoading(context); Utils.hideLoading(context);
print("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}"); log("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}");
if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) { if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) {
Utils.showToast("Payment Failed!"); Utils.showToast("Payment Failed!");
@ -96,7 +96,7 @@ class PaymentVM extends ChangeNotifier {
await Future.delayed(const Duration(seconds: 2)); await Future.delayed(const Duration(seconds: 2));
Utils.hideLoading(context); Utils.hideLoading(context);
print("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}"); log("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}");
if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) { if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) {
Utils.showToast("Payment Failed!"); Utils.showToast("Payment Failed!");
@ -114,6 +114,7 @@ class PaymentVM extends ChangeNotifier {
case PaymentTypes.subscription: case PaymentTypes.subscription:
return orderProviderSubscriptionId; return orderProviderSubscriptionId;
case PaymentTypes.appointment: case PaymentTypes.appointment:
case PaymentTypes.partialAppointment:
return -1; return -1;
case PaymentTypes.request: case PaymentTypes.request:
return requestId; return requestId;
@ -151,10 +152,15 @@ class PaymentVM extends ChangeNotifier {
break; break;
case PaymentTypes.extendAds: case PaymentTypes.extendAds:
// TODO: Handle this case. // TODO: Handle this case.
break;
case PaymentTypes.partialAppointment:
log("Partial Appointment Payment has been Failed!!");
break; break;
} }
}, },
onSuccess: () async { onSuccess: () async {
// TOD0: we have to take payment confirmation methods from Backend team and make success callbacks like onAdsPaymentSuccess
switch (paymentTypeEnum) { switch (paymentTypeEnum) {
case PaymentTypes.subscription: case PaymentTypes.subscription:
break; break;
@ -168,6 +174,9 @@ class PaymentVM extends ChangeNotifier {
case PaymentTypes.extendAds: case PaymentTypes.extendAds:
await onAdsPaymentSuccess(context: context, paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), currentAdId: currentAdId); await onAdsPaymentSuccess(context: context, paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), currentAdId: currentAdId);
break; break;
case PaymentTypes.partialAppointment:
log("Partial Appointment Payment has been Succeeded");
break;
} }
}, },
); );
@ -177,6 +186,7 @@ class PaymentVM extends ChangeNotifier {
currentPaymentType = paymentType; currentPaymentType = paymentType;
switch (currentPaymentType) { switch (currentPaymentType) {
case PaymentTypes.appointment: case PaymentTypes.appointment:
case PaymentTypes.partialAppointment:
if (appointmentIdsForPayment.isEmpty) return; if (appointmentIdsForPayment.isEmpty) return;
await placeThePayment(context: context, paymentTypeEnum: paymentType); await placeThePayment(context: context, paymentTypeEnum: paymentType);
break; break;
@ -184,7 +194,7 @@ class PaymentVM extends ChangeNotifier {
// TODO: Handle this case. // TODO: Handle this case.
break; break;
case PaymentTypes.subscription: case PaymentTypes.subscription:
// TODO: Handle this case. await placeThePayment(context: context, paymentTypeEnum: paymentType);
break; break;
case PaymentTypes.ads: case PaymentTypes.ads:
case PaymentTypes.adReserve: case PaymentTypes.adReserve:

@ -40,7 +40,8 @@ class RequestsVM extends BaseVM {
List<EnumsModel> myRequestsTypeEnum = []; List<EnumsModel> myRequestsTypeEnum = [];
populateRequestsFilterList() async { populateRequestsFilterList() async {
requestsTypeFilterOptions.clear(); if (requestsTypeFilterOptions.isNotEmpty) return;
if (myRequestsTypeEnum.isEmpty) { if (myRequestsTypeEnum.isEmpty) {
myRequestsTypeEnum = await commonRepo.getEnumTypeValues(enumTypeID: 16); //TODO: 16 is to get Requests Filter Enums myRequestsTypeEnum = await commonRepo.getEnumTypeValues(enumTypeID: 16); //TODO: 16 is to get Requests Filter Enums
} }
@ -241,8 +242,7 @@ class RequestsVM extends BaseVM {
} }
//Request Management //Request Management
String price = "", String price = "", description = "";
description = "";
updatePrice(String v) { updatePrice(String v) {
price = v; price = v;
@ -255,9 +255,7 @@ class RequestsVM extends BaseVM {
Future<VehiclePostingImages> convertFileToRequestPostingImages({required File file}) async { Future<VehiclePostingImages> convertFileToRequestPostingImages({required File file}) async {
List<int> imageBytes = await file.readAsBytes(); List<int> imageBytes = await file.readAsBytes();
String image = base64Encode(imageBytes); String image = base64Encode(imageBytes);
String fileName = file.path String fileName = file.path.split('/').last;
.split('/')
.last;
VehiclePostingImages vehiclePostingImages = VehiclePostingImages( VehiclePostingImages vehiclePostingImages = VehiclePostingImages(
imageName: fileName, imageName: fileName,
imageStr: image, imageStr: image,
@ -425,20 +423,30 @@ class RequestsVM extends BaseVM {
context: context, context: context,
); );
if (status) { if (status) {
final senderName = AppState().getUser.data!.userInfo!.firstName; final senderName = AppState().getUser.data!.userInfo!.firstName;
final senderId = AppState().getUser.data!.userInfo!.userId; final senderId = AppState().getUser.data!.userInfo!.userId;
// resetSendOfferBottomSheet(); // resetSendOfferBottomSheet();
Navigator.pop(context); Navigator.pop(context);
ChatMessageModel chatMessageModel = ChatMessageModel( ChatMessageModel chatMessageModel = ChatMessageModel(
isMyMessage: true, isMyMessage: true,
chatText: message, chatText: message,
messageType: ChatMessageTypeEnum.freeText.getIdFromChatMessageTypeEnum(), messageType: ChatMessageTypeEnum.offer.getIdFromChatMessageTypeEnum(),
senderName: senderName, senderName: senderName,
senderUserID: senderId, senderUserID: senderId,
); receiverUserID: receiverId,
context.read<ChatVM>().onNewMessageReceived(messages: [chatMessageModel], context: context); chatMessageTypeEnum: ChatMessageTypeEnum.offer,
requestID: requestModel.id,
offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
reqOffer: ReqOffer(
offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
requestID: requestModel.id,
price: double.parse(offerPrice),
requestOfferStatusEnum: RequestOfferStatusEnum.offer,
comment: message,
offerStatusText: "",
));
context.read<ChatVM>().onNewMessageReceived(messages: [chatMessageModel], context: context, isMyOwnOffer: true);
if (!isFromChatScreen) { if (!isFromChatScreen) {
ChatViewArguments chatViewArguments = ChatViewArguments( ChatViewArguments chatViewArguments = ChatViewArguments(
chatTypeEnum: ChatTypeEnum.requestOffer, chatTypeEnum: ChatTypeEnum.requestOffer,

@ -139,11 +139,7 @@ class AdDuration extends StatelessWidget {
children: [ children: [
"Contact Details".toText(fontSize: 18, isBold: true), "Contact Details".toText(fontSize: 18, isBold: true),
8.height, 8.height,
Row( "Do you want to show your number to the buyers?".toText(fontSize: 14),
children: [
"Do you want to show your number to the buyers?".toText(fontSize: 14),
],
),
6.height, 6.height,
Container( Container(
width: 38, width: 38,
@ -215,7 +211,7 @@ class AdDuration extends StatelessWidget {
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
"Is this number registered on WhatsApp?".toString().toText(fontSize: 14) "Is this number registered on WhatsApp?".toString().toText()
], ],
), ),
], ],

@ -18,7 +18,7 @@ class ReviewAd extends StatelessWidget {
VehicleDetailsReview().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)), VehicleDetailsReview().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)),
DamagePartsReview().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)), DamagePartsReview().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)),
AdDurationReview().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)), AdDurationReview().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)),
AdContactDetailsReview().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)), AdContactDetailsReview(),
], ],
); );
} }
@ -112,11 +112,11 @@ class VehicleDetailsReview extends StatelessWidget {
"Vehicle Pictures:".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), "Vehicle Pictures:".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
if (adVM.pickedVehicleImages.isNotEmpty) ...[ if (adVM.pickedVehicleImages.isNotEmpty) ...[
// 10.height, // 10.height,
PickedImagesContainer( PickedFilesContainer(
pickedImages: adVM.pickedVehicleImages, pickedFiles: adVM.pickedVehicleImages,
onCrossPressedPrimary: adVM.removeImageFromList, onCrossPressedPrimary: adVM.removeImageFromList,
isReview: true, isReview: true,
onAddImagePressed: () {}, onAddFilePressed: () {},
), ),
], ],
], ],
@ -141,11 +141,11 @@ class DamagePartsReview extends StatelessWidget {
), ),
if (adVM.vehicleDamageCards[index].partImages != null && adVM.vehicleDamageCards[index].partImages!.isNotEmpty) ...[ if (adVM.vehicleDamageCards[index].partImages != null && adVM.vehicleDamageCards[index].partImages!.isNotEmpty) ...[
"Damage Part Pictures:".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true), "Damage Part Pictures:".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
PickedImagesContainer( PickedFilesContainer(
pickedImages: adVM.vehicleDamageCards[index].partImages!, pickedFiles: adVM.vehicleDamageCards[index].partImages!,
onCrossPressedPrimary: adVM.removeImageFromList, onCrossPressedPrimary: adVM.removeImageFromList,
isReview: true, isReview: true,
onAddImagePressed: () {}, onAddFilePressed: () {},
), ),
], ],
if (index != adVM.vehicleDamageCards.length - 1) const Divider(thickness: 2), if (index != adVM.vehicleDamageCards.length - 1) const Divider(thickness: 2),
@ -309,7 +309,7 @@ class AdContactDetailsReview extends StatelessWidget {
], ],
), ),
], ],
) ).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4))
: const SizedBox.shrink(); : const SizedBox.shrink();
} }
} }

@ -101,13 +101,13 @@ class DamageParts extends StatelessWidget {
// }), // }),
// 8.height, // 8.height,
// vehicleDamageCard.partImages != null ? // vehicleDamageCard.partImages != null ?
PickedImagesContainer( PickedFilesContainer(
pickedImages: vehicleDamageCard.partImages ?? [], pickedFiles: vehicleDamageCard.partImages ?? [],
onCrossPressedSecondary: (imageIndex, filePath) { onCrossPressedSecondary: (imageIndex, filePath) {
adVM.removeDamageImageFromCard(imageIndex, filePath, index); adVM.removeDamageImageFromCard(imageIndex, filePath, index);
}, },
index: index, index: index,
onAddImagePressed: () { onAddFilePressed: () {
context.read<AdVM>().pickDamagePartImage(index); context.read<AdVM>().pickDamagePartImage(index);
}, },
), ),

@ -278,10 +278,10 @@ class VehicleDetails extends StatelessWidget {
], ],
if (adVM.pickedVehicleImages.isNotEmpty) ...[ if (adVM.pickedVehicleImages.isNotEmpty) ...[
16.height, 16.height,
PickedImagesContainer( PickedFilesContainer(
pickedImages: adVM.pickedVehicleImages, pickedFiles: adVM.pickedVehicleImages,
onCrossPressedPrimary: adVM.removeImageFromList, onCrossPressedPrimary: adVM.removeImageFromList,
onAddImagePressed: () { onAddFilePressed: () {
context.read<AdVM>().pickMultipleImages(); context.read<AdVM>().pickMultipleImages();
}, },
), ),

@ -1,13 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.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/classes/consts.dart';
import 'package:mc_common_app/config/dependencies.dart';
import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart';
import 'package:mc_common_app/models/advertisment_models/ads_bank_details_model.dart';
import 'package:mc_common_app/models/advertisment_models/special_service_model.dart'; import 'package:mc_common_app/models/advertisment_models/special_service_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/theme/colors.dart';
@ -19,6 +19,8 @@ import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/payment_view_model.dart'; import 'package:mc_common_app/view_models/payment_view_model.dart';
import 'package:mc_common_app/views/advertisement/ad_duration_selection_sheet_content.dart'; import 'package:mc_common_app/views/advertisement/ad_duration_selection_sheet_content.dart';
import 'package:mc_common_app/views/advertisement/ads_images_slider.dart'; import 'package:mc_common_app/views/advertisement/ads_images_slider.dart';
import 'package:mc_common_app/views/advertisement/custom_add_button.dart';
import 'package:mc_common_app/views/advertisement/picked_images_container.dart';
import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart'; import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart';
import 'package:mc_common_app/widgets/bottom_sheet.dart'; import 'package:mc_common_app/widgets/bottom_sheet.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart';
@ -42,6 +44,7 @@ class AdsDetailView extends StatefulWidget {
class _AdsDetailViewState extends State<AdsDetailView> { class _AdsDetailViewState extends State<AdsDetailView> {
@override @override
void initState() { void initState() {
log("ad: ${widget.adDetails.adReserveStatus}");
scheduleMicrotask(() { scheduleMicrotask(() {
onAdDetailsLoaded(); onAdDetailsLoaded();
}); });
@ -298,7 +301,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
"Reservation Amounts".toText(fontSize: 16, isBold: true), "Reservation Amount".toText(fontSize: 16, isBold: true),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
@ -360,7 +363,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
], ],
), ),
12.height, 12.height,
"Special service charges will be added based on desired insurance and delivery Location".toText(fontSize: 12), "Special service charges will be added based on desired insurance and delivery Location".toText(fontSize: 12, maxLines: 2),
30.height, 30.height,
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -383,7 +386,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
"Estimated".toText(fontSize: 10, isBold: true), "Estimated".toText(fontSize: 10, isBold: true),
], ],
), ),
44.height, 30.height,
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
@ -393,7 +396,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
size: 19, size: 19,
).paddingOnly(bottom: 2), ).paddingOnly(bottom: 2),
3.width, 3.width,
"Some services are mandatory while reserving the Ad.".toText( "Some services are mandatory while reserving Ad.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -407,9 +410,12 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
child: ShowFillButton( child: ShowFillButton(
maxHeight: 55, maxHeight: 55,
title: "Complete Reservation", title: "Complete Reservation",
onPressed: () { onPressed: () async {
Navigator.pop(context); // Navigator.pop(context);
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.adReserve); bool status = await context.read<AdVM>().createReserveAd(adId: adDetailsModel.id!, context: context);
if (status) {
navigateReplaceWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.adReserve);
}
}, },
), ),
), ),
@ -441,8 +447,10 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
width: 55, width: 55,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)), decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)),
child: MyAssets.whatsAppIcon.buildSvg() child: MyAssets.whatsAppIcon.buildSvg(
).onPress(() { height: 35,
width: 35,
)).onPress(() {
Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? ""); Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? "");
}), }),
], ],
@ -484,13 +492,12 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
if (adDetailsModel.whatsAppNo == null) ...[ if (adDetailsModel.whatsAppNo == null) ...[
8.width, 8.width,
Container( Container(
height: 55, height: 55,
width: 55, width: 55,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)), decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)),
//TODO: It Will be replaced by a WhatsApp Icon child: MyAssets.whatsAppIcon.buildSvg(height: 33, width: 35))
child: MyAssets.whatsAppIcon.buildSvg() .onPress(() {
).onPress(() {
Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? ""); Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? "");
}), }),
], ],
@ -531,7 +538,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
builder: (BuildContext context) { builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) { return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) {
return InfoBottomSheet( return InfoBottomSheet(
title: "Set Date and Time".toText(fontSize: 28, isBold: true, letterSpacing: -1.44, height: 1.2), title: "Set Date and Time".toText(fontSize: 16, isBold: true, letterSpacing: -1.44, height: 1.2),
description: Column( description: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -901,63 +908,69 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
title: "Cancel Reservation".toText(fontSize: 28, isBold: true, letterSpacing: -1.44), title: "Cancel Reservation".toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding( description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column( child: Consumer<AdVM>(
crossAxisAlignment: CrossAxisAlignment.start, builder: (BuildContext context, AdVM adVM, Widget? child) {
children: [ return Column(
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
12.height, Column(
TxtField( crossAxisAlignment: CrossAxisAlignment.start,
maxLines: 5, children: [
value: "", 12.height,
errorValue: "", TxtField(
keyboardType: TextInputType.text, maxLines: 5,
hint: "Reason for cancellation", value: adVM.reservationCancelReason,
onChanged: (v) => () {}, errorValue: adVM.reservationCancelError,
keyboardType: TextInputType.text,
hint: "Reason for cancellation",
onChanged: (v) => adVM.updateReservationCancelReason(v),
),
],
),
25.height,
ShowFillButton(
title: "Submit",
onPressed: () {
bool status = adVM.validateReservationCancelReason();
if (status) {
Navigator.pop(context);
return actionConfirmationBottomSheet(
context: context,
title: "Do you want to cancel the reservation?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: "Your ad reservation will be cancelled and this ad will be again visible to everyone to buy.",
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: "Yes",
fontSize: 15,
onPressed: () {
Navigator.pop(context);
adVM.cancelMyAdReservation(context, adId: adDetails.id!, reason: "");
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: "No",
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
},
maxWidth: double.infinity,
), ),
19.height,
], ],
), );
25.height, },
ShowFillButton(
title: "Submit",
onPressed: () {
Navigator.pop(context);
AdVM adVM = context.read<AdVM>();
return actionConfirmationBottomSheet(
context: context,
title: "Do you want to cancel the reservation?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: "Your ad reservation will be cancelled and this ad will be again visible to everyone to buy.",
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: "Yes",
fontSize: 15,
onPressed: () {
Navigator.pop(context);
adVM.cancelMyAdReservation(context, adId: adDetails.id!);
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: "No",
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
},
maxWidth: double.infinity,
),
19.height,
],
), ),
)); ));
}, },
@ -983,6 +996,104 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
); );
} }
Widget completeDealAction(BuildContext context, {required AdDetailsModel adDetails}) {
return Row(
children: [
Expanded(
child: ShowFillButton(
maxHeight: 55,
backgroundColor: MyColors.darkPrimaryColor,
txtColor: MyColors.white,
isBold: false,
title: "Complete Deal",
onPressed: () {
buildCompleteDealBottomSheet(context, adDetails: adDetails);
}),
),
],
);
}
Future buildCompleteDealBottomSheet(BuildContext context, {required AdDetailsModel adDetails}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return InfoBottomSheet(
title: "Upload Bank Receipt".toText(fontSize: 26, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Consumer<AdVM>(
builder: (BuildContext context, AdVM adVM, Widget? child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
TxtField(
maxLines: 4,
value: adVM.completeDealNotesForAdmin,
errorValue: "",
keyboardType: TextInputType.text,
hint: "Notes For Admin",
onChanged: (v) => adVM.updateCompleteDealNotesForAdmin(v),
),
],
),
15.height,
"Attach File".toText(fontSize: 20, isBold: true, letterSpacing: -0.5),
if (adVM.pickedReceiptPdfFiles.isNotEmpty) ...[
16.height,
PickedFilesContainer(
pickedFiles: adVM.pickedReceiptPdfFiles,
onCrossPressedPrimary: adVM.removePdfFileFromList,
onAddFilePressed: () {
context.read<AdVM>().pickPdfReceiptFile(context);
},
isPdf: true,
),
] else ...[
Row(
children: [
Container(
height: 90,
width: 90,
decoration: BoxDecoration(color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)),
margin: const EdgeInsets.all(8),
alignment: Alignment.center,
child: Container(
height: 24,
width: 24,
decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
child: const Icon(Icons.add, color: MyColors.white),
),
).onPress(() {
context.read<AdVM>().pickPdfReceiptFile(context);
}),
],
),
],
15.height,
ShowFillButton(
title: "Submit",
onPressed: () {
//Upload Attachment
},
maxWidth: double.infinity,
),
19.height,
],
);
},
),
));
},
);
}
Widget expiredAdAction(BuildContext context) { Widget expiredAdAction(BuildContext context) {
return Row( return Row(
children: [ children: [
@ -1036,6 +1147,37 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
); );
} }
Widget reservedAdActions(context, {required AdDetailsModel adDetailsModel, required AdVM adVM}) {
switch (adDetailsModel.adReserveStatus) {
case AdReserveStatus.defaultStatus:
return pendingForReviewAction(pendingText: "Waiting for Admins Response");
case AdReserveStatus.reserved:
return cancelReservationAction(context, adDetails: adDetailsModel);
case AdReserveStatus.cancelledByOwner:
return pendingForReviewAction(pendingText: "Cancelled by Owner");
case AdReserveStatus.cancelledByAdmin:
return pendingForReviewAction(pendingText: "Cancelled by Admin");
case AdReserveStatus.timeOver:
return pendingForReviewAction(pendingText: "Reservation Time Over");
case AdReserveStatus.dealDone:
if (adVM.adsBankDetailsModel != null) {
return completeDealAction(context, adDetails: adDetailsModel);
}
return pendingForReviewAction(pendingText: "Waiting for Admins Response");
case AdReserveStatus.fullPaymentVerified:
return pendingForReviewAction(pendingText: "Payment Verified");
default:
return pendingForReviewAction(pendingText: "Waiting for Admins Response");
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (adDetailsModel.adPostStatus!) { switch (adDetailsModel.adPostStatus!) {
@ -1044,7 +1186,13 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
case AdPostStatus.active: case AdPostStatus.active:
return markAsSoldAction(context); return markAsSoldAction(context);
case AdPostStatus.reserved: case AdPostStatus.reserved:
return cancelReservationAction(context, adDetails: adDetailsModel); AdVM adVM = context.watch<AdVM>();
if (adVM.state == ViewState.busy) {
return const CircularProgressIndicator();
} else {
reservedAdActions(context, adDetailsModel: adDetailsModel, adVM: adVM);
}
break;
case AdPostStatus.buyingService: case AdPostStatus.buyingService:
case AdPostStatus.reserveCancel: case AdPostStatus.reserveCancel:
case AdPostStatus.rejected: case AdPostStatus.rejected:

@ -69,7 +69,7 @@ class AdCard extends StatelessWidget {
Row( Row(
children: [ children: [
Image.network( Image.network(
adDetails.vehicle!.image!.first.imageUrl!, adDetails.vehicle!.image == null || adDetails.vehicle!.image!.isEmpty ? "" : adDetails.vehicle!.image!.first.imageUrl!,
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) { errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return const SizedBox( return const SizedBox(
width: 80, width: 80,

@ -5,22 +5,24 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class PickedImagesContainer extends StatelessWidget { class PickedFilesContainer extends StatelessWidget {
final List<File> pickedImages; final List<File> pickedFiles;
final Function(String filePath)? onCrossPressedPrimary; final Function(String filePath)? onCrossPressedPrimary;
final Function(int index, String filePath)? onCrossPressedSecondary; final Function(int index, String filePath)? onCrossPressedSecondary;
final int? index; final int? index;
final bool isReview; final bool isReview;
final Function() onAddImagePressed; final bool isPdf;
final Function() onAddFilePressed;
const PickedImagesContainer({ const PickedFilesContainer({
Key? key, Key? key,
required this.pickedImages, required this.pickedFiles,
this.onCrossPressedPrimary, this.onCrossPressedPrimary,
this.onCrossPressedSecondary, this.onCrossPressedSecondary,
this.index, this.index,
required this.onAddImagePressed, required this.onAddFilePressed,
this.isReview = false, this.isReview = false,
this.isPdf = false,
}) : super(key: key); }) : super(key: key);
@override @override
@ -32,11 +34,11 @@ class PickedImagesContainer extends StatelessWidget {
crossAxisSpacing: 4.0, crossAxisSpacing: 4.0,
mainAxisSpacing: 8.0, mainAxisSpacing: 8.0,
children: List.generate( children: List.generate(
isReview ? pickedImages.length : pickedImages.length + 1, isReview ? pickedFiles.length : pickedFiles.length + 1,
(index) { (index) {
if (index == pickedImages.length && !isReview) { if (index == pickedFiles.length && !isReview) {
return Container( return Container(
decoration: BoxDecoration( color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)), decoration: BoxDecoration(color: MyColors.greyButtonColor, border: Border.all(width: 2, color: MyColors.greyAddBorderColor)),
margin: const EdgeInsets.all(8), margin: const EdgeInsets.all(8),
alignment: Alignment.center, alignment: Alignment.center,
child: Container( child: Container(
@ -45,17 +47,17 @@ class PickedImagesContainer extends StatelessWidget {
decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor), decoration: const BoxDecoration(shape: BoxShape.circle, color: MyColors.darkTextColor),
child: const Icon(Icons.add, color: MyColors.white), child: const Icon(Icons.add, color: MyColors.white),
), ),
).onPress(onAddImagePressed); ).onPress(onAddFilePressed);
} }
return Center( return Center(
child: BuildImageContainer( child: BuildFilesContainer(
file: pickedImages[index], file: pickedFiles[index],
onCrossPressedPrimary: onCrossPressedPrimary, onCrossPressedPrimary: onCrossPressedPrimary,
onCrossPressedSecondary: onCrossPressedSecondary, onCrossPressedSecondary: onCrossPressedSecondary,
index: index, index: index,
isReview: isReview, isReview: isReview,
), isPdf: isPdf,
); ));
}, },
), ),
); );
@ -73,37 +75,51 @@ class PickedImagesContainer extends StatelessWidget {
} }
} }
class BuildImageContainer extends StatelessWidget { class BuildFilesContainer extends StatelessWidget {
final File file; final File file;
final Function(String filePath)? onCrossPressedPrimary; final Function(String filePath)? onCrossPressedPrimary;
final Function(int index, String filePath)? onCrossPressedSecondary; final Function(int index, String filePath)? onCrossPressedSecondary;
final int? index; final int? index;
final bool isReview; final bool isReview;
final bool isPdf;
const BuildImageContainer({ const BuildFilesContainer({
Key? key, Key? key,
required this.file, required this.file,
this.onCrossPressedPrimary, this.onCrossPressedPrimary,
this.onCrossPressedSecondary, this.onCrossPressedSecondary,
this.index, this.index,
this.isReview = false, this.isReview = false,
this.isPdf = false,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Stack( return Stack(
children: [ children: [
Container( SizedBox(
height: 90, height: 90,
width: 90, width: 90,
child: Stack( child: Stack(
children: [ children: [
Image.file( isPdf
file, ? Container(
fit: BoxFit.fill, height: 72,
height: 72, width: 70,
width: 70, margin: const EdgeInsets.all(8),
).paddingAll(8), decoration: BoxDecoration(
border: Border.all(
width: 2,
color: MyColors.darkPrimaryColor,
)),
child: const Icon(Icons.picture_as_pdf).paddingAll(8),
)
: Image.file(
file,
fit: BoxFit.fill,
height: 72,
width: 70,
).paddingAll(8),
!isReview !isReview
? Align( ? Align(
alignment: Alignment.topRight, alignment: Alignment.topRight,

@ -33,6 +33,7 @@ class AppointmentDetailView extends StatelessWidget {
} }
Widget getArrivedBottomActionButton({required BuildContext context, required AppointmentPaymentStatusEnum appointmentPaymentStatusEnum}) { Widget getArrivedBottomActionButton({required BuildContext context, required AppointmentPaymentStatusEnum appointmentPaymentStatusEnum}) {
final appointmentVM = context.read<AppointmentsVM>();
switch (appointmentPaymentStatusEnum) { switch (appointmentPaymentStatusEnum) {
case AppointmentPaymentStatusEnum.defaultStatus: case AppointmentPaymentStatusEnum.defaultStatus:
case AppointmentPaymentStatusEnum.paid: case AppointmentPaymentStatusEnum.paid:
@ -42,19 +43,34 @@ class AppointmentDetailView extends StatelessWidget {
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Row( child: Row(
children: [ children: [
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "In Progress"), getBaseActionButtonWidget(
color: MyColors.grey98Color.withOpacity(0.3),
textColor: MyColors.lightTextColor,
onPressed: () {},
text: "Work In Progress",
),
], ],
), ),
); );
case AppointmentPaymentStatusEnum.payNow: case AppointmentPaymentStatusEnum.payNow:
return Expanded( return Align(
child: ShowFillButton( alignment: Alignment.bottomCenter,
maxHeight: 55, child: Row(
title: "Pay Now", children: [
onPressed: () {}, getBaseActionButtonWidget(
backgroundColor: MyColors.darkPrimaryColor, color: MyColors.darkPrimaryColor,
txtColor: MyColors.white, textColor: MyColors.white,
fontSize: 18, onPressed: () {
if (appointmentListModel.remainingAmount != null && appointmentListModel.remainingAmount! > 0.0) {
appointmentVM.onPayNowPressedForAppointment(
context: context,
appointmentID: appointmentListModel.id ?? 0,
);
}
},
text: "Pay Now",
),
],
), ),
); );
} }
@ -87,7 +103,23 @@ class AppointmentDetailView extends StatelessWidget {
], ],
), ),
); );
case AppointmentStatusEnum.visitCompleted:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.grey98Color.withOpacity(0.3),
textColor: MyColors.lightTextColor,
onPressed: () {},
text: "Visit Completed",
),
],
),
);
case AppointmentStatusEnum.arrived: case AppointmentStatusEnum.arrived:
case AppointmentStatusEnum.workStarted:
return getArrivedBottomActionButton(appointmentPaymentStatusEnum: appointmentListModel.appointmentPaymentStatusEnum ?? AppointmentPaymentStatusEnum.defaultStatus, context: context); return getArrivedBottomActionButton(appointmentPaymentStatusEnum: appointmentListModel.appointmentPaymentStatusEnum ?? AppointmentPaymentStatusEnum.defaultStatus, context: context);
case AppointmentStatusEnum.cancelled: case AppointmentStatusEnum.cancelled:
return Align( return Align(
@ -223,10 +255,7 @@ class AppointmentDetailView extends StatelessWidget {
((service.currentTotalServicePrice).toString()).toText(fontSize: 25, isBold: true), ((service.currentTotalServicePrice).toString()).toText(fontSize: 25, isBold: true),
2.width, 2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), "SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
Icon( const Icon(Icons.arrow_drop_down, size: 30)
Icons.arrow_drop_down,
size: 30,
)
], ],
).onPress(() => appointmentsVM.priceBreakDownClicked(context, service)), ).onPress(() => appointmentsVM.priceBreakDownClicked(context, service)),
], ],
@ -235,28 +264,30 @@ class AppointmentDetailView extends StatelessWidget {
), ),
], ],
15.height, 15.height,
Row( if (appointmentListModel.appointmentStatusEnum != AppointmentStatusEnum.workStarted && appointmentListModel.appointmentStatusEnum != AppointmentStatusEnum.visitCompleted) ...[
children: [ Row(
CardButtonWithIcon( children: [
title: "Reschedule Appointment",
onCardTapped: () {
context.read<AppointmentsVM>().onRescheduleAppointmentPressed(context: context, appointmentListModel: appointmentListModel);
},
icon: MyAssets.scheduleAppointmentIcon.buildSvg(),
),
if (appointmentListModel.appointmentStatusEnum == AppointmentStatusEnum.booked) ...[
10.width,
CardButtonWithIcon( CardButtonWithIcon(
title: "Pay for Appointment", title: "Reschedule Appointment",
onCardTapped: () { onCardTapped: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id); context.read<AppointmentsVM>().onRescheduleAppointmentPressed(context: context, appointmentListModel: appointmentListModel);
}, },
icon: MyAssets.creditCardIcon.buildSvg(), icon: MyAssets.scheduleAppointmentIcon.buildSvg(),
), ),
if (appointmentListModel.appointmentStatusEnum == AppointmentStatusEnum.booked) ...[
10.width,
CardButtonWithIcon(
title: "Pay for Appointment",
onCardTapped: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
icon: MyAssets.creditCardIcon.buildSvg(),
),
],
], ],
], ),
), 15.height,
15.height, ],
], ],
).toWhiteContainer(width: double.infinity, allPading: 12), ).toWhiteContainer(width: double.infinity, allPading: 12),
buildBottomActionButton(appointmentStatusEnum: appointmentListModel.appointmentStatusEnum!, context: context), buildBottomActionButton(appointmentStatusEnum: appointmentListModel.appointmentStatusEnum!, context: context),

@ -136,7 +136,7 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Icon( const Icon(
Icons.warning, Icons.warning,
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
size: 19, size: 19,
@ -163,7 +163,7 @@ class AppointmentServicePickBottomSheet extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
// TODO: This Price will be decided according to the service selected // TODO: This Price will be decided according to the service selected, We will calculate the KMs and multiple it with price per KMs
150.toString().toText(fontSize: 30, isBold: true), 150.toString().toText(fontSize: 30, isBold: true),
"SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), "SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
], ],

@ -33,7 +33,7 @@ class CustomerAppointmentSliderWidget extends StatelessWidget {
return CustomAddButton( return CustomAddButton(
needsBorder: true, needsBorder: true,
bgColor: MyColors.white, bgColor: MyColors.white,
onTap: () => context.read<DashboardVmCustomer>().onNavbarTapped(1), onTap: () => context.read<DashboardVmCustomer>().onNavbarTapped(0),
text: "Add New Appointment", text: "Add New Appointment",
icon: Container( icon: Container(
height: 24, height: 24,

@ -146,12 +146,12 @@ class ChatView extends StatelessWidget {
size: 30, size: 30,
).onPress( ).onPress(
() async { () async {
log("chatViewArguments.receiverId:${chatViewArguments.receiverId}"); log("chatViewArguments.requestId:${chatViewArguments.requestId}");
final status = await chatVM.onTextMessageSend( final status = await chatVM.onTextMessageSend(
context: context, context: context,
receiverId: chatViewArguments.receiverId, receiverId: chatViewArguments.receiverId,
message: chatVM.chatMessageText, message: chatVM.chatMessageText,
requestId: chatViewArguments.chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArguments.requestId ?? 0 : 0, requestId: chatViewArguments.requestId ?? 0,
offerPrice: "0.0", offerPrice: "0.0",
chatMessageType: ChatMessageTypeEnum.freeText, chatMessageType: ChatMessageTypeEnum.freeText,
); );
@ -253,7 +253,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
"${chatMessageModel.reqOffer!.price}".toText(fontSize: 19, isBold: true, color: MyColors.white), "${chatMessageModel.reqOffer!.price}".toText(fontSize: 19, isBold: true, color: AppState().currentAppType == AppType.provider ? MyColors.white : MyColors.darkTextColor),
5.width, 5.width,
"SAR".toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), "SAR".toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
], ],
@ -363,7 +363,6 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Directionality( return Directionality(
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr, textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr,
// textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [

@ -0,0 +1,270 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class ProfileScreen extends StatelessWidget {
const ProfileScreen({Key? key}) : super(key: key);
// final ImagePicker _picker = ImagePicker();
Widget showItem({required String icon, required String title, required VoidCallback onTap, String? subTitle}) {
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 16,
height: 16,
child: SvgPicture.asset(
icon,
color: subTitle == null ? MyColors.black : MyColors.primaryColor,
),
),
12.width,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
title.toText(isBold: true, fontSize: 14),
if (subTitle != null) subTitle.toText(fontSize: 14, color: MyColors.primaryColor),
],
),
),
const Icon(
Icons.arrow_forward,
size: 16,
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
backgroundColor: const Color(0xffefefef),
body: Stack(
children: [
Column(
children: [
Expanded(
flex: 3,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(MyAssets.icLogoWhitePng),
fit: BoxFit.cover,
),
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 7.0, sigmaY: 7.0),
child: Container(
// height:,
color: Colors.white.withOpacity(0.0),
),
),
),
),
Expanded(
flex: 8,
child: Container(
width: double.infinity,
color: Colors.white,
child: ListView(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
height: 90,
alignment: Alignment.centerLeft,
child: ClipOval(
child: Image.asset(
MyAssets.carBanner,
width: 90,
height: 90,
fit: BoxFit.fill,
),
)),
Container(
height: 35,
width: 35,
decoration: BoxDecoration(color: MyColors.white, shape: BoxShape.circle, border: Border.all(color: MyColors.darkTextColor, width: 0.1)),
child: const Icon(Icons.edit_note, color: MyColors.darkIconColor, size: 27).paddingOnly(left: 5),
).onPress(() {})
],
).horPaddingMain(),
10.height,
"Muhammad Ahmad".toText(fontSize: 20).paddingOnly(left: 25),
Column(
children: [
CustomProfileOptionsTile(
titleText: "Country",
subtitleText: "Saudi Arabia",
needBorderBelow: true,
onTap: () {},
),
CustomProfileOptionsTile(
titleText: "Email",
subtitleText: "mail@gmail.com",
needBorderBelow: true,
onTap: () {},
),
CustomProfileOptionsTile(
titleText: "Phone Number",
subtitleText: "0504278212",
needBorderBelow: true,
onTap: () {},
),
CustomProfileOptionsTile(
titleText: "Password",
subtitleText: "************",
onTap: () {},
),
],
).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0),
],
),
),
),
],
),
CircleAvatar(
radius: 20,
backgroundColor: Colors.white,
child: const Icon(Icons.arrow_back_ios_rounded, color: MyColors.darkIconColor, size: 18).paddingOnly(right: 4),
).onPress(() {
Navigator.pop(context);
}).paddingOnly(left: 21, right: 21, top: 50),
// SingleChildScrollView(
// scrollDirection: Axis.vertical,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// CircleAvatar(
// radius: 20,
// backgroundColor: Colors.white,
// child: const Icon(Icons.arrow_back_ios_rounded, color: MyColors.darkIconColor, size: 18).paddingOnly(right: 4),
// ).onPress(() {
// Navigator.pop(context);
// }),
// ],
// ).paddingOnly(left: 21, right: 21, top: 50),
// Stack(
// children: [
// Column(
// children: [
// showItem(
// icon: MyAssets.icEmail,
// title: "Country",
// onTap: () {
// navigateWithName(context, AppRoutes.changeEmailPage);
// },
// ),
// const Divider(
// height: 1,
// ),
// showItem(
// icon: MyAssets.icPassword,
// title: "Country",
// onTap: () {
// navigateWithName(context, AppRoutes.changePassword);
// },
// ),
// const Divider(
// height: 1,
// ),
// showItem(
// icon: MyAssets.icPhoneNumber,
// title: "Country",
// onTap: () {
// navigateWithName(context, AppRoutes.changeMobilePage);
// },
// ),
// const Divider(
// height: 1,
// ),
// ],
// ).toWhiteContainer(width: double.infinity, pading: const EdgeInsets.symmetric(horizontal: 21)),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Container(
// height: 68,
// alignment: Alignment.centerLeft,
// child: ClipOval(
// child: Image.asset(
// MyAssets.carBanner,
// width: 68,
// height: 68,
// fit: BoxFit.fill,
// ),
// )),
// InkWell(
// onTap: () {},
// child: Container(
// padding: const EdgeInsets.only(left: 17, right: 17, top: 8, bottom: 8),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(30),
// color: Colors.black.withOpacity(.21),
// ),
// child: Row(
// children: [
// const Icon(Icons.photo, color: Colors.white, size: 16),
// 4.width,
// "edit".toText(fontSize: 12, color: Colors.white),
// ],
// ),
// ),
// ),
// ],
// ),
// ],
// ).horPaddingMain(),
// ],
// ),
// ),
// Container(
// height: MediaQuery.of(context).size.height,
// width: MediaQuery.of(context).size.width,
// color: Colors.white,
// child: Column(
// children: [
// CustomSettingOptionsTile(
// leadingWidget: const Icon(Icons.person, size: 20), titleText: "Invite Friends", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.myRequestsPage)),
// CustomSettingOptionsTile(
// leadingWidget: const Icon(Icons.help, size: 20), titleText: "Help", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs)),
// CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "Account", onTap: () => navigateWithName(context, AppRoutes.profileView)),
// ],
// ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, borderRadius: 0),
// ),
],
),
);
}
}
//
//

@ -192,10 +192,10 @@ class CreateRequestPage extends StatelessWidget {
], ],
if (requestsVM.pickedVehicleImages.isNotEmpty) ...[ if (requestsVM.pickedVehicleImages.isNotEmpty) ...[
16.height, 16.height,
PickedImagesContainer( PickedFilesContainer(
pickedImages: requestsVM.pickedVehicleImages, pickedFiles: requestsVM.pickedVehicleImages,
onCrossPressedPrimary: requestsVM.removeImageFromList, onCrossPressedPrimary: requestsVM.removeImageFromList,
onAddImagePressed: () { onAddFilePressed: () {
context.read<RequestsVM>().pickMultipleImages(); context.read<RequestsVM>().pickMultipleImages();
}, },
), ),

@ -6,7 +6,6 @@ import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'; import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/navigator.dart';
@ -26,7 +25,7 @@ class OfferListPage extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: const CustomAppBar(title: "Offers"), appBar: const CustomAppBar(title: "Offers"),
body: serviceProviderOffers.isEmpty body: serviceProviderOffers.isEmpty
? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) ? Center(child: "No Offers to show.".toText(fontSize: 16, color: MyColors.lightTextColor))
: ListView.separated( : ListView.separated(
itemCount: serviceProviderOffers.length, itemCount: serviceProviderOffers.length,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),

@ -81,7 +81,7 @@ class RequestItem extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
request.price.toString().toText( request.price.toInt().toString().toText(
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
isBold: true, isBold: true,

@ -2,9 +2,7 @@ import 'package:flutter/material.dart';
import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_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/navigator.dart'; import 'package:mc_common_app/utils/navigator.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/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
@ -28,10 +26,22 @@ class SettingOptionsInviteFriends extends StatelessWidget {
Column( Column(
children: [ children: [
CustomSettingOptionsTile( CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.person, size: 20), titleText: "Invite Friends", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.myRequestsPage)), leadingWidget: const Icon(Icons.person, size: 20),
titleText: "Invite Friends",
needBorderBelow: true,
onTap: () {},
),
CustomSettingOptionsTile( CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.help, size: 20), titleText: "Help", needBorderBelow: true, onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs)), leadingWidget: const Icon(Icons.help, size: 20),
CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "Account", onTap: () {}), titleText: "Help",
needBorderBelow: true,
onTap: () => navigateWithName(context, AppRoutes.settingOptionsFaqs),
),
CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.person, size: 20),
titleText: "Account",
onTap: () => navigateWithName(context, AppRoutes.profileView),
),
], ],
).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0),
], ],

@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart'; import 'package:mc_common_app/views/setting_options/widgets/custom_setting_options_tile.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class SettingOptionsLanguage extends StatelessWidget { class SettingOptionsLanguage extends StatelessWidget {
const SettingOptionsLanguage({super.key}); const SettingOptionsLanguage({super.key});
@ -26,7 +28,14 @@ class SettingOptionsLanguage extends StatelessWidget {
children: [ children: [
Column( Column(
children: [ children: [
CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "My Requests", needBorderBelow: true, onTap: () {}), CustomSettingOptionsTile(
leadingWidget: const Icon(Icons.person, size: 20),
titleText: "My Requests",
needBorderBelow: true,
onTap: () {
context.read<DashboardVmCustomer>().onNavbarTapped(4);
Navigator.pop(context);
}),
CustomSettingOptionsTile(leadingWidget: const Icon(Icons.favorite, size: 20), titleText: "Favorite list", needBorderBelow: true, onTap: () {}), CustomSettingOptionsTile(leadingWidget: const Icon(Icons.favorite, size: 20), titleText: "Favorite list", needBorderBelow: true, onTap: () {}),
CustomSettingOptionsTile(leadingWidget: const Icon(Icons.settings, size: 20), titleText: "Settings", onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends)), CustomSettingOptionsTile(leadingWidget: const Icon(Icons.settings, size: 20), titleText: "Settings", onTap: () => navigateWithName(context, AppRoutes.settingOptionsInviteFriends)),
], ],

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_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/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class CustomSettingOptionsTile extends StatelessWidget { class CustomSettingOptionsTile extends StatelessWidget {
@ -37,3 +38,37 @@ class CustomSettingOptionsTile extends StatelessWidget {
); );
} }
} }
class CustomProfileOptionsTile extends StatelessWidget {
final String titleText;
final bool needBorderBelow;
final Function() onTap;
final String subtitleText;
const CustomProfileOptionsTile({super.key, required this.onTap, required this.titleText, required this.subtitleText, this.needBorderBelow = false});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
titleText.toText(fontSize: 16),
subtitleText.toText(fontSize: 12, color: MyColors.darkTextColor.withOpacity(0.7)),
],
),
const Icon(Icons.edit_note, size: 25),
],
).onPress(onTap),
5.height,
if (needBorderBelow) ...[
const Divider(thickness: 1),
],
],
);
}
}

@ -33,7 +33,8 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
ClassType type = ClassType.EMAIL; ClassType type = ClassType.EMAIL;
//TODO: ONLY FOR DEVELOPMENT PURPOSE //TODO: ONLY FOR DEVELOPMENT PURPOSE
String phoneNum = "966504278213", password = "Fa@1234"; // String phoneNum = "966504278213", password = "Fa@1234";
String phoneNum = "966504278214", password = "Fa@1234";
String email = ""; String email = "";
String countryCode = ""; String countryCode = "";
Country? _country; Country? _country;

@ -0,0 +1,67 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class ConfirmDialog extends StatelessWidget {
final String? title;
final String message;
final String? okTitle;
final VoidCallback? onTap;
final VoidCallback? onCloseTap;
const ConfirmDialog({Key? key, this.title, required this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key);
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(),
insetPadding: const EdgeInsets.only(left: 21, right: 21),
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
title ?? LocaleKeys.confirm.tr(),
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, height: 35 / 24, letterSpacing: -0.96),
).paddingOnly(top: 16),
),
IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.close),
color: MyColors.darkTextColor,
constraints: const BoxConstraints(),
onPressed: () => onCloseTap ?? Navigator.pop(context),
// onPressed: () => Navigator.pop(context),
)
],
),
message.toText(fontSize: 18, color: MyColors.lightTextColor),
28.height,
Row(
children: [
Expanded(
child: ShowFillButton(
title: okTitle ?? "OK",
onPressed: onTap ?? () => Navigator.pop(context),
),
),
],
),
],
),
),
);
}
}

@ -17,12 +17,13 @@ dependencies:
provider: ^6.0.0 provider: ^6.0.0
easy_localization: ^3.0.3 easy_localization: ^3.0.3
http: ^0.13.3 http: ^0.13.3
permission_handler: any permission_handler: ^10.2.0
device_info_plus: any
flutter_svg: ^1.0.3 flutter_svg: ^1.0.3
sizer: ^2.0.15 sizer: ^2.0.15
fluttertoast: ^8.0.8 fluttertoast: ^8.0.8
shared_preferences: ^2.0.6 shared_preferences: ^2.0.6
file_picker: ^4.4.0 file_picker: ^6.1.1
image_picker: ^0.8.4+4 image_picker: ^0.8.4+4
equatable: ^2.0.3 equatable: ^2.0.3
logger: ^1.1.0 logger: ^1.1.0

Loading…
Cancel
Save