Merge branch 'wala_plus_with_dialog_removal' of http://34.17.182.140/Haroon6138/mohemm-flutter-app into wala_plus_with_dialog_removal

* 'wala_plus_with_dialog_removal' of http://34.17.182.140/Haroon6138/mohemm-flutter-app:
  Toast fixed

# Conflicts:
#	lib/generated/codegen_loader.g.dart
#	lib/generated/locale_keys.g.dart
wala_plus_with_dialog_removal
Sultan khan 2 weeks ago
commit bd6e9577e3

@ -66,6 +66,20 @@ allprojects {
jvmTarget = "21" jvmTarget = "21"
} }
} }
if (project.name == 'nfc_manager') {
project.tasks.matching { it.name.startsWith("compile") && it.name.endsWith("Kotlin") }.configureEach {
doFirst {
def translatorFile = project.file("src/main/kotlin/io/flutter/plugins/nfcmanager/Translator.kt")
if (translatorFile.exists()) {
def content = translatorFile.text
if (content.contains("toLowerCase(Locale.ROOT)")) {
translatorFile.text = content.replace("toLowerCase(Locale.ROOT)", "lowercase(Locale.ROOT)")
}
}
}
}
}
} }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 778 KiB

@ -782,6 +782,7 @@
"showAllLocations": "عرض جميع الفروع ({})", "showAllLocations": "عرض جميع الفروع ({})",
"searchBranches": "البحث عن الفروع أو المدن...", "searchBranches": "البحث عن الفروع أو المدن...",
"allCities": "الكل", "allCities": "الكل",
"allCitiesOption": "جميع المدن",
"noBranchesFound": "لم يتم العثور على فروع مطابقة", "noBranchesFound": "لم يتم العثور على فروع مطابقة",
"browseAllBranches": "تصفح جميع الفروع ({}) في مختلف المدن", "browseAllBranches": "تصفح جميع الفروع ({}) في مختلف المدن",
"branchesCount": "{} فرع", "branchesCount": "{} فرع",
@ -829,6 +830,27 @@
"merchantWebsiteNotAvailable": "الموقع الإلكتروني للتاجر غير متاح", "merchantWebsiteNotAvailable": "الموقع الإلكتروني للتاجر غير متاح",
"locationServicesRequiredForNearestOffers": "فعّل خدمات الموقع لعرض العروض القريبة منك", "locationServicesRequiredForNearestOffers": "فعّل خدمات الموقع لعرض العروض القريبة منك",
"locationPermissionRequiredForNearestOffers": "اسمح للتطبيق بالوصول إلى موقعك لعرض العروض القريبة منك", "locationPermissionRequiredForNearestOffers": "اسمح للتطبيق بالوصول إلى موقعك لعرض العروض القريبة منك",
"mazayaDeals": "عروض مزايا",
"findABrandOrSomething": "ابحث عن علامة تجارية أو شيء بسيط",
"exploreYourBenefits": "استكشف مزاياك",
"theMazayaEdit": "اختيارات مزايا",
"exclusivelyYours": "حصرياً لك",
"aLittleExtraJustForMazayaMembers": "مزايا إضافية، خاصة بأعضاء مزايا.",
"seeAll": "عرض الكل",
"latestOnMazaya": "أحدث ما في مزايا",
"goodFoodGreatCompanyEvenBetterPerks": "طعام رائع. رفقة ممتعة. مزايا أفضل.",
"travelEssentials": "أساسيات السفر",
"fromEverydayCareToYourNextAdventure": "من العناية اليومية إلى مغامرتك القادمة.",
"homeAndKitchen": "المنزل والمطبخ",
"curatedPicksForYourHomeAndKitchen": "اختيارات مميزة لمنزلك ومطبخك.",
"discover": "اكتشف",
"saved": "المحفوظات",
"myFamily": "عائلتي",
"educationAndTraining": "التعليم والتدريب",
"foodsAndDrinks": "الأطعمة والمشروبات",
"travelAndEntertainment": "السفر والترفيه",
"beautyAndFitness": "الجمال واللياقة",
"healthcare": "الرعاية الصحية",
"favoriteMerchant": "العروض المفضلة", "favoriteMerchant": "العروض المفضلة",
"favoriteOffers": "العروض المفضلة", "favoriteOffers": "العروض المفضلة",
"goodToKnow": "معلومات مهمة", "goodToKnow": "معلومات مهمة",

@ -786,6 +786,7 @@
"showAllLocations": "Show all {} locations", "showAllLocations": "Show all {} locations",
"searchBranches": "Search branches or cities...", "searchBranches": "Search branches or cities...",
"allCities": "All", "allCities": "All",
"allCitiesOption": "All Cities",
"noBranchesFound": "No branches found", "noBranchesFound": "No branches found",
"browseAllBranches": "Browse all {} branches across cities", "browseAllBranches": "Browse all {} branches across cities",
"branchesCount": "{} Branches", "branchesCount": "{} Branches",
@ -830,6 +831,27 @@
"merchantWebsiteNotAvailable": "Merchant website not available", "merchantWebsiteNotAvailable": "Merchant website not available",
"locationServicesRequiredForNearestOffers": "You need to enable location services to show nearest offers", "locationServicesRequiredForNearestOffers": "You need to enable location services to show nearest offers",
"locationPermissionRequiredForNearestOffers": "You need to give location permission to show nearest offers", "locationPermissionRequiredForNearestOffers": "You need to give location permission to show nearest offers",
"mazayaDeals": "Mazaya Deals",
"findABrandOrSomething": "Find a brand or a little something",
"exploreYourBenefits": "Explore your benefits",
"theMazayaEdit": "The Mazaya Edit",
"exclusivelyYours": "Exclusively yours",
"aLittleExtraJustForMazayaMembers": "A little extra, just for Mazaya members.",
"seeAll": "See all",
"latestOnMazaya": "Latest on Mazaya",
"goodFoodGreatCompanyEvenBetterPerks": "Good food. Great company. Even better perks.",
"travelEssentials": "Travel Essentials",
"fromEverydayCareToYourNextAdventure": "From everyday care to your next adventure.",
"homeAndKitchen": "Home & Kitchen",
"curatedPicksForYourHomeAndKitchen": "Curated picks for your home and kitchen.",
"discover": "Discover",
"saved": "Saved",
"myFamily": "My Family",
"educationAndTraining": "Education & Training",
"foodsAndDrinks": "Foods & Drinks",
"travelAndEntertainment": "Travel & Entertainment",
"beautyAndFitness": "Beauty & Fitness",
"healthcare": "Healthcare",
"favoriteAddedSuccess": "Added to favorites successfully", "favoriteAddedSuccess": "Added to favorites successfully",
"favoriteRemovedSuccess": "Removed from favorites successfully", "favoriteRemovedSuccess": "Removed from favorites successfully",
"favoriteError": "An error occurred while updating favorites", "favoriteError": "An error occurred while updating favorites",

@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>12.0</string> <string>13.0</string>
</dict> </dict>
</plist> </plist>

