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:
  chip set

# Conflicts:
#	lib/ui/landing/dashboard_screen_for_dependet.dart
wala_plus_with_dialog_removal
Sultan khan 3 weeks ago
commit 5a96d64d9c

@ -4,12 +4,12 @@ class ApiConsts {
// static String baseUrl = "http://10.201.214.101:2027"; // Local server // 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://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 = "http://10.201.204.101:2024";
// static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://webservices.hmg.com"; // PreProd
// static String baseUrl = "https://hmgwebservices.com"; // Live server // 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 ssoBaseUrl = "https://sso.hmg.com"; // New Live server
// //
// static String baseUrl = "https://uat.hmgwebservices.com"; // // static String baseUrl = "https://uat.hmgwebservices.com"; //

@ -313,6 +313,11 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
String catParam = (isFavoriteOnly || targetCategoryId == "all" || targetCategoryId == "0") ? "" : targetCategoryId; String catParam = (isFavoriteOnly || targetCategoryId == "all" || targetCategoryId == "0") ? "" : targetCategoryId;
String? offerTypeParam = isFavoriteOnly ? null : OffersAndDiscountsApiClient.mapOfferTypeFilter(selectedTypeFilter); String? offerTypeParam = isFavoriteOnly ? null : OffersAndDiscountsApiClient.mapOfferTypeFilter(selectedTypeFilter);
String? queryParam = searchQuery.trim().isNotEmpty ? searchQuery.trim() : null; 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",
);
int? radiusParam = isFavoriteOnly ? null : _radiusFromFilterDistance(_filterDistance); int? radiusParam = isFavoriteOnly ? null : _radiusFromFilterDistance(_filterDistance);
int? minDiscountParam = (isFavoriteOnly || _filterMinDiscount == 'any') ? null : int.tryParse(_filterMinDiscount); int? minDiscountParam = (isFavoriteOnly || _filterMinDiscount == 'any') ? null : int.tryParse(_filterMinDiscount);
String? userRef = isFavoriteOnly ? _getUserReferenceForFavorites() : null; String? userRef = isFavoriteOnly ? _getUserReferenceForFavorites() : null;
@ -605,10 +610,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
_changeLanguage(const Locale("ar", "SA")); _changeLanguage(const Locale("ar", "SA"));
} }
}, },
child: const Padding( child: const Padding(padding: EdgeInsets.all(3), child: Icon(Icons.language_rounded, size: 24, color: Color(0xFF1E293B))),
padding: EdgeInsets.all(3),
child: Icon(Icons.language_rounded, size: 24, color: Color(0xFF1E293B)),
),
), ),
); );
} }
@ -643,10 +645,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
_performLogout(); _performLogout();
} }
}, },
child: const Padding( child: const Padding(padding: EdgeInsets.symmetric(horizontal: 4, vertical: 8), child: Icon(Icons.arrow_back_ios_new_rounded, size: 20, color: Color(0xFF1E293B))),
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 8),
child: Icon(Icons.arrow_back_ios_new_rounded, size: 20, color: Color(0xFF1E293B)),
),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
], ],
@ -656,8 +655,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (ApiConsts.isDependetLogin) if (ApiConsts.isDependetLogin) _buildLanguageIconButton(isAr),
_buildLanguageIconButton(isAr),
if (!ApiConsts.isDependetLogin) ...[ if (!ApiConsts.isDependetLogin) ...[
const SizedBox(width: 4), const SizedBox(width: 4),
Tooltip( Tooltip(
@ -669,12 +667,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.all(3), padding: const EdgeInsets.all(3),
child: SvgPicture.asset( child: SvgPicture.asset('assets/icons/ic_dependents_top.svg', width: 24, height: 24, colorFilter: const ColorFilter.mode(Color(0xFF1E293B), BlendMode.srcIn)),
'assets/icons/ic_dependents_top.svg',
width: 24,
height: 24,
colorFilter: const ColorFilter.mode(Color(0xFF1E293B), BlendMode.srcIn),
),
), ),
), ),
), ),
@ -692,11 +685,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.all(3), padding: const EdgeInsets.all(3),
child: Icon( child: Icon(isFavoriteOnly ? Icons.favorite_rounded : Icons.favorite_border_rounded, size: 24, color: isFavoriteOnly ? Colors.black : const Color(0xFF1E293B)),
isFavoriteOnly ? Icons.favorite_rounded : Icons.favorite_border_rounded,
size: 24,
color: isFavoriteOnly ? Colors.black : const Color(0xFF1E293B),
),
), ),
), ),
), ),
@ -705,10 +694,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
InkWell( InkWell(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
onTap: _performLogout, onTap: _performLogout,
child: Padding( child: Padding(padding: const EdgeInsets.all(3), child: SvgPicture.asset('assets/images/drawer/logout.svg', height: 20, width: 20)),
padding: const EdgeInsets.all(3),
child: SvgPicture.asset('assets/images/drawer/logout.svg', height: 20, width: 20),
),
), ),
], ],
], ],
@ -943,50 +929,49 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
Expanded( Expanded(
child: child:
isSubcategoriesLoading isSubcategoriesLoading
? Shimmer.fromColors( ? Shimmer.fromColors(
baseColor: const Color(0xFFE2E8F0), baseColor: const Color(0xFFE2E8F0),
highlightColor: const Color(0xFFF8FAFC), highlightColor: const Color(0xFFF8FAFC),
child: ListView.separated( 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, scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(), physics: const BouncingScrollPhysics(),
itemCount: 4, // +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), separatorBuilder: (BuildContext _, int __) => const SizedBox(width: 8),
itemBuilder: itemBuilder: (BuildContext context, int index) {
(BuildContext _, int __) => Container(width: 70, height: 32, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(6))), 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),
),
);
},
),
), ),
], ],
], ],
@ -1022,11 +1007,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
child: Container( child: Container(
height: 44, height: 44,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(borderRadius: BorderRadius.circular(8), color: isSelected ? darkBg : Colors.white, border: Border.all(color: isSelected ? darkBg : borderColor)),
borderRadius: BorderRadius.circular(8),
color: isSelected ? darkBg : Colors.white,
border: Border.all(color: isSelected ? darkBg : borderColor),
),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
@ -1043,10 +1024,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
Widget sectionHeader(String title, {String? hint}) { Widget sectionHeader(String title, {String? hint}) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[title.toText14(isBold: true, color: darkBg), if (hint != null) hint.toText12(color: const Color(0xFF94A3B8))],
title.toText14(isBold: true, color: darkBg),
if (hint != null) hint.toText12(color: const Color(0xFF94A3B8)),
],
); );
} }
@ -1060,7 +1038,12 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Center( 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( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -1362,122 +1345,131 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
children: [ children: [
// Inset image with rounded corners // Inset image with rounded corners
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(14), borderRadius: BorderRadius.circular(14),
child: SizedBox( child: SizedBox(
height: 120, height: 120,
width: double.infinity, width: double.infinity,
child: Stack( child: Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
// Banner Image // Banner Image
(bannerUrl != null && bannerUrl.isNotEmpty) (bannerUrl != null && bannerUrl.isNotEmpty)
? Image.network( ? Image.network(
bannerUrl, bannerUrl,
fit: BoxFit.cover, fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Container(color: const Color(0xFFF1F5F9), child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32)), 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)), : Container(color: const Color(0xFFF1F5F9), child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32)),
// Gradient overlay at bottom of image // Gradient overlay at bottom of image
Positioned( Positioned(
bottom: 0, bottom: 0,
left: 0, left: 0,
right: 0, right: 0,
height: 50, 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,
child: Container( child: Container(
padding: const EdgeInsetsDirectional.only(start: 14, end: 10, top: 4, bottom: 4), decoration: BoxDecoration(gradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [Colors.black.withOpacity(0.75), Colors.transparent])),
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),
), ),
), ),
// Brand logo & name at bottom left // Discount badge at top right (flush with edge, pill on start side)
PositionedDirectional( if (discountText.isNotEmpty)
bottom: 8, PositionedDirectional(
start: 8, top: 20,
end: 8, end: 0,
child: Row( child: Container(
children: [ padding: const EdgeInsetsDirectional.only(start: 14, end: 10, top: 4, bottom: 4),
Container( decoration: const BoxDecoration(color: Color(0xFFED1C24), borderRadius: BorderRadiusDirectional.only(topStart: Radius.circular(20), bottomStart: Radius.circular(20))),
width: 18, child: discountText.toText14(isBold: true, color: Colors.white),
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)),
], // 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), const SizedBox(height: 8),
// Retailer Name / Bundle Title // Retailer Name / Bundle Title
(item.name ?? "").toText14(isBold: true, maxlines: 1, overflow: TextOverflow.ellipsis, color: const Color(0xFF1E293B)), (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 // 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. // vertical space here, instead of shorter-content cards ending up shorter overall.
const SizedBox(height: 2), const SizedBox(height: 2),
offerSubtitle.toText14(maxlines: 1, overflow: TextOverflow.ellipsis, isBold: true, color: const Color(0xFF1E293B)), offerSubtitle.toText14(maxlines: 1, overflow: TextOverflow.ellipsis, isBold: true, color: const Color(0xFF1E293B)),
// Badges row: Type, Location & Usage - always exactly one row, fixed height. // 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 // 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. // section is the same height regardless of how many badges it has.
const SizedBox(height: 6), const SizedBox(height: 6),
SizedBox( SizedBox(
height: 28, height: 28,
child: ListView( child: ListView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
children: <Widget>[ children: <Widget>[
// One Type Chip per distinct type - Online (green) and/or In Store (blue). // One Type Chip per distinct type - Online (green) and/or In Store (blue).
for (String offerType in offerTypes) ...<Widget>[ // Same pill styling (padding/border/icon size) as the retailer details screen.
InfoChipWidget( for (String offerType in offerTypes) ...<Widget>[
text: _getTypeFilterTitle(offerType), InfoChipWidget(
icon: _getTypeFilterIcon(offerType), text: _getTypeFilterTitle(offerType),
backgroundColor: offerType == 'online' ? const Color(0xFFE6F7F0) : const Color(0xFFEFF6FF), icon: _getTypeFilterIcon(offerType),
textColor: offerType == 'online' ? const Color(0xFF00A651) : const Color(0xFF2563EB), backgroundColor: offerType == 'online' ? const Color(0xFFE6F7F0) : const Color(0xFFEFF6FF),
), textColor: offerType == 'online' ? const Color(0xFF00A651) : const Color(0xFF2563EB),
const SizedBox(width: 5), 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 // Usage badge
if (cityText.trim().isNotEmpty) ...<Widget>[ if (usageText.trim().isNotEmpty)
InfoChipWidget( InfoChipWidget(text: usageText, backgroundColor: const Color(0xFFFEF3C7), textColor: const Color(0xFFB45309), horizontalPadding: 10, verticalPadding: 5, borderRadius: 12),
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)),
],
), ),
), ],
], ),
), ),
), ),
),
); );
} }

Loading…
Cancel
Save