Request Module Completed

master_new_changes
Faiz Hashmi 1 year ago
parent 55363e4e9f
commit f290f2e04f

@ -594,7 +594,41 @@
"reserved": "محجوز",
"delete": "يمسح",
"deleteBranchConfirmation": "هل تريد حذف الفرع؟",
"deleteBranchConfirmationMessage": "سيتم حذف فرعك بشكل دائم ولن تتمكن من التراجع عن هذا الإجراء"
"deleteBranchConfirmationMessage": "سيتم حذف فرعك بشكل دائم ولن تتمكن من التراجع عن هذا الإجراء",
"reviewAndPayment": "المراجعة والدفع",
"awaitingPaymentFromCustomer": "في انتظار الدفع من العميل",
"awaitingResponseFromProvider": "في انتظار الرد من المزود",
"offerNoLongerAvailable": "العرض لم يعد متاحاً",
"doYouWantToCompleteThisDeal": "هل تريد إكمال هذه الصفقة؟",
"customerCompletingDealMeansThat": "إكمال هذه الصفقة يعني أن الطلب الذي قمت بنشره قد تم استلامه بنجاح ولا يمكن التراجع عن هذا الإجراء.",
"providerCompletingDealMeansThat": "إكمال هذه الصفقة يعني أنك قد قدمت الخدمة للعميل بنجاح وفقاً لطلبه ولا يمكن التراجع عن هذا الإجراء.",
"offerSummary": "ملخص العرض",
"personalInformation": "المعلومات الشخصية",
"name": "الاسم",
"phone": "الهاتف",
"serviceInformation": "معلومات الخدمة",
"locationInformation": "معلومات الموقع",
"additionalAddressDetails": "تفاصيل العنوان الإضافية",
"serviceName": "اسم الخدمة",
"serviceCategory": "فئة الخدمة",
"offerPrice": "سعر العرض",
"totalPrice": "السعر الإجمالي",
"providerName": "اسم المزود",
"createdOn": "تم الإنشاء في",
"manufacturedBy": "صُنع بواسطة",
"manufacturedOn": "تاريخ الصنع",
"shipping": "الشحن",
"delivery": "التسليم",
"selectDeliveryType": "اختر نوع التسليم",
"shippingManagementInstruction": "يرجى الذهاب إلى الإعدادات > شاشة إدارة الشحن لإدارة عناصر الشحن/التسليم بشكل أكبر.",
"withdrawOffer": "سحب العرض",
"manufacturer": "الصانع",
"priceRange": "نطاق السعر",
"cancelOffer": "إلغاء العرض",
"pleaseSpecify": "يرجى التحديد",
"customerNotResponding": "العميل غير متجاوب",
"cancelRequestPrompt": "هل تريد إلغاء هذا الطلب؟",
"requestPermanentlyCancelled": "سيتم إلغاء طلبك بشكل نهائي. لا يمكنك التراجع عن هذا الإجراء.",
"awaitingResponseFromCustomer": "في انتظار الرد من العميل",
"acknowledgePaymentToMowater": "أقر بأنه عند إكمال هذه الصفقة، سأدفع 2٪ من إجمالي هذه الصفقة لشركة مواتر مقابل خدماتهم."
}

@ -595,5 +595,41 @@
"reserved": "Reserved",
"delete": "Delete",
"deleteBranchConfirmation": "Do you want to delete the Branch?",
"deleteBranchConfirmationMessage": "Your branch will be permanently deleted and you cannot undo this action"
"deleteBranchConfirmationMessage": "Your branch will be permanently deleted and you cannot undo this action",
"reviewAndPayment": "Review & Payment",
"awaitingPaymentFromCustomer": "Awaiting Payment from Customer",
"awaitingResponseFromProvider": "Awaiting Response From Provider",
"offerNoLongerAvailable": "Offer is No Longer Available",
"doYouWantToCompleteThisDeal": "Do you want to complete this deal?",
"customerCompletingDealMeansThat": "Completing this deal means that the you request that you have posted has been successfully received and this action cannot be undone.",
"providerCompletingDealMeansThat": "Completing this deal means that you have successfully provided the customer according to his request and this action cannot be undone.",
"offerSummary": "Offer Summary",
"personalInformation": "Personal Information",
"name": "Name",
"phone": "Phone",
"locationInformation": "Location Information",
"serviceInformation": "Service Information",
"additionalAddressDetails": "Additional Address Details",
"serviceName": "Service Name",
"serviceCategory": "Service Category",
"offerPrice": "Offer Price",
"totalPrice": "Total Price",
"providerName": "Provider Name",
"createdOn": "Created On",
"manufacturedBy": "Manufactured By",
"manufacturedOn": "Manufactured On",
"shipping": "Shipping",
"delivery": "Delivery",
"selectDeliveryType": "Select Delivery Type",
"shippingManagementInstruction": "Please go to Settings > Shipping Management screen to manage shipping/delivery items further.",
"withdrawOffer": "Withdraw Offer",
"manufacturer": "Manufacturer",
"priceRange": "Price Range",
"cancelOffer": "Cancel Offer",
"pleaseSpecify": "Please Specify",
"customerNotResponding": "Customer Not Responding",
"cancelRequestPrompt": "Do you want to cancel this request?",
"requestPermanentlyCancelled": "Your request will be permanently cancelled. You cannot undo this action.",
"awaitingResponseFromCustomer": "Awaiting Response From Customer",
"acknowledgePaymentToMowater": "I acknowledge that upon completion of this deal, I will pay 2% of this total deal to the Mowater company for their service."
}

