bug fixing

faiz_development_common
Faiz Hashmi 17 hours ago
parent 9c4df54156
commit 9bdb105212

@ -0,0 +1,63 @@
# View-Only Mode: CustomerID Parameter Exclusion
## Summary
Updated all API calls to exclude `customerID` parameter when `AppState().getIsViewOnly` is `true`.
## Files Modified
### 1. **request_repo.dart**
- ✅ `createRequest()` - Excludes customerID in view-only mode
- ✅ `getRequests()` - Excludes customerID for customer app type in view-only mode
- ✅ `getRequestBasedOnFilters()` - Excludes customerID for customer app type in view-only mode
- ✅ `getOffersFromProvidersByRequest()` - Excludes customerID in view-only mode
### 2. **ads_repo.dart**
- ✅ `cancelMyAdReservation()` - Excludes customerID in view-only mode
- ✅ `createReserveAd()` - Excludes customerID in view-only mode
- ✅ `uploadBankReceiptsOnReserveDealDone()` - Excludes customerID in view-only mode
### 3. **appointment_repo.dart**
- ✅ `createAppointmentsMulti()` - Excludes customerID in view-only mode
- ✅ `getMyAppointmentsForCustomersByFilters()` - Excludes customerID in view-only mode
### 4. **branch_repo.dart**
- ✅ `submitBranchRatings()` - Excludes customerID in view-only mode
- ✅ `addProviderToFavourite()` - Excludes customerID in view-only mode
- ✅ `getMyFavoriteProviders()` - Excludes customerID in view-only mode
### 5. **setting_options_repo.dart**
- ✅ `appInvitationCreate()` - Excludes customerID for customer app type in view-only mode
## How It Works
When `AppState().getIsViewOnly` is `true`, the application is in demonstration/view-only mode. In this mode:
1. **API parameters are conditionally added**: Instead of always including `customerID`, we check the view-only flag first
2. **Pattern used**:
```dart
var params = {
"otherParam": value,
};
if (!appState.getIsViewOnly) {
params["customerID"] = customerId;
}
```
3. **App types considered**: The changes properly handle both customer and provider app types
## Testing Recommendations
1. Test in normal mode (getIsViewOnly = false):
- All APIs should include customerID as before
- All existing functionality should work
2. Test in view-only mode (getIsViewOnly = true):
- APIs should NOT include customerID parameter
- Read-only operations should work
- Write operations should be blocked by view-only mode
## Note
The `createComplainFromProvider()` method in `common_repo.dart` was NOT modified because the `customerID` in that API represents the target customer being complained about, not the authenticated user making the request.

@ -413,6 +413,13 @@
"deleteAdConfirmationMessage": "سيتم حذف إعلانك نهائيًا ولا يمكنك التراجع عن هذا الإجراء",
"mileage": "المسافة المقطوعة",
"transmission": "ناقل الحركة",
"odometer": "عداد المسافات",
"accidentFree": "خالية من الحوادث",
"brandNew": "جديدة",
"used": "مستعملة",
"hasYourCarHadAccident": "هل تعرضت سيارتك لأي حادث؟",
"odometerReading": "قراءة عداد المسافات (كم)",
"currentlyInUse": "قيد الاستخدام حاليًا",
"demand": "الطلب",
"adDurationExpired": "انتهت مدة عرض الإعلان الخاص بك",
"bankDetails": "تفاصيل البنك",
@ -720,6 +727,8 @@
"ownerInformation": "معلومات المالك",
"acceptedRequests": "الطلبات المقبولة",
"specialRequestChat": "دردشة الطلب الخاص",
"sparePartRequestChat": "دردشة طلب قطع الغيار",
"specialCarRequestChat": "دردشة طلب السيارة الخاصة",
"companyName": "اسم الشركة",
"noAvailableItems": "لا توجد عناصر متاحة.",
"serviceDeliveryType": "نوع تقديم الخدمة",
@ -814,5 +823,7 @@
"selfPickupStatus": "حالة الالتقاط الذاتي",
"myDraftAds": "مسودتي للإعلانات",
"scheduleDeletedSuccessfully": "تم حذف الجدول بنجاح",
"addValidAddress": "رجى إضافة عنوان صالح"
"addValidAddress": "رجى إضافة عنوان صالح",
"termsPrivacyPolicy": "الشروط وسياسة الخصوصية"
}

@ -414,6 +414,13 @@
"deleteAdConfirmationMessage": "Your ad will be permanently deleted and you cannot undo this action",
"mileage": "Mileage",
"transmission": "Transmission",
"odometer": "Odometer",
"accidentFree": "Accident Free",
"brandNew": "Brand New",
"used": "Used",
"hasYourCarHadAccident": "Has your car had any accident?",
"odometerReading": "Odometer Reading (Km)",
"currentlyInUse": "Currently in Use",
"demand": "Demand",
"adDurationExpired": "Your Ad Duration time is over",
"bankDetails": "Bank Details",
@ -715,6 +722,8 @@
"noItemsToShow": "There are no Items no show.",
"acceptedRequests": "Accepted Requests",
"specialRequestChat": "Special Request Chat",
"sparePartRequestChat": "Spare Part Request Chat",
"specialCarRequestChat": "Special Car Request Chat",
"companyName": "Company Name",
"noAvailableItems": "There are no available items.",
"serviceDeliveryType": "Service Delivery Type",
@ -812,5 +821,6 @@
"selfPickupStatus": "Self Pickup Status",
"myDraftAds": "My Draft Ads",
"scheduleDeletedSuccessfully": "The schedule has been deleted successfully",
"addValidAddress": "Please add a valid address"
"addValidAddress": "Please add a valid address",
"termsPrivacyPolicy": "Terms & Privacy Policy"
}