@ -7,8 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0AD79F8B1905C17FADC8B42C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8F0CC8EAC13AF3E07BEE567 /* Pods_Runner.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
1CCF895CFB3C95DEBB84A05F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8F0CC8EAC13AF3E07BEE567 /* Pods_Runner.framework */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
76AE113C28E078EA00513112 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 76AE113B28E078EA00513112 /* GoogleService-Info.plist */; }; 76AE113C28E078EA00513112 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 76AE113B28E078EA00513112 /* GoogleService-Info.plist */; };
@ -57,7 +57,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1CCF895CFB3C95DEBB84A05F /* Pods_Runner.framework in Frameworks */, 0AD79F8B1905C17FADC8B42C /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -144,7 +144,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
C4CFBC4C5CAC00182015ACD5 /* [CP] Embed Pods Frameworks */, C4CFBC4C5CAC00182015ACD5 /* [CP] Embed Pods Frameworks */,
EF29CE25BB57F5AA48CE1136 /* [CP] Copy Pods Resources */, 8ED1493DD0011941609D6F99 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@ -242,7 +242,7 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
6C6E76E4FB19FAF11C24005D /* [CP] Copy Pods Resources */ = { 8ED1493DD0011941609D6F99 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@ -367,7 +367,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -454,7 +454,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -504,7 +504,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;

@ -3,9 +3,23 @@
LastUpgradeVersion = "1510" LastUpgradeVersion = "1510"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "NO"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8B74B458B450D74B75744B87BD747314"
BuildableName = "Pods_Runner.framework"
BlueprintName = "Pods-Runner"
ReferencedContainer = "container:Pods/Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
buildForRunning = "YES" buildForRunning = "YES"

@ -31,6 +31,11 @@
<string>sms</string> <string>sms</string>
<string>tel</string> <string>tel</string>
<string>mailto</string> <string>mailto</string>
<string>https</string>
<string>http</string>
<string>comgooglemaps</string>
<string>googlechromes</string>
<string>waze</string>
</array> </array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
@ -46,19 +51,23 @@
<true/> <true/>
</dict> </dict>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>This app requires camera access to capture &amp; upload picture as profile image.</string> <string>This app requires camera access to capture &amp; upload picture as profile image, and scan QR codes.</string>
<key>NSFaceIDUsageDescription</key> <key>NSFaceIDUsageDescription</key>
<string>This app requires Face ID to allow biometric authentication for app login.</string> <string>This app requires Face ID to allow biometric authentication for app login.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>This app requires access to local network to connect to company Wi-Fi networks for attendance.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This App requires access to your location to mark your attendance.</string> <string>This app requires access to your location to mark your attendance and discover nearby offers and merchants.</string>
<key>NSLocationAlwaysUsageDescription</key> <key>NSLocationAlwaysUsageDescription</key>
<string>This App requires access to your location to mark your attendance.</string> <string>This app requires access to your location to mark your attendance and discover nearby offers and merchants.</string>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string>This App requires access to your location to mark your attendance.</string> <string>This app requires access to your location to mark your attendance and discover nearby offers and merchants.</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access to for call.</string> <string>This app requires microphone access for audio calls and recording voice messages.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>This app requires photo library access to select image as document &amp; upload it.</string> <string>This app requires photo library access to select image as document &amp; upload it.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app requires access to save photos and documents to your photo library.</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>NSSpeechRecognitionUsageDescription</key> <key>NSSpeechRecognitionUsageDescription</key>

@ -20,6 +20,7 @@
<true/> <true/>
<key>com.apple.developer.nfc.readersession.formats</key> <key>com.apple.developer.nfc.readersession.formats</key>
<array> <array>
<string>NDEF</string>
<string>TAG</string> <string>TAG</string>
</array> </array>
<key>com.apple.developer.ubiquity-container-identifiers</key> <key>com.apple.developer.ubiquity-container-identifiers</key>

@ -294,9 +294,9 @@ class LoginApiClient {
return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams); return await ApiClient().postJsonForObject((json) => GenericResponseModel.fromJson(json).basicMemberInformation, url, postParams);
} }
Future<GenericResponseModel?> checkActivationCode(bool isDeviceNFC, String? mobileNumber, String activationCode, String? pUserName) async { Future<GenericResponseModel?> checkActivationCode(bool isDeviceNFC, String? mobileNumber, String activationCode, String? pUserName, String loginType) async {
String url = "${ApiConsts.erpRest}CheckActivationCode"; String url = "${ApiConsts.erpRest}CheckActivationCode";
Map<String, dynamic> postParams = {"IsDeviceNFC": isDeviceNFC, "MobileNumber": mobileNumber, "activationCode": activationCode, "P_USER_NAME": pUserName}; Map<String, dynamic> postParams = {"IsDeviceNFC": isDeviceNFC, "MobileNumber": mobileNumber, "activationCode": activationCode, "P_USER_NAME": pUserName, "LoginType":loginType};
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);
return await ApiClient().postJsonForObject((json) { return await ApiClient().postJsonForObject((json) {
GenericResponseModel responseData = GenericResponseModel.fromJson(json); GenericResponseModel responseData = GenericResponseModel.fromJson(json);

@ -258,6 +258,11 @@ class OffersAndDiscountsApiClient {
Future<WalaPlusRetailersResponse?> getWalaPlusRetailers({ Future<WalaPlusRetailersResponse?> getWalaPlusRetailers({
String? categoryId, String? categoryId,
// Every existing caller passes categoryId (even "" for "all") and relies on WP_CategoryID
// always being present in the request - default true preserves that for everyone. Pass
// false only when the category filter must be fully omitted (e.g. a cross-category newest
// feed), not just set to empty.
bool includeCategoryId = true,
int page = 1, int page = 1,
int perPage = 10, int perPage = 10,
String? geoLocation, String? geoLocation,
@ -285,7 +290,9 @@ class OffersAndDiscountsApiClient {
} }
postParams["MemberType"] = ApiConsts.isDependetLogin ? 2 : 1; postParams["MemberType"] = ApiConsts.isDependetLogin ? 2 : 1;
postParams["CompanyID"] = 1; postParams["CompanyID"] = 1;
postParams["WP_CategoryID"] = effectiveCatId; if (includeCategoryId) {
postParams["WP_CategoryID"] = effectiveCatId;
}
if (offerType != null && offerType.trim().isNotEmpty) { if (offerType != null && offerType.trim().isNotEmpty) {
postParams["WP_Offer_Type"] = offerType.trim(); postParams["WP_Offer_Type"] = offerType.trim();
} }
@ -323,8 +330,6 @@ class OffersAndDiscountsApiClient {
postParams["WP_User_Reference"] = null; postParams["WP_User_Reference"] = null;
} }
log("postParams: ${postParams.toString()}");
return await ApiClient().postJsonForObject( return await ApiClient().postJsonForObject(
(response) { (response) {
if (response != null) { if (response != null) {

@ -551,6 +551,7 @@ class ProfileApiClient {
if (personId != null) { if (personId != null) {
postParams["P_PERSON_ID"] = personId; postParams["P_PERSON_ID"] = personId;
} }
postParams["PatientIdentificationID"] = patientIdentificationId?.replaceAll('-', '').trim();
// Only the Grant call carries these - Revoke's payload stays exactly as it was before // Only the Grant call carries these - Revoke's payload stays exactly as it was before
// this feature. P_BIRTH_DATE/P_NHIC_VERIFIED_FLAG are always sent (even as null, if the // this feature. P_BIRTH_DATE/P_NHIC_VERIFIED_FLAG are always sent (even as null, if the
// source contact from GET_EMPLOYEE_CONTACTS didn't have a value) once we're granting. // source contact from GET_EMPLOYEE_CONTACTS didn't have a value) once we're granting.
@ -561,7 +562,7 @@ class ProfileApiClient {
if (patientIdentificationId != null && patientIdentificationId.trim().isNotEmpty) { if (patientIdentificationId != null && patientIdentificationId.trim().isNotEmpty) {
// National ID comes formatted with dashes (e.g. "2-5616-1545-7") - the backend wants // National ID comes formatted with dashes (e.g. "2-5616-1545-7") - the backend wants
// the plain digit string (e.g. "2561615457"). // the plain digit string (e.g. "2561615457").
postParams["PatientIdentificationID"] = patientIdentificationId.replaceAll('-', '').trim();
} }
} }
postParams.addAll(AppState().postParamsJson); postParams.addAll(AppState().postParamsJson);

@ -14,8 +14,6 @@ import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_create_request.dart';
import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_ovr_home.dart'; import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_ovr_home.dart';
import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_request_detailed.dart'; import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_request_detailed.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen_for_dependet.dart';
import 'package:mohem_flutter_app/ui/landing/wala_plus_retailer_details_screen.dart';
import 'package:mohem_flutter_app/ui/landing/itg/change_itg_ad_password_screen.dart'; import 'package:mohem_flutter_app/ui/landing/itg/change_itg_ad_password_screen.dart';
import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart'; import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart';
import 'package:mohem_flutter_app/ui/landing/itg/survey_screen.dart'; import 'package:mohem_flutter_app/ui/landing/itg/survey_screen.dart';
@ -33,10 +31,15 @@ import 'package:mohem_flutter_app/ui/marathon/marathon_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_sponsor_video_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_sponsor_video_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_tutorial_viewer_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_tutorial_viewer_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_waiting_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_waiting_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/retailers_list_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/mazaya_discover_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/saved_retailers_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/retailer_details_screen.dart';
import 'package:mohem_flutter_app/ui/misc/request_submit_screen.dart'; import 'package:mohem_flutter_app/ui/misc/request_submit_screen.dart';
import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart';
import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart';
import 'package:mohem_flutter_app/ui/my_attendance/services_menu_list_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/services_menu_list_screen.dart';
// import 'package:mohem_flutter_app/ui/my_attendance/my_attendance_screen.dart'; // import 'package:mohem_flutter_app/ui/my_attendance/my_attendance_screen.dart';
import 'package:mohem_flutter_app/ui/my_team/create_request.dart'; import 'package:mohem_flutter_app/ui/my_team/create_request.dart';
import 'package:mohem_flutter_app/ui/my_team/employee_details.dart'; import 'package:mohem_flutter_app/ui/my_team/employee_details.dart';
@ -64,6 +67,7 @@ import 'package:mohem_flutter_app/ui/screens/announcements/announcement_details.
import 'package:mohem_flutter_app/ui/screens/announcements/announcements.dart'; import 'package:mohem_flutter_app/ui/screens/announcements/announcements.dart';
import 'package:mohem_flutter_app/ui/screens/child_education/child_education_assistance.dart'; import 'package:mohem_flutter_app/ui/screens/child_education/child_education_assistance.dart';
import 'package:mohem_flutter_app/ui/screens/courses/courses_screen.dart'; import 'package:mohem_flutter_app/ui/screens/courses/courses_screen.dart';
// import 'package:mohem_flutter_app/ui/my_attendance/work_from_home_screen.dart'; // import 'package:mohem_flutter_app/ui/my_attendance/work_from_home_screen.dart';
import 'package:mohem_flutter_app/ui/screens/eit/add_eit.dart'; import 'package:mohem_flutter_app/ui/screens/eit/add_eit.dart';
import 'package:mohem_flutter_app/ui/screens/event_activity/event_activity.dart'; import 'package:mohem_flutter_app/ui/screens/event_activity/event_activity.dart';
@ -103,8 +107,10 @@ class AppRoutes {
static const String newPassword = "/newPassword"; static const String newPassword = "/newPassword";
static const String loginVerification = "/loginVerification"; static const String loginVerification = "/loginVerification";
static const String dashboard = "/dashboard"; static const String dashboard = "/dashboard";
static const String dashboardScreenForDependet = "/dashboardScreenForDependet"; static const String retailersListScreen = "/dashboardScreenForDependet";
static const String walaPlusRetailerDetails = "/walaPlusRetailerDetails"; static const String retailerDetailsScreen = "/walaPlusRetailerDetails";
static const String mazayaDiscover = "/mazayaDiscover";
static const String savedReyailersScreen = "/mazayaSaved";
static const String todayAttendance = "/todayAttendance"; static const String todayAttendance = "/todayAttendance";
static const String subMenuScreen = "/submenuScreen"; static const String subMenuScreen = "/submenuScreen";
static const String addEitScreen = "/addeitScreen"; static const String addEitScreen = "/addeitScreen";
@ -243,15 +249,14 @@ class AppRoutes {
verifyLogin: (BuildContext context) => VerifyLoginScreen(), verifyLogin: (BuildContext context) => VerifyLoginScreen(),
verifyLastLogin: (BuildContext context) => VerifyLastLoginScreen(), verifyLastLogin: (BuildContext context) => VerifyLastLoginScreen(),
dashboard: (BuildContext context) => DashboardScreen(), dashboard: (BuildContext context) => DashboardScreen(),
dashboardScreenForDependet: (BuildContext context) => const DashboardScreenForDependet(), retailersListScreen: (BuildContext context) => const RetailersListScreen(),
walaPlusRetailerDetails: (BuildContext context) => const WalaPlusRetailerDetailsScreen(), retailerDetailsScreen: (BuildContext context) => const RetailerDetailsScreen(),
mazayaDiscover: (BuildContext context) => const MazayaDiscoverScreen(),
savedReyailersScreen: (BuildContext context) => const SavedRetailersScreen(),
appInAppWebView: (BuildContext context) { appInAppWebView: (BuildContext context) {
Object? args = ModalRoute.of(context)?.settings.arguments; Object? args = ModalRoute.of(context)?.settings.arguments;
if (args is Map<String, dynamic>) { if (args is Map<String, dynamic>) {
return AppInAppWebViewScreen( return AppInAppWebViewScreen(url: args['url'] ?? "", title: args['title']);
url: args['url'] ?? "",
title: args['title'],
);
} }
return const Scaffold(); return const Scaffold();
}, },
@ -262,8 +267,8 @@ class AppRoutes {
newPassword: (BuildContext context) => NewPasswordScreen(), newPassword: (BuildContext context) => NewPasswordScreen(),
forgotPassword: (BuildContext context) => ForgotPasswordScreen(), forgotPassword: (BuildContext context) => ForgotPasswordScreen(),
todayAttendance: (BuildContext context) => TodayAttendanceScreen2(), todayAttendance: (BuildContext context) => TodayAttendanceScreen2(),
//eit
//eit
addEitScreen: (BuildContext context) => AddEITScreen(), addEitScreen: (BuildContext context) => AddEITScreen(),
//Work List //Work List
@ -274,7 +279,6 @@ class AppRoutes {
worklistSettings: (BuildContext context) => WorklistSettings(), worklistSettings: (BuildContext context) => WorklistSettings(),
// Leave Balance // Leave Balance
leaveBalance: (BuildContext context) => LeaveBalance(), leaveBalance: (BuildContext context) => LeaveBalance(),
addLeaveBalance: (BuildContext context) => AddLeaveBalanceScreen(), addLeaveBalance: (BuildContext context) => AddLeaveBalanceScreen(),
@ -373,7 +377,7 @@ class AppRoutes {
unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(), unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(),
appUpdateScreen: (BuildContext context) => const AppUpdateScreen(), appUpdateScreen: (BuildContext context) => const AppUpdateScreen(),
childEducation: (BuildContext context) => ChildEducationAssistance(), childEducation: (BuildContext context) => ChildEducationAssistance(),
activityScreen:(BuildContext context) => const EventActivityScreen(), activityScreen: (BuildContext context) => const EventActivityScreen(),
//ETQAN OVR //ETQAN OVR
etqanOvr: (BuildContext context) => EtqanOvrHome(), etqanOvr: (BuildContext context) => EtqanOvrHome(),
@ -382,6 +386,5 @@ class AppRoutes {
//Parking QR //Parking QR
parkingQr: (BuildContext context) => const ParkingQrScreen(), parkingQr: (BuildContext context) => const ParkingQrScreen(),
}; };
} }

@ -798,6 +798,7 @@ class CodegenLoader extends AssetLoader{
"showAllLocations": "عرض جميع الفروع ({})", "showAllLocations": "عرض جميع الفروع ({})",
"searchBranches": "البحث عن الفروع أو المدن...", "searchBranches": "البحث عن الفروع أو المدن...",
"allCities": "الكل", "allCities": "الكل",
"allCitiesOption": "جميع المدن",
"noBranchesFound": "لم يتم العثور على فروع مطابقة", "noBranchesFound": "لم يتم العثور على فروع مطابقة",
"browseAllBranches": "تصفح جميع الفروع ({}) في مختلف المدن", "browseAllBranches": "تصفح جميع الفروع ({}) في مختلف المدن",
"branchesCount": "{} فرع", "branchesCount": "{} فرع",
@ -845,6 +846,27 @@ class CodegenLoader extends AssetLoader{
"merchantWebsiteNotAvailable": "الموقع الإلكتروني للتاجر غير متاح", "merchantWebsiteNotAvailable": "الموقع الإلكتروني للتاجر غير متاح",
"locationServicesRequiredForNearestOffers": "فعّل خدمات الموقع لعرض العروض القريبة منك", "locationServicesRequiredForNearestOffers": "فعّل خدمات الموقع لعرض العروض القريبة منك",
"locationPermissionRequiredForNearestOffers": "اسمح للتطبيق بالوصول إلى موقعك لعرض العروض القريبة منك", "locationPermissionRequiredForNearestOffers": "اسمح للتطبيق بالوصول إلى موقعك لعرض العروض القريبة منك",
"mazayaDeals": "عروض مزايا",
"findABrandOrSomething": "ابحث عن علامة تجارية أو شيء بسيط",
"exploreYourBenefits": "استكشف مزاياك",
"theMazayaEdit": "اختيارات مزايا",
"exclusivelyYours": "حصرياً لك",
"aLittleExtraJustForMazayaMembers": "مزايا إضافية، خاصة بأعضاء مزايا.",
"seeAll": "عرض الكل",
"latestOnMazaya": "أحدث ما في مزايا",
"goodFoodGreatCompanyEvenBetterPerks": "طعام رائع. رفقة ممتعة. مزايا أفضل.",
"travelEssentials": "أساسيات السفر",
"fromEverydayCareToYourNextAdventure": "من العناية اليومية إلى مغامرتك القادمة.",
"homeAndKitchen": "المنزل والمطبخ",
"curatedPicksForYourHomeAndKitchen": "اختيارات مميزة لمنزلك ومطبخك.",
"discover": "اكتشف",
"saved": "المحفوظات",
"myFamily": "عائلتي",
"educationAndTraining": "التعليم والتدريب",
"foodsAndDrinks": "الأطعمة والمشروبات",
"travelAndEntertainment": "السفر والترفيه",
"beautyAndFitness": "الجمال واللياقة",
"healthcare": "الرعاية الصحية",
"favoriteMerchant": "العروض المفضلة", "favoriteMerchant": "العروض المفضلة",
"favoriteOffers": "العروض المفضلة", "favoriteOffers": "العروض المفضلة",
"goodToKnow": "معلومات مهمة", "goodToKnow": "معلومات مهمة",
@ -858,15 +880,13 @@ class CodegenLoader extends AssetLoader{
"phone": "الهاتف", "phone": "الهاتف",
"viewAll": "عرض الكل", "viewAll": "عرض الكل",
"moreCitiesCount": "+ {} مدن أخرى", "moreCitiesCount": "+ {} مدن أخرى",
"mazayaDeals": "عروض مزايا",
"exploreYourBenefits": "استكشف مزاياك",
"findAnEscapeOrAnExperience": "ابحث عن تجربة أو عطلة", "findAnEscapeOrAnExperience": "ابحث عن تجربة أو عطلة",
"allOffers": "جميع العروض", "allOffers": "جميع العروض",
"sampleAvailability": "حسب التوفر", "sampleAvailability": "حسب التوفر",
"offersCount": "{} عروض", "offersCount": "{} عروض",
"mazayaExclusive": "حصري لمزايا", "mazayaExclusive": "حصري لمزايا",
"youAreAllCaughtUp": "لقد شاهدت كل العروض. المزيد قريباً.", "youAreAllCaughtUp": "لقد شاهدت كل العروض. المزيد قريباً.",
"selectCity": "اختر المدينة" "selectCity": "اختر المدينة",
}; };
static const Map<String,dynamic> _en_US = { static const Map<String,dynamic> _en_US = {
"mohemm": "Mohemm", "mohemm": "Mohemm",
@ -1652,6 +1672,7 @@ static const Map<String,dynamic> _en_US = {
"showAllLocations": "Show all {} locations", "showAllLocations": "Show all {} locations",
"searchBranches": "Search branches or cities...", "searchBranches": "Search branches or cities...",
"allCities": "All", "allCities": "All",
"allCitiesOption": "All Cities",
"noBranchesFound": "No branches found", "noBranchesFound": "No branches found",
"browseAllBranches": "Browse all {} branches across cities", "browseAllBranches": "Browse all {} branches across cities",
"branchesCount": "{} Branches", "branchesCount": "{} Branches",
@ -1696,6 +1717,27 @@ static const Map<String,dynamic> _en_US = {
"merchantWebsiteNotAvailable": "Merchant website not available", "merchantWebsiteNotAvailable": "Merchant website not available",
"locationServicesRequiredForNearestOffers": "You need to enable location services to show nearest offers", "locationServicesRequiredForNearestOffers": "You need to enable location services to show nearest offers",
"locationPermissionRequiredForNearestOffers": "You need to give location permission to show nearest offers", "locationPermissionRequiredForNearestOffers": "You need to give location permission to show nearest offers",
"mazayaDeals": "Mazaya Deals",
"findABrandOrSomething": "Find a brand or a little something",
"exploreYourBenefits": "Explore your benefits",
"theMazayaEdit": "The Mazaya Edit",
"exclusivelyYours": "Exclusively yours",
"aLittleExtraJustForMazayaMembers": "A little extra, just for Mazaya members.",
"seeAll": "See all",
"latestOnMazaya": "Latest on Mazaya",
"goodFoodGreatCompanyEvenBetterPerks": "Good food. Great company. Even better perks.",
"travelEssentials": "Travel Essentials",
"fromEverydayCareToYourNextAdventure": "From everyday care to your next adventure.",
"homeAndKitchen": "Home & Kitchen",
"curatedPicksForYourHomeAndKitchen": "Curated picks for your home and kitchen.",
"discover": "Discover",
"saved": "Saved",
"myFamily": "My Family",
"educationAndTraining": "Education & Training",
"foodsAndDrinks": "Foods & Drinks",
"travelAndEntertainment": "Travel & Entertainment",
"beautyAndFitness": "Beauty & Fitness",
"healthcare": "Healthcare",
"favoriteAddedSuccess": "Added to favorites successfully", "favoriteAddedSuccess": "Added to favorites successfully",
"favoriteRemovedSuccess": "Removed from favorites successfully", "favoriteRemovedSuccess": "Removed from favorites successfully",
"favoriteError": "An error occurred while updating favorites", "favoriteError": "An error occurred while updating favorites",
@ -1712,15 +1754,13 @@ static const Map<String,dynamic> _en_US = {
"phone": "Phone", "phone": "Phone",
"viewAll": "View all", "viewAll": "View all",
"moreCitiesCount": "+ {} more cities", "moreCitiesCount": "+ {} more cities",
"mazayaDeals": "Mazaya Deals",
"exploreYourBenefits": "Explore your benefits",
"findAnEscapeOrAnExperience": "Find an escape or an experience", "findAnEscapeOrAnExperience": "Find an escape or an experience",
"allOffers": "All offers", "allOffers": "All offers",
"sampleAvailability": "sample availability", "sampleAvailability": "sample availability",
"offersCount": "{} offers", "offersCount": "{} offers",
"mazayaExclusive": "MAZAYA EXCLUSIVE", "mazayaExclusive": "MAZAYA EXCLUSIVE",
"youAreAllCaughtUp": "You're all caught up. More little extras soon.", "youAreAllCaughtUp": "You're all caught up. More little extras soon.",
"selectCity": "Select City" "selectCity": "Select City",
}; };
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};
} }

@ -770,6 +770,7 @@ abstract class LocaleKeys {
static const showAllLocations = 'showAllLocations'; static const showAllLocations = 'showAllLocations';
static const searchBranches = 'searchBranches'; static const searchBranches = 'searchBranches';
static const allCities = 'allCities'; static const allCities = 'allCities';
static const allCitiesOption = 'allCitiesOption';
static const noBranchesFound = 'noBranchesFound'; static const noBranchesFound = 'noBranchesFound';
static const browseAllBranches = 'browseAllBranches'; static const browseAllBranches = 'browseAllBranches';
static const branchesCount = 'branchesCount'; static const branchesCount = 'branchesCount';
@ -817,6 +818,27 @@ abstract class LocaleKeys {
static const merchantWebsiteNotAvailable = 'merchantWebsiteNotAvailable'; static const merchantWebsiteNotAvailable = 'merchantWebsiteNotAvailable';
static const locationServicesRequiredForNearestOffers = 'locationServicesRequiredForNearestOffers'; static const locationServicesRequiredForNearestOffers = 'locationServicesRequiredForNearestOffers';
static const locationPermissionRequiredForNearestOffers = 'locationPermissionRequiredForNearestOffers'; static const locationPermissionRequiredForNearestOffers = 'locationPermissionRequiredForNearestOffers';
static const mazayaDeals = 'mazayaDeals';
static const findABrandOrSomething = 'findABrandOrSomething';
static const exploreYourBenefits = 'exploreYourBenefits';
static const theMazayaEdit = 'theMazayaEdit';
static const exclusivelyYours = 'exclusivelyYours';
static const aLittleExtraJustForMazayaMembers = 'aLittleExtraJustForMazayaMembers';
static const seeAll = 'seeAll';
static const latestOnMazaya = 'latestOnMazaya';
static const goodFoodGreatCompanyEvenBetterPerks = 'goodFoodGreatCompanyEvenBetterPerks';
static const travelEssentials = 'travelEssentials';
static const fromEverydayCareToYourNextAdventure = 'fromEverydayCareToYourNextAdventure';
static const homeAndKitchen = 'homeAndKitchen';
static const curatedPicksForYourHomeAndKitchen = 'curatedPicksForYourHomeAndKitchen';
static const discover = 'discover';
static const saved = 'saved';
static const myFamily = 'myFamily';
static const educationAndTraining = 'educationAndTraining';
static const foodsAndDrinks = 'foodsAndDrinks';
static const travelAndEntertainment = 'travelAndEntertainment';
static const beautyAndFitness = 'beautyAndFitness';
static const healthcare = 'healthcare';
static const favoriteMerchant = 'favoriteMerchant'; static const favoriteMerchant = 'favoriteMerchant';
static const favoriteOffers = 'favoriteOffers'; static const favoriteOffers = 'favoriteOffers';
static const goodToKnow = 'goodToKnow'; static const goodToKnow = 'goodToKnow';
@ -830,8 +852,6 @@ abstract class LocaleKeys {
static const phone = 'phone'; static const phone = 'phone';
static const viewAll = 'viewAll'; static const viewAll = 'viewAll';
static const moreCitiesCount = 'moreCitiesCount'; static const moreCitiesCount = 'moreCitiesCount';
static const mazayaDeals = 'mazayaDeals';
static const exploreYourBenefits = 'exploreYourBenefits';
static const findAnEscapeOrAnExperience = 'findAnEscapeOrAnExperience'; static const findAnEscapeOrAnExperience = 'findAnEscapeOrAnExperience';
static const allOffers = 'allOffers'; static const allOffers = 'allOffers';
static const sampleAvailability = 'sampleAvailability'; static const sampleAvailability = 'sampleAvailability';

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart';
class WalaPlusRetailersResponse { class WalaPlusRetailersResponse {
@ -208,6 +209,11 @@ class WalaPlusRetailerModel {
String? countryNameEn; String? countryNameEn;
bool? isFavorite; bool? isFavorite;
// Drives the "MAZAYA EXCLUSIVE" badge on the retailer card (Figma node 927:496). NOT parsed
// from the WalaPlus_Retailers/WalaPlus_RetailersByID API - this is a different, separate
// "Exclusive" API/section, so callers set this manually on the model after fetching from there.
bool? isExclusive;
WalaPlusRetailerModel({ WalaPlusRetailerModel({
this.id, this.id,
this.name, this.name,
@ -221,6 +227,7 @@ class WalaPlusRetailerModel {
this.logoThumb, this.logoThumb,
this.website, this.website,
this.isFavorite, this.isFavorite,
this.isExclusive,
this.type, this.type,
this.usage, this.usage,
this.weight, this.weight,
@ -338,38 +345,44 @@ class WalaPlusRetailerModel {
categoriesList.add(WalaPlusCategoryModel(name: rawCats.toString().trim())); categoriesList.add(WalaPlusCategoryModel(name: rawCats.toString().trim()));
} }
String? singleCatName = (targetJson['category_name'] ?? String? singleCatName =
targetJson['categoryName'] ?? (targetJson['category_name'] ??
targetJson['cat_name'] ?? targetJson['categoryName'] ??
targetJson['catName'] ?? targetJson['cat_name'] ??
targetJson['CategoryName'] ?? targetJson['catName'] ??
targetJson['Category_Name'] ?? targetJson['CategoryName'] ??
targetJson['category_title'] ?? targetJson['Category_Name'] ??
targetJson['categoryTitle'])?.toString().trim(); targetJson['category_title'] ??
String? singleCatAr = (targetJson['category_name_ar'] ?? targetJson['categoryTitle'])
targetJson['categoryNameAr'] ?? ?.toString()
targetJson['cat_name_ar'] ?? .trim();
targetJson['catNameAr'] ?? String? singleCatAr =
targetJson['category_ar'] ?? (targetJson['category_name_ar'] ??
targetJson['country_name_ar'] ?? targetJson['categoryNameAr'] ??
targetJson['countryNameAr'])?.toString().trim(); targetJson['cat_name_ar'] ??
String? singleCatEn = (targetJson['category_name_en'] ?? targetJson['catNameAr'] ??
targetJson['categoryNameEn'] ?? targetJson['category_ar'] ??
targetJson['cat_name_en'] ?? targetJson['country_name_ar'] ??
targetJson['catNameEn'] ?? targetJson['countryNameAr'])
targetJson['category_en'] ?? ?.toString()
targetJson['country_name_en'] ?? .trim();
targetJson['countryNameEn'])?.toString().trim(); String? singleCatEn =
String? foundCategoryId = (targetJson['category_id'] ?? (targetJson['category_name_en'] ??
targetJson['categoryId'] ?? targetJson['categoryNameEn'] ??
targetJson['cat_id'] ?? targetJson['cat_name_en'] ??
targetJson['catId'] ?? targetJson['catNameEn'] ??
targetJson['WP_CategoryID'] ?? targetJson['category_en'] ??
targetJson['WP_CategoryId'])?.toString().trim(); targetJson['country_name_en'] ??
targetJson['countryNameEn'])
?.toString()
.trim();
String? foundCategoryId =
(targetJson['category_id'] ?? targetJson['categoryId'] ?? targetJson['cat_id'] ?? targetJson['catId'] ?? targetJson['WP_CategoryID'] ?? targetJson['WP_CategoryId'])?.toString().trim();
String? rawType = targetJson['type']?.toString().trim(); String? rawType = targetJson['type']?.toString().trim();
String? lowerType = rawType?.toLowerCase(); String? lowerType = rawType?.toLowerCase();
bool isTypeStoreKeyword = lowerType == null || bool isTypeStoreKeyword =
lowerType == null ||
lowerType.isEmpty || lowerType.isEmpty ||
lowerType == 'online' || lowerType == 'online' ||
lowerType == 'instore' || lowerType == 'instore' ||
@ -387,12 +400,9 @@ class WalaPlusRetailerModel {
if ((singleCatName != null && singleCatName.isNotEmpty && singleCatName != "null") || if ((singleCatName != null && singleCatName.isNotEmpty && singleCatName != "null") ||
(singleCatAr != null && singleCatAr.isNotEmpty && singleCatAr != "null") || (singleCatAr != null && singleCatAr.isNotEmpty && singleCatAr != "null") ||
(singleCatEn != null && singleCatEn.isNotEmpty && singleCatEn != "null")) { (singleCatEn != null && singleCatEn.isNotEmpty && singleCatEn != "null")) {
categoriesList.add(WalaPlusCategoryModel( categoriesList.add(
id: foundCategoryId, WalaPlusCategoryModel(id: foundCategoryId, name: singleCatName ?? singleCatEn ?? singleCatAr, countryNameAr: singleCatAr ?? singleCatName, countryNameEn: singleCatEn ?? singleCatName),
name: singleCatName ?? singleCatEn ?? singleCatAr, );
countryNameAr: singleCatAr ?? singleCatName,
countryNameEn: singleCatEn ?? singleCatName,
));
} }
} }
@ -429,7 +439,8 @@ class WalaPlusRetailerModel {
} }
} }
String? foundWebsite = targetJson['website']?.toString() ?? String? foundWebsite =
targetJson['website']?.toString() ??
targetJson['merchant_website']?.toString() ?? targetJson['merchant_website']?.toString() ??
targetJson['merchantWebsite']?.toString() ?? targetJson['merchantWebsite']?.toString() ??
targetJson['website_url']?.toString() ?? targetJson['website_url']?.toString() ??
@ -464,25 +475,13 @@ class WalaPlusRetailerModel {
bool? parsedIsFavorite; bool? parsedIsFavorite;
if (targetJson.containsKey('isfavorite')) { if (targetJson.containsKey('isfavorite')) {
parsedIsFavorite = targetJson['isfavorite'] == true || parsedIsFavorite = targetJson['isfavorite'] == true || targetJson['isfavorite'] == 1 || targetJson['isfavorite'] == 'true' || targetJson['isfavorite'] == '1';
targetJson['isfavorite'] == 1 ||
targetJson['isfavorite'] == 'true' ||
targetJson['isfavorite'] == '1';
} else if (targetJson.containsKey('is_favorite')) { } else if (targetJson.containsKey('is_favorite')) {
parsedIsFavorite = targetJson['is_favorite'] == true || parsedIsFavorite = targetJson['is_favorite'] == true || targetJson['is_favorite'] == 1 || targetJson['is_favorite'] == 'true' || targetJson['is_favorite'] == '1';
targetJson['is_favorite'] == 1 ||
targetJson['is_favorite'] == 'true' ||
targetJson['is_favorite'] == '1';
} else if (targetJson.containsKey('isFavorite')) { } else if (targetJson.containsKey('isFavorite')) {
parsedIsFavorite = targetJson['isFavorite'] == true || parsedIsFavorite = targetJson['isFavorite'] == true || targetJson['isFavorite'] == 1 || targetJson['isFavorite'] == 'true' || targetJson['isFavorite'] == '1';
targetJson['isFavorite'] == 1 ||
targetJson['isFavorite'] == 'true' ||
targetJson['isFavorite'] == '1';
} else if (targetJson.containsKey('favorite')) { } else if (targetJson.containsKey('favorite')) {
parsedIsFavorite = targetJson['favorite'] == true || parsedIsFavorite = targetJson['favorite'] == true || targetJson['favorite'] == 1 || targetJson['favorite'] == 'true' || targetJson['favorite'] == '1';
targetJson['favorite'] == 1 ||
targetJson['favorite'] == 'true' ||
targetJson['favorite'] == '1';
} }
return WalaPlusRetailerModel( return WalaPlusRetailerModel(
@ -532,6 +531,54 @@ class WalaPlusRetailerModel {
); );
} }
// Adapter for the Mazaya "Exclusive Offers" section, which reuses the legacy Offers &
// Discounts (ITG) API/implementation (lib/ui/screens/offers_and_discounts) rather than a
// WalaPlus endpoint. Maps whatever overlaps onto this model so the existing grid/card/detail
// screen can render it unchanged - fields with no equivalent on OffersListModel (favorite,
// redeem, locations, contact, ...) are simply left null, and isExclusive hides the actions
// that depend on them (see WalaPlusRetailerCard, RetailerDetailsScreen).
factory WalaPlusRetailerModel.fromExclusiveOffer(OffersListModel offer, {bool isAr = false}) {
String? name = decodeUnicodeString(isAr ? (offer.titleAr ?? offer.titleEn) : (offer.titleEn ?? offer.titleAr));
String? description = decodeUnicodeString(isAr ? (offer.descriptionAr ?? offer.descriptionEn) : (offer.descriptionEn ?? offer.descriptionAr));
String? categoryName = decodeUnicodeString(isAr ? (offer.categoryNameAr ?? offer.categoryNameEn) : (offer.categoryNameEn ?? offer.categoryNameAr));
String? discountText = _formatExclusiveDiscountText(decodeUnicodeString(offer.discountDescription) ?? decodeUnicodeString(offer.discount));
return WalaPlusRetailerModel(
id: "exclusive-${offer.offersDiscountId ?? ''}",
name: name,
// WalaPlusRetailerCard reads offerTitle as the promo subtitle shown under the name (e.g.
// "20% off") - the discount text is the closest equivalent on a legacy offer.
offerTitle: discountText,
description: description,
info: description,
image: offer.bannerImage,
logo: offer.logo,
website: offer.websiteUrl,
endDate: offer.endDate,
validTo: offer.endDate,
categoryName: categoryName,
categories: categoryName != null ? <WalaPlusCategoryModel>[WalaPlusCategoryModel(name: categoryName)] : null,
isExclusive: true,
);
}
// The legacy Offers & Discounts API sometimes returns a bare number ("10", "20") for its
// discount fields rather than a formatted "10% off" string. WalaPlusRetailerCard's badge
// logic (getDiscountBadge) only recognizes a "<digits>%" pattern, so a bare number would
// otherwise render verbatim instead of as a discount. Already-formatted text (containing "%",
// "off", or "خصم") is left untouched.
static String? _formatExclusiveDiscountText(String? raw) {
if (raw == null) return null;
String trimmed = raw.trim();
if (trimmed.isEmpty) return null;
if (trimmed.contains('%') || trimmed.toLowerCase().contains('off') || trimmed.contains('خصم')) {
return trimmed;
}
if (RegExp(r'^\d+(\.\d+)?$').hasMatch(trimmed)) {
return LocaleKeys.offDiscount.tr(args: ['$trimmed%']);
}
return trimmed;
}
static String? decodeUnicodeString(Object? val) { static String? decodeUnicodeString(Object? val) {
if (val == null) return null; if (val == null) return null;
String s = val.toString().trim(); String s = val.toString().trim();
@ -614,6 +661,7 @@ class WalaPlusRetailerModel {
data['country_name_en'] = countryNameEn; data['country_name_en'] = countryNameEn;
data['is_favorite'] = isFavorite; data['is_favorite'] = isFavorite;
data['isfavorite'] = isFavorite; data['isfavorite'] = isFavorite;
data['is_exclusive'] = isExclusive;
if (offers != null) { if (offers != null) {
data['offers'] = offers!.map((v) => v.toJson()).toList(); data['offers'] = offers!.map((v) => v.toJson()).toList();
} }
@ -633,9 +681,8 @@ class WalaPlusRetailerModel {
for (var cat in categories!) { for (var cat in categories!) {
String? primary = isAr ? cat.countryNameAr : cat.countryNameEn; String? primary = isAr ? cat.countryNameAr : cat.countryNameEn;
String? secondary = isAr ? (cat.name ?? cat.countryNameEn) : (cat.name ?? cat.countryNameAr); String? secondary = isAr ? (cat.name ?? cat.countryNameEn) : (cat.name ?? cat.countryNameAr);
String? resolved = (primary != null && primary.trim().isNotEmpty && primary != "null") String? resolved =
? primary.trim() (primary != null && primary.trim().isNotEmpty && primary != "null") ? primary.trim() : (secondary != null && secondary.trim().isNotEmpty && secondary != "null" ? secondary.trim() : null);
: (secondary != null && secondary.trim().isNotEmpty && secondary != "null" ? secondary.trim() : null);
if (resolved != null && resolved.isNotEmpty) { if (resolved != null && resolved.isNotEmpty) {
return resolved; return resolved;
} }
@ -649,9 +696,10 @@ class WalaPlusRetailerModel {
if (cat.id?.toString() == categoryId?.toString()) { if (cat.id?.toString() == categoryId?.toString()) {
String? primary = isAr ? cat.countryNameAr : cat.countryNameEn; String? primary = isAr ? cat.countryNameAr : cat.countryNameEn;
String? secondary = isAr ? (cat.name ?? cat.countryNameEn) : (cat.name ?? cat.countryNameAr); String? secondary = isAr ? (cat.name ?? cat.countryNameEn) : (cat.name ?? cat.countryNameAr);
String? resolved = (primary != null && primary.trim().isNotEmpty && primary != "null") String? resolved =
? primary.trim() (primary != null && primary.trim().isNotEmpty && primary != "null")
: (secondary != null && secondary.trim().isNotEmpty && secondary != "null" ? secondary.trim() : null); ? primary.trim()
: (secondary != null && secondary.trim().isNotEmpty && secondary != "null" ? secondary.trim() : null);
if (resolved != null && resolved.isNotEmpty) { if (resolved != null && resolved.isNotEmpty) {
return resolved; return resolved;
} }

@ -460,7 +460,7 @@ class _DashboardScreenState extends State<DashboardScreen> with WidgetsBindingOb
InkWell( InkWell(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
onTap: () { onTap: () {
Navigator.pushNamed(context, AppRoutes.dashboardScreenForDependet); Navigator.pushNamed(context, AppRoutes.mazayaDiscover);
}, },
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),

@ -111,7 +111,7 @@ class ServicesWidget extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Expanded(child: data.homeMenus![parentIndex].menuEntiesList[index].prompt!.toText11(isBold: true)), Expanded(child: data.homeMenus![parentIndex].menuEntiesList[index].prompt!.toText11(isBold: true, maxLine: 3)),
RotatedBox(quarterTurns: AppState().isArabic(context) ? 2 : 4, child: SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)), RotatedBox(quarterTurns: AppState().isArabic(context) ? 2 : 4, child: SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)),
], ],
), ),

@ -920,7 +920,7 @@ class _LoginScreenState extends State<LoginScreen> {
if (effectiveToken.isNotEmpty && mounted) { if (effectiveToken.isNotEmpty && mounted) {
Utils.hideLoading(context); Utils.hideLoading(context);
Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboardScreenForDependet, (Route<dynamic> route) => false); Navigator.pushNamedAndRemoveUntil(context, AppRoutes.mazayaDiscover, (Route<dynamic> route) => false);
return true; return true;
} }
} catch (e) { } catch (e) {
@ -990,7 +990,7 @@ class _LoginScreenState extends State<LoginScreen> {
Utils.saveStringFromPrefs("P_DEP_LOGIN_TYPE", loginType.toString()); Utils.saveStringFromPrefs("P_DEP_LOGIN_TYPE", loginType.toString());
if (navigateToDashboard && ApiConsts.ISDependetLogin) { if (navigateToDashboard && ApiConsts.ISDependetLogin) {
Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboardScreenForDependet, (Route<dynamic> route) => false); Navigator.pushNamedAndRemoveUntil(context, AppRoutes.mazayaDiscover, (Route<dynamic> route) => false);
} }
} }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,386 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/offers_and_discounts_api_client.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/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_retailer_model.dart';
import 'package:mohem_flutter_app/ui/mazaya/widgets/mazaya_bottom_nav.dart';
import 'package:mohem_flutter_app/ui/mazaya/widgets/wala_plus_retailer_grid.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
// Mazaya Saved tab - full-screen version of what was FavoriteMerchantsBottomSheet (a modal
// bottom sheet). Same fetch/search/pagination/toggle-favorite logic, now its own pushed route
// with the shared MazayaBottomNav instead of a dismissible sheet - matches Discover's "own
// route, fetches fresh on mount" pattern.
class SavedRetailersScreen extends StatefulWidget {
const SavedRetailersScreen({Key? key}) : super(key: key);
@override
State<SavedRetailersScreen> createState() => _SavedRetailersScreenState();
}
class _SavedRetailersScreenState extends State<SavedRetailersScreen> {
List<WalaPlusRetailerModel> favoriteRetailers = <WalaPlusRetailerModel>[];
bool isLoading = true;
bool isLoadingMore = false;
bool hasMore = true;
bool _isFetchingPage = false;
int currentPage = 1;
int totalPages = 1;
final int pageSize = 10;
String searchQuery = "";
Timer? _debounceTimer;
final TextEditingController _searchController = TextEditingController();
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_scrollController.addListener(_scrollListener);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) fetchFavorites();
});
}
@override
void dispose() {
_debounceTimer?.cancel();
_scrollController.removeListener(_scrollListener);
_scrollController.dispose();
_searchController.dispose();
super.dispose();
}
void _scrollListener() {
if (!_scrollController.hasClients) return;
double maxScroll = _scrollController.position.maxScrollExtent;
double currentScroll = _scrollController.position.pixels;
if (currentScroll >= maxScroll - 35) {
if (!isLoading && !isLoadingMore && !_isFetchingPage && hasMore) {
fetchNextPage();
}
}
}
String? _getUserReferenceForFavorites() {
String ref = OffersAndDiscountsApiClient().resolveUserReference();
if (ref.trim().isNotEmpty) return ref.trim();
String? userName = AppState().getUserName;
if (userName != null && userName.trim().isNotEmpty) return userName.trim();
return null;
}
Future<void> fetchFavorites({String? query}) async {
if (query != null) searchQuery = query;
setState(() {
isLoading = true;
currentPage = 1;
hasMore = true;
});
try {
String? queryParam =
searchQuery.trim().isNotEmpty ? searchQuery.trim() : null;
String? userRef = _getUserReferenceForFavorites();
WalaPlusRetailersResponse? response = await OffersAndDiscountsApiClient()
.getWalaPlusRetailers(
categoryId: "",
page: 1,
perPage: pageSize,
offerType: null,
query: queryParam,
sortBy: null,
radius: null,
minDiscount: null,
context: null,
userReference: userRef,
);
favoriteRetailers = response?.retailers ?? <WalaPlusRetailerModel>[];
for (WalaPlusRetailerModel r in favoriteRetailers) {
r.isFavorite = true;
}
currentPage = 1;
totalPages = response?.pagesCount ?? 1;
hasMore =
currentPage < totalPages &&
(response?.retailers?.isNotEmpty ?? false);
} catch (e) {
debugPrint("MazayaSavedScreen: error fetching favorites: $e");
favoriteRetailers = <WalaPlusRetailerModel>[];
} finally {
if (mounted) setState(() => isLoading = false);
}
}
Future<void> fetchNextPage() async {
if (isLoading || isLoadingMore || _isFetchingPage || !hasMore) return;
_isFetchingPage = true;
setState(() => isLoadingMore = true);
try {
int nextPage = currentPage + 1;
String? queryParam =
searchQuery.trim().isNotEmpty ? searchQuery.trim() : null;
String? userRef = _getUserReferenceForFavorites();
WalaPlusRetailersResponse? response = await OffersAndDiscountsApiClient()
.getWalaPlusRetailers(
categoryId: "",
page: nextPage,
perPage: pageSize,
offerType: null,
query: queryParam,
sortBy: null,
radius: null,
minDiscount: null,
context: null,
userReference: userRef,
);
List<WalaPlusRetailerModel> newRetailers =
response?.retailers ?? <WalaPlusRetailerModel>[];
if (newRetailers.isNotEmpty) {
for (WalaPlusRetailerModel r in newRetailers) {
r.isFavorite = true;
}
currentPage = nextPage;
totalPages = response?.pagesCount ?? totalPages;
hasMore = currentPage < totalPages;
favoriteRetailers.addAll(newRetailers);
} else {
hasMore = false;
}
} catch (e) {
debugPrint("MazayaSavedScreen: error fetching next page: $e");
} finally {
_isFetchingPage = false;
if (mounted) setState(() => isLoadingMore = false);
}
}
void _onSearchChanged(String query) {
_debounceTimer?.cancel();
searchQuery = query;
if (query.trim().isEmpty) {
fetchFavorites(query: "");
} else {
_debounceTimer = Timer(const Duration(milliseconds: 600), () {
fetchFavorites(query: query.trim());
});
}
}
Future<void> _toggleFavorite(WalaPlusRetailerModel item) async {
String retailerId = item.id ?? "";
if (retailerId.isEmpty) return;
bool currentFav = item.isFavorite ?? true;
bool targetFav = !currentFav;
setState(() {
item.isFavorite = targetFav;
if (!targetFav) {
favoriteRetailers.removeWhere(
(WalaPlusRetailerModel r) => r.id == item.id,
);
}
});
Utils.showLoading(context);
try {
GenericResponseModel? response;
String? userRef = _getUserReferenceForFavorites();
if (targetFav) {
response = await OffersAndDiscountsApiClient().addWalaPlusFavorite(
retailerId: retailerId,
context: context,
userReference: userRef,
);
} else {
response = await OffersAndDiscountsApiClient().removeWalaPlusFavorite(
retailerId: retailerId,
context: context,
userReference: userRef,
);
}
Utils.hideLoading(context);
if (response != null && response.errorMessage == null) {
bool isAr = AppState().isArabic(context);
String msg =
isAr
? (targetFav
? LocaleKeys.favoriteAddedSuccess.tr()
: LocaleKeys.favoriteRemovedSuccess.tr())
: (response.wpFavoritesAddRemoveMessage ??
(targetFav
? LocaleKeys.favoriteAddedSuccess.tr()
: LocaleKeys.favoriteRemovedSuccess.tr()));
Utils.showToast(msg, isSuccess: true);
} else {
Utils.showToast(
response?.errorEndUserMessage ??
response?.errorMessage ??
LocaleKeys.favoriteError.tr(),
);
await fetchFavorites(query: _searchController.text);
}
} catch (e) {
Utils.hideLoading(context);
debugPrint("MazayaSavedScreen: error toggling favorite: $e");
Utils.showToast(LocaleKeys.favoriteError.tr());
await fetchFavorites(query: _searchController.text);
}
}
void _openRetailer(WalaPlusRetailerModel item) async {
await Navigator.pushNamed(
context,
AppRoutes.retailerDetailsScreen,
arguments: item,
);
if (mounted) {
await fetchFavorites(query: _searchController.text);
}
}
@override
Widget build(BuildContext context) {
bool isAr = AppState().isArabic(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBarWidget(
context,
title: LocaleKeys.favoriteOffers.tr(),
showHomeButton: false,
),
bottomNavigationBar: const MazayaBottomNav(currentTab: MazayaTab.saved),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Container(
width: double.infinity,
height: 70,
padding: const EdgeInsets.only(
top: 14,
right: 16,
bottom: 14,
left: 16,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE8E8E8), width: 1.0),
boxShadow: <BoxShadow>[
BoxShadow(
color: const Color(0xFF000000).withOpacity(0.0314),
offset: const Offset(0, 2),
blurRadius: 5,
spreadRadius: 0,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
LocaleKeys.search.tr().toText12(
isBold: true,
color: const Color(0xFF2A333C),
),
const SizedBox(height: 2),
TextField(
controller: _searchController,
onChanged: _onSearchChanged,
onSubmitted: (String val) {
_debounceTimer?.cancel();
fetchFavorites(query: val.trim());
},
textInputAction: TextInputAction.search,
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.zero,
hintText: LocaleKeys.searchItems.tr(),
hintStyle: const TextStyle(
color: Color(0xFFACAFB1),
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.2,
),
border: InputBorder.none,
suffixIcon: ValueListenableBuilder<TextEditingValue>(
valueListenable: _searchController,
builder: (
BuildContext context,
TextEditingValue value,
Widget? child,
) {
if (value.text.isEmpty)
return const SizedBox.shrink();
return InkWell(
child: const Icon(
Icons.clear,
size: 18,
color: Color(0xFF848486),
),
onTap: () {
_searchController.clear();
_onSearchChanged("");
},
);
},
),
suffixIconConstraints: const BoxConstraints(
maxWidth: 24,
maxHeight: 28,
),
),
style: const TextStyle(
color: Color(0xFF2A333C),
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
Expanded(
child: RefreshIndicator(
onRefresh: () => fetchFavorites(query: _searchController.text),
color: MyColors.textMixColor,
child: SingleChildScrollView(
controller: _scrollController,
physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
child: Column(
children: <Widget>[
WalaPlusRetailerGrid(
isLoading: isLoading,
retailers: favoriteRetailers,
isAr: isAr,
isLoadingMore: isLoadingMore,
onTapRetailer: _openRetailer,
onFavoriteTap: _toggleFavorite,
),
const SizedBox(height: 20),
],
),
),
),
),
],
),
);
}
}

@ -0,0 +1,85 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/mazaya/mazaya_discover_screen.dart';
import 'package:mohem_flutter_app/ui/mazaya/saved_retailers_screen.dart';
import 'package:mohem_flutter_app/ui/profile/family_members.dart';
enum MazayaTab { discover, saved, myFamily }
// Shared bottom nav for the Mazaya module (Discover / Saved / My Family). Each tab is its own
// screen, swapped in with zero-duration transition (see _onTabTapped) so switching tabs feels
// instant rather than like a new screen opening - not an IndexedStack shell, since each tab still
// needs to reload its own data fresh on every switch, not preserve state across tabs.
class MazayaBottomNav extends StatelessWidget {
final MazayaTab currentTab;
const MazayaBottomNav({Key? key, required this.currentTab}) : super(key: key);
static const Color _activeColor = Color(0xFF147D6B);
static const Color _inactiveColor = Color(0xFF78817F);
void _onTabTapped(BuildContext context, MazayaTab tab) {
if (tab == currentTab) return;
Widget targetScreen;
RouteSettings? settings;
switch (tab) {
case MazayaTab.discover:
targetScreen = const MazayaDiscoverScreen();
break;
case MazayaTab.saved:
targetScreen = const SavedRetailersScreen();
break;
case MazayaTab.myFamily:
targetScreen = const FamilyMembers();
settings = const RouteSettings(arguments: <String, dynamic>{'fromMazaya': true});
break;
}
Navigator.pushReplacement(
context,
PageRouteBuilder<void>(
settings: settings,
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => targetScreen,
transitionDuration: Duration.zero,
reverseTransitionDuration: Duration.zero,
),
);
}
Widget _buildItem(BuildContext context, {required MazayaTab tab, required String svgAsset, required String label}) {
bool isActive = tab == currentTab;
return Expanded(
child: InkWell(
onTap: () => _onTabTapped(context, tab),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SvgPicture.asset(svgAsset, width: 22, height: 22, colorFilter: ColorFilter.mode(isActive ? _activeColor : _inactiveColor, BlendMode.srcIn)),
const SizedBox(height: 5),
label.toText11(weight: isActive ? FontWeight.w600 : FontWeight.w400, color: isActive ? _activeColor : _inactiveColor),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: Container(
decoration: const BoxDecoration(color: Colors.white, border: Border(top: BorderSide(color: Color(0xFFE8EAE4)))),
padding: const EdgeInsets.only(top: 13, bottom: 9),
child: Row(
children: <Widget>[
_buildItem(context, tab: MazayaTab.discover, svgAsset: "assets/icons/mazaya_compass.svg", label: LocaleKeys.discover.tr()),
_buildItem(context, tab: MazayaTab.saved, svgAsset: "assets/icons/mazaya_heart_outline.svg", label: LocaleKeys.saved.tr()),
_buildItem(context, tab: MazayaTab.myFamily, svgAsset: "assets/icons/mazaya_user_round.svg", label: LocaleKeys.myFamily.tr()),
],
),
),
);
}
}

@ -0,0 +1,97 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_retailer_model.dart';
import 'package:mohem_flutter_app/ui/mazaya/retailers_list_screen.dart';
import 'package:shimmer/shimmer.dart';
class WalaPlusRetailerGrid extends StatelessWidget {
final bool isLoading;
final List<WalaPlusRetailerModel> retailers;
final bool isAr;
final void Function(WalaPlusRetailerModel item) onTapRetailer;
final void Function(WalaPlusRetailerModel item) onFavoriteTap;
final bool isLoadingMore;
final int shimmerItemCount;
const WalaPlusRetailerGrid({
Key? key,
required this.isLoading,
required this.retailers,
required this.isAr,
required this.onTapRetailer,
required this.onFavoriteTap,
this.isLoadingMore = false,
this.shimmerItemCount = 4,
}) : super(key: key);
SliverGridDelegateWithFixedCrossAxisCount get _gridDelegate =>
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: isTablet ? 4 : 2, mainAxisExtent: 235, crossAxisSpacing: 12, mainAxisSpacing: 16);
@override
Widget build(BuildContext context) {
if (isLoading) {
return Shimmer.fromColors(
baseColor: const Color(0xFFE2E8F0),
highlightColor: const Color(0xFFF8FAFC),
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: _gridDelegate,
itemCount: shimmerItemCount,
itemBuilder: (BuildContext _, int __) => _buildShimmerCard(),
),
);
}
if (retailers.isEmpty) {
return Padding(padding: const EdgeInsets.only(top: 40), child: Center(child: LocaleKeys.noOffersAvailable.tr().toText14(color: const Color(0xFF64748B))));
}
return Column(
children: <Widget>[
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: _gridDelegate,
itemCount: retailers.length,
itemBuilder: (BuildContext context, int index) {
return WalaPlusRetailerCard(item: retailers[index], isAr: isAr, onTap: () => onTapRetailer(retailers[index]), onFavoriteTap: () => onFavoriteTap(retailers[index]));
},
),
if (isLoadingMore) ...<Widget>[
16.height,
Center(child: Padding(padding: const EdgeInsets.symmetric(vertical: 12), child: Image.asset("assets/images/logos/loading_mohemm_logo.gif", height: 48.0, width: 48.0))),
],
],
);
}
Widget _buildShimmerCard() {
return Container(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20)),
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(height: 120, width: double.infinity, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(14))),
const SizedBox(height: 8),
Container(height: 14, width: 110, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))),
const SizedBox(height: 6),
Container(height: 14, width: 80, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))),
const SizedBox(height: 6),
Row(
children: <Widget>[
Container(height: 28, width: 50, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(6))),
const SizedBox(width: 5),
Container(height: 28, width: 45, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(6))),
],
),
],
),
);
}
}

@ -1858,7 +1858,7 @@ class _AddDependentVerifyScreenState extends State<AddDependentVerifyScreen> {
onHomeTapped: () { onHomeTapped: () {
Navigator.pushNamedAndRemoveUntil( Navigator.pushNamedAndRemoveUntil(
context, context,
ApiConsts.ISDependetLogin ? AppRoutes.dashboardScreenForDependet : AppRoutes.dashboard, ApiConsts.ISDependetLogin ? AppRoutes.mazayaDiscover : AppRoutes.dashboard,
(Route<dynamic> route) => false, (Route<dynamic> route) => false,
); );
}, },

@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
import 'package:mohem_flutter_app/api/profile_api_client.dart'; import 'package:mohem_flutter_app/api/profile_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
@ -52,13 +53,22 @@ class _ShieldIconPainter extends CustomPainter {
checkPath.lineTo(w * 0.66, h * 0.39); checkPath.lineTo(w * 0.66, h * 0.39);
canvas.drawPath(checkPath, paint); canvas.drawPath(checkPath, paint);
} else { } else {
canvas.drawLine(Offset(w * 0.38, h * 0.38), Offset(w * 0.62, h * 0.62), paint); canvas.drawLine(
canvas.drawLine(Offset(w * 0.62, h * 0.38), Offset(w * 0.38, h * 0.62), paint); Offset(w * 0.38, h * 0.38),
Offset(w * 0.62, h * 0.62),
paint,
);
canvas.drawLine(
Offset(w * 0.62, h * 0.38),
Offset(w * 0.38, h * 0.62),
paint,
);
} }
} }
@override @override
bool shouldRepaint(covariant _ShieldIconPainter oldDelegate) => color != oldDelegate.color || isGranted != oldDelegate.isGranted; bool shouldRepaint(covariant _ShieldIconPainter oldDelegate) =>
color != oldDelegate.color || isGranted != oldDelegate.isGranted;
} }
class DependentDetailsScreen extends StatefulWidget { class DependentDetailsScreen extends StatefulWidget {
@ -71,7 +81,8 @@ class DependentDetailsScreen extends StatefulWidget {
// equivalent local state directly instead of needing this. // equivalent local state directly instead of needing this.
final bool? recentAction; final bool? recentAction;
const DependentDetailsScreen({Key? key, this.contact, this.recentAction}) : super(key: key); const DependentDetailsScreen({Key? key, this.contact, this.recentAction})
: super(key: key);
@override @override
_DependentDetailsScreenState createState() => _DependentDetailsScreenState(); _DependentDetailsScreenState createState() => _DependentDetailsScreenState();
@ -107,7 +118,8 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
bool get _isAccessAllowed { bool get _isAccessAllowed {
if (_contact == null) return false; if (_contact == null) return false;
String? flag = _contact!.mAZAYAACCESSFLG ?? _contact!.mazayaAccess?.toString(); String? flag =
_contact!.mAZAYAACCESSFLG ?? _contact!.mazayaAccess?.toString();
return flag?.trim().toUpperCase() == 'Y'; return flag?.trim().toUpperCase() == 'Y';
} }
@ -118,17 +130,29 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
String _getInitials(String? name) { String _getInitials(String? name) {
if (name == null || name.trim().isEmpty) return "D"; if (name == null || name.trim().isEmpty) return "D";
List<String> parts = name.trim().split(RegExp(r'\s+')).where((String p) => p.isNotEmpty).toList(); List<String> parts =
name
.trim()
.split(RegExp(r'\s+'))
.where((String p) => p.isNotEmpty)
.toList();
if (parts.isEmpty) return "D"; if (parts.isEmpty) return "D";
if (parts.length == 1) { if (parts.length == 1) {
return parts.first.length >= 2 ? parts.first.substring(0, 2).toUpperCase() : parts.first.toUpperCase(); return parts.first.length >= 2
? parts.first.substring(0, 2).toUpperCase()
: parts.first.toUpperCase();
} }
return "${parts.first[0]}${parts.last[0]}".toUpperCase(); return "${parts.first[0]}${parts.last[0]}".toUpperCase();
} }
String _getFirstName(String? name) { String _getFirstName(String? name) {
if (name == null || name.trim().isEmpty) return ""; if (name == null || name.trim().isEmpty) return "";
List<String> parts = name.trim().split(RegExp(r'\s+')).where((String p) => p.isNotEmpty).toList(); List<String> parts =
name
.trim()
.split(RegExp(r'\s+'))
.where((String p) => p.isNotEmpty)
.toList();
if (parts.isEmpty) return ""; if (parts.isEmpty) return "";
return parts.first; return parts.first;
} }
@ -142,7 +166,10 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
if (match != null) { if (match != null) {
try { try {
int millis = int.parse(match.group(1)!); int millis = int.parse(match.group(1)!);
DateTime dtUtc = DateTime.fromMillisecondsSinceEpoch(millis, isUtc: true); DateTime dtUtc = DateTime.fromMillisecondsSinceEpoch(
millis,
isUtc: true,
);
return DateTime(dtUtc.year, dtUtc.month, dtUtc.day); return DateTime(dtUtc.year, dtUtc.month, dtUtc.day);
} catch (_) {} } catch (_) {}
} }
@ -150,7 +177,10 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
if (RegExp(r'^-?\d{10,14}$').hasMatch(s)) { if (RegExp(r'^-?\d{10,14}$').hasMatch(s)) {
try { try {
int millis = int.parse(s); int millis = int.parse(s);
DateTime dtUtc = DateTime.fromMillisecondsSinceEpoch(millis, isUtc: true); DateTime dtUtc = DateTime.fromMillisecondsSinceEpoch(
millis,
isUtc: true,
);
return DateTime(dtUtc.year, dtUtc.month, dtUtc.day); return DateTime(dtUtc.year, dtUtc.month, dtUtc.day);
} catch (_) {} } catch (_) {}
} }
@ -161,11 +191,28 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
// DATE_OF_BIRTH comes back as "M/d/yyyy h:mm:ss a" (e.g. "10/25/1997 12:00:00 AM") - strip // DATE_OF_BIRTH comes back as "M/d/yyyy h:mm:ss a" (e.g. "10/25/1997 12:00:00 AM") - strip
// that clock-time suffix first, since none of the date-only patterns below can match it // that clock-time suffix first, since none of the date-only patterns below can match it
// with it still attached (parseStrict requires the whole string to match the pattern). // with it still attached (parseStrict requires the whole string to match the pattern).
String dateOnly = s.replaceFirst(RegExp(r'\s+\d{1,2}:\d{2}(:\d{2})?\s*(AM|PM)?$', caseSensitive: false), '').trim(); String dateOnly =
s
.replaceFirst(
RegExp(
r'\s+\d{1,2}:\d{2}(:\d{2})?\s*(AM|PM)?$',
caseSensitive: false,
),
'',
)
.trim();
// MM/dd/yyyy before dd/MM/yyyy - the real source is confirmed month-first, so for an // MM/dd/yyyy before dd/MM/yyyy - the real source is confirmed month-first, so for an
// ambiguous case like "5/6/1990" this must resolve to May 6, not be misread as 5 June. // ambiguous case like "5/6/1990" this must resolve to May 6, not be misread as 5 June.
List<String> formats = <String>['yyyy-MM-dd', 'MM/dd/yyyy', 'dd/MM/yyyy', 'dd-MM-yyyy', 'yyyy/MM/dd', 'dd-MMM-yyyy', 'dd MMM yyyy']; List<String> formats = <String>[
'yyyy-MM-dd',
'MM/dd/yyyy',
'dd/MM/yyyy',
'dd-MM-yyyy',
'yyyy/MM/dd',
'dd-MMM-yyyy',
'dd MMM yyyy',
];
for (String fmt in formats) { for (String fmt in formats) {
try { try {
DateTime parsed = DateFormat(fmt, 'en_US').parseStrict(dateOnly); DateTime parsed = DateFormat(fmt, 'en_US').parseStrict(dateOnly);
@ -205,9 +252,20 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
ConfirmBottomSheet.show( ConfirmBottomSheet.show(
context, context,
title: isAllowed ? LocaleKeys.revokeMazayaAccess.tr() : LocaleKeys.allowMazayaAccess.tr(), title:
message: isAllowed ? LocaleKeys.confirmRevokeMazayaAccess.tr(args: <String>[dependentName]) : LocaleKeys.confirmGrantMazayaAccess.tr(args: <String>[dependentName]), isAllowed
icon: isAllowed ? Icons.remove_circle_outline : Icons.check_circle_outline, ? LocaleKeys.revokeMazayaAccess.tr()
: LocaleKeys.allowMazayaAccess.tr(),
message:
isAllowed
? LocaleKeys.confirmRevokeMazayaAccess.tr(
args: <String>[dependentName],
)
: LocaleKeys.confirmGrantMazayaAccess.tr(
args: <String>[dependentName],
),
icon:
isAllowed ? Icons.remove_circle_outline : Icons.check_circle_outline,
iconColor: isAllowed ? const Color(0xFFDC2626) : const Color(0xFF168A89), iconColor: isAllowed ? const Color(0xFFDC2626) : const Color(0xFF168A89),
confirmText: LocaleKeys.yes.tr(), confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(), cancelText: LocaleKeys.no.tr(),
@ -271,7 +329,10 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
// carry identical content, so this is the single source of truth for it. // carry identical content, so this is the single source of truth for it.
String _buildMazayaInviteMessage(String linkToShare) { String _buildMazayaInviteMessage(String linkToShare) {
String? nationalId = _contact?.nATIONALIDENTIFIER?.trim(); String? nationalId = _contact?.nATIONALIDENTIFIER?.trim();
String nationalIdLine = (nationalId == null || nationalId.isEmpty) ? LocaleKeys.nationalIdOrIqamaFallback.tr() : "${LocaleKeys.nationalId.tr()}: ${nationalId.replaceAll('-', '')}"; String nationalIdLine =
(nationalId == null || nationalId.isEmpty)
? LocaleKeys.nationalIdOrIqamaFallback.tr()
: "${LocaleKeys.nationalId.tr()}: ${nationalId.replaceAll('-', '')}";
return "$linkToShare\n\n${LocaleKeys.mazayaInviteInstructions.tr()}\n\n$nationalIdLine\n${LocaleKeys.dateOfBirth.tr()}: ${_formatDobForShare()}"; return "$linkToShare\n\n${LocaleKeys.mazayaInviteInstructions.tr()}\n\n$nationalIdLine\n${LocaleKeys.dateOfBirth.tr()}: ${_formatDobForShare()}";
} }
@ -291,11 +352,16 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
// (including WhatsApp) just attaches the file with nothing to drop. // (including WhatsApp) just attaches the file with nothing to drop.
Future<void> _shareMazayaAccessGuide(bool isAr) async { Future<void> _shareMazayaAccessGuide(bool isAr) async {
try { try {
ByteData bytes = await rootBundle.load(MazayaAccessResultScreen.getShareGuidePdfAssetPath(isAr)); ByteData bytes = await rootBundle.load(
MazayaAccessResultScreen.getShareGuidePdfAssetPath(isAr),
);
Directory tempDir = await getTemporaryDirectory(); Directory tempDir = await getTemporaryDirectory();
String localFileName = isAr ? "mazaya_access_guide_ar.pdf" : "mazaya_access_guide_en.pdf"; String localFileName =
isAr ? "mazaya_access_guide_ar.pdf" : "mazaya_access_guide_en.pdf";
File pdfFile = File('${tempDir.path}/$localFileName'); File pdfFile = File('${tempDir.path}/$localFileName');
await pdfFile.writeAsBytes(bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes)); await pdfFile.writeAsBytes(
bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes),
);
await SharePlus.instance.share(ShareParams(files: [XFile(pdfFile.path)])); await SharePlus.instance.share(ShareParams(files: [XFile(pdfFile.path)]));
} catch (_) {} } catch (_) {}
@ -307,7 +373,11 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
String initials = _getInitials(_contact?.cONTACTNAME); String initials = _getInitials(_contact?.cONTACTNAME);
String fullName = _contact?.cONTACTNAME ?? ""; String fullName = _contact?.cONTACTNAME ?? "";
String firstName = _getFirstName(_contact?.cONTACTNAME); String firstName = _getFirstName(_contact?.cONTACTNAME);
String relation = (_contact?.rELATIONSHIP != null && _contact!.rELATIONSHIP!.trim().isNotEmpty) ? _contact!.rELATIONSHIP!.trim() : (_contact?.cONTACTTYPE?.trim() ?? ""); String relation =
(_contact?.rELATIONSHIP != null &&
_contact!.rELATIONSHIP!.trim().isNotEmpty)
? _contact!.rELATIONSHIP!.trim()
: (_contact?.cONTACTTYPE?.trim() ?? "");
String formattedId = _formatNationalId(_contact?.nATIONALIDENTIFIER); String formattedId = _formatNationalId(_contact?.nATIONALIDENTIFIER);
String formattedDob = _formatDob(dob, _contact?.dATEOFBIRTH); String formattedDob = _formatDob(dob, _contact?.dATEOFBIRTH);
bool isAllowed = _isAccessAllowed; bool isAllowed = _isAccessAllowed;
@ -328,15 +398,39 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
InkWell( InkWell(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
onTap: () => Navigator.pop(context), onTap: () => Navigator.pop(context),
child: const Padding(padding: EdgeInsets.all(8.0), child: Icon(Icons.arrow_back_ios_new_rounded, size: 20, color: Color(0xFF1E293B))), child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.arrow_back_ios_new_rounded,
size: 20,
color: Color(0xFF1E293B),
),
),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
LocaleKeys.dependentDetails.tr().toText20(color: const Color(0xFF1E293B)), LocaleKeys.dependentDetails.tr().toText20(
color: const Color(0xFF1E293B),
),
const Spacer(), const Spacer(),
InkWell( InkWell(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
onTap: () => Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)), onTap:
child: const Padding(padding: EdgeInsets.all(8.0), child: Icon(Icons.home_outlined, size: 24, color: Color(0xFF1E293B))), () => Navigator.popUntil(
context,
ModalRoute.withName(
ApiConsts.isDependetLogin
? AppRoutes.mazayaDiscover
: AppRoutes.dashboard,
),
),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.home_outlined,
size: 24,
color: Color(0xFF1E293B),
),
),
), ),
], ],
), ),
@ -360,21 +454,39 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
child: Container( child: Container(
width: 72, width: 72,
height: 72, height: 72,
decoration: BoxDecoration(color: _recentAction! ? const Color(0xFFE6F7F2) : const Color(0xFFFEECEB), shape: BoxShape.circle), decoration: BoxDecoration(
color:
_recentAction!
? const Color(0xFFE6F7F2)
: const Color(0xFFFEECEB),
shape: BoxShape.circle,
),
child: Center( child: Center(
child: CustomPaint( child: CustomPaint(
size: const Size(36, 36), size: const Size(36, 36),
painter: _ShieldIconPainter(color: _recentAction! ? const Color(0xFF168A89) : const Color(0xFFED1C24), isGranted: _recentAction!), painter: _ShieldIconPainter(
color:
_recentAction!
? const Color(0xFF168A89)
: const Color(0xFFED1C24),
isGranted: _recentAction!,
),
), ),
), ),
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Center( Center(
child: (_recentAction! ? LocaleKeys.mazayaAccessGranted.tr() : LocaleKeys.mazayaAccessRevoked.tr()).toText16( child: (_recentAction!
weight: FontWeight.w500, ? LocaleKeys.mazayaAccessGranted.tr()
color: _recentAction! ? const Color(0xFF168A89) : const Color(0xFFED1C24), : LocaleKeys.mazayaAccessRevoked.tr())
), .toText16(
weight: FontWeight.w500,
color:
_recentAction!
? const Color(0xFF168A89)
: const Color(0xFFED1C24),
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
], ],
@ -383,7 +495,14 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
Container( Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: const Color(0xFFE8F7F2), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFFBCE6D7), width: 1.2)), decoration: BoxDecoration(
color: const Color(0xFFE8F7F2),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: const Color(0xFFBCE6D7),
width: 1.2,
),
),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
@ -391,8 +510,15 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
Container( Container(
width: 52, width: 52,
height: 52, height: 52,
decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle), decoration: const BoxDecoration(
child: Center(child: initials.toText17(color: const Color(0xFF168A89))), color: Colors.white,
shape: BoxShape.circle,
),
child: Center(
child: initials.toText17(
color: const Color(0xFF168A89),
),
),
), ),
const SizedBox(width: 14), const SizedBox(width: 14),
@ -402,14 +528,32 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
fullName.toText16(maxlines: 2, weight: FontWeight.w500, color: const Color(0xFF1E293B)), fullName.toText16(
if (relation.isNotEmpty) ...<Widget>[const SizedBox(height: 3), relation.toText12(maxLine: 1, weight: FontWeight.w500, color: const Color(0xFF168A89))], maxlines: 2,
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
if (relation.isNotEmpty) ...<Widget>[
const SizedBox(height: 3),
relation.toText12(
maxLine: 1,
weight: FontWeight.w500,
color: const Color(0xFF168A89),
),
],
], ],
), ),
), ),
// Verified badge on right - only when NHIC has actually verified this contact // Verified badge on right - only when NHIC has actually verified this contact
if (_isNhicVerified) ...<Widget>[const SizedBox(width: 8), const Icon(Icons.verified_rounded, color: Color(0xFF059669), size: 24)], if (_isNhicVerified) ...<Widget>[
const SizedBox(width: 8),
const Icon(
Icons.verified_rounded,
color: Color(0xFF059669),
size: 24,
),
],
], ],
), ),
), ),
@ -424,33 +568,62 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFF1F5F9)), border: Border.all(color: const Color(0xFFF1F5F9)),
boxShadow: const <BoxShadow>[BoxShadow(color: Color(0x08000000), blurRadius: 8, offset: Offset(0, 2))], boxShadow: const <BoxShadow>[
BoxShadow(
color: Color(0x08000000),
blurRadius: 8,
offset: Offset(0, 2),
),
],
), ),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
LocaleKeys.keyDetails.tr().toText16(weight: FontWeight.w500, color: const Color(0xFF1E293B)), LocaleKeys.keyDetails.tr().toText16(
if (_isNhicVerified) LocaleKeys.verified.tr().toText13(weight: FontWeight.w500, color: const Color(0xFF059669)), weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
if (_isNhicVerified)
LocaleKeys.verified.tr().toText13(
weight: FontWeight.w500,
color: const Color(0xFF059669),
),
], ],
), ),
const SizedBox(height: 14), const SizedBox(height: 14),
const Divider(height: 1, thickness: 1, color: Color(0xFFF1F5F9)), const Divider(
height: 1,
thickness: 1,
color: Color(0xFFF1F5F9),
),
const SizedBox(height: 14), const SizedBox(height: 14),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
LocaleKeys.nationalId.tr().toText13(weight: FontWeight.w400, color: const Color(0xFF64748B)), LocaleKeys.nationalId.tr().toText13(
formattedId.toText13(weight: FontWeight.w500, color: const Color(0xFF1E293B)), weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
formattedId.toText13(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
LocaleKeys.dateOfBirth.tr().toText13(weight: FontWeight.w400, color: const Color(0xFF64748B)), LocaleKeys.dateOfBirth.tr().toText13(
formattedDob.toText13(weight: FontWeight.w500, color: const Color(0xFF1E293B)), weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
formattedDob.toText13(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
], ],
), ),
], ],
@ -460,9 +633,15 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
const SizedBox(height: 24), const SizedBox(height: 24),
// Services & actions section header // Services & actions section header
LocaleKeys.servicesAndActions.tr().toText17(weight: FontWeight.w500, color: const Color(0xFF1E293B)), LocaleKeys.servicesAndActions.tr().toText17(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
const SizedBox(height: 4), const SizedBox(height: 4),
LocaleKeys.startDependentFlowsDesc.tr().toText13(weight: FontWeight.w400, color: const Color(0xFF64748B)), LocaleKeys.startDependentFlowsDesc.tr().toText13(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
const SizedBox(height: 12), const SizedBox(height: 12),
@ -474,7 +653,13 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFF1F5F9)), border: Border.all(color: const Color(0xFFF1F5F9)),
boxShadow: const <BoxShadow>[BoxShadow(color: Color(0x08000000), blurRadius: 8, offset: Offset(0, 2))], boxShadow: const <BoxShadow>[
BoxShadow(
color: Color(0x08000000),
blurRadius: 8,
offset: Offset(0, 2),
),
],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -483,29 +668,66 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
LocaleKeys.mazayaAccess.tr().toText16(weight: FontWeight.w500, color: const Color(0xFF1E293B)), LocaleKeys.mazayaAccess.tr().toText16(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 3), padding: const EdgeInsets.symmetric(
decoration: BoxDecoration(color: isAllowed ? const Color(0xFFECFDF5) : const Color(0xFFFEE2E2), borderRadius: BorderRadius.circular(12)), horizontal: 10,
child: (isAllowed ? LocaleKeys.active.tr() : LocaleKeys.accessOff.tr()).toText12( vertical: 3,
weight: FontWeight.w500,
color: isAllowed ? const Color(0xFF059669) : const Color(0xFFEF4444),
), ),
decoration: BoxDecoration(
color:
isAllowed
? const Color(0xFFECFDF5)
: const Color(0xFFFEE2E2),
borderRadius: BorderRadius.circular(12),
),
child: (isAllowed
? LocaleKeys.active.tr()
: LocaleKeys.accessOff.tr())
.toText12(
weight: FontWeight.w500,
color:
isAllowed
? const Color(0xFF059669)
: const Color(0xFFEF4444),
),
), ),
], ],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
LocaleKeys.allowDependentMazayaOffersDesc.tr(args: <String>[firstName.isNotEmpty ? firstName : fullName]).toText13(weight: FontWeight.w400, color: const Color(0xFF64748B)), LocaleKeys.allowDependentMazayaOffersDesc
.tr(
args: <String>[
firstName.isNotEmpty ? firstName : fullName,
],
)
.toText13(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
const SizedBox(height: 16), const SizedBox(height: 16),
const Divider(height: 1, thickness: 1, color: Color(0xFFF1F5F9)), const Divider(
height: 1,
thickness: 1,
color: Color(0xFFF1F5F9),
),
const SizedBox(height: 14), const SizedBox(height: 14),
// Login method row // Login method row
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
LocaleKeys.loginMethod.tr().toText13(weight: FontWeight.w400, color: const Color(0xFF64748B)), LocaleKeys.loginMethod.tr().toText13(
LocaleKeys.nationalIdAndDob.tr().toText13(weight: FontWeight.w500, color: const Color(0xFF1E293B)), weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
LocaleKeys.nationalIdAndDob.tr().toText13(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
@ -514,8 +736,14 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
LocaleKeys.verification.tr().toText13(weight: FontWeight.w400, color: const Color(0xFF64748B)), LocaleKeys.verification.tr().toText13(
LocaleKeys.sponsorOtp.tr().toText13(weight: FontWeight.w500, color: const Color(0xFF1E293B)), weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
LocaleKeys.sponsorOtp.tr().toText13(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
], ],
), ),
@ -529,19 +757,46 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
height: 48, height: 48,
width: double.infinity, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isAllowed ? const Color(0xFFFEF2F2) : const Color(0xFF22B0A6), color:
isAllowed
? const Color(0xFFFEF2F2)
: const Color(0xFF22B0A6),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: isAllowed ? Border.all(color: const Color(0xFFFECACA), width: 1.2) : null, border:
isAllowed
? Border.all(
color: const Color(0xFFFECACA),
width: 1.2,
)
: null,
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
SizedBox(width: 18, height: 18, child: CustomPaint(painter: _ShieldIconPainter(color: isAllowed ? const Color(0xFFDC2626) : Colors.white, isGranted: !isAllowed))), SizedBox(
const SizedBox(width: 8), width: 18,
(isAllowed ? LocaleKeys.revokeMazayaAccess.tr() : LocaleKeys.allowMazayaAccess.tr()).toText16( height: 18,
weight: FontWeight.w500, child: CustomPaint(
color: isAllowed ? const Color(0xFFDC2626) : Colors.white, painter: _ShieldIconPainter(
color:
isAllowed
? const Color(0xFFDC2626)
: Colors.white,
isGranted: !isAllowed,
),
),
), ),
const SizedBox(width: 8),
(isAllowed
? LocaleKeys.revokeMazayaAccess.tr()
: LocaleKeys.allowMazayaAccess.tr())
.toText16(
weight: FontWeight.w500,
color:
isAllowed
? const Color(0xFFDC2626)
: Colors.white,
),
], ],
), ),
), ),
@ -549,7 +804,15 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
if (isAllowed) ...<Widget>[ if (isAllowed) ...<Widget>[
const SizedBox(height: 12), const SizedBox(height: 12),
Center(child: LocaleKeys.revokingPreventsAccessDesc.tr().toText11(weight: FontWeight.w400, color: const Color(0xFF64748B), isCenter: true)), Center(
child: LocaleKeys.revokingPreventsAccessDesc
.tr()
.toText11(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
isCenter: true,
),
),
], ],
], ],
), ),
@ -570,13 +833,24 @@ class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
child: DefaultButton( child: DefaultButton(
LocaleKeys.shareAccessGuide.tr(), LocaleKeys.shareAccessGuide.tr(),
() => _shareMazayaAccessGuide(isAr), () => _shareMazayaAccessGuide(isAr),
colors: const <Color>[Color(0xFFE8F7F2), Color(0xFFE8F7F2)], colors: const <Color>[
border: Border.all(color: const Color(0xFFBCE6D7), width: 1.2), Color(0xFFE8F7F2),
Color(0xFFE8F7F2),
],
border: Border.all(
color: const Color(0xFFBCE6D7),
width: 1.2,
),
textColor: const Color(0xFF168A89), textColor: const Color(0xFF168A89),
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded(child: DefaultButton(LocaleKeys.shareDownloadLink.tr(), () => _shareMazayaInviteLink(isAr, linkToShare))), Expanded(
child: DefaultButton(
LocaleKeys.shareDownloadLink.tr(),
() => _shareMazayaInviteLink(isAr, linkToShare),
),
),
], ],
).insideContainer, ).insideContainer,
], ],

