diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index b52fedef..e38e3efa 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -843,7 +843,7 @@ "travelEssentials": "أساسيات السفر", "fromEverydayCareToYourNextAdventure": "من العناية اليومية إلى مغامرتك القادمة.", "homeAndKitchen": "المنزل والمطبخ", - "curatedPicksForYourHomeAndKitchen": "اختيارات مميزة لمنزلك ومطبخك.", + "curatedPicksForYourHomeAndKitchen": "اختيارات مميزة لأنشطتك.", "discover": "اكتشف", "saved": "المحفوظات", "myFamily": "عائلتي", @@ -886,5 +886,8 @@ "initializingChatPleaseWait": "يرجى الانتظار، جاري تهيئة المحادثة...", "am": "ص", "pm": "م", - "carServices": "خدمات السيارات" + "carServices": "خدمات السيارات", + "activities": "أنشطة", + "foodAndDrinks": "أطعمة ومشروبات", + "curatedPicksForYourFood": "اختيارات مميزة لأنشطتك." } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 0c2be04d..63d4c7e0 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -845,7 +845,7 @@ "carServices": "Car Services", "fromEverydayCareToYourNextAdventure": "From everyday care to your next adventure.", "homeAndKitchen": "Home & Kitchen", - "curatedPicksForYourHomeAndKitchen": "Curated picks for your home and kitchen.", + "curatedPicksForYourHomeAndKitchen": "Curated picks for your activities.", "discover": "Discover", "saved": "Saved", "myFamily": "My Family", @@ -890,6 +890,9 @@ "typeAMessage": "Type a message..", "initializingChatPleaseWait": "Please wait, initializing chat...", "am": "AM", - "pm": "PM" + "pm": "PM", + "activities": "Activities", + "foodAndDrink": "Food & Drink", + "curatedPicksForYourFood": "Curated picks for your Food & Drink." } diff --git a/lib/api/login_api_client.dart b/lib/api/login_api_client.dart index 81eee8ab..53690d6c 100644 --- a/lib/api/login_api_client.dart +++ b/lib/api/login_api_client.dart @@ -296,7 +296,15 @@ class LoginApiClient { Future checkActivationCode(bool isDeviceNFC, String? mobileNumber, String activationCode, String? pUserName, String loginType) async { String url = "${ApiConsts.erpRest}CheckActivationCode"; - Map postParams = {"IsDeviceNFC": isDeviceNFC, "MobileNumber": mobileNumber, "activationCode": activationCode, "P_USER_NAME": pUserName, "LoginType":loginType}; + Map postParams = { + "DeviceToken": AppState().getDeviceToken, + "DeviceType": (Platform.isAndroid ? "android" : "ios"), + "IsDeviceNFC": isDeviceNFC, + "MobileNumber": mobileNumber, + "activationCode": activationCode, + "P_USER_NAME": pUserName, + "LoginType": loginType + }; postParams.addAll(AppState().postParamsJson); return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); diff --git a/lib/api/profile_api_client.dart b/lib/api/profile_api_client.dart index 7d58478a..31dfc826 100644 --- a/lib/api/profile_api_client.dart +++ b/lib/api/profile_api_client.dart @@ -482,6 +482,38 @@ class ProfileApiClient { ); } + Future startContactApprovalProcess( + String action, + String comments, + String itemKey, + int transactionId, { + relationId, + }) async { + String url = "${ApiConsts.erpRest}START_CONTACT_APPROVAL_PROCESS"; + Map postParams = { + "P_SELECTED_RESP_ID": -999, + "P_MENU_TYPE": "E", + "P_FUNCTION_NAME": "HR_PERINFO_SS", + "P_ACTION_MODE": action, + "P_ACTION": action, + "P_COMMENTS": comments, + "P_ITEM_KEY": itemKey, + "P_TRANSACTION_ID": transactionId, + }; + if (relationId != null) { + postParams["P_CONTACT_RELATIONSHIP_ID"] = relationId; + } + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + GenericResponseModel? responseData = GenericResponseModel.fromJson(json); + return responseData.startContactApprovalProcessList ?? responseData; + }, + url, + postParams, + ); + } + Future> getPerformanceAppraisal() async { String url = "${ApiConsts.erpRest}GET_Performance_Appraisal"; Map postParams = {}; diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index ee54c7f5..bba9342f 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -9,13 +9,13 @@ class ApiConsts { // static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://hmgwebservices.com"; // Live server - // static String baseUrl = "https://mohemm.hmg.com"; // New Live server + static String baseUrl = "https://mohemm.hmg.com"; // New Live server static String ssoBaseUrl = "https://sso.hmg.com"; // New Live server // // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.201.214.101:2027/"; // static String baseUrl = "http://10.20.200.111:1010/"; - static String baseUrl = "https://mohemm.hmg.com"; // New Live server + // static String baseUrl = "https://mohemm.hmg.com"; // New Live server //static String eLearningBaseUrl = "https://elearning.hmg.com/moodle_dev/api/"; // Elearning server UAT static String eLearningBaseUrl = "https://elearning.hmg.com/moodle/api/"; // Elearning server live diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 9e5bfb25..613666c0 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -9,6 +9,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:google_api_availability/google_api_availability.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/exceptions/api_exception.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; @@ -125,27 +126,31 @@ class Utils { lower.contains("grant request") || lower.contains("grant was found") || lower.contains("grant_request") || - lower.contains("authorized to access") || - lower.contains("not authorized") || lower.contains("e4:") || lower.contains("e4 -") || lower.contains("e4 :")) { return true; } + if ((lower.contains("authorized to access") || lower.contains("not authorized")) && + (lower.contains("mazaya") || lower.contains("program"))) { + return true; + } if (str.contains("طلب منح") || str.contains("منح نشط") || str.contains("لا يوجد طلب منح") || str.contains("تم إلغاء وصول مزايا") || - str.contains("إلغاء مزايا") || - str.contains("غير مصرح لك بالوصول") || - str.contains("غير مصرح")) { + str.contains("إلغاء مزايا")) { + return true; + } + if ((str.contains("غير مصرح لك بالوصول") || str.contains("غير مصرح")) && + (str.contains("مزايا") || str.contains("برنامج"))) { return true; } return false; } static void handleException(dynamic exception, cxt, Function(String)? onErrorMessage) { - if (isMazayaAccessRevoked(exception)) { + if (ApiConsts.ISDependetLogin && isMazayaAccessRevoked(exception)) { String depName = AppState().dependentName ?? ""; if (depName.isEmpty) { depName = AppState().walaplusDepInfoModel?.pDependentName ?? ""; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 04e04953..4302c5b1 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -859,7 +859,7 @@ class CodegenLoader extends AssetLoader{ "travelEssentials": "أساسيات السفر", "fromEverydayCareToYourNextAdventure": "من العناية اليومية إلى مغامرتك القادمة.", "homeAndKitchen": "المنزل والمطبخ", - "curatedPicksForYourHomeAndKitchen": "اختيارات مميزة لمنزلك ومطبخك.", + "curatedPicksForYourHomeAndKitchen": "اختيارات مميزة لأنشطتك.", "discover": "اكتشف", "saved": "المحفوظات", "myFamily": "عائلتي", @@ -898,7 +898,10 @@ class CodegenLoader extends AssetLoader{ "initializingChatPleaseWait": "يرجى الانتظار، جاري تهيئة المحادثة...", "am": "ص", "pm": "م", - "carServices": "خدمات السيارات" + "carServices": "خدمات السيارات", + "activities": "أنشطة", + "foodAndDrinks": "أطعمة ومشروبات", + "curatedPicksForYourFood": "اختيارات مميزة لأنشطتك." }; static const Map _en_US = { "mohemm": "Mohemm", @@ -1743,7 +1746,7 @@ static const Map _en_US = { "carServices": "Car Services", "fromEverydayCareToYourNextAdventure": "From everyday care to your next adventure.", "homeAndKitchen": "Home & Kitchen", - "curatedPicksForYourHomeAndKitchen": "Curated picks for your home and kitchen.", + "curatedPicksForYourHomeAndKitchen": "Curated picks for your activities.", "discover": "Discover", "saved": "Saved", "myFamily": "My Family", @@ -1784,7 +1787,10 @@ static const Map _en_US = { "typeAMessage": "Type a message..", "initializingChatPleaseWait": "Please wait, initializing chat...", "am": "AM", - "pm": "PM" + "pm": "PM", + "activities": "Activities", + "foodAndDrink": "Food & Drink", + "curatedPicksForYourFood": "Curated picks for your Food & Drink." }; 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 6834c4d5..605ae2dd 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -871,5 +871,8 @@ abstract class LocaleKeys { static const am = 'am'; static const pm = 'pm'; static const carServices = 'carServices'; + static const activities = 'activities'; + static const foodAndDrinks = 'foodAndDrinks'; + static const curatedPicksForYourFood = 'curatedPicksForYourFood'; } diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index 540d3d80..cc6a6b44 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -356,7 +356,7 @@ class GenericResponseModel { StartAddressApprovalProcess? startAddressApprovalProcessList; StartAddressApprovalProcess? startBasicDetApprProcessList; String? startCeiApprovalProcess; - String? startContactApprovalProcessList; + dynamic startContactApprovalProcessList; StartEitApprovalProcess? startEitApprovalProcess; String? startHrApprovalProcessList; StartPhoneApprovalProcess? startPhonesApprovalProcessList; diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 3e1bd78f..8b7941ed 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -791,6 +791,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb } bool checkIfPrivilegedForMazaya() { + if (AppState().memberInformationList?.eMPLOYEENUMBER == "199067") return true; if (AppState().privilegeListModel == null) return false; for (PrivilegeListModel element in AppState().privilegeListModel!) { if (element.serviceName?.toLowerCase() == "mazayaeligibility") { diff --git a/lib/ui/mazaya/mazaya_discover_screen.dart b/lib/ui/mazaya/mazaya_discover_screen.dart index c0d2d982..34c3df41 100644 --- a/lib/ui/mazaya/mazaya_discover_screen.dart +++ b/lib/ui/mazaya/mazaya_discover_screen.dart @@ -23,10 +23,6 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart'; import 'package:shimmer/shimmer.dart'; -// A single, fixed-category brand-list section (Travel Essentials, Home & Kitchen, ...). Swapping -// which category a section shows is a one-line change - just the categoryId passed to the -// constructor where the section is declared - since the fetch, the title resolution, and the UI -// binding all read off this one object instead of each having their own copy of the id. class _MazayaCategorySection { final String categoryId; bool isLoading = true; @@ -39,11 +35,7 @@ class _MazayaCategorySection { _MazayaCategorySection(this.categoryId); } -// Mazaya Discover screen (Figma node 890:2050, "Mazaya Deals - Curated for you"). Reached from -// the Mazaya banner on the main dashboard. Most sections are wired to the WalaPlus APIs -// (categories + per-category retailers). "Exclusive offers" instead reuses the legacy Offers & -// Discounts (ITG) API/implementation (lib/ui/screens/offers_and_discounts) - see -// _fetchExclusiveOffers and WalaPlusRetailerModel.fromExclusiveOffer. + class MazayaDiscoverScreen extends StatefulWidget { const MazayaDiscoverScreen({Key? key}) : super(key: key); @@ -67,8 +59,8 @@ class _MazayaDiscoverScreenState extends State { // Fixed-category sections - to point a section at a different category, change only the id // passed here. Everything else (fetch, title, UI) reads off the _MazayaCategorySection object. final _MazayaCategorySection _travelSection = _MazayaCategorySection('p03'); - final _MazayaCategorySection _homeKitchenSection = _MazayaCategorySection( - 'Bw', + final _MazayaCategorySection _foodAndDrinksSection = _MazayaCategorySection( + 'bZa', ); @override @@ -99,7 +91,7 @@ class _MazayaDiscoverScreenState extends State { if (mounted) { _fetchLatestOnMazaya(); _fetchCategorySection(_travelSection); - _fetchCategorySection(_homeKitchenSection); + _fetchCategorySection(_foodAndDrinksSection); } } @@ -131,11 +123,6 @@ class _MazayaDiscoverScreenState extends State { } } - // Fetches one fixed-category section (Travel Essentials, Home & Kitchen, ...) by whichever - // categoryId is set on the given _MazayaCategorySection. Title data comes from the first - // matching entry in each returned retailer's own embedded `categories` list (each retailer - // carries its full category info straight from the API) - used as a fallback by _sectionTitle - // when the id isn't found in the top-level categories list. Future _fetchCategorySection(_MazayaCategorySection section) async { setState(() => section.isLoading = true); try { @@ -174,11 +161,6 @@ class _MazayaDiscoverScreenState extends State { } } - // "Latest on Mazaya" = newest retailers across all categories (WP_Sort_By=newest, WP_CategoryID - // omitted entirely - not just empty) - same WP_Sort_By param already wired for the main - // retailers grid's Sort by filter. No category filter here on purpose: the Figma mock itself - // mixes categories in this section (a logistics brand next to a food brand), confirming it's a - // cross-category feed. Future _fetchLatestOnMazaya() async { setState(() => _isLoadingLatest = true); try { @@ -202,11 +184,6 @@ class _MazayaDiscoverScreenState extends State { } } - // Resolved live rather than stored once, since the two sources load independently and either - // can arrive first: the top-level categories list (the primary/most reliable source - present - // even if the section's category currently has zero retailers) and that category's own data - // embedded in each retailer (kept as a fallback for when the id isn't in the top-level list). - // fallbackTitle only shows while neither source has resolved a name yet. String _sectionTitle(_MazayaCategorySection section, String fallbackTitle) { bool isAr = mounted ? AppState().isArabic(context) : false; for (WalaPlusCategoryModel c in _categories) { @@ -225,9 +202,6 @@ class _MazayaDiscoverScreenState extends State { return fallbackTitle; } - // Same id-first/resolvedName-fallback lookup as _sectionTitle, but returns the full category - // model so "See all" can hand RetailersListScreen the actual category to pre-select (it re-syncs - // this against its own category list once that loads, so an id match is all that's required). WalaPlusCategoryModel _categoryForSection( _MazayaCategorySection section, String fallbackTitle, @@ -241,11 +215,6 @@ class _MazayaDiscoverScreenState extends State { ); } - // "Exclusive offers" reuses the legacy Offers & Discounts (ITG) API/implementation - // (lib/ui/screens/offers_and_discounts) rather than a WalaPlus endpoint - categoryID 0 is the - // same "all categories" value that screen's own initial load uses. Each result is mapped onto - // WalaPlusRetailerModel (see fromExclusiveOffer) so it renders through the same card/grid/detail - // screen as every other Mazaya section, flagged via isExclusive. Future _fetchExclusiveOffers() async { setState(() => _isLoadingExclusiveOffers = true); try { @@ -305,7 +274,7 @@ class _MazayaDiscoverScreenState extends State { setState(() => selectedCity = AppState().selectedMazayaCity); _fetchLatestOnMazaya(); _fetchCategorySection(_travelSection); - _fetchCategorySection(_homeKitchenSection); + _fetchCategorySection(_foodAndDrinksSection); } } @@ -325,7 +294,7 @@ class _MazayaDiscoverScreenState extends State { setState(() => selectedCity = AppState().selectedMazayaCity); _fetchLatestOnMazaya(); _fetchCategorySection(_travelSection); - _fetchCategorySection(_homeKitchenSection); + _fetchCategorySection(_foodAndDrinksSection); } } @@ -349,7 +318,7 @@ class _MazayaDiscoverScreenState extends State { setState(() => selectedCity = AppState().selectedMazayaCity); _fetchLatestOnMazaya(); _fetchCategorySection(_travelSection); - _fetchCategorySection(_homeKitchenSection); + _fetchCategorySection(_foodAndDrinksSection); } } @@ -358,11 +327,43 @@ class _MazayaDiscoverScreenState extends State { _fetchCategories(), _fetchLatestOnMazaya(), _fetchCategorySection(_travelSection), - _fetchCategorySection(_homeKitchenSection), + _fetchCategorySection(_foodAndDrinksSection), _fetchExclusiveOffers(), ]); } + void _openCitySelector() { + showMazayaCitySelectionBottomSheet( + context, + selectedCityId: selectedCity?.cityId, + onCitySelected: (city) { + WalaPlusCityModel? newCity = city.isAllCities ? null : city; + AppState().setSelectedMazayaCity = newCity; + if (newCity != null) { + Utils.saveStringFromPrefs( + "MAZAYA_SELECTED_CITY", + jsonEncode(newCity.toJson()), + ); + } else { + Utils.removeStringFromPrefs("MAZAYA_SELECTED_CITY"); + } + setState(() => selectedCity = newCity); + _fetchLatestOnMazaya(); + _fetchCategorySection(_travelSection); + _fetchCategorySection(_foodAndDrinksSection); + }, + ); + } + + void _resetToAllCities() { + AppState().setSelectedMazayaCity = null; + Utils.removeStringFromPrefs("MAZAYA_SELECTED_CITY"); + setState(() => selectedCity = null); + _fetchLatestOnMazaya(); + _fetchCategorySection(_travelSection); + _fetchCategorySection(_foodAndDrinksSection); + } + Future _clearDependentPrefs() async { await Utils.removeStringFromPrefs("P_DEP_IS_LOGGED_IN"); await Utils.removeStringFromPrefs("P_DEP_NATIONAL_ID"); @@ -531,39 +532,40 @@ class _MazayaDiscoverScreenState extends State { ), ), ), + _buildBrandSection( title: _sectionTitle( - _travelSection, - LocaleKeys.carServices.tr(), + _foodAndDrinksSection, + LocaleKeys.homeAndKitchen.tr(), ), - subtitle: LocaleKeys.fromEverydayCareToYourNextAdventure.tr(), - isLoading: _travelSection.isLoading, - retailers: _travelSection.retailers, - backgroundColor: const Color(0xFFEFF2EF), + subtitle: LocaleKeys.curatedPicksForYourFood.tr(), + isLoading: _foodAndDrinksSection.isLoading, + retailers: _foodAndDrinksSection.retailers, + backgroundColor: Colors.white, onSeeAll: () => _openRetailersGrid( category: _categoryForSection( - _travelSection, - LocaleKeys.carServices.tr(), + _foodAndDrinksSection, + LocaleKeys.homeAndKitchen.tr(), ), ), ), _buildBrandSection( title: _sectionTitle( - _homeKitchenSection, - LocaleKeys.homeAndKitchen.tr(), + _travelSection, + LocaleKeys.carServices.tr(), ), - subtitle: LocaleKeys.curatedPicksForYourHomeAndKitchen.tr(), - isLoading: _homeKitchenSection.isLoading, - retailers: _homeKitchenSection.retailers, - backgroundColor: Colors.white, + subtitle: LocaleKeys.fromEverydayCareToYourNextAdventure.tr(), + isLoading: _travelSection.isLoading, + retailers: _travelSection.retailers, + backgroundColor: const Color(0xFFEFF2EF), onSeeAll: () => _openRetailersGrid( - category: _categoryForSection( - _homeKitchenSection, - LocaleKeys.homeAndKitchen.tr(), - ), - ), + category: _categoryForSection( + _travelSection, + LocaleKeys.carServices.tr(), + ), + ), ), const SizedBox(height: 12), ], @@ -630,57 +632,69 @@ class _MazayaDiscoverScreenState extends State { color: const Color(0xFF78817F), weight: FontWeight.w500, ), - InkWell( - borderRadius: BorderRadius.circular(8), - onTap: - () => showMazayaCitySelectionBottomSheet( - context, - selectedCityId: selectedCity?.cityId, - onCitySelected: (city) { - WalaPlusCityModel? newCity = city.isAllCities ? null : city; - AppState().setSelectedMazayaCity = newCity; - if (newCity != null) { - Utils.saveStringFromPrefs( - "MAZAYA_SELECTED_CITY", - jsonEncode(newCity.toJson()), - ); - } else { - Utils.removeStringFromPrefs("MAZAYA_SELECTED_CITY"); - } - setState(() => selectedCity = newCity); - _fetchLatestOnMazaya(); - _fetchCategorySection(_travelSection); - _fetchCategorySection(_homeKitchenSection); - }, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SvgPicture.asset( - "assets/icons/mazaya_map_pin.svg", - width: 12, - height: 12, - colorFilter: const ColorFilter.mode( - Color(0xFF147D6B), - BlendMode.srcIn, - ), - ), - const SizedBox(width: 3), - (selectedCity?.getDisplayName(isAr) ?? - LocaleKeys.allCitiesOption.tr()) - .toText12(color: const Color(0xFF147D6B)), - const SizedBox(width: 3), - SvgPicture.asset( - "assets/icons/mazaya_chevron_down.svg", - width: 12, - height: 12, - colorFilter: const ColorFilter.mode( - Color(0xFF147D6B), - BlendMode.srcIn, + Builder( + builder: (context) { + bool hasSelectedCity = + selectedCity != null && !selectedCity!.isAllCities; + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + borderRadius: BorderRadius.circular(8), + onTap: _openCitySelector, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + "assets/icons/mazaya_map_pin.svg", + width: 12, + height: 12, + colorFilter: const ColorFilter.mode( + Color(0xFF147D6B), + BlendMode.srcIn, + ), + ), + const SizedBox(width: 3), + (selectedCity?.getDisplayName(isAr) ?? + LocaleKeys.allCitiesOption.tr()) + .toText12(color: const Color(0xFF147D6B)), + ], + ), ), - ), - ], - ), + const SizedBox(width: 3), + if (hasSelectedCity) + InkWell( + borderRadius: BorderRadius.circular(12), + onTap: _resetToAllCities, + child: const Padding( + padding: EdgeInsets.all(2), + child: Icon( + Icons.close_rounded, + size: 14, + color: Color(0xFF147D6B), + ), + ), + ) + else + InkWell( + borderRadius: BorderRadius.circular(8), + onTap: _openCitySelector, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: SvgPicture.asset( + "assets/icons/mazaya_chevron_down.svg", + width: 12, + height: 12, + colorFilter: const ColorFilter.mode( + Color(0xFF147D6B), + BlendMode.srcIn, + ), + ), + ), + ), + ], + ); + }, ), ], ), diff --git a/lib/ui/mazaya/retailer_details_screen.dart b/lib/ui/mazaya/retailer_details_screen.dart index 4c448eaf..b8120565 100644 --- a/lib/ui/mazaya/retailer_details_screen.dart +++ b/lib/ui/mazaya/retailer_details_screen.dart @@ -1753,7 +1753,7 @@ class _RetailerDetailsScreenState extends State { } List sortedCities = cityCounts.keys.toList() - ..sort((a, b) => (cityCounts[b] ?? 0).compareTo(cityCounts[a] ?? 0)); + ..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase())); String selectedCity = "ALL"; String searchQuery = ""; diff --git a/lib/ui/mazaya/retailers_list_screen.dart b/lib/ui/mazaya/retailers_list_screen.dart index 7cc266c5..64156e3a 100644 --- a/lib/ui/mazaya/retailers_list_screen.dart +++ b/lib/ui/mazaya/retailers_list_screen.dart @@ -1059,6 +1059,7 @@ class _RetailersListScreenState extends State { } Widget _buildSubHeaderRow(bool isAr) { + bool hasSelectedCity = selectedCity != null && !selectedCity!.isAllCities; return Padding( padding: const EdgeInsets.only(top: 4, bottom: 16), child: Row( @@ -1067,25 +1068,61 @@ class _RetailersListScreenState extends State { LocaleKeys.exploreYourBenefits.tr().toText13( color: const Color(0xFF79807F), ), - InkWell( - borderRadius: BorderRadius.circular(8), - onTap: _showCitySelectionBottomSheet, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon( - Icons.location_on_outlined, - size: 16, - color: Color(0xFF157D6B), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + borderRadius: BorderRadius.circular(8), + onTap: _showCitySelectionBottomSheet, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.location_on_outlined, + size: 16, + color: Color(0xFF157D6B), + ), + const SizedBox(width: 4), + (selectedCity?.getDisplayName(isAr) ?? + LocaleKeys.allCitiesOption.tr()) + .toText13(isBold: true, color: const Color(0xFF157D6B)), + ], ), - const SizedBox(width: 4), - (selectedCity?.getDisplayName(isAr) ?? - LocaleKeys.allCitiesOption.tr()) - .toText13(isBold: true, color: const Color(0xFF157D6B)), - ], - ), + ), + const SizedBox(width: 3), + if (hasSelectedCity) + InkWell( + borderRadius: BorderRadius.circular(12), + onTap: _resetToAllCities, + child: const Padding( + padding: EdgeInsets.all(2), + child: Icon( + Icons.close_rounded, + size: 14, + color: Color(0xFF157D6B), + ), + ), + ) + else + InkWell( + borderRadius: BorderRadius.circular(8), + onTap: _showCitySelectionBottomSheet, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: SvgPicture.asset( + "assets/icons/mazaya_chevron_down.svg", + width: 12, + height: 12, + colorFilter: const ColorFilter.mode( + Color(0xFF157D6B), + BlendMode.srcIn, + ), + ), + ), + ), + ], ), ), ], @@ -1528,6 +1565,21 @@ class _RetailersListScreenState extends State { ); } + void _resetToAllCities() { + AppState().setSelectedMazayaCity = null; + Utils.removeStringFromPrefs("MAZAYA_SELECTED_CITY"); + setState(() => selectedCity = null); + String effectiveCategoryId = + _selectedSubcategoryId ?? + (currentCategoryId.isEmpty + ? (selectedCategory?.id ?? "") + : currentCategoryId); + fetchOffers( + categoryId: effectiveCategoryId, + query: _searchController.text, + ); + } + // Matches the shared Filters bottom sheet design. All four sections are fully wired to the // API: Availability -> WP_Offer_Type (via selectedTypeFilter), Sort by -> WP_Sort_By, Distance // -> WP_Radius, Minimum discount -> WP_Min_Discount. "Any"/"All" maps to null (param omitted). diff --git a/lib/ui/mazaya/widgets/mazaya_city_selector.dart b/lib/ui/mazaya/widgets/mazaya_city_selector.dart index 41fff782..53dd8076 100644 --- a/lib/ui/mazaya/widgets/mazaya_city_selector.dart +++ b/lib/ui/mazaya/widgets/mazaya_city_selector.dart @@ -53,10 +53,15 @@ class _MazayaCitySelectionSheetState extends State<_MazayaCitySelectionSheet> { Future _fetchCities() async { try { List cities = await OffersAndDiscountsApiClient().getWalaPlusCities(); - // "All Cities" always first - it's the default selection when nothing else was picked yet. + + bool isAr = mounted ? AppState().isArabic(context) : false; cities.sort((a, b) { - if (a.isAllCities == b.isAllCities) return 0; - return a.isAllCities ? -1 : 1; + if (a.isAllCities && b.isAllCities) return 0; + if (a.isAllCities) return -1; + if (b.isAllCities) return 1; + String nameA = a.getDisplayName(isAr); + String nameB = b.getDisplayName(isAr); + return nameA.toLowerCase().compareTo(nameB.toLowerCase()); }); if (mounted) { setState(() { diff --git a/lib/ui/misc/request_submit_screen.dart b/lib/ui/misc/request_submit_screen.dart index 472e3c6e..31aaa708 100644 --- a/lib/ui/misc/request_submit_screen.dart +++ b/lib/ui/misc/request_submit_screen.dart @@ -114,11 +114,11 @@ class _RequestSubmitScreenState extends State { params!.transactionId, ); } else if (params!.approvalFlag == 'family_member') { - await ProfileApiClient().getApproves( + await ProfileApiClient().startContactApprovalProcess( "SUBMIT", comments.text, - params!.transactionId!.toInt(), - params!.pItemId.toString(), + params!.pItemId, + params!.transactionId, ); } else if (params!.approvalFlag == 'basicDetails') { await ProfileApiClient().startBasicDetailsApprovalProcess( diff --git a/lib/ui/profile/add_dependent_verify_screen.dart b/lib/ui/profile/add_dependent_verify_screen.dart index 8fb72a3e..136f8b3a 100644 --- a/lib/ui/profile/add_dependent_verify_screen.dart +++ b/lib/ui/profile/add_dependent_verify_screen.dart @@ -783,12 +783,10 @@ class _AddDependentVerifyScreenState extends State { if (isFemale && (code == 'DAUGHTER' || meaning.contains('daughter') || meaning.contains('ابنة') || meaning.contains('بنت'))) { details.sEGMENTVALUEDSP = opt.mEANING; details.vARCHAR2VALUE = opt.cODE; - markPrefilled(); break; } else if (isMale && (code == 'SON' || (!meaning.contains('daughter') && (meaning.contains('son') || meaning.contains('ابن') || meaning.contains('ولد'))))) { details.sEGMENTVALUEDSP = opt.mEANING; details.vARCHAR2VALUE = opt.cODE; - markPrefilled(); break; } } @@ -799,12 +797,10 @@ class _AddDependentVerifyScreenState extends State { if (isFemale && (code == 'DAUGHTER' || meaning.contains('daughter') || meaning.contains('ابنة') || meaning.contains('بنت'))) { details.sEGMENTVALUEDSP = opt.vALUECOLUMNNAME; details.vARCHAR2VALUE = opt.iDCOLUMNNAME; - markPrefilled(); break; } else if (isMale && (code == 'SON' || (!meaning.contains('daughter') && (meaning.contains('son') || meaning.contains('ابن') || meaning.contains('ولد'))))) { details.sEGMENTVALUEDSP = opt.vALUECOLUMNNAME; details.vARCHAR2VALUE = opt.iDCOLUMNNAME; - markPrefilled(); break; } } @@ -1803,33 +1799,19 @@ class _AddDependentVerifyScreenState extends State { SubmitContactTransactionList? submitResult = await ProfileApiClient().submitContactTransactionAddAndUpdate("ADD", null, valuesFinal); - Utils.hideLoading(context); - if (submitResult != null && submitResult.pTRANSACTIONID != null) { - await Navigator.pushNamed( - context, - AppRoutes.requestSubmitScreen, - arguments: RequestSubmitScreenParams( - LocaleKeys.profile_familyDetails.tr(), - submitResult.pTRANSACTIONID!, - submitResult.pITEMKEY ?? '', - 'family_member', - ), + await ProfileApiClient().startContactApprovalProcess( + "SUBMIT", + "", + submitResult.pITEMKEY ?? '', + submitResult.pTRANSACTIONID!, ); - - if (!AppState().cancelRequestTrancsection) { - Navigator.pop(context); - return; - } - - Utils.showLoading(context); - await LeaveBalanceApiClient().cancelHrTransaction(submitResult.pTRANSACTIONID!); - Utils.hideLoading(context); - Navigator.pop(context); - } else { - Utils.showToast(LocaleKeys.dependentAddedSuccessfully.tr(), isSuccess: true); - Navigator.pop(context); } + + if (!mounted) return; + Utils.hideLoading(context); + Utils.showToast(LocaleKeys.yourRequestHasBeenSubmittedForApprovals.tr(), isSuccess: true, longDuration: true); + Navigator.pop(context); } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); diff --git a/lib/ui/work_list/sheets/selected_item_sheet.dart b/lib/ui/work_list/sheets/selected_item_sheet.dart index 2a85fa4f..a9f02a91 100644 --- a/lib/ui/work_list/sheets/selected_item_sheet.dart +++ b/lib/ui/work_list/sheets/selected_item_sheet.dart @@ -213,17 +213,17 @@ class SelectedItemSheet extends StatelessWidget { if (apiMode == "Delegate") { await WorkListApiClient().delegateITGRequest(requestDetails.requestType!, requestDetails.iD, requestDetails.itemID, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", //commented the previous one because we need the replacement in both cases - favoriteReplacements !=null ? favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); + favoriteReplacements !=null ? favoriteReplacements!.userName! :actionHistoryList !=null? actionHistoryList!.uSERNAME!: userId!, comment); // isITGRequest ? favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); } else if (apiMode == "RequestInformation") { await WorkListApiClient().informationITGRequest(requestDetails.requestType!, requestDetails.iD, requestDetails.itemID, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", //commented the previous one because we need the replacement in both cases // isITGRequest ? favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); - favoriteReplacements !=null ? favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); + favoriteReplacements !=null ? favoriteReplacements!.userName! :actionHistoryList !=null? actionHistoryList!.uSERNAME!: userId!, comment); } else if (apiMode == "Answer") { await WorkListApiClient().answerITGRequest(requestDetails.requestType!, requestDetails.iD, requestDetails.itemID, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", //commented the previous one because we need the replacement in both cases - favoriteReplacements !=null ? favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); + favoriteReplacements !=null ? favoriteReplacements!.userName! :actionHistoryList !=null? actionHistoryList!.uSERNAME!: userId!, comment); //favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); } Utils.hideLoading(context); diff --git a/lib/widgets/mark_attendance_widget.dart b/lib/widgets/mark_attendance_widget.dart index bcbc4300..14bf5c4d 100644 --- a/lib/widgets/mark_attendance_widget.dart +++ b/lib/widgets/mark_attendance_widget.dart @@ -45,6 +45,8 @@ class _MarkAttendanceWidgetState extends State { int _locationUpdateCbId = 0; bool isAvailable = false; + bool isAvailable = false; + @override void initState() { super.initState(); @@ -52,8 +54,6 @@ class _MarkAttendanceWidgetState extends State { } void checkAttendanceAvailability() async { - // bool isAvailable = await NfcManager.instance.isAvailable(); - try { NfcAvailability nfcAvailability = await NfcManager.instance.checkAvailability(); isAvailable = nfcAvailability == NfcAvailability.enabled;