@ -157,6 +157,8 @@ class ApiConsts {
static String getRequest = "${baseUrlServices}api/RequestManagement/Request_Get";
static String getRequestOffers = "${baseUrlServices}api/RequestManagement/ReqOffer_Get";
static String updateRequestOffer = "${baseUrlServices}api/RequestManagement/RequestOffer_UpdateStatus";
static String updateRequestCustomer = "${baseUrlServices}api/RequestManagement/Request_StatusUpdate_Customer";
static String updateRequestProvider = "${baseUrlServices}api/RequestManagement/Request_StatusUpdate_Provider";
static String requestOffersSpsGet = "${baseUrlServices}api/RequestManagement/Request_OfferSPs_Get";
static String getServiceRequestsForProvider = "${baseUrlServices}api/RequestManagement/Request_ServiceProvider";
@ -321,8 +323,6 @@ class MyFonts {
static const FontWeight ItalicBold = FontWeight.w700;
static const FontWeight ItalicExtraBold = FontWeight.w800;
static const FontWeight ItalicBlack = FontWeight.w900;
}
class SignalrConsts {

@ -15,6 +15,7 @@ import 'package:mc_common_app/views/profile/profile_view.dart';
import 'package:mc_common_app/views/requests/create_request_page.dart';
import 'package:mc_common_app/views/requests/offer_list_page.dart';
import 'package:mc_common_app/views/requests/request_detail_page.dart';
import 'package:mc_common_app/views/requests/review_request_offer.dart';
import 'package:mc_common_app/views/setting_options/provider_license_page.dart';
import 'package:mc_common_app/views/setting_options/setting_option_help.dart';
import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart';
@ -99,6 +100,7 @@ class AppRoutes {
static const String offersListPage = "/offersListPage";
static const String requestsDetailPage = "/requestsDetailPage";
static const String sendOfferPage = "/sendOfferPage";
static const String reviewRequestOffer = "/reviewRequestOffer";
//Setting Options
static const String settingOptionsFaqs = "/settingOptionsFaqs";
@ -157,9 +159,7 @@ class AppRoutes {
AppRoutes.requestsDetailPage: (context) => RequestDetailPage(requestDetailPageArguments: ModalRoute.of(context)!.settings.arguments as RequestDetailPageArguments),
AppRoutes.createRequestPage: (context) => const CreateRequestPage(),
AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments),
AppRoutes.reviewRequestOffer: (context) => const ReviewRequestOffer(),
};
}

@ -25,7 +25,7 @@ extension EmailValidator on String {
int? maxLines}) {
bool isArabic = AppState().postParamsObject!.languageID == 1; // arabic -> 1 , English -> 2
return AutoSizeText(
isUpperCase ? this.toUpperCase() : this,
isUpperCase ? toUpperCase() : this,
textAlign: textAlign,
maxLines: maxLines,
style: TextStyle(
@ -163,23 +163,53 @@ extension FormatDate on String {
}
extension RequestEnum on int {
RequestStatus toRequestStatusEnum() {
RequestStatusEnum toRequestStatusEnum() {
if (this == 1) {
return RequestStatus.submitted;
return RequestStatusEnum.submitted;
} else if (this == 2) {
return RequestStatus.inProgress;
return RequestStatusEnum.inProgress;
} else if (this == 3) {
return RequestStatus.completed;
return RequestStatusEnum.completed;
} else if (this == 4) {
return RequestStatus.cancelled;
return RequestStatusEnum.cancelled;
} else if (this == 5) {
return RequestStatus.paid;
return RequestStatusEnum.paid;
} else if (this == 6) {
return RequestStatus.expired;
return RequestStatusEnum.expired;
} else if (this == 7) {
return RequestStatus.shipping;
return RequestStatusEnum.shipping;
} else if (this == 8) {
return RequestStatusEnum.delivery;
} else {
return RequestStatus.pending;
return RequestStatusEnum.pending;
}
}
}
extension RequestStatusEnumToInt on RequestStatusEnum {
int getIdFromRequestStatusEnum() {
switch (this) {
case RequestStatusEnum.submitted:
return 1;
case RequestStatusEnum.inProgress:
return 2;
case RequestStatusEnum.completed:
return 3;
case RequestStatusEnum.cancelled:
return 4;
case RequestStatusEnum.paid:
return 5;
case RequestStatusEnum.expired:
return 6;
case RequestStatusEnum.shipping:
return 7;
case RequestStatusEnum.delivery:
return 8;
case RequestStatusEnum.pending:
return 0;
default:
return 0;
}
}
}
@ -353,7 +383,7 @@ extension AppointmentPaymentEnum on int {
}
}
extension RequestTypeTypeEnum on int {
extension RequestTypeTypEnum on int {
RequestsTypeEnum toRequestTypeStatusEnum() {
if (this == 1) {
return RequestsTypeEnum.specialCarRequest;

@ -610,7 +610,43 @@ class CodegenLoader extends AssetLoader{
"reserved": "محجوز",
"delete": "يمسح",
"deleteBranchConfirmation": "هل تريد حذف الفرع؟",
"deleteBranchConfirmationMessage": "سيتم حذف فرعك بشكل دائم ولن تتمكن من التراجع عن هذا الإجراء"
"deleteBranchConfirmationMessage": "سيتم حذف فرعك بشكل دائم ولن تتمكن من التراجع عن هذا الإجراء",
"reviewAndPayment": "المراجعة والدفع",
"awaitingPaymentFromCustomer": "في انتظار الدفع من العميل",
"awaitingResponseFromProvider": "في انتظار الرد من المزود",
"offerNoLongerAvailable": "العرض لم يعد متاحاً",
"doYouWantToCompleteThisDeal": "هل تريد إكمال هذه الصفقة؟",
"customerCompletingDealMeansThat": "إكمال هذه الصفقة يعني أن الطلب الذي قمت بنشره قد تم استلامه بنجاح ولا يمكن التراجع عن هذا الإجراء.",
"providerCompletingDealMeansThat": "إكمال هذه الصفقة يعني أنك قد قدمت الخدمة للعميل بنجاح وفقاً لطلبه ولا يمكن التراجع عن هذا الإجراء.",
"offerSummary": "ملخص العرض",
"personalInformation": "المعلومات الشخصية",
"name": "الاسم",
"phone": "الهاتف",
"serviceInformation": "معلومات الخدمة",
"locationInformation": "معلومات الموقع",
"additionalAddressDetails": "تفاصيل العنوان الإضافية",
"serviceName": "اسم الخدمة",
"serviceCategory": "فئة الخدمة",
"offerPrice": "سعر العرض",
"totalPrice": "السعر الإجمالي",
"providerName": "اسم المزود",
"createdOn": "تم الإنشاء في",
"manufacturedBy": "صُنع بواسطة",
"manufacturedOn": "تاريخ الصنع",
"shipping": "الشحن",
"delivery": "التسليم",
"selectDeliveryType": "اختر نوع التسليم",
"shippingManagementInstruction": "يرجى الذهاب إلى الإعدادات > شاشة إدارة الشحن لإدارة عناصر الشحن/التسليم بشكل أكبر.",
"withdrawOffer": "سحب العرض",
"manufacturer": "الصانع",
"priceRange": "نطاق السعر",
"cancelOffer": "إلغاء العرض",
"pleaseSpecify": "يرجى التحديد",
"customerNotResponding": "العميل غير متجاوب",
"cancelRequestPrompt": "هل تريد إلغاء هذا الطلب؟",
"requestPermanentlyCancelled": "سيتم إلغاء طلبك بشكل نهائي. لا يمكنك التراجع عن هذا الإجراء.",
"awaitingResponseFromCustomer": "في انتظار الرد من العميل",
"acknowledgePaymentToMowater": "أقر بأنه عند إكمال هذه الصفقة، سأدفع 2٪ من إجمالي هذه الصفقة لشركة مواتر مقابل خدماتهم."
};
static const Map<String,dynamic> en_US = {
"firstTimeLogIn": "First Time Log In",
@ -1209,7 +1245,43 @@ static const Map<String,dynamic> en_US = {
"reserved": "Reserved",
"delete": "Delete",
"deleteBranchConfirmation": "Do you want to delete the Branch?",
"deleteBranchConfirmationMessage": "Your branch will be permanently deleted and you cannot undo this action"
"deleteBranchConfirmationMessage": "Your branch will be permanently deleted and you cannot undo this action",
"reviewAndPayment": "Review & Payment",
"awaitingPaymentFromCustomer": "Awaiting Payment from Customer",
"awaitingResponseFromProvider": "Awaiting Response From Provider",
"offerNoLongerAvailable": "Offer is No Longer Available",
"doYouWantToCompleteThisDeal": "Do you want to complete this deal?",
"customerCompletingDealMeansThat": "Completing this deal means that the you request that you have posted has been successfully received and this action cannot be undone.",
"providerCompletingDealMeansThat": "Completing this deal means that you have successfully provided the customer according to his request and this action cannot be undone.",
"offerSummary": "Offer Summary",
"personalInformation": "Personal Information",
"name": "Name",
"phone": "Phone",
"locationInformation": "Location Information",
"serviceInformation": "Service Information",
"additionalAddressDetails": "Additional Address Details",
"serviceName": "Service Name",
"serviceCategory": "Service Category",
"offerPrice": "Offer Price",
"totalPrice": "Total Price",
"providerName": "Provider Name",
"createdOn": "Created On",
"manufacturedBy": "Manufactured By",
"manufacturedOn": "Manufactured On",
"shipping": "Shipping",
"delivery": "Delivery",
"selectDeliveryType": "Select Delivery Type",
"shippingManagementInstruction": "Please go to Settings > Shipping Management screen to manage shipping/delivery items further.",
"withdrawOffer": "Withdraw Offer",
"manufacturer": "Manufacturer",
"priceRange": "Price Range",
"cancelOffer": "Cancel Offer",
"pleaseSpecify": "Please Specify",
"customerNotResponding": "Customer Not Responding",
"cancelRequestPrompt": "Do you want to cancel this request?",
"requestPermanentlyCancelled": "Your request will be permanently cancelled. You cannot undo this action.",
"awaitingResponseFromCustomer": "Awaiting Response From Customer",
"acknowledgePaymentToMowater": "I acknowledge that upon completion of this deal, I will pay 2% of this total deal to the Mowater company for their service."
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": ar_SA, "en_US": en_US};
}

@ -574,5 +574,41 @@ abstract class LocaleKeys {
static const delete = 'delete';
static const deleteBranchConfirmation = 'deleteBranchConfirmation';
static const deleteBranchConfirmationMessage = 'deleteBranchConfirmationMessage';
static const reviewAndPayment = 'reviewAndPayment';
static const awaitingPaymentFromCustomer = 'awaitingPaymentFromCustomer';
static const awaitingResponseFromProvider = 'awaitingResponseFromProvider';
static const offerNoLongerAvailable = 'offerNoLongerAvailable';
static const doYouWantToCompleteThisDeal = 'doYouWantToCompleteThisDeal';
static const customerCompletingDealMeansThat = 'customerCompletingDealMeansThat';
static const providerCompletingDealMeansThat = 'providerCompletingDealMeansThat';
static const offerSummary = 'offerSummary';
static const personalInformation = 'personalInformation';
static const name = 'name';
static const phone = 'phone';
static const serviceInformation = 'serviceInformation';
static const locationInformation = 'locationInformation';
static const additionalAddressDetails = 'additionalAddressDetails';
static const serviceName = 'serviceName';
static const serviceCategory = 'serviceCategory';
static const offerPrice = 'offerPrice';
static const totalPrice = 'totalPrice';
static const providerName = 'providerName';
static const createdOn = 'createdOn';
static const manufacturedBy = 'manufacturedBy';
static const manufacturedOn = 'manufacturedOn';
static const shipping = 'shipping';
static const delivery = 'delivery';
static const selectDeliveryType = 'selectDeliveryType';
static const shippingManagementInstruction = 'shippingManagementInstruction';
static const withdrawOffer = 'withdrawOffer';
static const manufacturer = 'manufacturer';
static const priceRange = 'priceRange';
static const cancelOffer = 'cancelOffer';
static const pleaseSpecify = 'pleaseSpecify';
static const customerNotResponding = 'customerNotResponding';
static const cancelRequestPrompt = 'cancelRequestPrompt';
static const requestPermanentlyCancelled = 'requestPermanentlyCancelled';
static const awaitingResponseFromCustomer = 'awaitingResponseFromCustomer';
static const acknowledgePaymentToMowater = 'acknowledgePaymentToMowater';
}

@ -101,11 +101,11 @@ class ReqOffer {
offerStatusText = json['offerStatusText'];
comment = json['comment'];
serviceItemName = json['serviceItem'];
manufacturedById = json['itemManufacturer'];
manufacturedOn = json['manufacturedDate'];
manufacturedById = json['offeredItemCreatedBy'];
manufacturedOn = json['offeredItemCreatedOn'];
price = json['price'];
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();
requestsTypeEnum = RequestsTypeEnum.serviceRequest; // TODO: THIS SHOULD COME FROM API
requestsTypeEnum = RequestsTypeEnum.serviceRequest;
}
}

@ -7,7 +7,7 @@ class RequestModel {
int requestType;
String requestTypeName;
String requestStatusName;
RequestStatus requestStatus;
RequestStatusEnum requestStatus;
String cityName;
String vehicleTypeName;
String countryName;

@ -20,6 +20,7 @@ abstract class RequestRepo {
required int cityID,
required String price,
required String description,
required String address,
required bool isSpecialServiceNeeded,
required List requestImages,
});
@ -32,7 +33,9 @@ abstract class RequestRepo {
Future<List<RequestModel>> getServiceRequestsForProviders({required int serviceProviderID});
Future<GenericRespModel> updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments});
Future<GenericRespModel> updateRequestStatus({required RequestStatusEnum requestStatusEnum, required int requestId});
Future<GenericRespModel> updateRequestOfferStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments});
}
class RequestRepoImp implements RequestRepo {
@ -50,6 +53,7 @@ class RequestRepoImp implements RequestRepo {
required int cityID,
required String price,
required String description,
required String address,
required bool isSpecialServiceNeeded,
required List requestImages,
}) async {
@ -65,6 +69,7 @@ class RequestRepoImp implements RequestRepo {
"cityID": cityID,
"price": price,
"description": description,
"address": address,
"isSpecialServiceNeeded": false,
"requestImages": requestImages,
};
@ -106,9 +111,18 @@ class RequestRepoImp implements RequestRepo {
@override
Future<List<RequestModel>> getServiceRequestsForProviders({required int serviceProviderID}) async {
List<String> requestStatusList = [
RequestStatusEnum.submitted.getIdFromRequestStatusEnum().toString(),
RequestStatusEnum.completed.getIdFromRequestStatusEnum().toString(),
RequestStatusEnum.inProgress.getIdFromRequestStatusEnum().toString(),
RequestStatusEnum.paid.getIdFromRequestStatusEnum().toString(),
RequestStatusEnum.delivery.getIdFromRequestStatusEnum().toString(),
RequestStatusEnum.shipping.getIdFromRequestStatusEnum().toString(),
];
final parameters = {
"ServiceProviderId": serviceProviderID.toString(),
"RequestTypeId": "2", // Only to get the Service Request
"RequestStatuses": requestStatusList,
};
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
@ -165,7 +179,23 @@ class RequestRepoImp implements RequestRepo {
}
@override
Future<GenericRespModel> updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments}) async {
Future<GenericRespModel> updateRequestStatus({required RequestStatusEnum requestStatusEnum, required int requestId}) async {
var queryParameters = {
"requestId": requestId.toString(),
"requestStatus": requestStatusEnum.getIdFromRequestStatusEnum().toString(),
};
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
appState.currentAppType == AppType.provider ? ApiConsts.updateRequestProvider : ApiConsts.updateRequestCustomer,
queryParameters,
token: appState.getUser.data!.accessToken,
);
return genericRespModel;
}
@override
Future<GenericRespModel> updateRequestOfferStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId, required String comments}) async {
var queryParameters = {
"requestOfferId": requestOfferId.toString(),
"offerStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum().toString(),

@ -42,7 +42,7 @@ class MyColors {
static const Color lightGreyDDColor = Color(0xffDDDDDD);
static const Color greyColor = Color(0xff787878);
static const Color provTextColor = Color(0xFF28323A);
static const Color provTextColor = Color(0xFF28323A);
//AdStatusColors:
static const Color adActiveStatusColor = Color(0xff5FC16A);
@ -50,6 +50,16 @@ class MyColors {
static const Color adSoldStatusColor = Color(0xff22742B);
static const Color adPendingStatusColor = Color(0xffEDB947);
static const Color pendingColor = Colors.amber;
static const Color submittedColor = Colors.blue;
static const Color inProgressColor = Colors.deepOrange;
static const Color completedColor = Colors.green;
static const Color cancelledColor = Colors.red;
static const Color paidColor = Colors.teal;
static const Color expiredColor = Colors.grey;
static const Color shippingColor = Colors.purple;
static const Color deliveryColor = Colors.brown;
static Decoration gradient = BoxDecoration(
gradient: const LinearGradient(colors: [
darkPrimaryColor,

@ -1,4 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/utils/app_permission_handler.dart';
@ -11,7 +13,7 @@ Future<ConfirmAction?> showConfirmDialogs(context, msg, positiveText, negativeTe
builder: (context) {
return AlertDialog(
backgroundColor: Colors.white,
title: Text(msg, style: TextStyle(fontSize: 16)),
title: Text(msg, style: const TextStyle(fontSize: 16)),
actions: <Widget>[
TextButton(
child: Text(
@ -35,19 +37,30 @@ Future<ConfirmAction?> showConfirmDialogs(context, msg, positiveText, negativeTe
);
}
void actionConfirmationBottomSheet({required BuildContext context, required Widget title, required subtitle, required Widget actionButtonYes, required Widget actionButtonNo}) {
void actionConfirmationBottomSheet({required BuildContext context, required Widget title, required subtitle, required Widget actionButtonYes, required Widget actionButtonNo, Widget? confirmationWidget, bool isOnlyOneButton = false}) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return InfoBottomSheet(
title: title,
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"$subtitle".toText(fontSize: 16),
25.height,
title: title,
description: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"$subtitle".toText(fontSize: 14, fontWeight: MyFonts.Medium),
if (confirmationWidget != null) ...[
10.height,
confirmationWidget,
],
25.height,
if (isOnlyOneButton) ...[
Row(
children: [
actionButtonYes,
],
),
] else ...[
Row(
children: [
actionButtonYes,
@ -55,9 +68,11 @@ void actionConfirmationBottomSheet({required BuildContext context, required Widg
actionButtonNo,
],
),
19.height,
],
));
19.height,
],
),
);
},
);
}

@ -56,7 +56,7 @@ enum AdPostStatus {
allAds,
}
enum RequestStatus {
enum RequestStatusEnum {
pending,
submitted,
inProgress,
@ -65,21 +65,8 @@ enum RequestStatus {
paid,
expired,
shipping,
delivery,
}
//TODO: Needs to remove
// public enum RequestType : int
// {
// Special_Car_Request = 1,
// Service_Request = 2 //SparePart
// }
// public enum OfferStatus : int
// {
// Offer = 1,//SP
// Negotiate = 2,
// Accepted = 3,
// Rejected = 4,
// Cancelled = 5 //SP
// }
enum PaymentMethods {
mada,

@ -222,30 +222,26 @@ class Utils {
}
}
static Color getChipColorByRequestStatus(RequestStatus requestStatus) {
static Color getChipColorByRequestStatus(RequestStatusEnum requestStatus) {
switch (requestStatus) {
case RequestStatus.pending:
return MyColors.adPendingStatusColor;
case RequestStatus.submitted:
return MyColors.lightTextColor;
case RequestStatus.inProgress:
return MyColors.lightTextColor;
case RequestStatus.completed:
return MyColors.greenColor;
case RequestStatus.cancelled:
return MyColors.redColor;
case RequestStatus.paid:
return MyColors.greenColor;
case RequestStatus.expired:
return MyColors.redColor;
case RequestStatus.shipping:
return MyColors.greenColor;
case RequestStatusEnum.pending:
return MyColors.pendingColor;
case RequestStatusEnum.submitted:
return MyColors.submittedColor;
case RequestStatusEnum.inProgress:
return MyColors.inProgressColor;
case RequestStatusEnum.completed:
return MyColors.completedColor;
case RequestStatusEnum.cancelled:
return MyColors.cancelledColor;
case RequestStatusEnum.paid:
return MyColors.paidColor;
case RequestStatusEnum.expired:
return MyColors.expiredColor;
case RequestStatusEnum.shipping:
return MyColors.shippingColor;
case RequestStatusEnum.delivery:
return MyColors.deliveryColor;
}
}
@ -271,7 +267,6 @@ class Utils {
}
}
static String getChipStatusTextByBranchStatus(BranchStatusEnum branchStatusEnum) {
switch (branchStatusEnum) {
case BranchStatusEnum.pending:
@ -301,11 +296,7 @@ class Utils {
borderRadius: const BorderRadius.all(Radius.circular(200)),
),
padding: padding,
child: text.toText(
fontSize: 10,
color: textColor,
letterSpacing: -0.50
));
child: text.toText(fontSize: 10, color: textColor, letterSpacing: -0.50));
}
static InputDecoration txtField(String label) {

@ -46,6 +46,13 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
}
bool acknowledgePaymentToMowaterStatus = false;
updateAcknowledgePaymentToMowaterStatus(var value) {
acknowledgePaymentToMowaterStatus = value;
notifyListeners();
}
bool isUserOnChatScreen = false;
final ScrollController scrollController = ScrollController();
@ -66,7 +73,7 @@ class ChatVM extends ChangeNotifier {
List<OfferRequestCommentModel> offerRejectModelList = [
OfferRequestCommentModel(
index: 0,
isSelected: false,
isSelected: true,
title: LocaleKeys.changedMind.tr(),
),
OfferRequestCommentModel(
@ -81,15 +88,20 @@ class ChatVM extends ChangeNotifier {
),
OfferRequestCommentModel(
index: 3,
isSelected: true,
isSelected: false,
title: LocaleKeys.customerNotResponding.tr(),
),
OfferRequestCommentModel(
index: 4,
isSelected: false,
title: LocaleKeys.otherVar.tr(),
),
];
OfferRequestCommentModel selectedOfferRequestCommentModel = OfferRequestCommentModel(
index: 3,
index: 0,
isSelected: true,
title: LocaleKeys.otherVar.tr(),
title: LocaleKeys.changedMind.tr(),
);
void updateSelectionInOfferRejectModelList(int index) {
@ -131,26 +143,44 @@ class ChatVM extends ChangeNotifier {
Future<void> onNewMessageReceivedForRequestOffer({required List<ChatMessageModel> messages, bool isMyOwnOffer = false, required RequestsVM requestsVM}) async {
if (AppState().currentAppType == AppType.customer) {
for (var msg in messages) {
int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == msg.senderUserID);
for (var currentMessage in messages) {
int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == currentMessage.senderUserID);
if (providerIndex != -1) {
serviceProviderOffersList[providerIndex].chatMessages!.add(msg);
if (currentMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer) {
for (var chatMessage in serviceProviderOffersList[providerIndex].chatMessages!) {
if (chatMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer && chatMessage.reqOffer!.id == currentMessage.reqOffer!.id) {
logger.i("chatMessage reqOfferID: ${chatMessage.reqOffer!.id.toString()}");
logger.i("currentMessage reqOfferID: ${currentMessage.reqOffer!.id.toString()}");
chatMessage.reqOffer!.requestOfferStatusEnum = currentMessage.reqOffer!.requestOfferStatusEnum;
}
}
}
serviceProviderOffersList[providerIndex].chatMessages!.add(currentMessage);
scrollChatDown();
}
}
} else {
for (var msg in messages) {
logger.i(msg);
for (var currentMessage in messages) {
logger.i(currentMessage);
// 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
int providerIndex = -1;
int requestIndex = -1;
if (isMyOwnOffer) {
providerIndex = requestsVM.myFilteredRequests.indexWhere((element) => (element.customerUserID == msg.receiverUserID && element.id == msg.requestID));
requestIndex = requestsVM.myFilteredRequests.indexWhere((element) => (element.customerUserID == currentMessage.receiverUserID && element.id == currentMessage.requestID));
} else {
providerIndex = requestsVM.myFilteredRequests.indexWhere((element) => (element.customerUserID == msg.senderUserID && element.id == msg.requestID));
requestIndex = requestsVM.myFilteredRequests.indexWhere((element) => (element.customerUserID == currentMessage.senderUserID && element.id == currentMessage.requestID));
}
log("providerIndex: $providerIndex");
if (providerIndex != -1) {
requestsVM.addChatMessagesInRequestsModel(msg: msg, index: providerIndex);
log("requestIndex: $requestIndex");
if (requestIndex != -1) {
if (currentMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer) {
for (var chatMessage in requestsVM.myFilteredRequests[requestIndex].chatMessages) {
if (chatMessage.chatMessageTypeEnum == ChatMessageTypeEnum.offer && chatMessage.reqOffer!.id == currentMessage.reqOffer!.id) {
logger.i("chatMessage reqOfferID: ${chatMessage.reqOffer!.id.toString()}");
logger.i("currentMessage reqOfferID: ${currentMessage.reqOffer!.id.toString()}");
chatMessage.reqOffer!.requestOfferStatusEnum = currentMessage.reqOffer!.requestOfferStatusEnum;
}
}
}
requestsVM.addChatMessagesInRequestsModel(msg: currentMessage, index: requestIndex);
scrollChatDown();
}
}
@ -158,8 +188,6 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
}
Future<void> onNewMessageReceivedForAds({required List<ChatMessageModel> messages}) async {
for (var msg in messages) {
currentMessagesForAds.add(msg);
@ -323,10 +351,7 @@ class ChatVM extends ChangeNotifier {
serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel);
}
} else {
int providerIndex = context
.read<RequestsVM>()
.myFilteredRequests
.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId));
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId));
log("providerIndex2:$providerIndex");
if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex);
@ -370,6 +395,10 @@ class ChatVM extends ChangeNotifier {
for (var message in serviceProviderOffersList[providerOfferIndex].chatMessages!) {
if (message.chatMessageTypeEnum == ChatMessageTypeEnum.offer) {
updateLatestOfferId(message.reqOfferID ?? 0);
if (message.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted) {
context.read<RequestsVM>().updateAcceptedReqOffer(message.reqOffer!);
context.read<RequestsVM>().updateAcceptedRequestOfferProviderName(serviceProviderOffersList[providerOfferIndex].name ?? "");
}
log("latestOfferId: $latestOfferId");
}
}
@ -404,9 +433,11 @@ class ChatVM extends ChangeNotifier {
}
Future<int> onActionOfferTapped({required BuildContext context, required RequestOfferStatusEnum requestOfferStatusEnum, required int reqOfferId, required String comments}) async {
Utils.showLoading(context);
try {
GenericRespModel genericRespModel = await requestRepo.updateOfferRequestStatus(requestOfferStatusEnum: requestOfferStatusEnum, requestOfferId: reqOfferId, comments: comments);
GenericRespModel genericRespModel = await requestRepo.updateRequestOfferStatus(requestOfferStatusEnum: requestOfferStatusEnum, requestOfferId: reqOfferId, comments: comments);
Utils.showToast(genericRespModel.message.toString());
Utils.showLoading(context);
return genericRespModel.messageStatus == 1 ? reqOfferId : -1;
} catch (e) {
logger.i(e.toString());
@ -563,7 +594,8 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
if (AppState().currentAppType == AppType.customer) {} else {}
if (AppState().currentAppType == AppType.customer) {
} else {}
return true;
}
return false;

@ -10,6 +10,7 @@ import 'package:mc_common_app/services/payments_service.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/subscriptions_view_model.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
@ -56,19 +57,19 @@ class PaymentVM extends ChangeNotifier {
Future<void> onContinuePressed(BuildContext context, {required PaymentTypes paymentType}) async {
switch (selectedPaymentMethod) {
case PaymentMethods.mada:
// TODO: Handle this case.
// TODO: Handle this case.
break;
case PaymentMethods.visa:
await onVisaCardSelected(context, paymentType);
break;
case PaymentMethods.applePay:
// TODO: Handle this case.
// TODO: Handle this case.
break;
case PaymentMethods.masterCard:
// TODO: Handle this case.
// TODO: Handle this case.
break;
case PaymentMethods.tamara:
// TODO: Handle this case.
// TODO: Handle this case.
break;
}
@ -132,35 +133,46 @@ class PaymentVM extends ChangeNotifier {
Future<void> placeThePayment({required PaymentTypes paymentTypeEnum, required BuildContext context}) async {
await paymentService.placePayment(
// This will be request ID for request payment || ad ID for Ad Related Payment || OrderProviderSubscriptionID for SubscriptionPayment
id: getIdTypeByPaymentType(paymentTypeEnum),
// This will be request ID for request payment || ad ID for Ad Related Payment || OrderProviderSubscriptionID for SubscriptionPayment
appointmentIds: appointmentIdsForPayment,
paymentType: paymentTypeEnum,
onFailure: () {
Utils.showToast(LocaleKeys.paymentFailed.tr());
switch (paymentTypeEnum) {
case PaymentTypes.subscription:
// TODO: Handle this case.
// TODO: Handle this case.
Utils.showToast("Appointment Payment has been Failed!!");
break;
case PaymentTypes.appointment:
// TODO: Handle this case.
log("Appointment Payment has been Failed!!");
case PaymentTypes.request:
// TODO: Handle this case.
Utils.showToast("Request Payment has been Failed!!");
break;
case PaymentTypes.adReserve:
// TODO: Handle this case.
// TODO: Handle this case.
Utils.showToast("adReserve Payment has been Failed!!");
break;
case PaymentTypes.ads:
// TODO: Handle this case.
break;
case PaymentTypes.request:
// TODO: Handle this case.
// TODO: Handle this case.
Utils.showToast("ads Payment has been Failed!!");
break;
case PaymentTypes.extendAds:
// TODO: Handle this case.
// TODO: Handle this case.
Utils.showToast("extendAds Payment has been Failed!!");
break;
case PaymentTypes.appointment:
// TODO: Handle this case.
Utils.showToast("Appointment Payment has been Failed!!");
break;
case PaymentTypes.partialAppointment:
log("Partial Appointment Payment has been Failed!!");
case PaymentTypes.partialAppointment:
Utils.showToast("partialAppointment Payment has been Failed!!");
break;
}
},
@ -168,35 +180,39 @@ class PaymentVM extends ChangeNotifier {
// TOD0: we have to take payment confirmation methods from Backend team and make success callbacks like onAdsPaymentSuccess
switch (paymentTypeEnum) {
case PaymentTypes.subscription:
onSubscriptionSuccess(context);
onSubscriptionPaymentSuccess(context);
break;
case PaymentTypes.appointment:
log("Appointment Payment has been Succeeded");
Utils.showToast("Appointment Payment has been Succeeded");
break;
case PaymentTypes.partialAppointment:
Utils.showToast("Partial Appointment Payment has been Succeeded");
break;
case PaymentTypes.request:
Utils.showToast("Request Payment has been Succeeded");
onRequestPaymentSuccess(context);
break;
case PaymentTypes.adReserve:
case PaymentTypes.ads:
case PaymentTypes.extendAds:
await onAdsPaymentSuccess(context: context, paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(), currentAdId: currentAdId);
break;
case PaymentTypes.partialAppointment:
log("Partial Appointment Payment has been Succeeded");
break;
}
},
);
}
onSubscriptionSuccess(BuildContext context) {
// pop(context);
// pop(context);
// pop(context);
void onRequestPaymentSuccess(BuildContext context) {
context.read<DashboardVmCustomer>().onNavbarTapped(4);
navigateReplaceWithName(context, AppRoutes.dashboard);
}
// context.read<SubscriptionsVM>().getMySubscriptions(AppState().getUser.data?.userInfo?.providerId.toString() ?? "");
context
.read<SubscriptionsVM>()
.mySubscriptionsBySp = [];
void onSubscriptionPaymentSuccess(BuildContext context) {
context.read<SubscriptionsVM>().mySubscriptionsBySp = [];
context.read<SubscriptionsVM>().getMySubscriptionsBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "");
navigateReplaceWithNameUntilRoute(context, AppRoutes.mySubscriptionsPage);
}
@ -210,7 +226,8 @@ class PaymentVM extends ChangeNotifier {
await placeThePayment(context: context, paymentTypeEnum: paymentType);
break;
case PaymentTypes.request:
// TODO: Handle this case.
if (requestId == -1) return;
await placeThePayment(context: context, paymentTypeEnum: paymentType);
break;
case PaymentTypes.subscription:
await placeThePayment(context: context, paymentTypeEnum: paymentType);

@ -9,6 +9,7 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/general_models/enums_model.dart';
@ -19,6 +20,7 @@ import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/repositories/common_repo.dart';
import 'package:mc_common_app/repositories/request_repo.dart';
import 'package:mc_common_app/services/common_services.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
@ -48,6 +50,20 @@ class RequestsVM extends BaseVM {
currentSelectedRequest = value;
}
ReqOffer? acceptedRequestOffer;
updateAcceptedReqOffer(ReqOffer value) {
acceptedRequestOffer = value;
notifyListeners();
}
String? acceptedRequestOfferProviderName;
updateAcceptedRequestOfferProviderName(String value) {
acceptedRequestOfferProviderName = value;
notifyListeners();
}
populateRequestsFilterList() async {
if (requestsTypeFilterOptions.isNotEmpty) return;
@ -95,6 +111,10 @@ class RequestsVM extends BaseVM {
for (var message in myFilteredRequests[index].chatMessages) {
if (message.chatMessageTypeEnum == ChatMessageTypeEnum.offer) {
context.read<ChatVM>().updateLatestOfferId(message.reqOfferID ?? 0);
if (message.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.accepted) {
updateAcceptedReqOffer(message.reqOffer!);
updateAcceptedRequestOfferProviderName(message.senderName ?? "");
}
}
}
}
@ -227,10 +247,10 @@ class RequestsVM extends BaseVM {
notifyListeners();
}
bool isShippingDeliveryEnabled = false;
bool shippingEnabled = false;
void updateShippingDeliverEnabled(bool v) {
isShippingDeliveryEnabled = v;
void updateShippingEnabled(bool v) {
shippingEnabled = v;
notifyListeners();
}
@ -256,15 +276,20 @@ class RequestsVM extends BaseVM {
//Request Management
String price = "";
String description = "";
String address = "";
updatePrice(String v) {
void updatePrice(String v) {
price = v;
}
updateDescription(String v) {
void updateDescription(String v) {
description = v;
}
void updateAddress(String v) {
address = v;
}
Future<RequestPostingImages> convertFileToRequestPostingImages({required File file}) async {
List<int> imageBytes = await file.readAsBytes();
String image = base64Encode(imageBytes);
@ -305,6 +330,7 @@ class RequestsVM extends BaseVM {
cityID: vehicleCityId.selectedId,
price: price,
description: description,
address: address,
isSpecialServiceNeeded: false,
requestImages: requestImages,
);
@ -353,6 +379,9 @@ class RequestsVM extends BaseVM {
} else if (description.isEmpty) {
Utils.showToast(LocaleKeys.addValidDescription.tr());
isValid = false;
} else if (address.isEmpty && requestTypeId.selectedId == 2) {
Utils.showToast(LocaleKeys.addValidDescription.tr());
isValid = false;
}
return isValid;
}
@ -410,6 +439,49 @@ class RequestsVM extends BaseVM {
}
}
List<OfferRequestCommentModel> providerDeliveryActionsList = [
OfferRequestCommentModel(
index: 0,
isSelected: false,
title: LocaleKeys.shipping.tr(),
),
OfferRequestCommentModel(
index: 1,
isSelected: true,
title: LocaleKeys.delivery.tr(),
),
];
OfferRequestCommentModel selectedProviderDeliveryAction = OfferRequestCommentModel(
index: 1,
isSelected: true,
title: LocaleKeys.delivery.tr(),
);
void updateSelectedProviderDeliveryAction(int index) {
for (var value in providerDeliveryActionsList) {
value.isSelected = false;
}
selectedProviderDeliveryAction = providerDeliveryActionsList[index];
providerDeliveryActionsList[index].isSelected = true;
notifyListeners();
}
Future<bool> onActionRequestTapped({required BuildContext context, required RequestStatusEnum requestStatusEnum, required int requestId}) async {
Utils.showLoading(context);
try {
GenericRespModel genericRespModel = await requestRepo.updateRequestStatus(requestStatusEnum: requestStatusEnum, requestId: requestId);
Utils.showToast(genericRespModel.message.toString());
Utils.hideLoading(context);
return genericRespModel.messageStatus == 1;
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
return false;
}
}
//SENDING OFFER
bool isSendOfferValidated() {
bool isValidated = true;

@ -175,6 +175,14 @@ class SubscriptionsVM extends BaseVM {
}
}
// Rights
// Branch CRUD
// Services in Branch (CRUD)
// Appointment Management
// Request Management with Chat
// Ads CRUD with Chat
// General Chat
// My Provider Subscription
getMySubscriptionsBySP(String? serviceProviderID) async {
setState(ViewState.busy);

@ -37,8 +37,8 @@ class SingleDetailWidget extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"$type:".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
text.toText(fontSize: 14, color: MyColors.black, isBold: true),
(type.isEmpty ? type : "$type:").toText(fontSize: 12, color: MyColors.lightTextColor),
text.toText(fontSize: 14, color: MyColors.darkTextColor),
],
);
}

@ -1,6 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
@ -22,7 +21,7 @@ class ReviewAppointment extends StatelessWidget {
AppointmentsVM appointmentsVM = context.read<AppointmentsVM>();
return Padding(
padding: const EdgeInsets.only(
bottom: 10,
bottom: 0,
left: 21,
right: 21,
),
@ -48,17 +47,12 @@ class ReviewAppointment extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
(appointmentsVM.selectedBranchModel!.branchName ??
"")
.toText(fontSize: 16, isBold: true),
(appointmentsVM.selectedBranchModel!.branchName ?? "").toText(fontSize: 16, isBold: true),
Row(
children: [
LocaleKeys.location.tr().toText(
color: MyColors.lightTextColor,
fontSize: 12),
LocaleKeys.location.tr().toText(color: MyColors.lightTextColor, fontSize: 12),
2.width,
": ${appointmentsVM.selectedBranchModel!.branchDescription ?? ""}"
.toText(fontSize: 12),
": ${appointmentsVM.selectedBranchModel!.branchDescription ?? ""}".toText(fontSize: 12),
],
),
],
@ -82,9 +76,7 @@ class ReviewAppointment extends StatelessWidget {
),
),
],
)
.onPress(() {})
.toWhiteContainer(width: double.infinity, allPading: 12));
).onPress(() {}).toWhiteContainer(width: double.infinity, allPading: 12));
}
Widget buildServicesInfoCard({required BuildContext context}) {
@ -93,18 +85,14 @@ class ReviewAppointment extends StatelessWidget {
shrinkWrap: true,
itemCount: appointmentsVM.serviceAppointmentScheduleList.length,
itemBuilder: (BuildContext context, int scheduleIndex) {
ServiceAppointmentScheduleModel scheduleData =
appointmentsVM.serviceAppointmentScheduleList[scheduleIndex];
ServiceAppointmentScheduleModel scheduleData = appointmentsVM.serviceAppointmentScheduleList[scheduleIndex];
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: LocaleKeys.services.tr().toText(
fontSize: 14,
isBold: true,
color: MyColors.lightTextColor),
child: LocaleKeys.services.tr().toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
),
],
),
@ -112,60 +100,38 @@ class ReviewAppointment extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
physics: NeverScrollableScrollPhysics(),
// physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: scheduleData.servicesListInAppointment!.length,
itemBuilder: (BuildContext context, int serviceIndex) {
String selectedTimeSlot = "";
if (scheduleData
.selectedCustomTimeDateSlotModel!.availableSlots !=
null) {
selectedTimeSlot = scheduleData
.selectedCustomTimeDateSlotModel!.availableSlots!
.firstWhere((element) => element.isSelected)
.slot;
if (scheduleData.selectedCustomTimeDateSlotModel!.availableSlots != null) {
selectedTimeSlot = scheduleData.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected).slot;
}
return Column(
children: [
if (scheduleData
.servicesListInAppointment!.isNotEmpty) ...[
if (scheduleData.servicesListInAppointment!.isNotEmpty) ...[
Column(
children: List.generate(
scheduleData.servicesListInAppointment!.length,
(serviceIndex) {
ServiceModel serviceData = scheduleData
.servicesListInAppointment![serviceIndex];
children: List.generate(scheduleData.servicesListInAppointment!.length, (serviceIndex) {
ServiceModel serviceData = scheduleData.servicesListInAppointment![serviceIndex];
return Column(
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${serviceData.providerServiceDescription}"
.toText(fontSize: 16, isBold: true),
"${serviceData.providerServiceDescription}".toText(fontSize: 16, isBold: true),
],
),
Column(
children: List.generate(
serviceData.serviceItems!.length,
(itemIndex) {
ItemData itemData =
serviceData.serviceItems![itemIndex];
children: List.generate(serviceData.serviceItems!.length, (itemIndex) {
ItemData itemData = serviceData.serviceItems![itemIndex];
return Column(
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
("${itemData.name}: ${itemData.price} " +
LocaleKeys.sar.tr())
.toText(
fontSize: 13,
isBold: true,
color: MyColors
.lightTextColor),
("${itemData.name}: ${itemData.price} ${LocaleKeys.sar.tr()}").toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor),
],
),
],
@ -183,33 +149,21 @@ class ReviewAppointment extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.timeLocation
.tr()
.toText(fontSize: 16, isBold: true),
LocaleKeys.timeLocation.tr().toText(fontSize: 16, isBold: true),
3.height,
Row(children: [
LocaleKeys.dateAndTime.tr().toText(
fontSize: 12,
color: MyColors.lightTextColor,
isBold: true),
"${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at ${selectedTimeSlot}"
.toText(fontSize: 12, isBold: true),
LocaleKeys.dateAndTime.tr().toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"${scheduleData.selectedCustomTimeDateSlotModel!.date!.date} at ${selectedTimeSlot}".toText(fontSize: 12, isBold: true),
]),
Row(children: [
(LocaleKeys.location.tr() + " ").toText(
fontSize: 12,
color: MyColors.lightTextColor,
isBold: true),
(scheduleData.appointmentType == 2
? LocaleKeys.home.tr()
: LocaleKeys.workshop.tr())
.toText(fontSize: 12, isBold: true),
("${LocaleKeys.location.tr()} ").toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
(scheduleData.appointmentType == 2 ? LocaleKeys.home.tr() : LocaleKeys.workshop.tr()).toText(fontSize: 12, isBold: true),
]),
],
),
),
10.height,
Divider(thickness: 0.7),
const Divider(thickness: 0.7),
Builder(builder: (BuildContext context) {
double totalServicePrice = 0.0;
double totalKms = 15.3;
@ -219,35 +173,20 @@ class ReviewAppointment extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.amountVAR
.tr()
.toText(fontSize: 16, isBold: true),
LocaleKeys.amountVAR.tr().toText(fontSize: 16, isBold: true),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.serviceCharges.tr().toText(
fontSize: 14,
color: MyColors.lightTextColor,
isBold: true),
"${scheduleData.amountTotal.toString()} SAR"
.toText(fontSize: 16, isBold: true),
LocaleKeys.serviceCharges.tr().toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true),
"${scheduleData.amountTotal.toString()} SAR".toText(fontSize: 16, isBold: true),
],
),
if (scheduleData.appointmentType == 1) ...[
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
(LocaleKeys.locationCharges.tr() +
" ($rangePricePerKm x $totalKms )")
.toText(
fontSize: 14,
color: MyColors.lightTextColor,
isBold: true),
("${totalServicePrice.toString()} " +
LocaleKeys.sar.tr())
.toText(fontSize: 16, isBold: true),
("${LocaleKeys.locationCharges.tr()} ($rangePricePerKm x $totalKms )").toText(fontSize: 14, color: MyColors.lightTextColor, isBold: true),
("${totalServicePrice.toString()} ${LocaleKeys.sar.tr()}").toText(fontSize: 16, isBold: true),
],
),
],
@ -258,16 +197,12 @@ class ReviewAppointment extends StatelessWidget {
],
);
},
separatorBuilder: (BuildContext context, int index) =>
Divider(thickness: 2),
separatorBuilder: (BuildContext context, int index) => Divider(thickness: 2),
).paddingOnly(bottom: 10),
],
),
],
).toWhiteContainer(
width: double.infinity,
allPading: 12,
margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 0));
).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.fromLTRB(21 , 0, 21, 12));
},
);
}
@ -289,17 +224,9 @@ class ReviewAppointment extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
appointmentsVM.amountToPayForAppointment
.toString()
.toText(fontSize: 16, isBold: true),
appointmentsVM.amountToPayForAppointment.toString().toText(fontSize: 16, isBold: true),
2.width,
LocaleKeys.sar
.tr()
.toText(
color: MyColors.lightTextColor,
fontSize: 12,
isBold: true)
.paddingOnly(bottom: 2),
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2),
],
)
],
@ -307,24 +234,13 @@ class ReviewAppointment extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.remainingAmount
.tr()
.toText(fontSize: 14, isBold: true),
LocaleKeys.remainingAmount.tr().toText(fontSize: 14, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
(appointmentsVM.totalAmount -
appointmentsVM.amountToPayForAppointment)
.toString()
.toText(fontSize: 16, isBold: true),
(appointmentsVM.totalAmount - appointmentsVM.amountToPayForAppointment).toString().toText(fontSize: 16, isBold: true),
2.width,
LocaleKeys.sar
.tr()
.toText(
color: MyColors.lightTextColor,
fontSize: 12,
isBold: true)
.paddingOnly(bottom: 2),
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 2),
],
)
],
@ -337,17 +253,9 @@ class ReviewAppointment extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
appointmentsVM.totalAmount
.toString()
.toText(fontSize: 29, isBold: true),
appointmentsVM.totalAmount.toString().toText(fontSize: 29, isBold: true),
2.width,
LocaleKeys.sar
.tr()
.toText(
color: MyColors.lightTextColor,
fontSize: 16,
isBold: true)
.paddingOnly(bottom: 5),
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
],
)
],
@ -383,7 +291,9 @@ class ReviewAppointment extends StatelessWidget {
children: [
ListView(
children: [
10.height,
buildBranchInfoCard(context: context),
10.height,
buildServicesInfoCard(context: context),
],
).expand(),

@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
@ -7,10 +8,14 @@ import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/payment_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart';
import 'package:mc_common_app/views/chat/widgets/chat_message_widget.dart';
import 'package:mc_common_app/views/requests/request_bottomsheets.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
@ -90,7 +95,7 @@ class _ChatViewState extends State<ChatView> {
if (chatTypeEnum == ChatTypeEnum.ads) {
chatMessages = chatVM.currentMessagesForAds;
} else if (chatTypeEnum == ChatTypeEnum.requestOffer) {
if (AppState().currentAppType == AppType.customer) {
if (AppState().currentAppType == AppType.customer && chatVM.serviceProviderOffersList.isNotEmpty) {
chatMessages = chatVM.serviceProviderOffersList[chatViewArgumentsForRequest!.providerIndex].chatMessages ?? [];
} else {
chatMessages = requestVM.myFilteredRequests[chatViewArgumentsForRequest!.requestIndex].chatMessages;
@ -107,7 +112,7 @@ class _ChatViewState extends State<ChatView> {
separatorBuilder: (BuildContext context, int index) => 20.height,
itemBuilder: (BuildContext context, int index) {
ChatMessageModel chatMessageModel = chatMessages[index];
return ChatMessageCustomWidget(chatMessageModel: chatMessageModel);
return ChatMessageCustomWidget(chatMessageModel: chatMessageModel, requestStatusEnum: requestVM.currentSelectedRequest!.requestStatus);
},
).horPaddingMain(),
),
@ -115,16 +120,48 @@ class _ChatViewState extends State<ChatView> {
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (chatTypeEnum == ChatTypeEnum.requestOffer && requestVM.currentSelectedRequest!.requestStatus == RequestStatus.inProgress) ...[
Center(
child: LocaleKeys.requestAlreadyInProgress.tr().toText(
textAlign: TextAlign.center,
color: MyColors.lightTextColor,
fontSize: 14,
),
).paddingAll(15)
if (chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestType.toRequestTypeStatusEnum() == RequestsTypeEnum.serviceRequest &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress &&
AppState().currentAppType == AppType.customer) ...[
Expanded(
child: ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
maxHeight: 55,
title: LocaleKeys.reviewAndPayment.tr(),
isBold: false,
onPressed: () {
context.read<PaymentVM>().updateRequestId(id: requestVM.currentSelectedRequest!.id);
navigateWithName(context, AppRoutes.reviewRequestOffer, arguments: PaymentTypes.request);
},
),
),
// Center(
// child: LocaleKeys.requestAlreadyInProgress.tr().toText(
// textAlign: TextAlign.center,
// color: MyColors.lightTextColor,
// fontSize: 14,
// ),
// ).paddingAll(15)
] else if (chatTypeEnum == ChatTypeEnum.requestOffer &&
requestVM.currentSelectedRequest!.requestType.toRequestTypeStatusEnum() == RequestsTypeEnum.specialCarRequest &&
requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.inProgress &&
AppState().currentAppType == AppType.customer) ...[
Expanded(
child: ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
maxHeight: 55,
title: LocaleKeys.completeDeal.tr(),
isBold: false,
onPressed: () {
return dealCompletedConfirmationBottomSheet(mainContext: context, requestStatusEnum: RequestStatusEnum.completed, requestId: requestVM.currentSelectedRequest!.id);
},
),
),
] else ...[
if (AppState().currentAppType == AppType.provider && chatTypeEnum == ChatTypeEnum.requestOffer) ...[
if (AppState().currentAppType == AppType.provider && chatTypeEnum == ChatTypeEnum.requestOffer && requestVM.currentSelectedRequest!.requestStatus == RequestStatusEnum.submitted) ...[
Expanded(
flex: 1,
child: const Icon(
@ -134,7 +171,7 @@ class _ChatViewState extends State<ChatView> {
).onPress(
() {
RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(requestIndex: chatViewArgumentsForRequest!.requestIndex, requestModel: chatViewArgumentsForRequest!.requestModel!);
buildSendOfferBottomSheet(context, requestDetailArguments);
buildSendOfferBottomSheet(context: context, requestDetailPageArguments: requestDetailArguments, isFromChatScreen: true);
},
),
),
@ -167,7 +204,7 @@ class _ChatViewState extends State<ChatView> {
),
],
],
).toContainer(isShadowEnabled: true),
).toContainer(isShadowEnabled: true)
],
);
}),

@ -0,0 +1,70 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:provider/provider.dart';
void dealCompletedConfirmationBottomSheet({required BuildContext mainContext, required RequestStatusEnum requestStatusEnum, required int requestId}) {
final requestVM = mainContext.read<RequestsVM>();
return actionConfirmationBottomSheet(
isOnlyOneButton: true,
context: mainContext,
title: LocaleKeys.doYouWantToCompleteThisDeal.tr().toText(fontSize: 26, isBold: true, letterSpacing: -1.44),
subtitle: AppState().currentAppType == AppType.provider ? LocaleKeys.providerCompletingDealMeansThat.tr() : LocaleKeys.customerCompletingDealMeansThat.tr(),
confirmationWidget: Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return Row(
children: [
Expanded(
child: CircleCheckBoxWithTitle(
isChecked: chatVM.acknowledgePaymentToMowaterStatus,
title: LocaleKeys.acknowledgePaymentToMowater.tr(),
onSelected: () {
chatVM.updateAcknowledgePaymentToMowaterStatus(!chatVM.acknowledgePaymentToMowaterStatus);
},
selectedColor: MyColors.darkPrimaryColor,
),
),
],
);
}),
actionButtonYes: Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return Expanded(
child: ShowFillButton(
maxHeight: 55,
isDisabled: !chatVM.acknowledgePaymentToMowaterStatus,
title: LocaleKeys.submit.tr(),
fontSize: 15,
onPressed: () async {
Navigator.pop(context);
bool status = await requestVM.onActionRequestTapped(context: mainContext, requestStatusEnum: requestStatusEnum, requestId: requestId);
if (status) {
chatVM.updateAcknowledgePaymentToMowaterStatus(false);
mainContext.read<DashboardVmCustomer>().onNavbarTapped(4);
navigateReplaceWithName(mainContext, AppRoutes.dashboard);
}
},
),
);
}),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.no.tr(),
fontSize: 15,
onPressed: () => Navigator.pop(mainContext),
),
),
);
}