@ -1,6 +1,6 @@
// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart
// ignore_for_file: prefer_single_quotes, avoid_renaming_method_parameters
// ignore_for_file: prefer_single_quotes, avoid_renaming_method_parameters, constant_identifier_names
import 'dart:ui';
@ -14,7 +14,7 @@ class CodegenLoader extends AssetLoader{
return Future.value(mapLocales[locale.toString()]);
}
static const Map<String,dynamic> ar_SA = {
static const Map<String,dynamic> _ar_SA = {
"firstTimeLogIn": "تسجيل الدخول لأول مره",
"signUp": "التسجيل",
"changeMobile": "تغيير رقم الجوال",
@ -429,6 +429,13 @@ class CodegenLoader extends AssetLoader{
"deleteAdConfirmationMessage": "سيتم حذف إعلانك نهائيًا ولا يمكنك التراجع عن هذا الإجراء",
"mileage": "المسافة المقطوعة",
"transmission": "ناقل الحركة",
"odometer": "عداد المسافات",
"accidentFree": "خالية من الحوادث",
"brandNew": "جديدة",
"used": "مستعملة",
"hasYourCarHadAccident": "هل تعرضت سيارتك لأي حادث؟",
"odometerReading": "قراءة عداد المسافات (كم)",
"currentlyInUse": "قيد الاستخدام حاليًا",
"demand": "الطلب",
"adDurationExpired": "انتهت مدة عرض الإعلان الخاص بك",
"bankDetails": "تفاصيل البنك",
@ -736,6 +743,8 @@ class CodegenLoader extends AssetLoader{
"ownerInformation": "معلومات المالك",
"acceptedRequests": "الطلبات المقبولة",
"specialRequestChat": "دردشة الطلب الخاص",
"sparePartRequestChat": "دردشة طلب قطع الغيار",
"specialCarRequestChat": "دردشة طلب السيارة الخاصة",
"companyName": "اسم الشركة",
"noAvailableItems": "لا توجد عناصر متاحة.",
"serviceDeliveryType": "نوع تقديم الخدمة",
@ -830,9 +839,10 @@ class CodegenLoader extends AssetLoader{
"selfPickupStatus": "حالة الالتقاط الذاتي",
"myDraftAds": "مسودتي للإعلانات",
"scheduleDeletedSuccessfully": "تم حذف الجدول بنجاح",
"addValidAddress": "رجى إضافة عنوان صالح"
"addValidAddress": "رجى إضافة عنوان صالح",
"termsPrivacyPolicy": "الشروط وسياسة الخصوصية"
};
static const Map<String,dynamic> en_US = {
static const Map<String,dynamic> _en_US = {
"firstTimeLogIn": "First Time Log In",
"signUp": "Sign Up",
"changeMobile": "Change Mobile",
@ -1248,6 +1258,13 @@ static const Map<String,dynamic> en_US = {
"deleteAdConfirmationMessage": "Your ad will be permanently deleted and you cannot undo this action",
"mileage": "Mileage",
"transmission": "Transmission",
"odometer": "Odometer",
"accidentFree": "Accident Free",
"brandNew": "Brand New",
"used": "Used",
"hasYourCarHadAccident": "Has your car had any accident?",
"odometerReading": "Odometer Reading (Km)",
"currentlyInUse": "Currently in Use",
"demand": "Demand",
"adDurationExpired": "Your Ad Duration time is over",
"bankDetails": "Bank Details",
@ -1549,6 +1566,8 @@ static const Map<String,dynamic> en_US = {
"noItemsToShow": "There are no Items no show.",
"acceptedRequests": "Accepted Requests",
"specialRequestChat": "Special Request Chat",
"sparePartRequestChat": "Spare Part Request Chat",
"specialCarRequestChat": "Special Car Request Chat",
"companyName": "Company Name",
"noAvailableItems": "There are no available items.",
"serviceDeliveryType": "Service Delivery Type",
@ -1646,7 +1665,8 @@ static const Map<String,dynamic> en_US = {
"selfPickupStatus": "Self Pickup Status",
"myDraftAds": "My Draft Ads",
"scheduleDeletedSuccessfully": "The schedule has been deleted successfully",
"addValidAddress": "Please add a valid address"
"addValidAddress": "Please add a valid address",
"termsPrivacyPolicy": "Terms & Privacy Policy"
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": ar_SA, "en_US": en_US};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US};
}

@ -1,5 +1,7 @@
// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart
// ignore_for_file: constant_identifier_names
abstract class LocaleKeys {
static const firstTimeLogIn = 'firstTimeLogIn';
static const signUp = 'signUp';
@ -392,6 +394,13 @@ abstract class LocaleKeys {
static const deleteAdConfirmationMessage = 'deleteAdConfirmationMessage';
static const mileage = 'mileage';
static const transmission = 'transmission';
static const odometer = 'odometer';
static const accidentFree = 'accidentFree';
static const brandNew = 'brandNew';
static const used = 'used';
static const hasYourCarHadAccident = 'hasYourCarHadAccident';
static const odometerReading = 'odometerReading';
static const currentlyInUse = 'currentlyInUse';
static const demand = 'demand';
static const adDurationExpired = 'adDurationExpired';
static const bankDetails = 'bankDetails';
@ -699,6 +708,8 @@ abstract class LocaleKeys {
static const ownerInformation = 'ownerInformation';
static const acceptedRequests = 'acceptedRequests';
static const specialRequestChat = 'specialRequestChat';
static const sparePartRequestChat = 'sparePartRequestChat';
static const specialCarRequestChat = 'specialCarRequestChat';
static const companyName = 'companyName';
static const noAvailableItems = 'noAvailableItems';
static const serviceDeliveryType = 'serviceDeliveryType';
@ -794,5 +805,6 @@ abstract class LocaleKeys {
static const myDraftAds = 'myDraftAds';
static const scheduleDeletedSuccessfully = 'scheduleDeletedSuccessfully';
static const addValidAddress = 'addValidAddress';
static const termsPrivacyPolicy = 'termsPrivacyPolicy';
}

@ -190,7 +190,11 @@ class Vehicle {
int? vehicleType;
String? vehicleVIN;
int? countryID;
String? countryName;
String? currency;
int? odometer;
int? isInUsed;
bool? isAccidentFree;
Vehicle(
{this.id,
@ -217,7 +221,11 @@ class Vehicle {
this.vehicleType,
this.vehicleVIN,
this.countryID,
this.currency});
this.countryName,
this.currency,
this.odometer,
this.isInUsed,
this.isAccidentFree});
Vehicle.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -254,7 +262,11 @@ class Vehicle {
vehicleType = json['vehicleType'];
vehicleVIN = json['vehicleVIN'];
countryID = json['countryID'];
countryName = json['duration'] != null && json['duration']['country'] != null ? json['duration']['country']['label'] : null;
currency = json['currency'];
odometer = json['odometer'];
isInUsed = json['isInUsed'];
isAccidentFree = json['isAccidentFree'];
}
}

@ -124,6 +124,9 @@ class VehiclePosting {
List<VehiclePostingDamageParts>? vehiclePostingDamageParts;
String? phoneNo;
String? whatsAppNo;
int? odometer;
int? isInUsed;
bool? isAccidentFree;
VehiclePosting({
this.id,
@ -151,6 +154,9 @@ class VehiclePosting {
this.whatsAppNo,
this.vehiclePostingImages,
this.vehiclePostingDamageParts,
this.odometer,
this.isInUsed,
this.isAccidentFree,
});
VehiclePosting.fromJson(Map<String, dynamic> json) {
@ -177,6 +183,9 @@ class VehiclePosting {
adStatus = json['adStatus'];
phoneNo = json['phoneNo'];
whatsAppNo = json['whatsAppNo'];
odometer = json['odometer'];
isInUsed = json['isInUsed'];
isAccidentFree = json['isAccidentFree'];
if (json['vehiclePostingImages'] != null) {
vehiclePostingImages = <VehiclePostingImages>[];
json['vehiclePostingImages'].forEach((v) {
@ -193,7 +202,7 @@ class VehiclePosting {
@override
String toString() {
return 'VehiclePosting{id: $id, userID: $userID, vehicleType: $vehicleType, vehicleModelID: $vehicleModelID, vehicleModelYearID: $vehicleModelYearID, vehicleColorID: $vehicleColorID, vehicleCategoryID: $vehicleCategoryID, vehicleConditionID: $vehicleConditionID, vehicleMileageID: $vehicleMileageID, vehicleTransmissionID: $vehicleTransmissionID, vehicleSellerTypeID: $vehicleSellerTypeID, cityID: $cityID, price: $price, vehicleVIN: $vehicleVIN, vehicleDescription: $vehicleDescription, vehicleTitle: $vehicleTitle, vehicleDescriptionN: $vehicleDescriptionN, isFinanceAvailable: $isFinanceAvailable, warantyYears: $warantyYears, demandAmount: $demandAmount, adStatus: $adStatus, vehiclePostingImages: $vehiclePostingImages, vehiclePostingDamageParts: $vehiclePostingDamageParts, phoneNo: $phoneNo, whatsAppNo: $whatsAppNo}';
return 'VehiclePosting{id: $id, userID: $userID, vehicleType: $vehicleType, vehicleModelID: $vehicleModelID, vehicleModelYearID: $vehicleModelYearID, vehicleColorID: $vehicleColorID, vehicleCategoryID: $vehicleCategoryID, vehicleConditionID: $vehicleConditionID, vehicleMileageID: $vehicleMileageID, vehicleTransmissionID: $vehicleTransmissionID, vehicleSellerTypeID: $vehicleSellerTypeID, cityID: $cityID, price: $price, vehicleVIN: $vehicleVIN, vehicleDescription: $vehicleDescription, vehicleTitle: $vehicleTitle, vehicleDescriptionN: $vehicleDescriptionN, isFinanceAvailable: $isFinanceAvailable, warantyYears: $warantyYears, demandAmount: $demandAmount, adStatus: $adStatus, vehiclePostingImages: $vehiclePostingImages, vehiclePostingDamageParts: $vehiclePostingDamageParts, phoneNo: $phoneNo, whatsAppNo: $whatsAppNo, odometer: $odometer, isInUsed: $isInUsed, isAccidentFree: $isAccidentFree}';
}
}

@ -199,6 +199,9 @@ class AdsRepoImp implements AdsRepo {
"vehiclePostingDamageParts": vehiclePostingDamageParts,
"mobileNo": adsCreationPayloadModel.vehiclePosting!.phoneNo,
"whatsAppNo": adsCreationPayloadModel.vehiclePosting!.whatsAppNo,
"odometer": adsCreationPayloadModel.vehiclePosting!.odometer,
"isInUsed": adsCreationPayloadModel.vehiclePosting!.isInUsed,
"isAccidentFree": adsCreationPayloadModel.vehiclePosting!.isAccidentFree,
}
};
@ -290,6 +293,9 @@ class AdsRepoImp implements AdsRepo {
"vehiclePostingDamagePartsDraft": vehiclePostingDamageParts,
"mobileNo": adsCreationPayloadModel.vehiclePosting!.phoneNo,
"whatsAppNo": adsCreationPayloadModel.vehiclePosting!.whatsAppNo,
"odometer": adsCreationPayloadModel.vehiclePosting!.odometer,
"isInUsed": adsCreationPayloadModel.vehiclePosting!.isInUsed,
"isAccidentFree": adsCreationPayloadModel.vehiclePosting!.isAccidentFree,
// "adStatus": 1,
},
"stepNo": stepNo.toIntFromStepsEnum(),
@ -585,11 +591,14 @@ class AdsRepoImp implements AdsRepo {
var postParams = {
"adsID": adId,
"customerID": customerID,
"adsReserveStatus": adsReserveStatus,
"comment": reason,
};
if (!AppState().getIsViewOnly) {
postParams["customerID"] = customerID;
}
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
@ -607,10 +616,13 @@ class AdsRepoImp implements AdsRepo {
var postParams = {
"adsID": adId,
"customerID": customerID,
"adsReserveStatus": 0,
};
if (!AppState().getIsViewOnly) {
postParams["customerID"] = customerID;
}
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
@ -667,11 +679,14 @@ class AdsRepoImp implements AdsRepo {
var postParams = {
"adsID": adId,
"customerID": customerId,
"detailNote": detailNote,
"receiptImages": receiptImages,
};
if (!AppState().getIsViewOnly) {
postParams["customerID"] = customerId;
}
String token = appState.getUser.data!.accessToken ?? "";
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),

@ -174,16 +174,21 @@ class AppointmentRepoImp implements AppointmentRepo {
slotId = schedule.selectedCustomTimeDateSlotModel!.availableSlots![index].slotId;
mapList.add({
Map<String, dynamic> appointmentMap = {
"serviceSlotID": slotId,
"serviceProviderID": serviceProviderID,
"customerID": customerId,
"serviceItemID": serviceItemIds,
"amountCustomerLocation": schedule.totalLocationCharges.toString(),
"customerLocLat": (schedule.locationInfoModel!.latitude).toString(),
"customerLocLong": (schedule.locationInfoModel!.longitude).toString(),
"customerLocAddress": (schedule.locationInfoModel!.address).toString(),
});
};
if (!appState.getIsViewOnly) {
appointmentMap["customerID"] = customerId;
}
mapList.add(appointmentMap);
}
log("maplist: ${mapList.toString()}");
@ -278,8 +283,7 @@ class AppointmentRepoImp implements AppointmentRepo {
List<String>? branchIdsList,
AppointmentStatusEnum? appointmentStatusEnum,
}) async {
var params = {
"customerID": appState.getUser.data!.userInfo!.customerId.toString(),
var params = <String, dynamic>{
"ServiceProviderIDs": providerIdsList ?? [],
"ProviderBranchIDs": branchIdsList ?? [],
"ServiceIDs": serviceIdsList ?? [],
@ -287,8 +291,12 @@ class AppointmentRepoImp implements AppointmentRepo {
"CategoryIDs": categoryIdsList ?? [],
};
if (!appState.getIsViewOnly) {
params["customerID"] = appState.getUser.data!.userInfo!.customerId.toString();
}
if (appointmentStatusEnum != null) {
params.addAll({"AppointmentStatusID": appointmentStatusEnum.getIdFromAppointmentStatusEnum().toString()});
params["AppointmentStatusID"] = appointmentStatusEnum.getIdFromAppointmentStatusEnum().toString();
}
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
token: appState.getUser.data!.accessToken,

@ -587,7 +587,11 @@ class BranchRepoImp implements BranchRepo {
@override
Future<GenericRespModel> submitBranchRatings({required int serviceProviderBranchID, required String title, required String review, required double ratingNo}) async {
final customerID = appState.getUser.data!.userInfo!.customerId;
final parameters = {"title": title, "review": review, "ratNo": ratingNo, "serviceProviderBranchID": serviceProviderBranchID, "customerID": "$customerID"};
final parameters = {"title": title, "review": review, "ratNo": ratingNo, "serviceProviderBranchID": serviceProviderBranchID};
if (!appState.getIsViewOnly) {
parameters["customerID"] = "$customerID";
}
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.createBranchRatings,
@ -602,8 +606,12 @@ class BranchRepoImp implements BranchRepo {
final customerID = appState.getUser.data!.userInfo!.customerId;
final parameters = {
"providerID": providerID.toString(),
"customerID": customerID.toString(),
};
if (!appState.getIsViewOnly) {
parameters["customerID"] = customerID.toString();
}
GenericRespModel adsGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.favouriteServiceProviderCreate,
@ -629,13 +637,17 @@ class BranchRepoImp implements BranchRepo {
Future<List<ProviderProfileModel>> getMyFavoriteProviders() async {
final customerID = appState.getUser.data!.userInfo!.customerId;
var postParams = {"customerID": customerID.toString()};
var postParams = <String, dynamic>{};
if (!appState.getIsViewOnly) {
postParams["customerID"] = customerID.toString();
}
GenericRespModel adsGenericModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.favouriteServiceProviderGet,
token: appState.getUser.data!.accessToken,
queryParameters: postParams,
queryParameters: postParams.isNotEmpty ? postParams : null,
);
List<ProviderProfileModel> favProviders = List.generate(adsGenericModel.data.length, (index) => ProviderProfileModel.fromJson(adsGenericModel.data[index]));
return favProviders;

@ -98,7 +98,6 @@ class RequestRepoImp implements RequestRepo {
required List requestImages,
}) async {
Map<String, dynamic> postParams = {
"customerID": appState.getUser.data!.userInfo!.customerId ?? 0,
"requestType": requestTypeId,
"vehicleTypeID": vehicleTypeId,
"brand": brand,
@ -113,6 +112,10 @@ class RequestRepoImp implements RequestRepo {
"isSpecialServiceNeeded": false,
"requestImages": requestImages,
};
if (!appState.getIsViewOnly) {
postParams["customerID"] = appState.getUser.data!.userInfo!.customerId ?? 0;
}
GenericRespModel enumGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.createRequest,
@ -132,7 +135,9 @@ class RequestRepoImp implements RequestRepo {
if (appState.currentAppType == AppType.provider) {
paramsForGetRequests.addEntries([MapEntry("providerID", providerOrCustomerID)]);
} else {
paramsForGetRequests.addEntries([MapEntry("customerID", providerOrCustomerID)]);
if (!appState.getIsViewOnly) {
paramsForGetRequests.addEntries([MapEntry("customerID", providerOrCustomerID)]);
}
}
GenericRespModel enumGenericModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
@ -180,7 +185,9 @@ class RequestRepoImp implements RequestRepo {
};
if (appState.currentAppType == AppType.customer) {
paramsForGetRequests.addEntries([MapEntry("customerID", appState.getUser.data!.userInfo!.customerId ?? 0)]);
if (!appState.getIsViewOnly) {
paramsForGetRequests.addEntries([MapEntry("customerID", appState.getUser.data!.userInfo!.customerId ?? 0)]);
}
} else {
paramsForGetRequests.addEntries([MapEntry("providerID", appState.getUser.data!.userInfo!.providerId.toString())]);
}
@ -322,9 +329,12 @@ class RequestRepoImp implements RequestRepo {
Future<ProviderOffersModel> getOffersFromProvidersByRequest({required int requestId}) async {
final customerId = appState.getUser.data!.userInfo!.customerId;
var queryParameters = {
"customerID": customerId.toString(),
"requestID": requestId.toString(),
};
if (!appState.getIsViewOnly) {
queryParameters["customerID"] = customerId.toString();
}
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.requestOffersSpsGet,

@ -166,10 +166,16 @@ class SettingOptionsRepoImp extends SettingOptionsRepo {
};
if (AppState().currentAppType == AppType.customer) {
params.addAll({
"customerID": (appState.getUser.data!.userInfo!.customerId ?? "0").toString(),
"channelID": "3",
});
if (!appState.getIsViewOnly) {
params.addAll({
"customerID": (appState.getUser.data!.userInfo!.customerId ?? "0").toString(),
"channelID": "3",
});
} else {
params.addAll({
"channelID": "3",
});
}
} else {
params.addAll({
"providerID": (appState.getUser.data!.userInfo!.providerId ?? "0").toString(),

@ -729,6 +729,29 @@ class AdVM extends BaseVM {
vehicleDemandAmount = amount;
}
String odometer = "";
void updateOdometer(String value) {
odometer = value;
notifyListeners();
}
int isInUsed = 0;
void updateIsInUsed(int value) {
isInUsed = value;
notifyListeners();
}
bool isAccidentFree = false;
bool isAccidentFreeSet = false;
void updateIsAccidentFree(bool value) {
isAccidentFree = value;
isAccidentFreeSet = true;
notifyListeners();
}
String reservationCancelReason = "";
String reservationCancelError = "";
@ -1310,6 +1333,9 @@ class AdVM extends BaseVM {
vehiclePostingDamageParts: vehicleDamageImages,
phoneNo: isPhoneNumberShown ? adPhoneNumberDialCode + adPhoneNumber : null,
whatsAppNo: (isPhoneNumberShown && isNumberOnWhatsApp) ? adPhoneNumberDialCode + adPhoneNumber : null,
odometer: odometer.isNotEmpty ? int.tryParse(odometer) : 0,
isInUsed: isInUsed,
isAccidentFree: isAccidentFree,
);
AdsCreationPayloadModel adsCreationPayloadModel = AdsCreationPayloadModel(ads: ads, vehiclePosting: vehiclePosting);
@ -1437,7 +1463,7 @@ class AdVM extends BaseVM {
// Added By Aamir
if (pickedPostingImages.length > GlobalConsts.maxFileCount) {
pickedPostingImages = pickedPostingImages.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
Utils.showToast(LocaleKeys.maxFileSelection.tr());
}
if (pickedPostingImages.isNotEmpty) vehicleImageError = "";
@ -1485,7 +1511,7 @@ class AdVM extends BaseVM {
// Added By Aamir
if (vehicleDamageCards[index].partImages!.length > GlobalConsts.maxFileCount) {
vehicleDamageCards[index].partImages = vehicleDamageCards[index].partImages!.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
Utils.showToast(LocaleKeys.maxFileSelection.tr());
}
}
@ -1541,7 +1567,7 @@ class AdVM extends BaseVM {
if (pickedDamageImages.length > GlobalConsts.maxFileCount) {
pickedDamageImages = pickedDamageImages.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
Utils.showToast(LocaleKeys.maxFileSelection.tr());
}
if (pickedDamageImages.isNotEmpty) vehicleDamageImageError = "";
notifyListeners();
@ -1577,6 +1603,10 @@ class AdVM extends BaseVM {
isNumberOnWhatsApp = false;
adPhoneNumberDialCode = "";
adPhoneNumber = "";
odometer = "";
isInUsed = 0;
isAccidentFree = false;
isAccidentFreeSet = false;
clearSpecialServiceCard();
updateFinanceAvailableStatus(false);
notifyListeners();
@ -1792,6 +1822,9 @@ class AdVM extends BaseVM {
vehiclePostingDamageParts: vehicleDamageImages,
phoneNo: isPhoneNumberShown ? adPhoneNumberDialCode + adPhoneNumber : null,
whatsAppNo: (isPhoneNumberShown && isNumberOnWhatsApp) ? adPhoneNumberDialCode + adPhoneNumber : null,
odometer: odometer.isNotEmpty ? int.tryParse(odometer) : 0,
isInUsed: isInUsed,
isAccidentFree: isAccidentFree,
);
AdsCreationPayloadModel adsCreationPayloadModel = AdsCreationPayloadModel(ads: ads, vehiclePosting: vehiclePosting);
@ -2455,6 +2488,10 @@ class AdVM extends BaseVM {
vehicleTitle = previousAdDetails!.vehicle!.vehicleTitle.toString();
vehicleDescription = previousAdDetails!.vehicle!.vehicleDescription.toString();
financeAvailableStatus = previousAdDetails!.vehicle!.isFinanceAvailable ?? false;
odometer = previousAdDetails!.vehicle!.odometer != null ? previousAdDetails!.vehicle!.odometer.toString() : "0";
isInUsed = previousAdDetails!.vehicle!.isInUsed ?? 0;
isAccidentFree = previousAdDetails!.vehicle!.isAccidentFree ?? false;
isAccidentFreeSet = true;
pickedPostingImages.clear();
if (previousAdDetails!.vehicle!.image != null && previousAdDetails!.vehicle!.image!.isNotEmpty) {
for (var element in previousAdDetails!.vehicle!.image!) {

@ -447,7 +447,7 @@ class ChatVM extends BaseVM {
pickedImagesForMessage.addAll(imageModels);
if (pickedImagesForMessage.length > GlobalConsts.maxFileCount) {
pickedImagesForMessage = pickedImagesForMessage.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
Utils.showToast(LocaleKeys.maxFileSelection.tr());
}
notifyListeners();

@ -294,7 +294,7 @@ class RequestsVM extends BaseVM {
pickedVehicleImages.addAll(imageModels);
if (pickedVehicleImages.length > GlobalConsts.maxFileCount) {
pickedVehicleImages = pickedVehicleImages.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
Utils.showToast(LocaleKeys.maxFileSelection.tr());
}
if (pickedVehicleImages.isNotEmpty) vehicleImageError = "";

@ -142,7 +142,7 @@ class ServiceVM extends BaseVM {
pickedBranchImages.addAll(imageModels);
if (pickedBranchImages.length > GlobalConsts.maxFileCount) {
pickedBranchImages = pickedBranchImages.sublist(0, GlobalConsts.maxFileCount);
Utils.showToast(LocaleKeys.maxFileSelection);
Utils.showToast(LocaleKeys.maxFileSelection.tr());
}
if (pickedBranchImages.isNotEmpty) branchImageError = "";
notifyListeners();

@ -78,6 +78,10 @@ class VehicleDetailsReview extends StatelessWidget {
16.height,
SingleDetailWidget(text: adVM.vehicleCityId.selectedOption, type: LocaleKeys.vehicleCity.tr()),
16.height,
SingleDetailWidget(text: adVM.isAccidentFree ? LocaleKeys.yes.tr() : LocaleKeys.no.tr(), type: LocaleKeys.accidentFree.tr()),
16.height,
SingleDetailWidget(text: "${adVM.odometer} Km", type: LocaleKeys.odometer.tr()),
16.height,
SingleDetailWidget(text: adVM.vehicleVin, type: LocaleKeys.vehicleVIN.tr()),
16.height,
SingleDetailWidget(text: ("${adVM.warrantyDuration} ${LocaleKeys.years.tr()}"), type: LocaleKeys.warrantyAvailable.tr()),
@ -104,7 +108,9 @@ class VehicleDetailsReview extends StatelessWidget {
16.height,
SingleDetailWidget(text: adVM.vehicleTitle, type: LocaleKeys.vehicleTitle.tr()),
16.height,
SingleDetailWidget(text: adVM.financeAvailableStatus ? "Yes" : "No", type: LocaleKeys.financeAvailable.tr()),
SingleDetailWidget(text: adVM.financeAvailableStatus ? LocaleKeys.yes.tr() : LocaleKeys.no.tr(), type: LocaleKeys.financeAvailable.tr()),
16.height,
SingleDetailWidget(text: adVM.isInUsed == 1 ? LocaleKeys.yes.tr() : LocaleKeys.no.tr(), type: LocaleKeys.currentlyInUse.tr()),
16.height,
],
),

@ -237,6 +237,28 @@ class VehicleDetails extends StatelessWidget {
errorValue: adVM.vehicleCityId.errorValue,
);
}),
8.height,
Builder(builder: (context) {
List<DropValue> accidentFreeDrop = [
DropValue(1, LocaleKeys.yes.tr(), ""),
DropValue(0, LocaleKeys.no.tr(), ""),
];
return DropdownField(
(DropValue value) => adVM.updateIsAccidentFree(value.id == 1),
list: accidentFreeDrop,
dropdownValue: adVM.isAccidentFreeSet ? DropValue(adVM.isAccidentFree ? 1 : 0, adVM.isAccidentFree ? LocaleKeys.yes.tr() : LocaleKeys.no.tr(), "") : null,
hint: LocaleKeys.hasYourCarHadAccident.tr(),
errorValue: "",
);
}),
8.height,
TxtField(
value: adVM.odometer,
keyboardType: TextInputType.number,
numbersOnly: true,
hint: LocaleKeys.odometerReading.tr(),
onChanged: (v) => adVM.updateOdometer(v),
),
],
],
8.height,
@ -282,28 +304,71 @@ class VehicleDetails extends StatelessWidget {
onChanged: (v) => adVM.updateVehicleDescription(v),
),
22.height,
LocaleKeys.financeAvailable.tr().toText(fontSize: 16),
8.height,
Container(
width: 50,
height: 30,
decoration: BoxDecoration(
color: adVM.financeAvailableStatus ? MyColors.darkPrimaryColor : MyColors.white,
borderRadius: BorderRadius.circular(25.0),
border: Border.all(color: MyColors.black, width: 1),
),
child: Transform.scale(
scale: 0.8,
child: CupertinoSwitch(
activeColor: MyColors.darkPrimaryColor,
trackColor: MyColors.white,
thumbColor: MyColors.grey98Color,
value: adVM.financeAvailableStatus,
onChanged: (value) {
adVM.updateFinanceAvailableStatus(value);
},
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.financeAvailable.tr().toText(fontSize: 16),
8.height,
Container(
width: 50,
height: 30,
decoration: BoxDecoration(
color: adVM.financeAvailableStatus ? MyColors.darkPrimaryColor : MyColors.white,
borderRadius: BorderRadius.circular(25.0),
border: Border.all(color: MyColors.black, width: 1),
),
child: Transform.scale(
scale: 0.8,
child: CupertinoSwitch(
activeColor: MyColors.darkPrimaryColor,
trackColor: MyColors.white,
thumbColor: MyColors.grey98Color,
value: adVM.financeAvailableStatus,
onChanged: (value) {
adVM.updateFinanceAvailableStatus(value);
},
),
),
),
],
),
),
),
8.width,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.currentlyInUse.tr().toText(fontSize: 16),
8.height,
Container(
width: 50,
height: 30,
decoration: BoxDecoration(
color: adVM.isInUsed == 1 ? MyColors.darkPrimaryColor : MyColors.white,
borderRadius: BorderRadius.circular(25.0),
border: Border.all(color: MyColors.black, width: 1),
),
child: Transform.scale(
scale: 0.8,
child: CupertinoSwitch(
activeColor: MyColors.darkPrimaryColor,
trackColor: MyColors.white,
thumbColor: MyColors.grey98Color,
value: adVM.isInUsed == 1,
onChanged: (value) {
adVM.updateIsInUsed(value ? 1 : 0);
},
),
),
),
],
),
),
],
),
28.height,
LocaleKeys.vehiclePictures.tr().toText(fontSize: 18, isBold: true),

@ -1,7 +1,5 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
@ -106,13 +104,20 @@ class _AdsDetailViewState extends State<AdsDetailView> {
children: [
Row(
children: [
("${LocaleKeys.model.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.modelyear!.label}".toText(fontSize: 12),
("${LocaleKeys.vehicleModel.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.model!.label}".toText(fontSize: 12),
],
),
"${widget.adDetails.vehicle!.countryID}".toText(fontSize: 10, color: MyColors.lightTextColor),
(widget.adDetails.vehicle!.countryName ?? '').toText(fontSize: 10, color: MyColors.lightTextColor),
],
),
Row(
children: [
("${LocaleKeys.vehicleModelYear.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.modelyear!.label}".toText(fontSize: 12),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -131,6 +136,24 @@ class _AdsDetailViewState extends State<AdsDetailView> {
"${widget.adDetails.vehicle!.transmission!.label}".toText(fontSize: 12),
],
),
Row(
children: [
("${LocaleKeys.odometer.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.odometer ?? 0} Km".toText(fontSize: 12),
],
),
Row(
children: [
("${LocaleKeys.vehicleCondition.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
(widget.adDetails.vehicle!.isInUsed == 0 ? LocaleKeys.brandNew.tr() : LocaleKeys.used.tr()).toText(fontSize: 12),
],
),
Row(
children: [
("${LocaleKeys.accidentFree.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
((widget.adDetails.vehicle!.isAccidentFree ?? false) ? LocaleKeys.yes.tr() : LocaleKeys.no.tr()).toText(fontSize: 12),
],
),
Row(
children: [
("${LocaleKeys.financeAvailable.tr()}: ").toText(fontSize: 12, color: MyColors.lightTextColor),
@ -390,7 +413,7 @@ class _AdsDetailViewState extends State<AdsDetailView> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: ("${widget.adDetails.specialservice![index].name}").toText(fontSize: 14, color: MyColors.lightTextColor)),
("${widget.adDetails.specialservice![index].price} ${LocaleKeys.sar.tr()} ").toText(fontSize: 14, color: MyColors.lightTextColor),
("${widget.adDetails.specialservice![index].price} ${widget.adDetails.vehicle!.currency ?? LocaleKeys.sar.tr()} ").toText(fontSize: 14, color: MyColors.lightTextColor),
],
)),
),

@ -56,7 +56,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
children: [
"${adDetailsModel.reservePrice}".toText(fontSize: 19, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, isBold: true).paddingOnly(bottom: 3),
(adDetailsModel.vehicle!.currency ?? LocaleKeys.sar.tr()).toText(color: MyColors.lightTextColor, fontSize: 10, isBold: true).paddingOnly(bottom: 3),
],
)
],
@ -73,7 +73,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
children: [
"${adDetailsModel.vehicle!.demandAmount ?? 0.0}".toText(fontSize: 19, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, isBold: true).paddingOnly(bottom: 3),
(adDetailsModel.vehicle!.currency ?? LocaleKeys.sar.tr()).toText(color: MyColors.lightTextColor, fontSize: 10, isBold: true).paddingOnly(bottom: 3),
],
)
],
@ -123,7 +123,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
children: [
"${(adDetailsModel.vehicle!.demandAmount ?? 0.0)}".toText(fontSize: 19, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 10, isBold: true).paddingOnly(bottom: 3),
(adDetailsModel.vehicle!.currency ?? LocaleKeys.sar.tr()).toText(color: MyColors.lightTextColor, fontSize: 10, isBold: true).paddingOnly(bottom: 3),
],
)
],

@ -349,6 +349,7 @@ class _AdsFilterViewState extends State<AdsFilterView> {
title: LocaleKeys.search.tr(),
onPressed: () {
Navigator.pop(context);
adVM.updateIsExploreAds(true);
adVM.getAdsBasedOnFilters(isFromLazyLoad: false, pageIndex: 0);
},
backgroundColor: MyColors.darkPrimaryColor,
@ -360,7 +361,10 @@ class _AdsFilterViewState extends State<AdsFilterView> {
),
8.height,
InkWell(
onTap: () => adVM.clearAdsFilters(),
onTap: () {
adVM.updateIsExploreAds(true);
adVM.clearAdsFilters();
},
child: LocaleKeys.clearFilters.tr().toText(
fontSize: 14,
isBold: true,

@ -36,17 +36,21 @@ class _AdDamagePartPicturesSheetState extends State<AdDamagePartPicturesSheet> {
void populateDamagePartPictures() {
List<dynamic> adDamageReportList = widget.adDamageReportList; // Assuming this is your data source.
Map<String, VehicleDamageCard> groupedDamageCards = {};
Map<int, VehicleDamageCard> groupedDamageCards = {};
for (var element in adDamageReportList) {
int partId = element.vehicleDamagePartID ?? 0;
String partName = element.partName ?? "Unknown";
ImageModel imageModel = ImageModel(id: element.id!, filePath: element.imageUrl!, isFromNetwork: true);
if (groupedDamageCards.containsKey(partName)) {
groupedDamageCards[partName]!.partImages!.add(imageModel);
if (groupedDamageCards.containsKey(partId)) {
groupedDamageCards[partId]!.partImages!.add(imageModel);
} else {
groupedDamageCards[partName] = VehicleDamageCard(
groupedDamageCards[partId] = VehicleDamageCard(
partSelectedId: SelectionModel(
selectedId: element.vehicleDamagePartID!,
selectedId: partId,
selectedOption: partName,
),
damagePartDescription: element.comment ?? "",

@ -39,11 +39,30 @@ class AdsListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final adVM = context.watch<AdVM>();
if (isAdsFragment && adsList.isEmpty) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.noAdsShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor, fontWeight: MyFonts.Medium),
const SizedBox(height: 25),
(adVM.adsFiltersCounter > 0 ? "No Ads that matches your search" : LocaleKeys.noAdsShow.tr()).toText(fontSize: 16, color: MyColors.lightTextColor, fontWeight: MyFonts.Medium),
if (adVM.adsFiltersCounter > 0) ...[
const SizedBox(height: 16),
GestureDetector(
onTap: () {
adVM.clearAdsFilters();
},
child: Text(
LocaleKeys.clearFilters.tr(),
style: const TextStyle(
color: MyColors.darkPrimaryColor,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
],
],
);
}

@ -328,7 +328,16 @@ class AppointmentDetailView extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
((service.currentTotalServicePrice).toString()).toText(fontSize: 19, isBold: true),
if (service.isHomeSelected) ...[
Builder(builder: (context) {
double totalKms = double.parse(service.servicelocationInfo.distanceToBranch.toStringAsFixed(2));
double pricePerKm = double.parse(service.servicelocationInfo.homeChargesInCurrentService.toStringAsFixed(2));
return ((service.currentTotalServicePrice + (pricePerKm * totalKms)).toStringAsFixed(2))
.toText(fontSize: 19, isBold: true);
}),
] else ...[
((service.currentTotalServicePrice).toString()).toText(fontSize: 19, isBold: true),
],
2.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, fontSize: 12, isBold: true).paddingOnly(bottom: 5),
const Icon(Icons.arrow_drop_down, size: 25)

@ -102,11 +102,12 @@ class BookAppointmentSchedulesView extends StatelessWidget {
Column(
children: [
CustomCalenderAppointmentWidget(
customTimeDateSlotList: scheduleData.customTimeDateSlotList ?? [],
onDateSelected: (int dateIndex) {
appointmentsVM.updateSelectedAppointmentDate(scheduleIndex: scheduleIndex, dateIndex: dateIndex);
},
selectedCustomTimeDateSlotModel: scheduleData.selectedCustomTimeDateSlotModel),
customTimeDateSlotList: scheduleData.customTimeDateSlotList ?? [],
onDateSelected: (int dateIndex) {
appointmentsVM.updateSelectedAppointmentDate(scheduleIndex: scheduleIndex, dateIndex: dateIndex);
},
selectedCustomTimeDateSlotModel: scheduleData.selectedCustomTimeDateSlotModel,
),
if (appointmentsVM.serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex != null) ...[
5.height,
Row(

@ -107,8 +107,12 @@ class BookAppointmentServicesView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (serviceData.isHomeSelected) ...[
((serviceData.currentTotalServicePrice + (serviceData.servicelocationInfo.distanceToBranch * double.parse(serviceData.rangePricePerKm!))).toStringAsFixed(2))
.toText(fontSize: 32, isBold: true),
Builder(builder: (context) {
double totalKms = double.parse(serviceData.servicelocationInfo.distanceToBranch.toStringAsFixed(2));
double pricePerKm = double.parse(serviceData.servicelocationInfo.homeChargesInCurrentService.toStringAsFixed(2));
return ((serviceData.currentTotalServicePrice + (pricePerKm * totalKms)).toStringAsFixed(2))
.toText(fontSize: 32, isBold: true);
}),
] else ...[
((serviceData.currentTotalServicePrice).toString()).toText(fontSize: 32, isBold: true),
],

@ -75,6 +75,8 @@ void dealCompletedConsentBottomSheet({
requestVM.myFilteredRequests[index].requestStatus = requestStatusEnum;
}
chatVM.updateAcknowledgePaymentToMowaterStatus(false);
// Refresh requests list before navigating
await requestVM.getRequestsBasedOnFilters();
mainContext.read<DashboardVmCustomer>().onNavbarTapped(4);
navigateReplaceWithNameUntilRoute(mainContext, AppRoutes.dashboard);
}
@ -93,6 +95,8 @@ void dealCompletedConsentBottomSheet({
requestVM.myFilteredRequests[index].requestStatus = requestStatusEnum;
}
chatVM.updateAcknowledgePaymentToMowaterStatus(false);
// Refresh requests list before navigating
await requestVM.getRequestsBasedOnFilters();
mainContext.read<DashboardVmCustomer>().onNavbarTapped(4);
navigateReplaceWithNameUntilRoute(mainContext, AppRoutes.dashboard);
}

@ -148,6 +148,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
}
setState(() {});
chatVM.updateRejectOfferDescription('');
// Refresh requests list
await requestVM.getRequestsBasedOnFilters();
Utils.showToast(LocaleKeys.offerRejected.tr());
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
@ -250,6 +252,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum;
}
setState(() {});
// Refresh requests list
await requestVM.getRequestsBasedOnFilters();
Utils.showToast(LocaleKeys.offerAccepted.tr());
return true;
} else {
@ -399,6 +403,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
setState(() {});
// Navigator.pop(context);
chatVM.updateRejectOfferDescription('');
// Refresh requests list
await requestVM.getRequestsBasedOnFilters();
Utils.showToast("Offer ${requestOfferStatusEnum == RequestOfferStatusEnum.rejected ? "Rejected" : "Cancelled"}");
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
@ -459,6 +465,8 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
}
setState(() {});
// Navigator.pop(context);
// Refresh requests list
await requestVM.getRequestsBasedOnFilters();
Utils.showToast(LocaleKeys.offerAccepted.tr());
// navigateReplaceWithName(context, AppRoutes.dashboard);
}

@ -141,11 +141,15 @@ class RequestDetailPage extends StatelessWidget {
}) {
switch (requestStatus) {
case RequestStatusEnum.submitted:
String chatButtonText = requestTypeEnum == RequestsTypeEnum.specialCarRequest
? LocaleKeys.specialCarRequestChat.tr()
: LocaleKeys.sparePartRequestChat.tr();
return ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: LocaleKeys.specialRequestChat.tr(),
title: chatButtonText,
isBold: false,
onPressed: () => onViewChatTapped(context),
);

@ -127,12 +127,12 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.locationInformation.tr().toText(fontSize: 18),
LocaleKeys.serviceDeliveryType.tr().toText(fontSize: 18),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// LocaleKeys.locationInformation.tr().toText(fontSize: 18),
// LocaleKeys.serviceDeliveryType.tr().toText(fontSize: 18),
// MyAssets.icEdit.buildSvg().onPress(() => buildLocationInformationEditBottomSheet(context, requestVM)),
// ],
// ),
@ -207,7 +207,7 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
}
Widget buildServiceInformation(BuildContext context) {
final requestVM = context.read<RequestsVM>();
final requestVM = context.watch<RequestsVM>();
String manufacturedOnFormattedDate = "";
if (requestVM.acceptedRequestOffer!.manufacturedOn != null) {
@ -217,6 +217,23 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
if (requestVM.currentSelectedRequest!.createdOn != null) {
requestCreatedOn = DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(requestVM.currentSelectedRequest!.createdOn.toString() ?? "")));
}
// Get selected delivery type
String selectedDeliveryType = "-";
String deliveryAddress = "";
bool showMapButton = false;
if (requestVM.selectedDeliveryOptionEnum != null) {
selectedDeliveryType = requestVM.selectedDeliveryOptionEnum!.getStringFromRequestDeliveryOptionEnum();
if (requestVM.selectedDeliveryOptionEnum == RequestDeliveryOptionEnum.delivery) {
deliveryAddress = requestVM.currentSelectedRequest!.address;
} else if (requestVM.selectedDeliveryOptionEnum == RequestDeliveryOptionEnum.selfPickup) {
deliveryAddress = requestVM.currentSelectedOffer!.providerAddress ?? "";
showMapButton = true;
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -237,6 +254,8 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleDetailWidget(text: requestVM.currentSelectedRequest!.requestTypeName, type: LocaleKeys.serviceCategory.tr()),
16.height,
SingleDetailWidget(text: requestVM.currentSelectedRequest!.vehicleTypeName, type: LocaleKeys.vehicleType.tr()),
16.height,
SingleDetailWidget(text: '${requestVM.currentSelectedRequest!.model} ${requestVM.currentSelectedRequest!.year}', type: LocaleKeys.model.tr()),
@ -244,11 +263,8 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
16.height,
SingleDetailWidget(text: (requestVM.acceptedRequestOffer!.manufacturedByName ?? "").toString(), type: LocaleKeys.manufacturedBy.tr()),
],
// 16.height,
// SingleDetailWidget(text: "${requestVM.acceptedRequestOffer!.price.toString()} ${LocaleKeys.sar.tr()}", type: LocaleKeys.offerPrice.tr()),
16.height,
SingleDetailWidget(text: requestVM.acceptedRequestOfferProviderName ?? "", type: LocaleKeys.providerName.tr()),
16.height,
SingleDetailWidget(text: LocaleKeys.online.tr(), type: LocaleKeys.paymentType.tr()),
],
@ -260,6 +276,8 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SingleDetailWidget(text: selectedDeliveryType, type: LocaleKeys.serviceDeliveryType.tr()),
16.height,
SingleDetailWidget(text: requestVM.currentSelectedRequest!.brand, type: LocaleKeys.vehicleBrand.tr()),
16.height,
SingleDetailWidget(text: requestVM.acceptedRequestOffer!.serviceItemName ?? "", type: LocaleKeys.serviceName.tr()),
@ -280,6 +298,52 @@ class _ReviewRequestOfferState extends State<ReviewRequestOffer> {
),
16.height,
SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()),
if (requestVM.selectedDeliveryOptionEnum != null && deliveryAddress.isNotEmpty) ...[
16.height,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: SingleDetailWidget(text: deliveryAddress, type: LocaleKeys.location.tr()),
),
if (showMapButton) ...[
8.width,
Row(
children: [
LocaleKeys.openMapLocation.tr().toText(
fontSize: 12,
isBold: true,
color: MyColors.primaryColor,
isUnderLine: true,
),
4.width,
Image.asset(
MyAssets.icRightUpPng,
height: 6,
width: 6,
color: MyColors.primaryColor,
),
],
).onPress(() async {
double latitude, longitude = 0.0;
latitude = double.parse(requestVM.currentSelectedOffer!.providerLatitude ?? "0.0");
longitude = double.parse(requestVM.currentSelectedOffer!.providerLongitude ?? "0.0");
await Utils.openLocationInMaps(latitude: latitude, longitude: longitude);
}),
],
],
),
if (requestVM.selectedDeliveryOptionEnum == RequestDeliveryOptionEnum.delivery && requestVM.additionalAddressSparePartRequestDelivery.isNotEmpty) ...[
8.height,
SingleDetailWidget(text: requestVM.additionalAddressSparePartRequestDelivery, type: LocaleKeys.additionalAddressDetails.tr()),
],
],
),
],
],
);
}

@ -48,10 +48,21 @@ class RequestItem extends StatelessWidget {
],
6.height,
"${request.brand} ${request.model} | ${request.id}".toText(fontSize: 16, letterSpacing: -0.64),
showItem("${LocaleKeys.year.tr()}:", "${request.year}"),
if (request.customerName.isNotEmpty) ...[
showItem("${LocaleKeys.customerName.tr()}:", request.customerName),
],
if (request.brand.isNotEmpty) ...[
showItem("${LocaleKeys.brand.tr()}:", request.brand),
],
if (request.model.isNotEmpty) ...[
showItem("${LocaleKeys.model.tr()}:", request.model),
],
if (request.year > 0) ...[
showItem("${LocaleKeys.year.tr()}:", "${request.year}"),
],
if (request.vehicleTypeName.isNotEmpty) ...[
showItem("${LocaleKeys.vehicleType.tr()}:", request.vehicleTypeName),
],
showItem("${LocaleKeys.description.tr()}:", request.description),
],
),

@ -55,7 +55,7 @@ class SettingOptionsHelp extends StatelessWidget {
size: 20,
color: MyColors.greyColor,
),
titleText: LocaleKeys.termPrivacy.tr(),
titleText: LocaleKeys.termsPrivacyPolicy.tr(),
needBorderBelow: true,
onTap: () => navigateWithName(context, AppRoutes.settingOptionsTermsAndConditions),
),

@ -60,7 +60,7 @@ class _SettingOptionsTermsAndConditionsState extends State<SettingOptionsTermsAn
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.termPrivacy.tr(),
title: LocaleKeys.termsPrivacyPolicy.tr(),
isRemoveBackButton: false,
isDrawerEnabled: false,
onBackButtonTapped: () => Navigator.pop(context),

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
@ -20,7 +19,7 @@ import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
class AddPhoneNumWidget extends StatefulWidget {
const AddPhoneNumWidget({Key? key}) : super(key: key);
const AddPhoneNumWidget({super.key});
@override
State<AddPhoneNumWidget> createState() => _AddPhoneNumWidgetState();
@ -82,11 +81,34 @@ class _AddPhoneNumWidgetState extends State<AddPhoneNumWidget> {
8.height,
TxtField(
keyboardType: TextInputType.phone,
hint: "546758594",
hint: "Phone without country code ",
value: phoneNum,
isSidePaddingZero: true,
onChanged: (v) {
phoneNum = v;
// Remove country code if user enters it
String cleanedPhone = v.trim();
// If country code is selected and phone starts with it, remove it
if (countryCode.isNotEmpty && cleanedPhone.startsWith(countryCode)) {
cleanedPhone = cleanedPhone.substring(countryCode.length).trim();
}
// Also handle cases where user adds + before country code
if (countryCode.isNotEmpty && cleanedPhone.startsWith('+$countryCode')) {
cleanedPhone = cleanedPhone.substring(countryCode.length + 1).trim();
}
// Handle case where user just types + followed by digits matching country code
if (countryCode.isNotEmpty && cleanedPhone.startsWith('+')) {
String codeWithoutPlus = countryCode.replaceAll('+', '');
if (cleanedPhone.substring(1).startsWith(codeWithoutPlus)) {
cleanedPhone = cleanedPhone.substring(codeWithoutPlus.length + 1).trim();
}
}
setState(() {
phoneNum = cleanedPhone;
});
},
),
16.height,
@ -161,10 +183,10 @@ class CustomBottomPicker extends StatelessWidget {
final void Function(DropValue selectedItem) onItemSelected;
const CustomBottomPicker({
Key? key,
super.key,
required this.items,
required this.onItemSelected,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

@ -58,6 +58,7 @@ class _DropdownFieldState extends State<DropdownField> {
dropdownValue = widget.dropdownValue;
return Column(
children: [
// Always show hint as label when provided
if (widget.hint != null) ...[
Row(
mainAxisAlignment: MainAxisAlignment.start,
@ -71,19 +72,41 @@ class _DropdownFieldState extends State<DropdownField> {
),
4.height,
],
IgnorePointer(
ignoring: !widget.isSelectAble,
child: Container(
decoration:
widget.showAppointmentPickerVariant ? null : Utils.containerColorRadiusBorderWidth(MyColors.white, 0, widget.isSelectAble ? MyColors.darkPrimaryColor : MyColors.greyACColor, 2),
// Show disabled text field when not selectable
if (!widget.isSelectAble)
Container(
decoration: Utils.containerColorRadiusBorderWidth(
MyColors.greyShadowColor,
0,
MyColors.greyACColor,
2
),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
width: double.infinity,
child: (dropdownValue?.value ?? "").toText(
color: MyColors.darkTextColor,
fontSize: 15,
fontWeight: MyFonts.Medium,
),
)
else
// Show normal dropdown when selectable
Container(
decoration: widget.showAppointmentPickerVariant
? null
: Utils.containerColorRadiusBorderWidth(
MyColors.white,
0,
MyColors.darkPrimaryColor,
2
),
margin: const EdgeInsets.all(0),
padding: const EdgeInsets.only(left: 8, right: 8),
width: widget.showAppointmentPickerVariant ? 170 : null,
child: DropdownButton<DropValue>(
value: dropdownValue,
icon: Icon(
icon: const Icon(
Icons.keyboard_arrow_down_sharp,
color: !widget.isSelectAble ? Colors.transparent : null,
size: 21,
),
elevation: 16,
@ -95,7 +118,6 @@ class _DropdownFieldState extends State<DropdownField> {
color: borderColor,
fontSize: 15,
),
// hint: (widget.hint ?? "").toText(color: borderColor, fontSize: 15, fontWeight: MyFonts.Medium),
underline: Container(height: 0),
onChanged: (DropValue? newValue) {
setState(() {
@ -105,17 +127,20 @@ class _DropdownFieldState extends State<DropdownField> {
},
onTap: widget.onTap,
items: (widget.list ?? defaultV).map<DropdownMenuItem<DropValue>>(
(DropValue value) {
(DropValue value) {
return DropdownMenuItem<DropValue>(
value: value,
enabled: value.isEnabled ?? true,
child: value.value.toText(fontSize: 15, color: value.isEnabled == false ? MyColors.darkTextColor : null, fontWeight: MyFonts.Medium),
child: value.value.toText(
fontSize: 15,
color: value.isEnabled == false ? MyColors.darkTextColor : null,
fontWeight: MyFonts.Medium
),
);
},
).toList(),
),
),
),
if (widget.errorValue != "")
Row(
mainAxisAlignment: MainAxisAlignment.end,

Loading…
Cancel
Save