diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 4c3ca3b..cf41afa 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -751,7 +751,7 @@ "userGender": "جنس", "userMale": "ذكر", "userFemale": "أنثى", - "maxFileSelection" :"يمكنك تحديد الحد الأقصى لملفات 7", + "maxFileSelection": "يمكنك تحديد الحد الأقصى لملفات 7", "maxFileSize": "يجب أن يكون حجم كل ملف أقل من 2 ميغابايت", "onlyJPGandPNG": "يُسمح فقط بملفات JPG وPNG", "expiryDate": "تاريخ انتهاء الصلاحية", @@ -775,5 +775,7 @@ "tapToSelect": "اضغط للاختيار", "noteCopyItemsExplanation": "ملاحظة: ستتمكن من نسخ العناصر من خدمة إلى أخرى في الفئة المحددة. يجب عليك إنشاء الخدمات أولاً ويجب أن تكون معتمدة. ثم ستتمكن من الحصول على الخدمات المتاحة التي يمكنك نسخ جميع العناصر منها أو تحديد العناصر التي تريد نسخها.", "requestCreatedOn": "تم إنشاء الطلب في", - "online": "عبر الإنترنت" + "online": "عبر الإنترنت", + "deliveryStatus": "حالة توصيلك / الشحن هي:", + "markAsCompleted": "ضع علامة كمكتمل" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 057bc66..3ce5eca 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -773,5 +773,8 @@ "maxFileSelection": "You can select a maximum of 7 files", "maxFileSize": "Each file size must be less than 2 MB", "onlyJPGandPNG": "Only JPG and PNG files are allowed", - "expiryDate": "Expiry Date" + "expiryDate": "Expiry Date", + "deliveryStatus": "Your Delivery / Shipping Status is:", + "markAsCompleted": "Mark as Completed" + } \ No newline at end of file diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index bf4948a..70ca99d 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -998,3 +998,12 @@ extension ShippingStatusEnumToInt on ShippingRequestStatusEnum { } } } + +extension CapitalizeFirstLetter on String { + String capitalizeFirstLetter() { + if (isEmpty) { + return this; // Return the string as-is if it's empty + } + return this[0].toUpperCase() + substring(1).toLowerCase(); + } +} diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index bdf7219..cdef4ea 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -791,7 +791,9 @@ class CodegenLoader extends AssetLoader{ "tapToSelect": "اضغط للاختيار", "noteCopyItemsExplanation": "ملاحظة: ستتمكن من نسخ العناصر من خدمة إلى أخرى في الفئة المحددة. يجب عليك إنشاء الخدمات أولاً ويجب أن تكون معتمدة. ثم ستتمكن من الحصول على الخدمات المتاحة التي يمكنك نسخ جميع العناصر منها أو تحديد العناصر التي تريد نسخها.", "requestCreatedOn": "تم إنشاء الطلب في", - "online": "عبر الإنترنت" + "online": "عبر الإنترنت", + "deliveryStatus": "حالة توصيلك / الشحن هي:", + "markAsCompleted": "ضع علامة كمكتمل" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1568,7 +1570,9 @@ static const Map en_US = { "maxFileSelection": "You can select a maximum of 7 files", "maxFileSize": "Each file size must be less than 2 MB", "onlyJPGandPNG": "Only JPG and PNG files are allowed", - "expiryDate": "Expiry Date" + "expiryDate": "Expiry Date", + "deliveryStatus": "Your Delivery / Shipping Status is:", + "markAsCompleted": "Mark as Completed" }; static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 086e1fc..d9ee1b2 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -755,5 +755,7 @@ abstract class LocaleKeys { static const noteCopyItemsExplanation = 'noteCopyItemsExplanation'; static const requestCreatedOn = 'requestCreatedOn'; static const online = 'online'; + static const deliveryStatus = 'deliveryStatus'; + static const markAsCompleted = 'markAsCompleted'; } diff --git a/lib/models/requests_models/request_model.dart b/lib/models/requests_models/request_model.dart index 8f4bd01..5322442 100644 --- a/lib/models/requests_models/request_model.dart +++ b/lib/models/requests_models/request_model.dart @@ -101,7 +101,7 @@ class RequestModel { requestStatusName: json["requestStatusName"], requestStatus: (json['requestStatus'] as int).toRequestStatusEnum(), shippingStatus: json['shippingRequestStatus'], - shippingStatusEnum: json['shippingRequestStatus'] != null ? (json['shippingRequestStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.initiated, + shippingStatusEnum: json['shippingRequestStatus'] != null ? (json['shippingRequestStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.pending, cityName: json["cityName"], vehicleTypeName: json["vehicleTypeName"], countryName: json["countryName"], diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index 91ab7b4..f0534ec 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -29,6 +29,7 @@ abstract class AdsRepo { required bool isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum, + int? vehicleBrandId, int? page, }); @@ -182,6 +183,7 @@ class AdsRepoImp implements AdsRepo { required isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum, + int? vehicleBrandId, int? page, }) async { Map onlyMyAdsParams = { @@ -207,6 +209,11 @@ class AdsRepoImp implements AdsRepo { "CreatedByRoles": ["${createdByRoleEnum.getIdFromCreatedByRoleEnum()}"], }); } + if (!isMyAds && vehicleBrandId != null && vehicleBrandId != 0) { + allAdsParams.addAll({ + "VehicleBrandIDs": ["$vehicleBrandId"], + }); + } GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), @@ -237,7 +244,7 @@ class AdsRepoImp implements AdsRepo { "VehicleNew": vehicleAdConditionIdsList ?? [], "CreatedOn": (vehicleAdCreatedDateList != null && vehicleAdCreatedDateList.isNotEmpty) ? vehicleAdCreatedDateList.first.toString() : "", "isActive": "true", //only Active ADS - "isExplore": isMyAds.toString(), + "isExplore": (!isMyAds).toString(), "PageSize": "30", "PageIndex": page != null ? page.toString() : "1", }; diff --git a/lib/repositories/appointment_repo.dart b/lib/repositories/appointment_repo.dart index cf99abe..288a877 100644 --- a/lib/repositories/appointment_repo.dart +++ b/lib/repositories/appointment_repo.dart @@ -15,6 +15,8 @@ import 'package:mc_common_app/utils/enums.dart'; abstract class AppointmentRepo { Future> getMyAppointmentsForProvider(Map map); + Future> getMyProviderAppointmentsByFilters({required int serviceProviderID}); + Future> getMyAppointmentsForCustomersByFilters({ List? providerIdsList, List? categoryIdsList, @@ -219,6 +221,21 @@ class AppointmentRepoImp implements AppointmentRepo { return appointmentList; } + @override + Future> getMyProviderAppointmentsByFilters({required int serviceProviderID}) async { + String t = appState.getUser.data!.accessToken ?? ""; + + final map = {"ServiceProviderID": "$serviceProviderID"}; + GenericRespModel genericRespModel = await apiClient.getJsonForObject( + token: t, + (json) => GenericRespModel.fromJson(json), + queryParameters: map, + ApiConsts.serviceProvidersAppointmentGet, + ); + List appointmentList = List.generate(genericRespModel.data.length, (index) => AppointmentListModel.fromJson(genericRespModel.data[index])); + return appointmentList; + } + @override Future> getMyAppointmentsForCustomersByFilters({ List? providerIdsList, diff --git a/lib/services/common_services.dart b/lib/services/common_services.dart index ccc77ea..81f2e8c 100644 --- a/lib/services/common_services.dart +++ b/lib/services/common_services.dart @@ -58,8 +58,7 @@ class CommonServicesImp implements CommonAppServices { return pickedFiles; } - - + @override Future> pickMultipleImages() async { final picker = ImagePicker(); List imageModels = []; @@ -74,16 +73,16 @@ class CommonServicesImp implements CommonAppServices { return []; } - if (await element.length() > GlobalConsts().maxFileSizeInBytes) { + if (await element.length() > GlobalConsts.maxFileSizeInBytes) { Utils.showToast(LocaleKeys.maxFileSize); return []; } imageModels.add(File(element.path)); } - if (imageModels.length > GlobalConsts().maxFileCount) { + if (imageModels.length > GlobalConsts.maxFileCount) { Utils.showToast(LocaleKeys.maxFileSelection); - imageModels = imageModels.sublist(0, GlobalConsts().maxFileCount); // Keep only the first 7 images + imageModels = imageModels.sublist(0, GlobalConsts.maxFileCount); // Keep only the first 7 images } pickedImages.addAll(imageModels); @@ -120,7 +119,7 @@ class CommonServicesImp implements CommonAppServices { destLongitude, ); - return (distance / 1000) ?? 0.0; + return (distance / 1000); } catch (e) { logger.e(e.toString()); return 0.0; diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index cbb19c1..86ba205 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -13,7 +13,7 @@ class AppEnums { static const int requestFilterEnumId = 16; // Requests Filter Enums static const int branchServicesEnumId = 6; // Branch Services Filter Enums static const int myAdsFilterEnumId = 18; // My Ads Filter Enums - static const int exploreAdsFilterEnumId = 23; // Explore Ads Filter Enums + static const int adOwnersFilterEnumId = 23; // Explore Ads Filter Enums static const int appointmentsFilterEnumId = 13; // Appointments Filter Enums static const int requestStatusesFilterEnumId = 15; // Appointments Filter Enums static const int conditionEnumId = -1; // to get the Condition Filter Enums diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 0dbfee6..240355d 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -165,9 +165,8 @@ class AdVM extends BaseVM { notifyListeners(); } - List exploreAdsEnums = []; - - List myAdsEnums = []; + List myAdsStatusEnums = []; + List vehicleBrandsForFilters = []; List exploreAdsFilterOptions = []; List myAdsFilterOptions = []; @@ -177,45 +176,49 @@ class AdVM extends BaseVM { return; } - if (myAdsEnums.isEmpty) { - myAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.myAdsFilterEnumId); + if (myAdsStatusEnums.isEmpty) { + myAdsStatusEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.myAdsFilterEnumId); } - if (exploreAdsEnums.isEmpty) { - exploreAdsEnums = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.exploreAdsFilterEnumId); + if (vehicleBrandsForFilters.isEmpty) { + vehicleBrandsForFilters = await commonRepo.getVehicleBrands(vehicleTypeId: -1); // to get all the brands } exploreAdsFilterOptions.clear(); myAdsFilterOptions.clear(); - for (int i = 0; i < myAdsEnums.length; i++) { - myAdsFilterOptions.add(FilterListModel(title: myAdsEnums[i].enumValueStr, isSelected: false, id: myAdsEnums[i].enumValue)); + for (int i = 0; i < myAdsStatusEnums.length; i++) { + myAdsFilterOptions.add(FilterListModel(title: myAdsStatusEnums[i].enumValueStr, isSelected: false, id: myAdsStatusEnums[i].enumValue)); } myAdsFilterOptions.insert(0, FilterListModel(title: "All Ads", isSelected: true, id: 0)); - log("myAdsFilterOptions: ${myAdsFilterOptions.last.id}"); - - for (int i = 0; i < exploreAdsEnums.length; i++) { - exploreAdsFilterOptions.add(FilterListModel(title: "${exploreAdsEnums[i].enumValueStr} Ads", isSelected: false, id: exploreAdsEnums[i].enumValue)); + for (int i = 0; i < vehicleBrandsForFilters.length; i++) { + exploreAdsFilterOptions.add(FilterListModel( + id: vehicleBrandsForFilters[i].id!, + title: "${vehicleBrandsForFilters[i].vehicleBrandDescription}", + iconUrl: "", + isSelected: false, + )); } exploreAdsFilterOptions.insert(0, FilterListModel(title: "All Ads", isSelected: true, id: 0)); notifyListeners(); } - applyFilterOnExploreAds({required CreatedByRoleEnum createdByRoleFilter}) async { + applyFilterOnExploreAds({required int vehicleBrandId}) async { pageIndexForExploreAds = 1; hasMoreDataForExploreAds = true; if (exploreAdsFilterOptions.isEmpty) return; - int index = exploreAdsFilterOptions.indexWhere((element) => element.id.toCreatedByRoleEnum() == createdByRoleFilter); + int index = exploreAdsFilterOptions.indexWhere((element) => element.id == vehicleBrandId); for (var value in exploreAdsFilterOptions) { value.isSelected = false; } exploreAdsFilterOptions[index].isSelected = true; - if (createdByRoleFilter == CreatedByRoleEnum.allAds) { + if (vehicleBrandId == 0) { + // all ads getExploreAds(); return; } setState(ViewState.busy); - exploreAdsFilteredList = await getAdsByFilter(createdByRoleEnum: createdByRoleFilter, isMyAds: false); + exploreAdsFilteredList = await getAdsByFilter(vehicleBrandId: vehicleBrandId, isMyAds: false); setState(ViewState.idle); notifyListeners(); @@ -233,7 +236,7 @@ class AdVM extends BaseVM { hasMoreDataForExploreAds = true; notifyListeners(); try { - final List newAds = await adsRepo.getExploreAdsBasedOnFilters(page: pageIndexForExploreAds); + final List newAds = await getAdsBasedOnFilters(pageIndex: pageIndexForExploreAds, isFromLazyLoad: true); if (newAds.isEmpty) { hasMoreDataForExploreAds = false; } else { @@ -279,8 +282,8 @@ class AdVM extends BaseVM { notifyListeners(); } - Future> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum}) async { - return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum); + Future> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum, int? vehicleBrandId}) async { + return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum, vehicleBrandId: vehicleBrandId); } applyFilterOnMyAds({required AdPostStatus adPostStatusEnum}) async { @@ -1655,6 +1658,7 @@ class AdVM extends BaseVM { // ************ ADS SEARCH VIEW **************** List vehicleConditionsEnum = []; + List adOwnerEnumsFilter = []; Future populateDataForAdFilter() async { setState(ViewState.busy); @@ -1664,16 +1668,22 @@ class AdVM extends BaseVM { hasMoreDataForMyAds = true; isLoadingMore = false; + if (adOwnerEnumsFilter.isEmpty) { + adOwnerEnumsFilter = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.adOwnersFilterEnumId); + } + if (vehicleConditionsEnum.isEmpty) { vehicleConditionsEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.conditionEnumId); } - if (vehicleBrands.isEmpty) { - vehicleBrands = await commonRepo.getVehicleBrands(vehicleTypeId: -1); + if (vehicleBrandsForFilters.isEmpty) { + vehicleBrandsForFilters = await commonRepo.getVehicleBrands(vehicleTypeId: -1); // to get all the brands } + if (vehicleModelYears.isEmpty) { vehicleModelYears = await commonRepo.getVehicleModelYears(vehicleTypeId: -1); } + vehicleCities = await commonRepo.getVehicleCities(countryId: -1); // fetch all the cities setState(ViewState.idle); } @@ -1742,9 +1752,9 @@ class AdVM extends BaseVM { SelectionModel vehicleOwnerId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); - void updateSelectionVehicleAdOwnerId(SelectionModel id, {bool isForSearch = false}) { + void updateSelectionVehicleAdOwnerId(SelectionModel id, {bool showSelectionInDropdown = true, bool isForSearch = false}) { if (isForSearch) { - EnumsModel owner = exploreAdsEnums.firstWhere((element) => element.enumValue == id.selectedId); + EnumsModel owner = adOwnerEnumsFilter.firstWhere((element) => element.enumValue == id.selectedId); DropValue ownerValue = DropValue(owner.enumValue, "${owner.enumValueStr} Ads", ""); if (!ifAlreadyExist(list: vehicleAdOwnerSearchHistory, value: ownerValue)) { addToVehicleAdOwnerSearchHistory(value: ownerValue); @@ -1752,7 +1762,9 @@ class AdVM extends BaseVM { notifyListeners(); return; } - vehicleOwnerId = id; + if (showSelectionInDropdown) { + vehicleOwnerId = id; + } notifyListeners(); } @@ -1883,9 +1895,14 @@ class AdVM extends BaseVM { vehicleOwnerId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); } - Future getAdsBasedOnFilters() async { - exploreAdsFilteredList.clear(); - setState(ViewState.busy); + Future> getAdsBasedOnFilters({ + required int pageIndex, + required bool isFromLazyLoad, + }) async { + if (!isFromLazyLoad) { + exploreAdsFilteredList.clear(); + setState(ViewState.busy); + } List cityIdsList = []; if (vehicleLocationAdSearchHistory.isNotEmpty) { for (var element in vehicleLocationAdSearchHistory) { @@ -1927,15 +1944,25 @@ class AdVM extends BaseVM { } } - exploreAdsFilteredList = await adsRepo.getExploreAdsBasedOnFilters( - cityIdsList: cityIdsList, - createdByRolesIdsList: adOwnerIdsList, - vehicleBrandIdsList: brandsIdsList, - vehicleModelYearIdsList: vehicleYearIdsList, - vehicleAdConditionIdsList: conditionsIdsList, - vehicleAdCreatedDateList: createdDatesList, - page: pageIndexForExploreAds); - setState(ViewState.idle); + List list = await adsRepo.getExploreAdsBasedOnFilters( + cityIdsList: cityIdsList, + createdByRolesIdsList: adOwnerIdsList, + vehicleBrandIdsList: brandsIdsList, + vehicleModelYearIdsList: vehicleYearIdsList, + vehicleAdConditionIdsList: conditionsIdsList, + vehicleAdCreatedDateList: createdDatesList, + page: pageIndex, + ); + + if (!isFromLazyLoad) { + exploreAdsFilteredList.clear(); + exploreAdsFilteredList = list; + setState(ViewState.idle); + } else { + return list; + } + + return []; } void onEditUpdateAdPressed({required BuildContext context, required AdDetailsModel previousDetails, required bool isFromExtendAd}) { diff --git a/lib/views/advertisement/ads_filter_view.dart b/lib/views/advertisement/ads_filter_view.dart index 479195b..f1fbaeb 100644 --- a/lib/views/advertisement/ads_filter_view.dart +++ b/lib/views/advertisement/ads_filter_view.dart @@ -78,13 +78,13 @@ class _AdsFilterViewState extends State { SearchEntityWidget( title: LocaleKeys.searchByCity.tr(), actionWidget: Builder(builder: (context) { - List vehicleBrandsDrop = []; + List vehicleCitiesDrop = []; for (var element in adVM.vehicleCities) { - vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "")); + vehicleCitiesDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "")); } return DropdownField( (DropValue value) => adVM.updateSelectionVehicleCityId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true), - list: vehicleBrandsDrop, + list: vehicleCitiesDrop, dropdownValue: adVM.vehicleCityId.selectedId != -1 ? DropValue(adVM.vehicleCityId.selectedId, adVM.vehicleCityId.selectedOption, "") : null, hint: LocaleKeys.selectCity.tr(), errorValue: adVM.vehicleCityId.errorValue, @@ -99,7 +99,7 @@ class _AdsFilterViewState extends State { title: LocaleKeys.searchByBrandName.tr(), actionWidget: Builder(builder: (context) { List vehicleBrandsDrop = []; - for (var element in adVM.vehicleBrands) { + for (var element in adVM.vehicleBrandsForFilters) { vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleBrandDescription ?? "", "")); } return DropdownField( @@ -160,6 +160,29 @@ class _AdsFilterViewState extends State { onHistoryItemTapped: (DropValue value) => null, ), const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7), + SearchEntityWidget( + title: LocaleKeys.searchByAdOwner.tr(), + actionWidget: Builder( + builder: (context) { + List vehicleOwnerDrop = []; + for (var element in adVM.adOwnerEnumsFilter) { + vehicleOwnerDrop.add(DropValue(element.enumValue.toInt(), element.enumValueStr, "")); + } + return DropdownField( + (DropValue value) => + adVM.updateSelectionVehicleAdOwnerId(SelectionModel(selectedId: value.id, selectedOption: value.value), isForSearch: true, showSelectionInDropdown: false), + list: vehicleOwnerDrop, + dropdownValue: adVM.vehicleOwnerId.selectedId != -1 ? DropValue(adVM.vehicleOwnerId.selectedId, adVM.vehicleOwnerId.selectedOption, "") : null, + hint: LocaleKeys.selectOwner.tr(), + errorValue: adVM.vehicleOwnerId.errorValue, + ); + }, + ), + historyContent: adVM.vehicleAdOwnerSearchHistory, + onHistoryItemDeleted: (index) => adVM.removeVehicleAdOwnerSearchHistory(index: index), + onHistoryItemTapped: (DropValue value) => null, + ), + const Divider(thickness: 1.2).paddingOnly(top: 7, bottom: 7), SearchEntityWidget( title: LocaleKeys.searchByCreatedDate.tr(), actionWidget: TxtField( @@ -197,7 +220,7 @@ class _AdsFilterViewState extends State { title: LocaleKeys.search.tr(), onPressed: () { Navigator.pop(context); - adVM.getAdsBasedOnFilters(); + adVM.getAdsBasedOnFilters(isFromLazyLoad: false, pageIndex: 0); }, backgroundColor: MyColors.darkPrimaryColor, txtColor: MyColors.white, diff --git a/lib/views/advertisement/components/ads_list_widget.dart b/lib/views/advertisement/components/ads_list_widget.dart index fb2f995..d3fce48 100644 --- a/lib/views/advertisement/components/ads_list_widget.dart +++ b/lib/views/advertisement/components/ads_list_widget.dart @@ -10,6 +10,7 @@ import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; @@ -107,7 +108,9 @@ class AdCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - if (isAdsFragment && !context.read().isExploreAdsTapped) ...[ + if (isAdsFragment && adDetails.adPostStatus! == AdPostStatus.sold) ...[ + Utils.statusContainerChip(text: adDetails.statuslabel!, chipColor: Utils.getChipColorByAdStatus(adDetails.adPostStatus!)), + ] else if (isAdsFragment && !context.read().isExploreAdsTapped) ...[ Utils.statusContainerChip(text: adDetails.statuslabel!, chipColor: Utils.getChipColorByAdStatus(adDetails.adPostStatus!)), ], (adDetails.vehicle!.vehicleTitle ?? "").toText( diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index 60861cc..a7d501b 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -13,6 +13,7 @@ import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/view_models/payment_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; +import 'package:mc_common_app/views/advertisement/ads_buyer_chats_view.dart'; import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart'; import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart'; import 'package:mc_common_app/views/chat/widgets/chat_message_widget.dart'; @@ -111,16 +112,37 @@ class _ChatViewState extends State { padding: const EdgeInsets.symmetric(horizontal: 21, vertical: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - "Your Delivery / Shipping Status is: ".toText( - fontSize: 12, - color: Colors.white, - decorationColor: MyColors.white, - ), - Utils.getNameByShippingRequestStatusEnum(requestVM.currentSelectedRequest?.shippingStatusEnum ?? ShippingRequestStatusEnum.initiated).toText( - fontSize: 16, - color: Colors.white, + Expanded( + flex: 4, + child: Row( + children: [ + Flexible( + child: "${LocaleKeys.deliveryStatus.tr()} ${Utils.getNameByShippingRequestStatusEnum(requestVM.currentSelectedRequest?.shippingStatusEnum ?? ShippingRequestStatusEnum.pending)}" + .toText( + fontSize: 12, + color: Colors.white, + decorationColor: MyColors.white, + ), + ), + ], + ), ), + 10.width, + if ((requestVM.currentSelectedRequest?.shippingStatusEnum ?? ShippingRequestStatusEnum.pending) == ShippingRequestStatusEnum.delivered) ...[ + Expanded( + flex: 2, + child: "${LocaleKeys.markAsCompleted.tr()} ".toText(isUnderLine: true, fontSize: 12, color: Colors.white, decorationColor: MyColors.white).onPress(() { + return dealCompletedConsentBottomSheet( + mainContext: context, + requestStatusEnum: RequestStatusEnum.completed, + requestId: requestVM.currentSelectedRequest!.id, + showAcknowledgement: false, + ); + }), + ), + ], ], ), ); diff --git a/lib/views/chat/widgets/chat_bottom_sheets.dart b/lib/views/chat/widgets/chat_bottom_sheets.dart index 1d59e4c..1a534ca 100644 --- a/lib/views/chat/widgets/chat_bottom_sheets.dart +++ b/lib/views/chat/widgets/chat_bottom_sheets.dart @@ -18,14 +18,20 @@ import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart'; import 'package:provider/provider.dart'; -void dealCompletedConsentBottomSheet({required BuildContext mainContext, required RequestStatusEnum requestStatusEnum, required int requestId, Function()? acceptRequestOffer}) { +void dealCompletedConsentBottomSheet({ + required BuildContext mainContext, + required RequestStatusEnum requestStatusEnum, + required int requestId, + required bool showAcknowledgement, + Function()? acceptRequestOffer, +}) { final requestVM = mainContext.read(); return actionConfirmationBottomSheet( - isOnlyOneButton: AppState().currentAppType == AppType.customer, + isOnlyOneButton: showAcknowledgement, context: mainContext, title: LocaleKeys.doYouWantToCompleteThisDeal.tr().toText(fontSize: 26, isBold: true, letterSpacing: -1.44), subtitle: AppState().currentAppType == AppType.provider ? LocaleKeys.providerCompletingDealMeansThat.tr() : LocaleKeys.customerCompletingDealMeansThat.tr(), - checkBoxConfirmationWidget: AppState().currentAppType == AppType.customer + checkBoxConfirmationWidget: showAcknowledgement ? Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { return Row( children: [ @@ -47,14 +53,13 @@ void dealCompletedConsentBottomSheet({required BuildContext mainContext, require return Expanded( child: ShowFillButton( maxHeight: 55, - isDisabled: !chatVM.acknowledgePaymentToMowaterStatus, - title: AppState().currentAppType == AppType.customer ? LocaleKeys.submit.tr() : LocaleKeys.yes.tr(), + isDisabled: showAcknowledgement == false ? false : !chatVM.acknowledgePaymentToMowaterStatus, + title: showAcknowledgement ? LocaleKeys.submit.tr() : LocaleKeys.yes.tr(), fontSize: 15, onPressed: () async { pop(context); bool statusForReqAccept = false; - - if (acceptRequestOffer != null) { + if (acceptRequestOffer != null && showAcknowledgement) { statusForReqAccept = await acceptRequestOffer(); if (statusForReqAccept) { bool status = await requestVM.onActionRequestTapped( @@ -79,7 +84,7 @@ void dealCompletedConsentBottomSheet({required BuildContext mainContext, require context: mainContext, requestStatusEnum: requestStatusEnum, requestId: requestId, - needLoading: false, + needLoading: showAcknowledgement ? true : false, ); log("status: $status"); if (status) { diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 9f169a0..c384417 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -208,6 +208,7 @@ class _ChatMessageCustomWidgetState extends State { mainContext: context, requestStatusEnum: RequestStatusEnum.completed, requestId: requestVM.currentSelectedRequest!.id, + showAcknowledgement: AppState().currentAppType == AppType.customer, ); } else { final requestVM = context.read(); @@ -215,6 +216,7 @@ class _ChatMessageCustomWidgetState extends State { mainContext: context, requestStatusEnum: RequestStatusEnum.completed, requestId: requestVM.currentSelectedRequest!.id, + showAcknowledgement: AppState().currentAppType == AppType.customer, acceptRequestOffer: () async { bool status = await chatVM.onSendMessageForActionOnRequestOffer( receiverId: chatMessageModel.senderUserID ?? "", diff --git a/lib/views/common_fragments/ads_fragment.dart b/lib/views/common_fragments/ads_fragment.dart index 9a30037..df84976 100644 --- a/lib/views/common_fragments/ads_fragment.dart +++ b/lib/views/common_fragments/ads_fragment.dart @@ -110,7 +110,7 @@ class AdsFragment extends StatelessWidget { FiltersList( filterList: adVM.exploreAdsFilterOptions, onFilterTapped: (index, selectedFilterId) { - adVM.applyFilterOnExploreAds(createdByRoleFilter: selectedFilterId.toCreatedByRoleEnum()); + adVM.applyFilterOnExploreAds(vehicleBrandId: selectedFilterId); }, needLeftPadding: false, ).paddingOnly(left: 21), @@ -134,8 +134,8 @@ class AdsFragment extends StatelessWidget { child: RefreshIndicator( onRefresh: () async { if (adVM.isExploreAdsTapped) { - CreatedByRoleEnum createdByRoleEnum = adVM.exploreAdsFilterOptions.firstWhere((element) => element.isSelected).id.toCreatedByRoleEnum(); - adVM.applyFilterOnExploreAds(createdByRoleFilter: createdByRoleEnum); + int vehicleBrandId = adVM.exploreAdsFilterOptions.firstWhere((element) => element.isSelected).id; + adVM.applyFilterOnExploreAds(vehicleBrandId: vehicleBrandId); } else { AdPostStatus adPostStatusEnum = adVM.myAdsFilterOptions.firstWhere((element) => element.isSelected).id.toAdPostEnum(); adVM.applyFilterOnMyAds(adPostStatusEnum: adPostStatusEnum); diff --git a/lib/views/common_fragments/requests_fragment.dart b/lib/views/common_fragments/requests_fragment.dart index 3d7544c..a1f397a 100644 --- a/lib/views/common_fragments/requests_fragment.dart +++ b/lib/views/common_fragments/requests_fragment.dart @@ -158,7 +158,7 @@ class MyRequestsFragment extends StatelessWidget { }, child: RequestItem( request: requestsVM.myFilteredRequests[index], - shouldShowStatuses: AppState().currentAppType == AppType.customer, + shouldShowStatuses: true, onTap: () async { RequestModel request = requestsVM.myFilteredRequests[index]; requestsVM.updateCurrentSelectedRequest(request); diff --git a/lib/views/requests/request_detail_page.dart b/lib/views/requests/request_detail_page.dart index 28ce552..953e38b 100644 --- a/lib/views/requests/request_detail_page.dart +++ b/lib/views/requests/request_detail_page.dart @@ -29,6 +29,7 @@ class RequestDetailPage extends StatelessWidget { required int requestId, required RequestStatusEnum requestStatus, required RequestsTypeEnum requestTypeEnum, + required ShippingRequestStatusEnum shippingRequestStatusEnum, required String statusText, required BuildContext context, }) { @@ -68,7 +69,28 @@ class RequestDetailPage extends StatelessWidget { case RequestStatusEnum.shipping: case RequestStatusEnum.delivery: - if (AppState().currentAppType == AppType.provider) { + if (AppState().currentAppType == AppType.provider && shippingRequestStatusEnum == ShippingRequestStatusEnum.delivered) { + return Column( + children: [ + Utils.buildStatusContainer("${LocaleKeys.deliveryStatus.tr()} ${Utils.getNameByShippingRequestStatusEnum(shippingRequestStatusEnum).capitalizeFirstLetter()}") + .paddingOnly(left: 8, right: 8), + ShowFillButton( + maxWidth: double.infinity, + margin: const EdgeInsets.all(15), + maxHeight: 55, + title: LocaleKeys.markAsCompleted.tr(), + onPressed: () { + return dealCompletedConsentBottomSheet( + mainContext: context, + requestStatusEnum: RequestStatusEnum.completed, + requestId: requestId, + showAcknowledgement: AppState().currentAppType == AppType.customer, + ); + }, + ) + ], + ); + } else if (AppState().currentAppType == AppType.provider) { return Utils.buildStatusContainer(LocaleKeys.shippingManagementInstruction.tr()); } else { return ShowFillButton( @@ -81,7 +103,7 @@ class RequestDetailPage extends StatelessWidget { mainContext: context, requestStatusEnum: RequestStatusEnum.completed, requestId: requestId, - acceptRequestOffer: () {}, + showAcknowledgement: AppState().currentAppType == AppType.customer, ); }, ); @@ -176,8 +198,9 @@ class RequestDetailPage extends StatelessWidget { requestId: requestDetailPageArguments.requestModel.id, requestStatus: requestDetailPageArguments.requestModel.requestStatus, statusText: "Offer ${requestDetailPageArguments.requestModel.requestStatusName}", - context: context, requestTypeEnum: requestDetailPageArguments.requestModel.requestType.toRequestTypeEnum(), + shippingRequestStatusEnum: requestDetailPageArguments.requestModel.shippingStatusEnum ?? ShippingRequestStatusEnum.pending, + context: context, ), ], ], diff --git a/lib/widgets/common_widgets/categories_list.dart b/lib/widgets/common_widgets/categories_list.dart index bb00d63..f3d0039 100644 --- a/lib/widgets/common_widgets/categories_list.dart +++ b/lib/widgets/common_widgets/categories_list.dart @@ -2,20 +2,21 @@ import 'package:flutter/material.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/general_models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; class FiltersList extends StatelessWidget { final List filterList; final Function(int, int) onFilterTapped; final bool needLeftPadding; - EdgeInsets? padding; + final EdgeInsets? padding; - FiltersList({ - Key? key, + const FiltersList({ + super.key, this.padding, required this.filterList, this.needLeftPadding = true, required this.onFilterTapped, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -23,38 +24,50 @@ class FiltersList extends StatelessWidget { height: 37, width: double.infinity, child: ListView.builder( - padding: padding ?? EdgeInsets.only(left: needLeftPadding ? 12 : 0), - itemCount: filterList.length, - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext context, int index) { - return InkWell( - onTap: () { - onFilterTapped(index, filterList[index].id); - }, + padding: padding ?? EdgeInsets.only(left: needLeftPadding ? 12 : 0), + itemCount: filterList.length, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () { + onFilterTapped(index, filterList[index].id); + }, + child: ConstrainedBox( + constraints: const BoxConstraints(minWidth: 80), // Set a minimum width for each filter chip child: Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 8), margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( - color: filterList[index].isSelected - ? MyColors.darkIconColor - : null, + color: filterList[index].isSelected ? MyColors.darkIconColor : null, border: Border.all( - color: filterList[index].isSelected - ? MyColors.darkIconColor - : MyColors.primaryColor, + color: filterList[index].isSelected ? MyColors.darkIconColor : MyColors.primaryColor, width: 2, ), ), - child: filterList[index].title.toText( - fontSize: 12, - letterSpacing: -0.14, - color: - filterList[index].isSelected ? MyColors.white : null, - ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + // Icon before the text + + // if (index != 0 || filterList[index].iconUrl.isNotEmpty) ...[ + // filterList[index].iconUrl.buildNetworkImage(height: 16, width: 16, fit: BoxFit.cover), + // const SizedBox(width: 4), // Space between icon and text + // ], + + // Text displayed in the chip + filterList[index].title.toText( + fontSize: 13, + letterSpacing: -0.14, + color: filterList[index].isSelected ? MyColors.white : null, + ), + ], + ), ), - ); - }), + ), + ); + }, + ), ); } }