wala_plus_with_dialog_removal
FaizHashmi 2 weeks ago
parent 37303458bf
commit 62058d6dd9

@ -4,12 +4,12 @@ class ApiConsts {
// static String baseUrl = "http://10.201.214.101:2027"; // Local server
// static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server
static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
// static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
// static String baseUrl = "http://10.201.204.101:2024";
// static String baseUrl = "https://webservices.hmg.com"; // PreProd
// static String baseUrl = "https://hmgwebservices.com"; // Live server
// static String baseUrl = "https://mohemm.hmg.com"; // New Live server
static String baseUrl = "https://mohemm.hmg.com"; // New Live server
static String ssoBaseUrl = "https://sso.hmg.com"; // New Live server
//
// static String baseUrl = "https://uat.hmgwebservices.com"; //

@ -315,7 +315,9 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
String? queryParam = searchQuery.trim().isNotEmpty ? searchQuery.trim() : null;
int? radiusParam = _radiusFromFilterDistance(_filterDistance);
int? minDiscountParam = _filterMinDiscount == 'any' ? null : int.tryParse(_filterMinDiscount);
print("Fetching WalaPlus retailers: category='$catParam', offerType='$offerTypeParam', query='$queryParam', sortBy='$_filterSortBy', radius=$radiusParam, minDiscount=$minDiscountParam, page=1, per_page=$pageSize");
print(
"Fetching WalaPlus retailers: category='$catParam', offerType='$offerTypeParam', query='$queryParam', sortBy='$_filterSortBy', radius=$radiusParam, minDiscount=$minDiscountParam, page=1, per_page=$pageSize",
);
String? userRef = isFavoriteOnly ? _getUserReferenceForFavorites() : null;
print("Fetching WalaPlus retailers: category='$catParam', offerType='$offerTypeParam', query='$queryParam', userReference='$userRef', page=1, per_page=$pageSize");
WalaPlusRetailersResponse? response = await OffersAndDiscountsApiClient().getWalaPlusRetailers(
@ -606,10 +608,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
_changeLanguage(const Locale("ar", "SA"));
}
},
child: const Padding(
padding: EdgeInsets.all(3),
child: Icon(Icons.language_rounded, size: 24, color: Color(0xFF1E293B)),
),
child: const Padding(padding: EdgeInsets.all(3), child: Icon(Icons.language_rounded, size: 24, color: Color(0xFF1E293B))),
),
);
}
@ -644,10 +643,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
_performLogout();
}
},
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 8),
child: Icon(Icons.arrow_back_ios_new_rounded, size: 20, color: 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),
],
@ -657,8 +653,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
Row(
mainAxisSize: MainAxisSize.min,
children: [
if (ApiConsts.isDependetLogin)
_buildLanguageIconButton(isAr),
if (ApiConsts.isDependetLogin) _buildLanguageIconButton(isAr),
if (!ApiConsts.isDependetLogin) ...[
const SizedBox(width: 4),
Tooltip(
@ -670,12 +665,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
},
child: Padding(
padding: const EdgeInsets.all(3),
child: SvgPicture.asset(
'assets/icons/ic_dependents_top.svg',
width: 24,
height: 24,
colorFilter: const ColorFilter.mode(Color(0xFF1E293B), BlendMode.srcIn),
),
child: SvgPicture.asset('assets/icons/ic_dependents_top.svg', width: 24, height: 24, colorFilter: const ColorFilter.mode(Color(0xFF1E293B), BlendMode.srcIn)),
),
),
),
@ -693,11 +683,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
},
child: Padding(
padding: const EdgeInsets.all(3),
child: Icon(
isFavoriteOnly ? Icons.favorite_rounded : Icons.favorite_border_rounded,
size: 24,
color: isFavoriteOnly ? const Color(0xFFE11D48) : const Color(0xFF1E293B),
),
child: Icon(isFavoriteOnly ? Icons.favorite_rounded : Icons.favorite_border_rounded, size: 24, color: isFavoriteOnly ? const Color(0xFFE11D48) : const Color(0xFF1E293B)),
),
),
),
@ -706,10 +692,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
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),
),
child: Padding(padding: const EdgeInsets.all(3), child: SvgPicture.asset('assets/images/drawer/logout.svg', height: 20, width: 20)),
),
],
],
@ -937,50 +920,49 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
Expanded(
child:
isSubcategoriesLoading
? Shimmer.fromColors(
baseColor: const Color(0xFFE2E8F0),
highlightColor: const Color(0xFFF8FAFC),
child: ListView.separated(
? Shimmer.fromColors(
baseColor: const Color(0xFFE2E8F0),
highlightColor: const Color(0xFFF8FAFC),
child: ListView.separated(
scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
itemCount: 4,
separatorBuilder: (BuildContext _, int __) => const SizedBox(width: 8),
itemBuilder: (BuildContext _, int __) => Container(width: 70, height: 32, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(6))),
),
)
: ListView.separated(
scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
itemCount: 4,
physics: const BouncingScrollPhysics(),
// +1 for the leading "All" chip (clears the subcategory selection,
// falling back to just the parent main category).
itemCount: subcategoriesList.length + 1,
separatorBuilder: (BuildContext _, int __) => const SizedBox(width: 8),
itemBuilder:
(BuildContext _, int __) => Container(width: 70, height: 32, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(6))),
itemBuilder: (BuildContext context, int index) {
bool isAllChip = index == 0;
WalaPlusCategoryModel? subcategory = isAllChip ? null : subcategoriesList[index - 1];
bool isSelected = isAllChip ? _selectedSubcategoryId == null : _selectedSubcategoryId == subcategory!.id;
String label = isAllChip ? LocaleKeys.allCategories.tr() : (subcategory!.name ?? "");
return InkWell(
borderRadius: BorderRadius.circular(6),
onTap: () {
setState(() {
_selectedSubcategoryId = isAllChip ? null : (isSelected ? null : subcategory!.id);
});
// Selecting a subcategory filters offers to just that subcategory;
// "All" (or deselecting) falls back to the parent main category.
fetchOffers(categoryId: _selectedSubcategoryId ?? currentCategoryId);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
alignment: Alignment.center,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(6), color: isSelected ? MyColors.darkIconColor : MyColors.lightGreyEAColor),
child: label.toText12(color: isSelected ? MyColors.white : MyColors.black),
),
);
},
),
)
: ListView.separated(
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
// +1 for the leading "All" chip (clears the subcategory selection,
// falling back to just the parent main category).
itemCount: subcategoriesList.length + 1,
separatorBuilder: (BuildContext _, int __) => const SizedBox(width: 8),
itemBuilder: (BuildContext context, int index) {
bool isAllChip = index == 0;
WalaPlusCategoryModel? subcategory = isAllChip ? null : subcategoriesList[index - 1];
bool isSelected = isAllChip ? _selectedSubcategoryId == null : _selectedSubcategoryId == subcategory!.id;
String label = isAllChip ? LocaleKeys.allCategories.tr() : (subcategory!.name ?? "");
return InkWell(
borderRadius: BorderRadius.circular(6),
onTap: () {
setState(() {
_selectedSubcategoryId = isAllChip ? null : (isSelected ? null : subcategory!.id);
});
// Selecting a subcategory filters offers to just that subcategory;
// "All" (or deselecting) falls back to the parent main category.
fetchOffers(categoryId: _selectedSubcategoryId ?? currentCategoryId);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
alignment: Alignment.center,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(6), color: isSelected ? MyColors.darkIconColor : MyColors.lightGreyEAColor),
child: label.toText12(color: isSelected ? MyColors.white : MyColors.black),
),
);
},
),
),
],
],
@ -1016,11 +998,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
child: Container(
height: 44,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: isSelected ? darkBg : Colors.white,
border: Border.all(color: isSelected ? darkBg : borderColor),
),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8), color: isSelected ? darkBg : Colors.white, border: Border.all(color: isSelected ? darkBg : borderColor)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
@ -1037,10 +1015,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
Widget sectionHeader(String title, {String? hint}) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
title.toText14(isBold: true, color: darkBg),
if (hint != null) hint.toText12(color: const Color(0xFF94A3B8)),
],
children: <Widget>[title.toText14(isBold: true, color: darkBg), if (hint != null) hint.toText12(color: const Color(0xFF94A3B8))],
);
}
@ -1054,7 +1029,12 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: Container(width: 44, height: 4.5, margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration(color: const Color(0xFFCBD5E1), borderRadius: BorderRadius.circular(2.5))),
child: Container(
width: 44,
height: 4.5,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(color: const Color(0xFFCBD5E1), borderRadius: BorderRadius.circular(2.5)),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -1357,122 +1337,131 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
children: [
// Inset image with rounded corners
ClipRRect(
borderRadius: BorderRadius.circular(14),
child: SizedBox(
height: 120,
width: double.infinity,
child: Stack(
fit: StackFit.expand,
children: [
// Banner Image
(bannerUrl != null && bannerUrl.isNotEmpty)
? Image.network(
bannerUrl,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Container(color: const Color(0xFFF1F5F9), child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32)),
)
: Container(color: const Color(0xFFF1F5F9), child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32)),
// Gradient overlay at bottom of image
Positioned(
bottom: 0,
left: 0,
right: 0,
height: 50,
child: Container(
decoration: BoxDecoration(gradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [Colors.black.withOpacity(0.75), Colors.transparent])),
),
),
// Discount badge at top right (flush with edge, pill on start side)
if (discountText.isNotEmpty)
PositionedDirectional(
top: 20,
end: 0,
borderRadius: BorderRadius.circular(14),
child: SizedBox(
height: 120,
width: double.infinity,
child: Stack(
fit: StackFit.expand,
children: [
// Banner Image
(bannerUrl != null && bannerUrl.isNotEmpty)
? Image.network(
bannerUrl,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Container(color: const Color(0xFFF1F5F9), child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32)),
)
: Container(color: const Color(0xFFF1F5F9), child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32)),
// Gradient overlay at bottom of image
Positioned(
bottom: 0,
left: 0,
right: 0,
height: 50,
child: Container(
padding: const EdgeInsetsDirectional.only(start: 14, end: 10, top: 4, bottom: 4),
decoration: const BoxDecoration(color: Color(0xFFED1C24), borderRadius: BorderRadiusDirectional.only(topStart: Radius.circular(20), bottomStart: Radius.circular(20))),
child: discountText.toText14(isBold: true, color: Colors.white),
decoration: BoxDecoration(gradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [Colors.black.withOpacity(0.75), Colors.transparent])),
),
),
// Brand logo & name at bottom left
PositionedDirectional(
bottom: 8,
start: 8,
end: 8,
child: Row(
children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4)),
clipBehavior: Clip.antiAlias,
child: (logoUrl != null && logoUrl.isNotEmpty) ? Image.network(logoUrl, fit: BoxFit.contain, errorBuilder: (_, __, ___) => _buildMerchantMIcon()) : _buildMerchantMIcon(),
// Discount badge at top right (flush with edge, pill on start side)
if (discountText.isNotEmpty)
PositionedDirectional(
top: 20,
end: 0,
child: Container(
padding: const EdgeInsetsDirectional.only(start: 14, end: 10, top: 4, bottom: 4),
decoration: const BoxDecoration(color: Color(0xFFED1C24), borderRadius: BorderRadiusDirectional.only(topStart: Radius.circular(20), bottomStart: Radius.circular(20))),
child: discountText.toText14(isBold: true, color: Colors.white),
),
const SizedBox(width: 5),
Expanded(child: (item.name ?? "").toText11(isBold: true, maxLine: 1, color: Colors.white)),
],
),
// Brand logo & name at bottom left
PositionedDirectional(
bottom: 8,
start: 8,
end: 8,
child: Row(
children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4)),
clipBehavior: Clip.antiAlias,
child: (logoUrl != null && logoUrl.isNotEmpty) ? Image.network(logoUrl, fit: BoxFit.contain, errorBuilder: (_, __, ___) => _buildMerchantMIcon()) : _buildMerchantMIcon(),
),
const SizedBox(width: 5),
Expanded(child: (item.name ?? "").toText11(isBold: true, maxLine: 1, color: Colors.white)),
],
),
),
),
],
],
),
),
),
),
const SizedBox(height: 8),
// Retailer Name / Bundle Title
(item.name ?? "").toText14(isBold: true, maxlines: 1, overflow: TextOverflow.ellipsis, color: const Color(0xFF1E293B)),
// Subtitle line is always rendered (even empty) so every card reserves the same
// vertical space here, instead of shorter-content cards ending up shorter overall.
const SizedBox(height: 2),
offerSubtitle.toText14(maxlines: 1, overflow: TextOverflow.ellipsis, isBold: true, color: const Color(0xFF1E293B)),
// Badges row: Type, Location & Usage - always exactly one row, fixed height.
// Horizontally scrollable instead of wrapping to a 2nd row, so every card's badge
// section is the same height regardless of how many badges it has.
const SizedBox(height: 6),
SizedBox(
height: 28,
child: ListView(
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
children: <Widget>[
// One Type Chip per distinct type - Online (green) and/or In Store (blue).
for (String offerType in offerTypes) ...<Widget>[
InfoChipWidget(
text: _getTypeFilterTitle(offerType),
icon: _getTypeFilterIcon(offerType),
backgroundColor: offerType == 'online' ? const Color(0xFFE6F7F0) : const Color(0xFFEFF6FF),
textColor: offerType == 'online' ? const Color(0xFF00A651) : const Color(0xFF2563EB),
),
const SizedBox(width: 5),
],
const SizedBox(height: 8),
// Retailer Name / Bundle Title
(item.name ?? "").toText14(isBold: true, maxlines: 1, overflow: TextOverflow.ellipsis, color: const Color(0xFF1E293B)),
// Subtitle line is always rendered (even empty) so every card reserves the same
// vertical space here, instead of shorter-content cards ending up shorter overall.
const SizedBox(height: 2),
offerSubtitle.toText14(maxlines: 1, overflow: TextOverflow.ellipsis, isBold: true, color: const Color(0xFF1E293B)),
// Badges row: Type, Location & Usage - always exactly one row, fixed height.
// Horizontally scrollable instead of wrapping to a 2nd row, so every card's badge
// section is the same height regardless of how many badges it has.
const SizedBox(height: 6),
SizedBox(
height: 28,
child: ListView(
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
children: <Widget>[
// One Type Chip per distinct type - Online (green) and/or In Store (blue).
// Same pill styling (padding/border/icon size) as the retailer details screen.
for (String offerType in offerTypes) ...<Widget>[
InfoChipWidget(
text: _getTypeFilterTitle(offerType),
icon: _getTypeFilterIcon(offerType),
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,
),
const SizedBox(width: 5),
],
// Location Badge
if (cityText.trim().isNotEmpty) ...<Widget>[
InfoChipWidget(
text: cityText,
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,
),
const SizedBox(width: 5),
],
// Location Badge
if (cityText.trim().isNotEmpty) ...<Widget>[
InfoChipWidget(
text: cityText,
svgIcon: "assets/icons/location.svg",
isBold: false,
backgroundColor: const Color(0xFFF3F4F6),
textColor: const Color(0xFF4B5563),
iconColor: const Color(0xFF64748B),
iconTextGap: 2.5,
),
const SizedBox(width: 5),
// Usage badge
if (usageText.trim().isNotEmpty)
InfoChipWidget(text: usageText, backgroundColor: const Color(0xFFFEF3C7), textColor: const Color(0xFFB45309), horizontalPadding: 10, verticalPadding: 5, borderRadius: 12),
],
// Usage badge
if (usageText.trim().isNotEmpty) InfoChipWidget(text: usageText, backgroundColor: const Color(0xFFFEF3C7), textColor: const Color(0xFFB45309)),
],
),
),
),
],
],
),
),
),
),
);
}

Loading…
Cancel
Save