@ -1,4 +1,3 @@
import 'dart:developer';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
@ -12,6 +11,7 @@ import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
@ -22,15 +22,16 @@ import 'package:easy_localization/easy_localization.dart' as lcl;
class ChatMessageCustomWidget extends StatefulWidget {
final ChatMessageModel chatMessageModel;
final RequestStatusEnum requestStatusEnum;
const ChatMessageCustomWidget({super.key, required this.chatMessageModel});
const ChatMessageCustomWidget({super.key, required this.chatMessageModel, required this.requestStatusEnum});
@override
State<ChatMessageCustomWidget> createState() => _ChatMessageCustomWidgetState();
}
class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Future buildRejectOfferBottomSheet({required ChatMessageModel chatMessageModel}) {
Future buildRejectOrCancelOfferBottomSheet({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
@ -38,7 +39,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.makeAnOffer.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
@ -51,8 +52,16 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
ListView.separated(
shrinkWrap: true,
itemCount: chatVM.offerRejectModelList.length,
separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5),
separatorBuilder: (BuildContext context, int index) {
if (chatVM.offerRejectModelList[index].index == 3 && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) {
return const SizedBox();
}
return const Divider(thickness: 0.5);
},
itemBuilder: (BuildContext context, int index) {
if (chatVM.offerRejectModelList[index].index == 3 && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) {
return const SizedBox();
}
OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index];
return CircleCheckBoxWithTitle(
isChecked: offerRequestCommentModel.isSelected ?? false,
@ -93,26 +102,28 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
return;
}
Navigator.pop(context);
bool status = await context.read<ChatVM>().onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.senderUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: comments,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
requestOfferStatusEnum: RequestOfferStatusEnum.rejected,
context: context,
);
bool status = await chatVM.onSendMessageForActionOnRequestOffer(
receiverId: (chatMessageModel.isMyMessage ?? false) ? chatMessageModel.receiverUserID ?? "" : chatMessageModel.senderUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: comments,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
requestOfferStatusEnum: requestOfferStatusEnum,
context: context,
);
if (status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected;
chatMessageModel.reqOffer!.requestOfferStatusEnum = requestOfferStatusEnum;
setState(() {});
// Navigator.pop(context);
Utils.showToast("Offer Rejected");
chatVM.updateRejectOfferDescription('');
Utils.showToast("Offer ${requestOfferStatusEnum == RequestOfferStatusEnum.rejected ? "Rejected" : "Cancelled"}");
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
@ -155,11 +166,15 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
);
if (status) {
final requestVM = context.read<RequestsVM>();
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
requestVM.currentSelectedRequest!.requestStatus = RequestStatusEnum.inProgress;
requestVM.updateAcceptedReqOffer(chatMessageModel.reqOffer!);
requestVM.updateAcceptedRequestOfferProviderName(chatMessageModel.senderName ?? "");
setState(() {});
// Navigator.pop(context);
Utils.showToast("Offer Accepted");
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
@ -181,9 +196,32 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
);
}
Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) {
Widget buildOfferDetailsInChatMessage({required RequestStatusEnum requestStatusEnum, required ChatMessageModel chatMessageModel, required BuildContext context}) {
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer;
if (requestStatusEnum != RequestStatusEnum.submitted && requestOfferStatusEnum != RequestOfferStatusEnum.accepted) {
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
5.height,
Center(
child: LocaleKeys.offerNoLongerAvailable.tr().toText(
color: chatMessageModel.isMyMessage! ? MyColors.adPendingStatusColor : MyColors.lightTextColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: chatMessageModel.isMyMessage! ? MyColors.adPendingStatusColor.withOpacity(0.16) : MyColors.grey98Color.withOpacity(0.1)),
],
);
}
//offerNoLongerAvailable
switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer:
return Column(
@ -225,13 +263,33 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
backgroundColor: MyColors.white,
txtColor: MyColors.redColor,
fontSize: 9,
onPressed: () async {
buildRejectOfferBottomSheet(chatMessageModel: chatMessageModel);
onPressed: () {
buildRejectOrCancelOfferBottomSheet(chatMessageModel: chatMessageModel, requestOfferStatusEnum: RequestOfferStatusEnum.rejected);
},
),
)
],
),
] else if (requestStatusEnum == RequestStatusEnum.submitted) ...[
10.height,
Row(
children: [
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.cancelOffer.tr(),
fontSize: 9,
borderColor: MyColors.lightTextColor,
isFilled: false,
onPressed: () {
buildRejectOrCancelOfferBottomSheet(chatMessageModel: chatMessageModel, requestOfferStatusEnum: RequestOfferStatusEnum.cancel);
},
backgroundColor: MyColors.white,
txtColor: MyColors.lightTextColor,
),
),
],
),
],
],
);
@ -250,6 +308,15 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
case RequestOfferStatusEnum.accepted:
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
5.height,
Center(
child: LocaleKeys.offerHasBeenAccepted.tr().toText(
color: MyColors.adPendingStatusColor,
@ -262,6 +329,15 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
case RequestOfferStatusEnum.rejected:
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
5.height,
Center(
child: LocaleKeys.offerHasBeenRejected.tr().toText(
color: MyColors.adPendingStatusColor,
@ -274,6 +350,15 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
case RequestOfferStatusEnum.cancel:
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
5.height,
Center(
child: LocaleKeys.offerHasBeenCancelled.tr().toText(
color: MyColors.adPendingStatusColor,
@ -369,8 +454,9 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
),
],
),
10.height,
if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[
buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context),
buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum, chatMessageModel: widget.chatMessageModel, context: context),
],
],
).toContainer(

@ -1,82 +0,0 @@
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/requests_view_model.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/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/views/requests/widget/request_item.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class MyRequestsFragment extends StatelessWidget {
const MyRequestsFragment({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.myRequests.tr(),
isRemoveBackButton: true,
),
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) {
return Column(
children: [
16.height,
FiltersList(
filterList: requestsVM.requestsTypeFilterOptions,
onFilterTapped: (index, selectedFilterId) {
requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: selectedFilterId.toRequestTypeStatusEnum());
},
),
8.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async => await requestsVM.getRequests(isNeedToRebuild: true, appType: AppType.provider),
child: requestsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: requestsVM.myFilteredRequests.isEmpty
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.noRequeststoShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor),
],
)
: ListView.separated(
itemBuilder: (context, index) {
return RequestItem(request: requestsVM.myFilteredRequests[index], appType: AppState().currentAppType, requestIndex: index);
},
separatorBuilder: (context, index) {
return 16.height;
},
itemCount: requestsVM.myFilteredRequests.length,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8),
),
))
],
);
}),
),
floatingActionButton: AppState().currentAppType == AppType.customer
? FloatingActionButton(
onPressed: () => navigateWithName(context, AppRoutes.createRequestPage),
backgroundColor: MyColors.darkPrimaryColor,
child: const Icon(
Icons.add,
color: MyColors.white,
),
)
: null,
);
}
}