@ -1,5 +1,4 @@
import 'dart:developer'; import 'dart:developer';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@ -13,6 +12,7 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart';
import 'package:mohem_flutter_app/models/get_employee_contacts.model.dart'; import 'package:mohem_flutter_app/models/get_employee_contacts.model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/ui/mazaya/widgets/mazaya_bottom_nav.dart';
import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart'; import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart';
import 'package:mohem_flutter_app/ui/profile/dependent_details_screen.dart'; import 'package:mohem_flutter_app/ui/profile/dependent_details_screen.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
@ -191,6 +191,9 @@ class _FamilyMembersState extends State<FamilyMembers> {
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFFF8FAFC), backgroundColor: const Color(0xFFF8FAFC),
// Only shown when reached via the Mazaya "My Family" tab - this screen is also opened from
// the regular profile flow (fromMazaya == false), where the Mazaya bottom nav doesn't apply.
bottomNavigationBar: fromMazaya ? const MazayaBottomNav(currentTab: MazayaTab.myFamily) : null,
appBar: PreferredSize( appBar: PreferredSize(
preferredSize: const Size.fromHeight(56), preferredSize: const Size.fromHeight(56),
child: SafeArea( child: SafeArea(
@ -206,13 +209,22 @@ class _FamilyMembersState extends State<FamilyMembers> {
child: const Padding(padding: EdgeInsets.all(8.0), child: Icon(Icons.arrow_back_ios_new_rounded, size: 20, color: Color(0xFF1E293B))), child: const Padding(padding: EdgeInsets.all(8.0), child: Icon(Icons.arrow_back_ios_new_rounded, size: 20, color: Color(0xFF1E293B))),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
(fromMazaya ? LocaleKeys.myDependents.tr() : LocaleKeys.profile_familyDetails.tr()).toText20(isBold: true, color: const Color(0xFF1E293B)), // Same title style as the other Mazaya tabs' headers (Discover/Saved use
// AppBarWidget's title.toText24(isBold: true, color: MyColors.darkTextColor)) -
// matched here since this screen builds its own header instead of AppBarWidget.
fromMazaya
? LocaleKeys.myFamily.tr().toText24(isBold: true, color: MyColors.darkTextColor)
: LocaleKeys.profile_familyDetails.tr().toText20(isBold: true, color: const Color(0xFF1E293B)),
const Spacer(), const Spacer(),
InkWell( // Home button hidden in the Mazaya flow too, matching AppBarWidget's
borderRadius: BorderRadius.circular(20), // showHomeButton: false on the other tabs - MazayaBottomNav's own Discover tab
onTap: () => Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)), // already serves as "home" for this module.
child: const Padding(padding: EdgeInsets.all(8.0), child: Icon(Icons.home_outlined, size: 24, color: Color(0xFF1E293B))), if (!fromMazaya)
), InkWell(
borderRadius: BorderRadius.circular(20),
onTap: () => Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)),
child: const Padding(padding: EdgeInsets.all(8.0), child: Icon(Icons.home_outlined, size: 24, color: Color(0xFF1E293B))),
),
], ],
), ),
), ),
@ -302,7 +314,7 @@ class _FamilyMembersState extends State<FamilyMembers> {
} }
// Same Shimmer.fromColors skeleton-card convention used elsewhere in the app (e.g. // Same Shimmer.fromColors skeleton-card convention used elsewhere in the app (e.g.
// dashboard_screen_for_dependet.dart's retailer/category shimmers), shaped like the // retailers_list_screen.dart's retailer/category shimmers), shaped like the
// real _buildDependentCard layout so the loading state doesn't jump around once it loads. // real _buildDependentCard layout so the loading state doesn't jump around once it loads.
Widget _buildDependentsShimmer() { Widget _buildDependentsShimmer() {
return Shimmer.fromColors( return Shimmer.fromColors(

@ -61,62 +61,58 @@ class _OffersAndDiscountsHomeState extends State<OffersAndDiscountsHome> {
LocaleKeys.browseCategories.tr().toText17().paddingOnly(left: 21, right: 21, top: 21), LocaleKeys.browseCategories.tr().toText17().paddingOnly(left: 21, right: 21, top: 21),
SizedBox( SizedBox(
height: 110.0, height: 110.0,
child: getCategoriesList.isNotEmpty child:
? ListView.separated( getCategoriesList.isNotEmpty
shrinkWrap: true, ? ListView.separated(
physics: const BouncingScrollPhysics(), shrinkWrap: true,
padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), physics: const BouncingScrollPhysics(),
scrollDirection: Axis.horizontal, padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13),
itemBuilder: (cxt, index) { scrollDirection: Axis.horizontal,
return AspectRatio( itemBuilder: (cxt, index) {
aspectRatio: 1 / 1, return AspectRatio(
child: InkWell( aspectRatio: 1 / 1,
onTap: () { child: InkWell(
setState(() { onTap: () {
currentCategoryID = getCategoriesList[index].id!; setState(() {
getCategoryOffersListAPI(); currentCategoryID = getCategoriesList[index].id!;
// getItemsForSaleList.clear(); getCategoryOffersListAPI();
// currentPageNo = 1; // getItemsForSaleList.clear();
// getItemsForSale(currentPageNo, currentCategoryID); // currentPageNo = 1;
}); // getItemsForSale(currentPageNo, currentCategoryID);
}, });
child: Column( },
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Column(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Row( children: [
mainAxisAlignment: MainAxisAlignment.spaceBetween, Row(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ crossAxisAlignment: CrossAxisAlignment.start,
SvgPicture.string( children: [
getCategoriesList[index].content!, SvgPicture.string(getCategoriesList[index].content!, fit: BoxFit.contain, width: 25, height: 25),
fit: BoxFit.contain, currentCategoryID == getCategoriesList[index].id ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(),
width: 25, ],
height: 25, ).expanded,
), AppState().isArabic(context) ? getCategoriesList[index].categoryNameAr!.toText10() : getCategoriesList[index].categoryNameEn!.toText10(),
currentCategoryID == getCategoriesList[index].id ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(), ],
], ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12).expanded.objectContainerView(disablePadding: true),
).expanded, ),
AppState().isArabic(context) ? getCategoriesList[index].categoryNameAr!.toText10() : getCategoriesList[index].categoryNameEn!.toText10() );
], },
).paddingOnly(left: 10, right: 10, bottom: 10, top: 12).expanded.objectContainerView(disablePadding: true), separatorBuilder: (cxt, index) => 12.width,
), itemCount: getCategoriesList.length,
); )
}, : Container(),
separatorBuilder: (cxt, index) => 12.width,
itemCount: getCategoriesList.length,
)
: Container(),
), ),
getOffersList.isNotEmpty getOffersList.isNotEmpty
? GridView( ? GridView(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 162 / 266, crossAxisSpacing: 12, mainAxisSpacing: 12), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 162 / 266, crossAxisSpacing: 12, mainAxisSpacing: 12),
padding: const EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 21), padding: const EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 21),
shrinkWrap: true, shrinkWrap: true,
primary: false, primary: false,
physics: const ScrollPhysics(), physics: const ScrollPhysics(),
children: getItemsForSaleWidgets(), children: getItemsForSaleWidgets(),
) )
: Utils.getNoDataWidget(context).paddingOnly(top: 50), : Utils.getNoDataWidget(context).paddingOnly(top: 50),
], ],
), ),
@ -144,13 +140,7 @@ class _OffersAndDiscountsHomeState extends State<OffersAndDiscountsHome> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
boxShadow: [ boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 26, offset: const Offset(0, -3))],
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -159,16 +149,7 @@ class _OffersAndDiscountsHomeState extends State<OffersAndDiscountsHome> {
Hero( Hero(
tag: "ItemImage" + getOffersList.offersDiscountId.toString()!, tag: "ItemImage" + getOffersList.offersDiscountId.toString()!,
transitionOnUserGestures: true, transitionOnUserGestures: true,
child: AspectRatio( child: AspectRatio(aspectRatio: 118 / 127, child: ClipRRect(borderRadius: BorderRadius.circular(6), child: Image.network(getOffersList.bannerImage ?? "", fit: BoxFit.contain))),
aspectRatio: 118 / 127,
child: ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Image.network(
getOffersList.bannerImage ?? "",
fit: BoxFit.contain,
),
),
),
), ),
AppState().isArabic(context) AppState().isArabic(context)
? getOffersList.titleAr!.toText16(isBold: true, color: const Color(0xff2B353E), maxlines: 1) ? getOffersList.titleAr!.toText16(isBold: true, color: const Color(0xff2B353E), maxlines: 1)
@ -183,10 +164,7 @@ class _OffersAndDiscountsHomeState extends State<OffersAndDiscountsHome> {
// 8.height, // 8.height,
getOffersList.discountDescription!.toText14(isBold: true, maxlines: 1), getOffersList.discountDescription!.toText14(isBold: true, maxlines: 1),
20.height, 20.height,
Row( Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [checkDate(getOffersList.endDate!), SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)]),
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [checkDate(getOffersList.endDate!), SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)],
),
], ],
), ),
), ),

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/classes/colors.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/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
@ -27,14 +28,20 @@ AppBar AppBarWidget(
GestureDetector( GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: Feedback.wrapForTap(() { onTap: Feedback.wrapForTap(() {
(onBackTapped == null ? Navigator.maybePop(context) : onBackTapped()); (onBackTapped == null
? Navigator.maybePop(context)
: onBackTapped());
}, context), }, context),
child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), child: const Icon(
Icons.arrow_back_ios,
color: MyColors.darkIconColor,
),
), ),
if (showLogo) 4.width, if (showLogo) 4.width,
if (showLogo) SvgPicture.asset(logoPath!), if (showLogo) SvgPicture.asset(logoPath!),
4.width, 4.width,
if (!showLogo)title.toText24(color: MyColors.darkTextColor, isBold: true).expanded, if (!showLogo)
title.toText24(color: MyColors.darkTextColor, isBold: true).expanded,
], ],
), ),
centerTitle: false, centerTitle: false,
@ -44,7 +51,18 @@ AppBar AppBarWidget(
if (showHomeButton) if (showHomeButton)
IconButton( IconButton(
onPressed: () { onPressed: () {
onHomeTapped == null ? Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)) : onHomeTapped(); if (onHomeTapped != null) {
onHomeTapped();
} else {
Navigator.popUntil(
context,
ModalRoute.withName(
ApiConsts.isDependetLogin
? AppRoutes.mazayaDiscover
: AppRoutes.dashboard,
),
);
}
}, },
icon: const Icon(Icons.home, color: MyColors.darkIconColor), icon: const Icon(Icons.home, color: MyColors.darkIconColor),
), ),
@ -53,7 +71,10 @@ AppBar AppBarWidget(
onPressed: () { onPressed: () {
Navigator.pushNamed(context, AppRoutes.worklistSettings); Navigator.pushNamed(context, AppRoutes.worklistSettings);
}, },
icon: const Icon(Icons.settings_rounded, color: MyColors.darkIconColor), icon: const Icon(
Icons.settings_rounded,
color: MyColors.darkIconColor,
),
), ),
if (showMemberButton) if (showMemberButton)
IconButton( IconButton(

@ -50,7 +50,7 @@ class _AppInAppWebViewScreenState extends State<AppInAppWebViewScreen> {
void _onHomeTapped() { void _onHomeTapped() {
if (ApiConsts.isDependetLogin) { if (ApiConsts.isDependetLogin) {
Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboardScreenForDependet)); Navigator.popUntil(context, ModalRoute.withName(AppRoutes.mazayaDiscover));
} else { } else {
Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)); Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard));
} }

Loading…
Cancel
Save