From f6865959e6fc398596e55a12604e8f534c50a456 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Sun, 18 Oct 2026 12:30:22 +0300 Subject: [PATCH] no message --- assets/images/mazaya.png | Bin 0 -> 794 bytes assets/images/mohem.png | Bin 0 -> 1052 bytes assets/langs/ar-SA.json | 4 +- assets/langs/en-US.json | 5 +- lib/api/login_api_client.dart | 1 + lib/api/offers_and_discounts_api_client.dart | 12 ++ .../wala_plus_retailer_model.dart | 70 ++++++- lib/models/privilege_list_model.dart | 15 +- lib/ui/landing/dashboard_screen.dart | 194 +++++++++--------- .../dashboard_screen_for_dependet.dart | 97 +++++---- .../wala_plus_retailer_details_screen.dart | 165 ++++++++++++--- lib/ui/login/login_screen.dart | 6 +- 12 files changed, 400 insertions(+), 169 deletions(-) create mode 100644 assets/images/mazaya.png create mode 100644 assets/images/mohem.png diff --git a/assets/images/mazaya.png b/assets/images/mazaya.png new file mode 100644 index 0000000000000000000000000000000000000000..b750724d379a55fddc2f674413390ca62d62aae6 GIT binary patch literal 794 zcmV+#1LgdQP)xq=?p=1z;A&3DKFn56mCRU?*LGQ-Q;z>gsegQe0!hJ|| zVL)oGQY?O}M^@YdfirQHWAW^$iu2e-VBv7*3)VE+t*0C$OTv&vMYV-%5Z?R% z?EE>)5A1=4C`l_}B){dFuzvRuwDUh~vB_IEVP@?G7MlfS;Vf|QpsB^%J+a{Oz0(!% z$`ioUB`n^z#)6 zbk~E*b4Ga=jGbLujQx%!q>Tu@fVLSWro~46c1udWfP1>{_T-n2pKq>jfGCy*12{Fg zcn3oGrh`C^AdH;~oK~&?svuq=0-5k!PGz9jEy&}h-F)BMb21#Ru!|h<1@wW-ISglE9Afa1u4WOu9sL~7 z)l6LHcuUWzBA|S zdd|^ZG$>zo;A4mRX1?$DU1rWQAw&cdI8+h^6F4T+c@iKA@ZSLFs>kMBaEne&bdb|I zh1raeM3*#EV~M~Q9KexBCy?t2p=wbTyxAo&D@>U1BINPtlO|kh_zYF$IK8eAJB#Nb zh3T6)=eX9?hzoD(;39q1!Xi{JT}_aR0VnYNcL&baU4bHkD4$b|tqUu0;6V-2Q~k(r z2H?O?>?yk#gNp^AMYH?id(?huN6usya?_o-`1TV{KCh3Z@K$p(Zh!oM9FGTCX&%(I ze!=l4cPLZ3zP;}k&c3(-PpT7BT~EIm+tuO4mv0W@}YBipUQp>UFj6oD`{dGpcX@5S5pADHQ$1V;+#8JE8* z?U_Ag8UYQVrKg2T1r8HKqk;{)jN0{)>8ifZm6GfmY23`d8{N>EWy zAfv0edX9R*Wr3_HC_?4@a%pFO?J)|%LjWA95|AT<`eOT%1My1`O`V78xF$bG&ZP** zvM91dI^>Sx6^33ILVDAk-M4C|JggW-wDT$UJB>KkSle8hmnjVoQ&=JAPRlk>#Aqfc z`b=t4Zk!v2920;xxX_!C89>bDNEil3;frpFHFKiJDuGUq-kfH;7iNOc>STvtbZeNA zB{p6FRZ$GX28`~*qBnUOkp*U)XBJp7c7Vk*GPJLVwRR>C`BSG;d|WRNgYE4A(i~2N zcnFE2PY6(wmyOQAK+J&MNG3hSU4EY_L&B0Gl^&sBIA|&n#@B?}>u_l0;^7q93}~)S ztlNTcBpkQH50)Jyh(Y5zictyT0%#7amoC7uHKphd1f&c^tWWK-yaF69uS8!kfZjnL z1}S3m$Z}_4*YdsS5BU%n@?j_v80p9U655CP`){8A&CtFZj49&-_}3%(r!)zW1o#6k W7F91V&nQU%0000 && response.containsKey('name')) { model = WalaPlusRetailerModel.fromJson(response); } if (model != null) { if (response is Map) { + if (model.website == null || model.website!.isEmpty || model.website == "null") { + String? web = (response['website'] ?? response['merchant_website'] ?? response['website_url'] ?? response['url'] ?? response['merchant_url'])?.toString(); + if (web != null && web.trim().isNotEmpty && web != "null") { + model.website = web.trim(); + } + } if ((model.offers == null || model.offers!.isEmpty) && (response.containsKey('offers') || response.containsKey('Offers'))) { model.offers = WalaPlusRetailerModel.parseOffersList(response['offers'] ?? response['Offers']); } diff --git a/lib/models/offers_and_discounts/wala_plus_retailer_model.dart b/lib/models/offers_and_discounts/wala_plus_retailer_model.dart index d2186b8..0c9e7c1 100644 --- a/lib/models/offers_and_discounts/wala_plus_retailer_model.dart +++ b/lib/models/offers_and_discounts/wala_plus_retailer_model.dart @@ -78,11 +78,17 @@ class WalaPlusRetailersByIDResponse { var model = WalaPlusRetailerModel.fromJson(r); if (model.locations == null || model.locations!.isEmpty) model.locations = locList; if (model.offers == null || model.offers!.isEmpty) model.offers = offList; + if ((model.website == null || model.website!.isEmpty || model.website == "null") && (json['website'] != null || json['merchant_website'] != null || json['url'] != null)) { + model.website = (json['website'] ?? json['merchant_website'] ?? json['url'])?.toString(); + } retList.add(model); } else if (r is Map) { var model = WalaPlusRetailerModel.fromJson(Map.from(r)); if (model.locations == null || model.locations!.isEmpty) model.locations = locList; if (model.offers == null || model.offers!.isEmpty) model.offers = offList; + if ((model.website == null || model.website!.isEmpty || model.website == "null") && (json['website'] != null || json['merchant_website'] != null || json['url'] != null)) { + model.website = (json['website'] ?? json['merchant_website'] ?? json['url'])?.toString(); + } retList.add(model); } } @@ -90,6 +96,9 @@ class WalaPlusRetailersByIDResponse { var model = WalaPlusRetailerModel.fromJson(Map.from(retData)); if (model.locations == null || model.locations!.isEmpty) model.locations = locList; if (model.offers == null || model.offers!.isEmpty) model.offers = offList; + if ((model.website == null || model.website!.isEmpty || model.website == "null") && (json['website'] != null || json['merchant_website'] != null || json['url'] != null)) { + model.website = (json['website'] ?? json['merchant_website'] ?? json['url'])?.toString(); + } retList.add(model); } @@ -164,6 +173,7 @@ class WalaPlusRetailerModel { String? phone; String? countryNameAr; String? countryNameEn; + bool? isFavorite; WalaPlusRetailerModel({ this.id, @@ -177,6 +187,7 @@ class WalaPlusRetailerModel { this.logo, this.logoThumb, this.website, + this.isFavorite, this.type, this.usage, this.weight, @@ -314,6 +325,39 @@ class WalaPlusRetailerModel { } } + String? foundWebsite = targetJson['website']?.toString() ?? + targetJson['merchant_website']?.toString() ?? + targetJson['merchantWebsite']?.toString() ?? + targetJson['website_url']?.toString() ?? + targetJson['websiteUrl']?.toString() ?? + targetJson['web_site']?.toString() ?? + targetJson['url']?.toString() ?? + targetJson['merchant_url']?.toString() ?? + targetJson['merchantUrl']?.toString() ?? + targetJson['merchantLink']?.toString() ?? + targetJson['merchant_link']?.toString() ?? + targetJson['link']?.toString() ?? + contactObj?.website; + + if (foundWebsite == null || foundWebsite.trim().isEmpty || foundWebsite == "null") { + if (targetJson['merchant'] is Map) { + var m = targetJson['merchant']; + foundWebsite = (m['website'] ?? m['merchant_website'] ?? m['url'] ?? m['link'])?.toString(); + } + } + + if (foundWebsite == null || foundWebsite.trim().isEmpty || foundWebsite == "null") { + for (var entry in targetJson.entries) { + String k = entry.key.toLowerCase(); + if ((k.contains('website') || k.contains('web_site') || k == 'url' || k.contains('merchant_url')) && !k.contains('image') && !k.contains('logo')) { + if (entry.value != null && entry.value.toString().trim().isNotEmpty && entry.value.toString() != "null") { + foundWebsite = entry.value.toString().trim(); + break; + } + } + } + } + return WalaPlusRetailerModel( id: targetJson['id']?.toString() ?? @@ -330,7 +374,12 @@ class WalaPlusRetailerModel { imageThumb: targetJson['image_thumb']?.toString(), logo: targetJson['logo']?.toString(), logoThumb: targetJson['logo_thumb']?.toString(), - website: targetJson['website']?.toString(), + website: foundWebsite, + isFavorite: targetJson['is_favorite'] == true || + targetJson['is_favorite'] == 1 || + targetJson['is_favorite'] == 'true' || + targetJson['isFavorite'] == true || + targetJson['favorite'] == true, type: targetJson['type']?.toString(), usage: targetJson['usage'] is String ? decodeUnicodeString(targetJson['usage']) : targetJson['usage'], weight: targetJson['weight'], @@ -438,6 +487,7 @@ class WalaPlusRetailerModel { data['phone'] = phone; data['country_name_ar'] = countryNameAr; data['country_name_en'] = countryNameEn; + data['is_favorite'] = isFavorite; if (offers != null) { data['offers'] = offers!.map((v) => v.toJson()).toList(); } @@ -463,12 +513,12 @@ class WalaPlusRetailerModel { } String? get resolvedWebsite { - if (contact?.website != null && contact!.website!.trim().isNotEmpty && contact!.website != "null") { - return contact!.website!.trim(); - } if (website != null && website!.trim().isNotEmpty && website != "null") { return website!.trim(); } + if (contact?.website != null && contact!.website!.trim().isNotEmpty && contact!.website != "null") { + return contact!.website!.trim(); + } return null; } @@ -521,6 +571,18 @@ class WalaPlusRetailerModel { return distinctCities.first; } + List getDistinctCities({bool isAr = false}) { + if (locations == null || locations!.isEmpty) return []; + Set distinctCities = {}; + for (var loc in locations!) { + String? resolved = _resolveLocationCity(loc, isAr); + if (resolved != null && resolved.trim().isNotEmpty && resolved != "null") { + distinctCities.add(resolved.trim()); + } + } + return distinctCities.toList(); + } + String get displayCity => getDisplayCity(isAr: false); String get displayUsage { diff --git a/lib/models/privilege_list_model.dart b/lib/models/privilege_list_model.dart index 3d39118..4c49041 100644 --- a/lib/models/privilege_list_model.dart +++ b/lib/models/privilege_list_model.dart @@ -7,20 +7,23 @@ class PrivilegeListModel { int? iD; String? serviceName; bool? previlege; + bool? previlegeHMG; - PrivilegeListModel({this.iD, this.serviceName, this.previlege}); + PrivilegeListModel({this.iD, this.serviceName, this.previlege, this.previlegeHMG}); PrivilegeListModel.fromJson(Map json) { iD = json['ID']; serviceName = json['ServiceName']; - previlege = json['Previlege']; + previlege = json['Previlege'] ?? json['previlege'] ?? json['Privilege'] ?? json['privilege']; + previlegeHMG = json['Previlege_HMG'] ?? json['PrevilegeHMG'] ?? json['previlege_HMG'] ?? json['previlegeHMG'] ?? json['Privilege_HMG'] ?? json['PrivilegeHMG']; } Map toJson() { - Map data = new Map(); - data['ID'] = this.iD; - data['ServiceName'] = this.serviceName; - data['Previlege'] = this.previlege; + Map data = {}; + data['ID'] = iD; + data['ServiceName'] = serviceName; + data['Previlege'] = previlege; + data['Previlege_HMG'] = previlegeHMG; return data; } diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 94a49da..17ee0d0 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -29,6 +29,7 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_banner.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/mark_attendance_widget.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/offers_shimmer_widget.dart'; import 'package:provider/provider.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; @@ -62,6 +63,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb void initState() { WidgetsBinding.instance.addObserver(this); super.initState(); + isDisplayMazaya = checkIfPrivilegedForMazaya(); cProvider = Provider.of(context, listen: false); scheduleMicrotask(() { data = Provider.of(context, listen: false); @@ -254,6 +256,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb @override Widget build(BuildContext context) { + isDisplayMazaya = checkIfPrivilegedForMazaya(); return SafeArea( bottom: Platform.isAndroid ? true : false, top: false, @@ -471,101 +474,98 @@ class _DashboardScreenState extends State with WidgetsBindingOb ), ), ).paddingOnly(left: 21, right: 21, top: 0, bottom: 21), - ], - - // Offers & Discounts commented out as requested - /* - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - LocaleKeys.offers.tr().toText12(), - Row( - children: [ - LocaleKeys.discounts.tr().toText24(isBold: true), - 6.width, - Container( - padding: const EdgeInsets.only(left: 8, right: 8), - decoration: BoxDecoration(color: MyColors.yellowColor, borderRadius: BorderRadius.circular(10)), - child: LocaleKeys.newString.tr().toText10(isBold: true), - ), - ], - ), - ], + ] else ...[ + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + LocaleKeys.offers.tr().toText12(), + Row( + children: [ + LocaleKeys.discounts.tr().toText24(isBold: true), + 6.width, + Container( + padding: const EdgeInsets.only(left: 8, right: 8), + decoration: BoxDecoration(color: MyColors.yellowColor, borderRadius: BorderRadius.circular(10)), + child: LocaleKeys.newString.tr().toText10(isBold: true), + ), + ], + ), + ], + ), ), - ), - LocaleKeys.viewAllOffers.tr().toText12(isUnderLine: true).onPress(() { - Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); - }), - ], - ).paddingOnly(left: 21, right: 21), - Consumer( - builder: (BuildContext context, DashboardProviderModel model, Widget? child) { - return SizedBox( - height: 103 + 33, - child: ListView.separated( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.only(left: 21, right: 21, top: 13), - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext cxt, int index) { - return model.isOffersLoading - ? const OffersShimmerWidget() - : InkWell( - onTap: () { - navigateToDetails(data.getOffersList[index]); - }, - child: SizedBox( - width: 73, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 73, - height: 73, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(100)), - border: Border.all(color: MyColors.lightGreyE3Color, width: 1), - ), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(50)), - child: Hero( - tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!, - transitionOnUserGestures: true, - child: Image.network(data.getOffersList[index].logo ?? "", fit: BoxFit.contain), + LocaleKeys.viewAllOffers.tr().toText12(isUnderLine: true).onPress(() { + Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); + }), + ], + ).paddingOnly(left: 21, right: 21), + Consumer( + builder: (BuildContext context, DashboardProviderModel model, Widget? child) { + return SizedBox( + height: 103 + 33, + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext cxt, int index) { + return model.isOffersLoading + ? const OffersShimmerWidget() + : InkWell( + onTap: () { + navigateToDetails(data.getOffersList[index]); + }, + child: SizedBox( + width: 73, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 73, + height: 73, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(100)), + border: Border.all(color: MyColors.lightGreyE3Color, width: 1), + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(50)), + child: Hero( + tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString(), + transitionOnUserGestures: true, + child: Image.network(data.getOffersList[index].logo ?? "", fit: BoxFit.contain), + ), + ), + ), + 4.height, + Expanded( + child: + AppState().isArabic(context) + ? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1) + : data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1), + ), + ], ), ), - ), - 4.height, - Expanded( - child: - AppState().isArabic(context) - ? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1) - : data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1), - ), - ], - ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 8.width, - itemCount: 9, - ), - ); - }, - ), - ], - ), - */ + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 8.width, + itemCount: model.isOffersLoading ? 9 : (data.getOffersList.length > 9 ? 9 : data.getOffersList.length), + ), + ); + }, + ), + ], + ), + ], Container( width: double.infinity, padding: const EdgeInsets.only(top: 31), @@ -774,4 +774,14 @@ class _DashboardScreenState extends State with WidgetsBindingOb } return false; } + + bool checkIfPrivilegedForMazaya() { + if (AppState().privilegeListModel == null) return false; + for (PrivilegeListModel element in AppState().privilegeListModel!) { + if (element.serviceName?.toLowerCase() == "mazayaeligibility") { + return (element.previlege ?? false) || (element.previlegeHMG ?? false); + } + } + return false; + } } diff --git a/lib/ui/landing/dashboard_screen_for_dependet.dart b/lib/ui/landing/dashboard_screen_for_dependet.dart index 37f838c..95f9271 100644 --- a/lib/ui/landing/dashboard_screen_for_dependet.dart +++ b/lib/ui/landing/dashboard_screen_for_dependet.dart @@ -443,16 +443,22 @@ class _DashboardScreenForDependetState extends State } Widget _buildLanguageIconButton(bool isAr) { - return IconButton( - icon: const Icon(Icons.language_rounded, size: 24, color: Color(0xFF1E293B)), - tooltip: isAr ? 'English' : 'العربية', - onPressed: () { - if (isAr) { - _changeLanguage(const Locale("en", "US")); - } else { - _changeLanguage(const Locale("ar", "SA")); - } - }, + return Tooltip( + message: isAr ? 'English' : 'العربية', + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () { + if (isAr) { + _changeLanguage(const Locale("en", "US")); + } else { + _changeLanguage(const Locale("ar", "SA")); + } + }, + child: const Padding( + padding: EdgeInsets.all(3), + child: Icon(Icons.language_rounded, size: 24, color: Color(0xFF1E293B)), + ), + ), ); } @@ -486,9 +492,9 @@ class _DashboardScreenForDependetState extends State _performLogout(); } }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), - child: Icon(isAr ? Icons.arrow_forward_ios_rounded : Icons.arrow_back_ios_new_rounded, size: 20, color: const Color(0xFF1E293B)), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 8), + child: Icon(Icons.arrow_back_ios_new_rounded, size: 20, color: Color(0xFF1E293B)), ), ), const SizedBox(width: 8), @@ -499,37 +505,56 @@ class _DashboardScreenForDependetState extends State Row( mainAxisSize: MainAxisSize.min, children: [ + if (ApiConsts.isDependetLogin) _buildLanguageIconButton(isAr), if (!ApiConsts.isDependetLogin) ...[ - IconButton( - icon: Image.asset( - 'assets/icons/ic_add_dependents.png', - width: 24, - height: 24, - color: const Color(0xFF1E293B), - errorBuilder: - (context, error, stackTrace) => - SvgPicture.asset('assets/icons/ic_dependents_top.svg', width: 24, height: 24, colorFilter: const ColorFilter.mode(Color(0xFF1E293B), BlendMode.srcIn)), + const SizedBox(width: 4), + Tooltip( + message: LocaleKeys.addDependents.tr(), + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () { + Navigator.pushNamed(context, AppRoutes.familyMembers, arguments: {'fromMazaya': true}); + }, + child: Padding( + padding: const EdgeInsets.all(3), + child: Image.asset( + 'assets/icons/ic_add_dependents.png', + width: 24, + height: 24, + color: const Color(0xFF1E293B), + errorBuilder: + (context, error, stackTrace) => + SvgPicture.asset('assets/icons/ic_dependents_top.svg', width: 24, height: 24, colorFilter: const ColorFilter.mode(Color(0xFF1E293B), BlendMode.srcIn)), + ), + ), ), - tooltip: LocaleKeys.addDependents.tr(), - onPressed: () { - Navigator.pushNamed(context, AppRoutes.familyMembers, arguments: {'fromMazaya': true}); - }, ), ], - IconButton( - icon: const Icon(Icons.home_rounded, size: 26, color: Color(0xFF1E293B)), - onPressed: () { - if (ApiConsts.isDependetLogin) { - _onRefresh(); - } else { - Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)); - } - }, + const SizedBox(width: 4), + Tooltip( + message: LocaleKeys.favorite.tr(), + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () { + // Favorite + }, + child: const Padding( + padding: EdgeInsets.all(3), + child: Icon(Icons.favorite_border_rounded, size: 24, color: Color(0xFF1E293B)), + ), + ), ), if (ApiConsts.isDependetLogin) ...[ const SizedBox(width: 4), - InkWell(onTap: _performLogout, child: SvgPicture.asset('assets/images/drawer/logout.svg', height: 20, width: 20)), + InkWell( + borderRadius: BorderRadius.circular(8), + onTap: _performLogout, + child: Padding( + padding: const EdgeInsets.all(3), + child: SvgPicture.asset('assets/images/drawer/logout.svg', height: 20, width: 20), + ), + ), ], ], ), diff --git a/lib/ui/landing/wala_plus_retailer_details_screen.dart b/lib/ui/landing/wala_plus_retailer_details_screen.dart index de5db02..7000b24 100644 --- a/lib/ui/landing/wala_plus_retailer_details_screen.dart +++ b/lib/ui/landing/wala_plus_retailer_details_screen.dart @@ -41,12 +41,14 @@ class _WalaPlusRetailerDetailsScreenState extends State _openMerchantWebsite() async { + String? web = _retailer?.resolvedWebsite; + if (web == null || web.trim().isEmpty || web == "null") { + Utils.showToast(LocaleKeys.merchantWebsiteNotAvailable.tr()); + return; + } + String url = web.trim(); + try { + Uri uri = Uri.parse(url); + if (!uri.hasScheme) { + uri = Uri.parse("https://$url"); + } + bool launched = await launchUrl(uri, mode: LaunchMode.externalApplication); + if (!launched) { + await launchUrl(uri); + } + } catch (e) { + debugPrint("Error opening merchant website with external application: $e"); + try { + await Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => AppInAppWebViewScreen( + url: url.startsWith("http") ? url : "https://$url", + title: _retailer?.name ?? LocaleKeys.visitMerchantWebsite.tr(), + ), + ), + ); + } catch (err) { + debugPrint("Error opening merchant website in webview: $err"); + Utils.showToast(url); + } + } + } + Future _openInAppBrowser(String url, {String? offerType}) async { String title = _retailer?.name ?? LocaleKeys.offerDetails.tr(); // Offer types seen so far: "promo", "discount", "unique_promo", "promo_unique_qr" (and @@ -497,38 +542,86 @@ class _WalaPlusRetailerDetailsScreenState extends State Container(color: const Color(0xFFF3F4F6), child: const Icon(Icons.broken_image, size: 48, color: Color(0xFF9CA3AF))), - ) - : Container(color: const Color(0xFFF3F4F6), child: const Icon(Icons.image_outlined, size: 48, color: Color(0xFF9CA3AF))), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: _openMerchantWebsite, + child: SizedBox( + height: 190, + width: double.infinity, + child: + (_retailer?.displayImage != null && _retailer!.displayImage!.isNotEmpty) + ? Image.network( + _retailer!.displayImage!, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => Container(color: const Color(0xFFF3F4F6), child: const Icon(Icons.broken_image, size: 48, color: Color(0xFF9CA3AF))), + ) + : Container(color: const Color(0xFFF3F4F6), child: const Icon(Icons.image_outlined, size: 48, color: Color(0xFF9CA3AF))), + ), + ), ), ), 14.height, // 2. Brand Strip (Human Resources - Retailer Logo - Dr Sulaiman Al Habib) _buildBrandStrip(isAr), - 18.height, + 16.height, - // 3. Retailer Name (Centered, Bold) & Offer Title Subtitle - (_retailer?.name ?? "").toText22(isBold: true, isCentered: false, color: const Color(0xFF1E293B)), - // if (_retailer?.offerTitle != null && _retailer!.offerTitle!.trim().isNotEmpty && _retailer!.offerTitle!.trim() != _retailer!.name?.trim()) ...[ - // 6.height, - // Center(child: _cleanHtml(_retailer!.offerTitle!).toText16(isCentered: true, color: const Color(0xFF168A89))), - // ], - 18.height, + // 3. Retailer Name & Favorite Button + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: (_retailer?.name ?? "").toText22(isBold: true, isCentered: false, color: const Color(0xFF1E293B)), + ), + const SizedBox(width: 12), + Container( + width: 44, + height: 44, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Color(0xFFF1F5F9), + ), + child: IconButton( + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + icon: Icon( + _isFavorite ? Icons.favorite_rounded : Icons.favorite_border_rounded, + size: 22, + color: _isFavorite ? const Color(0xFFE11D48) : const Color(0xFF1E293B), + ), + tooltip: LocaleKeys.favorite.tr(), + onPressed: () { + setState(() { + _isFavorite = !_isFavorite; + _retailer?.isFavorite = _isFavorite; + }); + }, + ), + ), + ], + ), + 12.height, - // 4. 3 Info Badges Row (Store Type, Validity, City) + // 4. Info Badges Row (Store Type, City Chips) _buildInfoBadgesRow(isAr), + + // 5. Visit Merchant Website Link + if (_retailer?.resolvedWebsite != null && _retailer!.resolvedWebsite!.trim().isNotEmpty) ...[ + 14.height, + InkWell( + onTap: _openMerchantWebsite, + child: LocaleKeys.visitMerchantWebsite.tr().toText14( + color: const Color(0xFF0D9488), + isUnderLine: true, + weight: FontWeight.w600, + ), + ), + ], 10.height, // 5. Description Section (parsed as HTML) @@ -609,18 +702,40 @@ class _WalaPlusRetailerDetailsScreenState extends State offerTypes = _retailer?.normalizedTypes ?? []; + List cities = _retailer?.getDistinctCities(isAr: isAr) ?? []; return Wrap( - spacing: 12, - runSpacing: 10, + spacing: 8, + runSpacing: 8, children: [ // One Type Chip per distinct type - Online (green) and/or In Store (blue). for (String offerType in offerTypes) InfoChipWidget( - text: offerType == 'online' ? LocaleKeys.online.tr() : (offerType == 'instore' ? LocaleKeys.inStore.tr() : (offerType.isNotEmpty ? (offerType[0].toUpperCase() + offerType.substring(1)) : offerType)), - icon: offerType == 'online' ? Icons.language_rounded : Icons.storefront_rounded, + text: offerType == 'online' + ? LocaleKeys.online.tr() + : (offerType == 'instore' + ? LocaleKeys.inStore.tr() + : (offerType.isNotEmpty ? (offerType[0].toUpperCase() + offerType.substring(1)) : offerType)), + icon: offerType == 'online' ? Icons.language_rounded : Icons.storefront_outlined, backgroundColor: offerType == 'online' ? const Color(0xFFE6F7F0) : const Color(0xFFEFF6FF), textColor: offerType == 'online' ? const Color(0xFF00A651) : const Color(0xFF2563EB), + borderColor: offerType == 'online' ? const Color(0xFF00A651) : const Color(0xFF3B82F6), + horizontalPadding: 10, + verticalPadding: 5, + borderRadius: 12, + iconSize: 14, + ), + for (String city in cities) + InfoChipWidget( + text: city, + icon: Icons.location_on_outlined, + backgroundColor: const Color(0xFFF8FAFC), + textColor: const Color(0xFF475569), + borderColor: const Color(0xFFCBD5E1), + horizontalPadding: 10, + verticalPadding: 5, + borderRadius: 12, + iconSize: 14, ), ], ); diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 5d5dc76..9b94420 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -542,8 +542,8 @@ class _LoginScreenState extends State { Widget _buildDependentCtaCard(BuildContext context) { return _buildCtaCard( context: context, - isSVG: true, - iconPath: 'assets/icons/mazaya_brand.svg', + isSVG: false, + iconPath: 'assets/images/mazaya.png', title: LocaleKeys.joinMazaya.tr(), subtitle: LocaleKeys.joinMazayaSubtitle.tr(), onTap: () => _switchToMode(MemberType.dependent), @@ -555,7 +555,7 @@ class _LoginScreenState extends State { return _buildCtaCard( context: context, - iconPath: channel == 31 ? 'assets/images/logos/mohem_logo.png' : 'assets/images/logos/hmg_mohem_logo.png', + iconPath: 'assets/images/mohem.png', title: LocaleKeys.continueAsEmployee.tr(), subtitle: LocaleKeys.continueAsEmployeeSubtitle.tr(), onTap: () => _switchToMode(MemberType.employee),