@ -0,0 +1,156 @@
import 'package:flutter/gestures.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/requests_view_model.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/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/views/requests/widget/request_item.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/categories_list.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class MyRequestsFragment extends StatelessWidget {
const MyRequestsFragment({super.key});
void cancelRequestBottomSheet(BuildContext context, int requestId) {
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.cancelRequestPrompt.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.requestPermanentlyCancelled.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () async {
Navigator.pop(context);
final requestVM = context.read<RequestsVM>();
bool status = await requestVM.onActionRequestTapped(context: context, requestStatusEnum: RequestStatusEnum.cancelled, requestId: requestId);
if (status) {
await requestVM.getRequests(isNeedToRebuild: true, appType: AppType.provider);
}
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
bool shouldDismiss(RequestStatusEnum requestStatusEnum) {
if (AppState().currentAppType == AppType.provider) {
return false;
}
if (requestStatusEnum == RequestStatusEnum.pending || requestStatusEnum == RequestStatusEnum.submitted) {
return true;
}
return false;
;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.myRequests.tr(),
isRemoveBackButton: true,
),
body: Container(
color: MyColors.backgroundColor,
width: double.infinity,
height: double.infinity,
child: Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) {
return Column(
children: [
16.height,
FiltersList(
filterList: requestsVM.requestsTypeFilterOptions,
onFilterTapped: (index, selectedFilterId) {
requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: selectedFilterId.toRequestTypeStatusEnum());
},
),
8.height,
Expanded(
child: RefreshIndicator(
onRefresh: () async => await requestsVM.getRequests(isNeedToRebuild: true, appType: AppType.provider),
child: requestsVM.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: requestsVM.myFilteredRequests.isEmpty
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.noRequeststoShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor),
],
)
: ListView.separated(
itemBuilder: (context, index) {
return Dismissible(
key: Key(index.toString()),
background: const SizedBox(),
secondaryBackground: Container(
color: const Color(0xFFDD0505),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: [
LocaleKeys.cancel.tr().toText(fontSize: 15, fontWeight: MyFonts.Medium, height: 23 / 25, color: MyColors.white).margin(right: 23),
],
),
),
dragStartBehavior: DragStartBehavior.start,
direction: shouldDismiss(requestsVM.myFilteredRequests[index].requestStatus) ? DismissDirection.endToStart : DismissDirection.none,
confirmDismiss: (DismissDirection direction) async {
cancelRequestBottomSheet(context, requestsVM.myFilteredRequests[index].id);
},
child: RequestItem(request: requestsVM.myFilteredRequests[index], appType: AppState().currentAppType, requestIndex: index),
);
},
separatorBuilder: (context, index) {
return 16.height;
},
itemCount: requestsVM.myFilteredRequests.length,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8),
),
))
],
);
}),
),
floatingActionButton: AppState().currentAppType == AppType.customer
? FloatingActionButton(
onPressed: () => navigateWithName(context, AppRoutes.createRequestPage),
backgroundColor: MyColors.darkPrimaryColor,
child: const Icon(
Icons.add,
color: MyColors.white,
),
)
: null,
);
}
}

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:developer';
import 'package:easy_localization/src/public_ext.dart';
import 'package:flutter/material.dart';
@ -16,9 +17,9 @@ import 'map_selection_widget.dart';
import 'package:geocoding/geocoding.dart';
class PickLocationPage extends StatefulWidget {
Function(double, double, String) onPickAddress;
final Function(double, double, String) onPickAddress;
PickLocationPage({required this.onPickAddress});
const PickLocationPage({super.key, required this.onPickAddress});
@override
State<PickLocationPage> createState() => _PickLocationPageState();
@ -28,14 +29,14 @@ class _PickLocationPageState extends State<PickLocationPage> {
double latitude = 0;
double longitude = 0;
final Set<Marker> markers = new Set();
final Set<Marker> markers = {};
late AppMap appMap;
static CameraPosition _kGooglePlex = CameraPosition(
static CameraPosition _kGooglePlex = const CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
late LatLng currentPostion;
late LatLng currentPosition;
Completer<GoogleMapController> mapController = Completer();
final ValueNotifier<String> _counter = ValueNotifier<String>("");
@ -80,7 +81,7 @@ class _PickLocationPageState extends State<PickLocationPage> {
return value.isNotEmpty
? Container(
width: double.infinity,
margin: EdgeInsets.all(12),
margin: const EdgeInsets.all(12),
child: Card(
child: Padding(
padding: const EdgeInsets.all(12.0),
@ -102,11 +103,12 @@ class _PickLocationPageState extends State<PickLocationPage> {
],
),
),
Container(
SizedBox(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.pickAddress.tr(),
onPressed: () {
widget.onPickAddress(latitude, longitude, _counter.value);
@ -120,18 +122,18 @@ class _PickLocationPageState extends State<PickLocationPage> {
);
}
Future<void> _updatePosition(CameraPosition _position) async {
latitude = _position.target.latitude;
longitude = _position.target.longitude;
print(latitude);
print(latitude);
Future<void> _updatePosition(CameraPosition position) async {
latitude = position.target.latitude;
longitude = position.target.longitude;
log(latitude.toString());
log(latitude.toString());
updateAddress(latitude, longitude);
}
void _getUserLocation() async {
if (await Permission.location.request().isGranted) {
var position = await GeolocatorPlatform.instance.getCurrentPosition();
currentPostion = LatLng(position.latitude, position.longitude);
currentPosition = LatLng(position.latitude, position.longitude);
latitude = position.latitude;
longitude = position.longitude;
setMap();
@ -141,7 +143,7 @@ class _PickLocationPageState extends State<PickLocationPage> {
(value) async {
if (value[Permission.location]!.isGranted) {
var position = await GeolocatorPlatform.instance.getCurrentPosition();
currentPostion = LatLng(position.latitude, position.longitude);
currentPosition = LatLng(position.latitude, position.longitude);
latitude = position.latitude;
longitude = position.longitude;
setMap();
@ -160,7 +162,7 @@ class _PickLocationPageState extends State<PickLocationPage> {
setMap() {
setState(() {
_kGooglePlex = CameraPosition(
target: currentPostion,
target: currentPosition,
zoom: 14.4746,
);
appMap.moveTo(cameraPostion: _kGooglePlex);

@ -99,7 +99,7 @@ class PaymentMethodContainer extends StatelessWidget {
return InkWell(
onTap: onTap,
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
margin: const EdgeInsets.symmetric(vertical: 10),
width: double.infinity,
height: 80,
decoration: BoxDecoration(
@ -120,11 +120,11 @@ class PaymentMethodContainer extends StatelessWidget {
Container(
height: 80.0,
width: 80.0,
padding: EdgeInsets.all(7.0),
padding: const EdgeInsets.all(7.0),
child: Image.asset(cardAsset),
),
Utils.mFlex(1),
isSelected ? Utils.statusContainerChip(text: "Selected", padding: EdgeInsets.symmetric(vertical: 6, horizontal: 10)) : SizedBox(),
isSelected ? Utils.statusContainerChip(text: "Selected", padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10)) : const SizedBox(),
],
),
),

@ -1,4 +1,6 @@
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
@ -8,6 +10,7 @@ import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart';
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/views/location_views/pick_location_page.dart';
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/dropdown/dropdow_field.dart';
@ -173,11 +176,40 @@ class CreateRequestPage extends StatelessWidget {
8.height,
TxtField(
hint: LocaleKeys.description.tr(),
maxLines: 5,
maxLines: 3,
value: requestsVM.description,
onChanged: (e) => requestsVM.updateDescription(e),
),
8.height,
// 1 -> SpecialCarRequest
// 2 -> ServiceRequest
if (requestsVM.requestTypeId.selectedId == 2) ...[
TxtField(
hint: LocaleKeys.address.tr(),
isNeedClickAll: false,
value: requestsVM.address,
postfixWidget: IconButton(
icon: const Icon(
size: 28,
Icons.add_location_outlined,
),
onPressed: () {
navigateTo(
context,
PickLocationPage(
onPickAddress: (double latitude, double longitude, String address) {
requestsVM.address = address;
requestsVM.setState(ViewState.idle);
},
),
);
}),
onChanged: (e) => requestsVM.updateAddress(e),
),
8.height,
],
DottedRectContainer(
onTap: () => context.read<RequestsVM>().pickMultipleImages(),
text: LocaleKeys.attachImage.tr(),
@ -202,19 +234,6 @@ class CreateRequestPage extends StatelessWidget {
},
),
],
16.height,
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Checkbox(
value: requestsVM.isShippingDeliveryEnabled,
onChanged: null,
),
LocaleKeys.shippingDelivery.tr().toText(color: MyColors.darkPrimaryColor, isBold: true, fontSize: 18)
],
).onPress(() {
requestsVM.updateShippingDeliverEnabled(!requestsVM.isShippingDeliveryEnabled);
}),
],
),
]
@ -225,6 +244,7 @@ class CreateRequestPage extends StatelessWidget {
ShowFillButton(
title: LocaleKeys.createRequest.tr(),
maxWidth: double.infinity,
maxHeight: 55,
margin: const EdgeInsets.all(16),
onPressed: () async {
await context.read<RequestsVM>().onCreateRequestTapped(context);

@ -3,17 +3,22 @@ import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArguments requestDetailPageArguments) {
Future buildSendOfferBottomSheet({required BuildContext context, required RequestDetailPageArguments requestDetailPageArguments, required bool isFromChatScreen}) {
final requestDetail = requestDetailPageArguments.requestModel;
return showModalBottomSheet(
context: context,
@ -54,13 +59,13 @@ Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArgument
value: requestsVM.itemManufacturer,
errorValue: requestsVM.offerPriceError,
keyboardType: TextInputType.number,
hint: "Manufacturer",
hint: LocaleKeys.manufacturer.tr(),
onChanged: (v) => requestsVM.updateItemManufacturer(v),
),
12.height,
TxtField(
errorValue: "",
hint: "Manufactured On",
hint: LocaleKeys.manufacturedOn.tr(),
value: requestsVM.serviceItemCreatedOn,
isNeedClickAll: true,
postfixData: Icons.calendar_month_rounded,
@ -95,7 +100,7 @@ Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArgument
offerPrice: requestsVM.offerPrice,
requestModel: requestDetail,
requestIndex: requestDetailPageArguments.requestIndex,
isFromChatScreen: false,
isFromChatScreen: isFromChatScreen,
manufacturedById: 1,
// Todo: It should be the ID of the manufacturer
manufacturedOn: requestsVM.serviceItemCreatedOn,
@ -111,3 +116,69 @@ Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArgument
},
);
}
Future buildRequestStatusUpdateSheetForProvider({required BuildContext mainContext, required int requestId}) {
return showModalBottomSheet(
context: mainContext,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, RequestsVM requestVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.selectDeliveryType.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
ListView.separated(
shrinkWrap: true,
itemCount: requestVM.providerDeliveryActionsList.length,
separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5),
itemBuilder: (BuildContext context, int index) {
OfferRequestCommentModel offerRequestCommentModel = requestVM.providerDeliveryActionsList[index];
return CircleCheckBoxWithTitle(
isChecked: offerRequestCommentModel.isSelected ?? false,
title: '${offerRequestCommentModel.title}',
onSelected: () {
requestVM.updateSelectedProviderDeliveryAction(index);
},
selectedColor: MyColors.darkPrimaryColor,
);
},
),
],
),
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
maxHeight: 55,
onPressed: () async {
// index 0-> shipping
// index 1-> delivery
pop(context);
bool status = false;
if (requestVM.selectedProviderDeliveryAction.index == 0) {
status = await requestVM.onActionRequestTapped(context: mainContext, requestStatusEnum: RequestStatusEnum.shipping, requestId: requestId);
} else if (requestVM.selectedProviderDeliveryAction.index == 1) {
status = await requestVM.onActionRequestTapped(context: mainContext, requestStatusEnum: RequestStatusEnum.delivery, requestId: requestId);
}
if (status) {
await requestVM.getRequests(isNeedToRebuild: true, appType: AppType.provider);
pop(mainContext);
}
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}

@ -9,7 +9,9 @@ import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart';
import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart';
import 'package:mc_common_app/views/requests/request_bottomsheets.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
@ -22,43 +24,78 @@ class RequestDetailPage extends StatelessWidget {
const RequestDetailPage({Key? key, required this.requestDetailPageArguments}) : super(key: key);
Widget buildBottomButton({
required RequestStatus requestStatus,
required String statusText,
required BuildContext context,
required Function() onViewChatTapped,
}) {
Widget buildRequestDetailActionFooter({required int requestId, required RequestStatusEnum requestStatus, required RequestsTypeEnum requestTypeEnum, required String statusText, required BuildContext context}) {
final requestsVM = context.read<RequestsVM>();
switch (requestStatus) {
case RequestStatus.submitted:
case RequestStatus.inProgress:
case RequestStatus.paid:
case RequestStatus.shipping:
case RequestStatusEnum.submitted:
return ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: LocaleKeys.viewChat.tr(),
isBold: false,
onPressed: onViewChatTapped,
onPressed: () => onViewChatTapped(context),
);
case RequestStatus.completed:
case RequestStatus.cancelled:
case RequestStatus.expired:
case RequestStatus.pending:
return buildDisabledButton(statusText);
case RequestStatusEnum.inProgress:
if (requestTypeEnum == RequestsTypeEnum.serviceRequest) {
return buildStatusContainer(LocaleKeys.awaitingPaymentFromCustomer.tr());
} else {
return buildStatusContainer(LocaleKeys.awaitingResponseFromCustomer.tr());
}
case RequestStatusEnum.paid:
if (AppState().currentAppType == AppType.provider) {
return ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: LocaleKeys.selectDeliveryType.tr(),
isBold: false,
onPressed: () {
buildRequestStatusUpdateSheetForProvider(mainContext: context, requestId: requestId);
},
);
} else {
return buildStatusContainer(LocaleKeys.awaitingResponseFromProvider.tr());
}
case RequestStatusEnum.shipping:
case RequestStatusEnum.delivery:
if (AppState().currentAppType == AppType.provider) {
return buildStatusContainer(LocaleKeys.shippingManagementInstruction.tr(), isForShippingOrDelivery: true);
} else {
return ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: LocaleKeys.completeDeal.tr(),
onPressed: () {
return dealCompletedConfirmationBottomSheet(mainContext: context, requestStatusEnum: RequestStatusEnum.completed, requestId: requestId);
},
);
}
case RequestStatusEnum.completed:
case RequestStatusEnum.cancelled:
case RequestStatusEnum.expired:
case RequestStatusEnum.pending:
return buildStatusContainer(statusText);
}
}
Widget buildDisabledButton(String text) {
return ShowFillButton(
backgroundColor: MyColors.grey98Color.withOpacity(0.3),
txtColor: MyColors.lightTextColor,
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: text,
isBold: false,
onPressed: () {},
Widget buildStatusContainer(String text, {bool isForShippingOrDelivery = true}) {
return Center(
child: text.toText(
color: isForShippingOrDelivery ? MyColors.lightTextColor : MyColors.adPendingStatusColor,
fontSize: isForShippingOrDelivery ? 14 : 16,
// isItalic: true,
),
).toContainer(
marginAll: 12,
paddingAll: 12,
borderRadius: 8,
width: double.infinity,
backgroundColor: isForShippingOrDelivery ? MyColors.grey98Color.withOpacity(0.1) : MyColors.adPendingStatusColor.withOpacity(0.16),
);
}
@ -94,6 +131,18 @@ class RequestDetailPage extends StatelessWidget {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.requestDetail.tr(),
actions: [
(requestDetailPageArguments.requestModel.isChatted)
? Padding(
padding: const EdgeInsets.only(top: 8, bottom: 8, right: 21),
child: const Icon(Icons.messenger_outline_rounded, color: Colors.black, size: 18).toContainer(
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
),
).onPress(() => onViewChatTapped(context))
: const SizedBox(),
],
),
body: SizedBox(
width: double.infinity,
@ -120,14 +169,15 @@ class RequestDetailPage extends StatelessWidget {
title: LocaleKeys.sendOffer.tr(),
isBold: false,
fontSize: 18,
onPressed: () => buildSendOfferBottomSheet(context, requestDetailPageArguments),
onPressed: () => buildSendOfferBottomSheet(context: context, requestDetailPageArguments: requestDetailPageArguments, isFromChatScreen: false),
),
] else ...[
buildBottomButton(
buildRequestDetailActionFooter(
requestId: requestDetailPageArguments.requestModel.id,
requestStatus: requestDetailPageArguments.requestModel.requestStatus,
statusText: "Offer ${requestDetailPageArguments.requestModel.requestStatusName}",
context: context,
onViewChatTapped: () async => onViewChatTapped(context),
requestTypeEnum: requestDetailPageArguments.requestModel.requestType.toRequestTypeStatusEnum(),
),
],
],
@ -151,8 +201,8 @@ class RequestDetailPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start,
children: [
requestDetail.vehicleTypeName.toText(fontSize: 16, letterSpacing: -0.64),
showItem("Manufacturer:", requestDetail.brand),
showItem("Model:", "${requestDetail.year}"),
showItem("${LocaleKeys.manufacturer.tr()}: ", requestDetail.brand),
showItem("${LocaleKeys.model.tr()}: ", "${requestDetail.year}"),
],
),
),
@ -172,10 +222,10 @@ class RequestDetailPage extends StatelessWidget {
),
],
),
showItem("Customer Name: ", requestDetail.customerName),
showItem("Description: ", requestDetail.description),
showItem("${LocaleKeys.customerName.tr()}: ", requestDetail.customerName),
showItem("${LocaleKeys.description.tr()}: ", requestDetail.description),
16.height,
showItem("Price Range:", ""),
showItem("${LocaleKeys.priceRange.tr()}:", ""),
Row(
children: [
Expanded(
@ -195,7 +245,7 @@ class RequestDetailPage extends StatelessWidget {
children: [
Utils.statusContainerChip(
text: (requestDetail.requestStatusName),
chipColor: MyColors.grey98Color.withOpacity(0.3),
chipColor: MyColors.grey98Color.withOpacity(0.1),
textColor: MyColors.lightTextColor,
),
],

@ -0,0 +1,197 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/user_models/user.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_review_containers.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class ReviewRequestOffer extends StatelessWidget {
const ReviewRequestOffer({super.key});
Widget buildPersonalInformation() {
UserInfo userInfo = AppState().getUser.data!.userInfo!;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.personalInformation.tr().toText(fontSize: 18),
MyAssets.icEdit.buildSvg().onPress(() {}),
],
),
8.height,
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleDetailWidget(text: "${userInfo.firstName ?? ""} ${userInfo.lastName ?? ""}", type: LocaleKeys.name.tr()),
16.height,
],
),
),
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleDetailWidget(text: userInfo.email ?? "", type: "${LocaleKeys.email.tr()} / ${LocaleKeys.phone.tr()}"),
16.height,
],
),
),
],
),
],
);
}
Widget buildLocationInformation(BuildContext context) {
// final requestVM = context.read<RequestsVM>();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.locationInformation.tr().toText(fontSize: 18),
MyAssets.icEdit.buildSvg().onPress(() {}),
],
),
8.height,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleDetailWidget(text: "${"Will get from API"} }", type: LocaleKeys.location.tr()),
16.height,
SingleDetailWidget(text: "N/A", type: "${LocaleKeys.email.tr()} / ${LocaleKeys.additionalAddressDetails.tr()}"),
],
),
],
);
}
Widget buildServiceInformation(BuildContext context) {
final requestVM = context.read<RequestsVM>();
String formattedDate = "";
if (requestVM.acceptedRequestOffer!.manufacturedOn != null) {
formattedDate = DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(requestVM.acceptedRequestOffer!.manufacturedOn.toString() ?? "")));
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.serviceInformation.tr().toText(fontSize: 18),
MyAssets.icEdit.buildSvg().onPress(() {}),
],
),
8.height,
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 6,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleDetailWidget(text: requestVM.currentSelectedRequest!.vehicleTypeName, type: LocaleKeys.vehicleType.tr()),
16.height,
SingleDetailWidget(text: '${requestVM.currentSelectedRequest!.model} ${requestVM.currentSelectedRequest!.year}', type: LocaleKeys.model.tr()),
if (requestVM.acceptedRequestOffer!.manufacturedById != null) ...[
16.height,
SingleDetailWidget(text: (requestVM.acceptedRequestOffer!.manufacturedById ?? "").toString(), type: LocaleKeys.manufacturedBy.tr()),
],
16.height,
SingleDetailWidget(text: "${requestVM.acceptedRequestOffer!.price.toString()} SAR", type: LocaleKeys.offerPrice.tr()),
16.height,
SingleDetailWidget(text: requestVM.acceptedRequestOfferProviderName ?? "", type: LocaleKeys.providerName.tr()),
16.height,
],
),
),
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SingleDetailWidget(text: requestVM.currentSelectedRequest!.brand, type: LocaleKeys.vehicleBrand.tr()),
16.height,
SingleDetailWidget(text: requestVM.acceptedRequestOffer!.serviceItemName ?? "", type: LocaleKeys.serviceName.tr()),
if (formattedDate.isNotEmpty) ...[
16.height,
SingleDetailWidget(text: formattedDate, type: LocaleKeys.manufacturedOn.tr()),
],
16.height,
SingleDetailWidget(text: "${requestVM.currentSelectedRequest!.price.toString()} SAR", type: LocaleKeys.totalPrice.tr()),
],
),
),
],
),
SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.offerSummary.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
child: Column(
children: [
10.height,
buildPersonalInformation().toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)),
10.height,
buildLocationInformation(context).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)),
10.height,
buildServiceInformation(context).toWhiteContainer(width: double.infinity, allPading: 12, margin: const EdgeInsets.symmetric(horizontal: 21, vertical: 4)),
],
),
).expand(),
ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 21),
maxHeight: 55,
title: LocaleKeys.payNow.tr(),
isBold: false,
onPressed: () {
// context.read<PaymentVM>().updateRequestId(id: requestVM.currentSelectedRequest!.id);
navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.request);
},
),
],
),
);
}
}

@ -53,7 +53,7 @@ class RequestItem extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (request.offerCount > 0 && appType == AppType.customer) ...[
if (request.offerCount > 0 && appType == AppType.customer && request.requestStatus == RequestStatusEnum.submitted) ...[
Center(
child: "${request.offerCount}".toText(
color: Colors.white,
@ -77,7 +77,7 @@ class RequestItem extends StatelessWidget {
],
),
showItem("${LocaleKeys.description.tr()}:", request.description),
showItem("Price Range: ", ""),
showItem("${LocaleKeys.priceRange.tr()}: ", ""),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
@ -85,23 +85,17 @@ class RequestItem extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
request.price.toInt().toString().toText(
fontSize: 19,
color: MyColors.darkTextColor,
isBold: true,
letterSpacing: -1.16
),
request.price.toInt().toString().toText(fontSize: 19, color: MyColors.darkTextColor, isBold: true, letterSpacing: -1.16),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, letterSpacing: -0.4).paddingOnly(bottom: 3),
],
),
request.requestStatus == RequestStatus.submitted
? SvgPicture.asset(
MyAssets.arrowRight,
height: 9.69,
width: 13,
)
request.requestStatus == RequestStatusEnum.submitted
? SvgPicture.asset(
MyAssets.arrowRight,
height: 9.69,
width: 13,
)
: const SizedBox()
],
),
@ -109,7 +103,7 @@ class RequestItem extends StatelessWidget {
).toContainer(isShadowEnabled: true).onPress(() async {
RequestsVM requestsVM = context.read<RequestsVM>();
requestsVM.updateCurrentSelectedRequest(request);
if (request.requestStatus == RequestStatus.pending) {
if (request.requestStatus == RequestStatusEnum.pending || request.requestStatus == RequestStatusEnum.cancelled) {
return;
}
if (appType == AppType.provider) {

@ -19,9 +19,9 @@ class RequestStatusChip extends StatelessWidget {
)
.toContainer(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
backgroundColor: request.requestStatus == RequestStatus.pending
backgroundColor: request.requestStatus == RequestStatusEnum.pending
? MyColors.adPendingStatusColor
: request.requestStatus == RequestStatus.cancelled
: request.requestStatus == RequestStatusEnum.cancelled
? MyColors.redColor
: MyColors.greenColor,
borderRadius: 100,

@ -75,12 +75,12 @@ class CircleCheckBoxWithTitle extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
title.toText(fontSize: 14,),
title.toText(fontSize: 14),
],
),
),
],
).onPress((){
).onPress(() {
onSelected();
}),
);

Loading…
Cancel
Save