Offers And Discounts Complete

offers_discounts
Aamir Muhammad 4 weeks ago
parent d4d245d299
commit ea124325f0

@ -25,6 +25,7 @@ class OffersAndDiscountsViewModel extends ChangeNotifier {
bool isAuthenticating = false;
bool isPurchasing = false;
bool isSaveQrCodePressed = false; // Track QR code capture state
bool hasOfferDetailsLoadingError = false; // Track offer details loading error
List<int> selectedCategories = []; // Empty means 'All Offers'
String searchQuery = '';
@ -235,6 +236,32 @@ class OffersAndDiscountsViewModel extends ChangeNotifier {
notifyListeners();
}
setHasOfferDetailsLoadingError(bool val) {
hasOfferDetailsLoadingError = val;
notifyListeners();
}
/// Reset offer details state (called when navigating to a new offer)
resetOfferDetailsState() {
offerDetails = null;
hasOfferDetailsLoadingError = false;
notifyListeners();
}
/// Format date to "31 Aug 2026" format
String formatExpiryDate(DateTime date) {
final List<String> months = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
];
final day = date.day;
final month = months[date.month - 1];
final year = date.year;
return '$day $month $year';
}
/// Authenticate patient before making a purchase
Future<bool> authenticatePatient({Function(PomsAuthTokenModel)? onSuccess, Function(String)? onError}) async {
final appState = getIt.get<AppState>();
@ -512,6 +539,7 @@ class OffersAndDiscountsViewModel extends ChangeNotifier {
stopAutoPlayTimer();
_progress = 0.0;
_currentPage = 0;
hasOfferDetailsLoadingError = false;
isOfferDetailsLoading = true;
notifyListeners();
@ -521,6 +549,7 @@ class OffersAndDiscountsViewModel extends ChangeNotifier {
result.fold(
(failure) async {
isOfferDetailsLoading = false;
hasOfferDetailsLoadingError = true;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
@ -529,6 +558,7 @@ class OffersAndDiscountsViewModel extends ChangeNotifier {
},
(response) {
isOfferDetailsLoading = false;
hasOfferDetailsLoadingError = false;
if (response.data != null) {
offerDetails = response.data;
}

@ -231,7 +231,7 @@ class _LandingPageState extends State<LandingPage> {
immediateLiveCareViewModel.initImmediateLiveCare();
immediateLiveCareViewModel.getPatientLiveCareHistory();
offersAndDiscountsViewModel.setIsoffersListLoading(true);
offersAndDiscountsViewModel.initOffersAndDiscounts();
offersAndDiscountsViewModel.initOffersAndDiscounts(forceRefresh: true);
// appointmentRatingViewModel.getLastRatingAppointment(
// onSuccess: (response) {
@ -555,7 +555,7 @@ class _LandingPageState extends State<LandingPage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
("${LocaleKeys.offers.tr(context: context)} & ${LocaleKeys.discount.tr(context: context)}").toText16(isBold: true),
(LocaleKeys.offerAndPackages.tr(context: context)).toText16(isBold: true),
Row(
children: [
LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),

@ -39,6 +39,16 @@ class _OffersAndDiscountsSuccessQrPageState extends State<OffersAndDiscountsSucc
late OffersAndDiscountsViewModel offersAndDiscountsViewModel;
String? qrCodeFilePath; // Store the QR code file path for sharing
final GlobalKey _screenshotKey = GlobalKey();
Future<String>? _qrCodeFuture; // Cache the QR code future
@override
void initState() {
super.initState();
// Initialize the QR code future once
if (widget.purchaseData.qrCode != null && widget.purchaseData.qrCode!.isNotEmpty) {
_qrCodeFuture = Utils.createFileFromString(widget.purchaseData.qrCode!, "png");
}
}
Color _getStatusColor(String status) {
switch (status) {
@ -92,8 +102,10 @@ class _OffersAndDiscountsSuccessQrPageState extends State<OffersAndDiscountsSucc
final result = await offersAndDiscountsViewModel.handleQRCodeAction(
screenshotKey: _screenshotKey,
voucherCode: widget.purchaseData.voucherCode!,
shouldSaveToStorage: true, // Always save to permanent storage
isFromPaymentScreen: isShareAction ? false : widget.isFromPaymentScreen, // Share action never pops
shouldSaveToStorage: true,
// Always save to permanent storage
isFromPaymentScreen: widget.isFromPaymentScreen,
// Always use the actual value
context: context,
offerName: widget.purchaseData.offer!.name,
offerNameAr: widget.purchaseData.offer!.nameAr,
@ -103,18 +115,29 @@ class _OffersAndDiscountsSuccessQrPageState extends State<OffersAndDiscountsSucc
if (result['success']) {
if (isShareAction) {
// Share action: Saved to permanent storage, now share the file
// Share button pressed: Open share dialog
final xFile = XFile(result['filePath']);
final offerName = result['offerName'];
// Share the file
// ignore: deprecated_member_use
await Share.shareXFiles(
[xFile],
text: '${LocaleKeys.saveQRCode.tr(context: context)} - ${LocaleKeys.orderLabel.tr(context: context)} #${widget.purchaseData.voucherCode} - $offerName',
subject: LocaleKeys.orderDetails.tr(context: context),
);
// If from payment screen, navigate after share (whether user shared or cancelled)
if (widget.isFromPaymentScreen) {
if (mounted) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).push(CustomPageRoute(page: OffersAndDiscountsHistoryPage()));
}
}
} else {
// Save action: Show success and optionally navigate
if (result['isFromPayment']) {
// Save QR Code button pressed: Show success and handle navigation based on source
if (widget.isFromPaymentScreen) {
// From payment screen: Show success bottom sheet and navigate to history
showCommonBottomSheetWithoutHeight(
context,
@ -156,11 +179,9 @@ class _OffersAndDiscountsSuccessQrPageState extends State<OffersAndDiscountsSucc
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
print(widget.isFromPaymentScreen);
return Consumer<OffersAndDiscountsViewModel>(
builder: (context, viewModel, child) {
offersAndDiscountsViewModel = viewModel;
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
@ -171,7 +192,7 @@ class _OffersAndDiscountsSuccessQrPageState extends State<OffersAndDiscountsSucc
bottomChild: CustomButton(
text: LocaleKeys.saveQRCode.tr(context: context),
onPressed: () {
_handleQRCodeAction(isShareAction: false); // Save action - saves and shows success
_handleQRCodeAction(isShareAction: true); // Save action - saves and shows success
},
backgroundColor: AppColors.successColor,
borderColor: AppColors.successColor,
@ -199,7 +220,17 @@ class _OffersAndDiscountsSuccessQrPageState extends State<OffersAndDiscountsSucc
SizedBox(height: 6.h),
_buildQRCode().paddingSymmetrical(0.h, 0.h),
AppCustomChipWidget(
labelText: "${LocaleKeys.expiresOn.tr(context: context)}: ${Utils.formatDateToDisplay(widget.purchaseData.offer!.validTo.toString())}",
richText: Row(
mainAxisSize: MainAxisSize.min,
children: [
"${LocaleKeys.expiresOn.tr(context: context)}: ".toText10(isEnglishOnly: true, color: AppColors.primaryRedColor, isBold: true, weight: FontWeight.w700),
Directionality(
textDirection: ui.TextDirection.ltr,
child: viewModel
.formatExpiryDate(widget.purchaseData.offer!.validTo!)
.toText10(isEnglishOnly: true, isBold: true, weight: FontWeight.w700, color: AppColors.primaryRedColor)),
],
),
backgroundColor: AppColors.lightRedButtonColor,
textColor: AppColors.primaryRedColor,
icon: "",
@ -288,7 +319,7 @@ class _OffersAndDiscountsSuccessQrPageState extends State<OffersAndDiscountsSucc
}
return FutureBuilder<String>(
future: Utils.createFileFromString(widget.purchaseData.qrCode!, "png"),
future: _qrCodeFuture, // Use cached future
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(

@ -76,9 +76,7 @@ class _OffersAndDiscountPaymentPageState extends State<OffersAndDiscountPaymentP
// If Saudi user (patientType == 1), return price without VAT
// Otherwise, return full price with VAT
return authenticatedUser.patientType == 1
? selectedOffer.vatExclusivePrice
: selectedOffer.listPrice;
return authenticatedUser.patientType == 1 ? selectedOffer.vatExclusivePrice : selectedOffer.listPrice;
}
@override
@ -111,7 +109,7 @@ class _OffersAndDiscountPaymentPageState extends State<OffersAndDiscountPaymentP
children: [
Expanded(
child: CollapsingListView(
title: "${LocaleKeys.offers.tr(context: context)} ${LocaleKeys.payment.tr(context: context)}",
title: "${LocaleKeys.offerAndPackages.tr(context: context)} ${LocaleKeys.payment.tr(context: context)}",
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -362,9 +360,7 @@ class _OffersAndDiscountPaymentPageState extends State<OffersAndDiscountPaymentP
LocaleKeys.vat15.tr().toText14(isBold: true, color: AppColors.greyTextColor),
// Check if user is Saudi (patientType == 1 means Saudi)
Utils.getPaymentAmountWithSymbol(
(appState.getAuthenticatedUser()?.patientType == 1
? "0.00"
: (selectedOffer.vatInclusivePrice - selectedOffer.vatExclusivePrice).toStringAsFixed(2))
(appState.getAuthenticatedUser()?.patientType == 1 ? "0.00" : (selectedOffer.vatInclusivePrice - selectedOffer.vatExclusivePrice).toStringAsFixed(2))
.toText14(isBold: true, color: AppColors.greyTextColor),
AppColors.greyTextColor,
13,
@ -378,10 +374,7 @@ class _OffersAndDiscountPaymentPageState extends State<OffersAndDiscountPaymentP
"".toText14(isBold: true),
// If Saudi user (patientType == 1), show price without VAT, otherwise show full price with VAT
Utils.getPaymentAmountWithSymbol(
(appState.getAuthenticatedUser()?.patientType == 1
? selectedOffer.vatExclusivePrice.toString()
: selectedOffer.listPrice.toString())
.toText24(isBold: true),
(appState.getAuthenticatedUser()?.patientType == 1 ? selectedOffer.vatExclusivePrice.toString() : selectedOffer.listPrice.toString()).toText24(isBold: true),
AppColors.blackColor,
17,
isSaudiCurrency: selectedOffer.currency == "SAR"),
@ -744,18 +737,16 @@ class _OffersAndDiscountPaymentPageState extends State<OffersAndDiscountPaymentP
// Payment result sent successfully, now fetch purchase history
await offersAndDiscountsViewModel.getOffersAndDiscountsHistory(
onSuccess: (historyResponse) {
LoaderBottomSheet.hideLoader();
// Find the purchased offer in the history by matching the offer ID
final purchaseData = offersAndDiscountsViewModel.historyList.firstWhere(
(item) => item.offer!.id == selectedOffer.id,
orElse: () => offersAndDiscountsViewModel.historyList.first,
);
final purchaseData = offersAndDiscountsViewModel.historyList.firstWhere((item) => item.offer!.id == selectedOffer.id, orElse: () => offersAndDiscountsViewModel.historyList.first);
LoaderBottomSheet.hideLoader();
// Navigate to success QR page with purchase data
Navigator.of(context).pushReplacement(
CustomPageRoute(
page: OffersAndDiscountsSuccessQrPage(purchaseData: purchaseData, isFromPaymentScreen: true,),
page: OffersAndDiscountsSuccessQrPage(
purchaseData: purchaseData,
isFromPaymentScreen: true,
),
),
);
},

@ -7,6 +7,7 @@ import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -37,6 +38,7 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
late OffersAndDiscountsViewModel viewModel;
late AppState appState;
bool hasLoadingError = false;
bool isImageLoaded = false;
@override
void initState() {
@ -45,6 +47,10 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
viewModel = Provider.of<OffersAndDiscountsViewModel>(context, listen: false);
// Clear previous offer details to prevent showing old data
viewModel.offerDetails = null;
// Reset image loaded state
setState(() {
isImageLoaded = false;
});
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context));
viewModel.getOfferDetails(
@ -73,14 +79,6 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
super.dispose();
}
String _formatDateString(DateTime date) {
try {
return DateFormat('d MMM, yyyy').format(date);
} catch (e) {
return '';
}
}
// Determine the status of the offer based on dates
String _getOfferStatus(DateTime validTo) {
DateTime now = DateTime.now();
@ -119,7 +117,7 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
viewModel = Provider.of<OffersAndDiscountsViewModel>(context, listen: false);
return CollapsingListView(
title: "${LocaleKeys.offers.tr(context: context)} ${LocaleKeys.details.tr(context: context)}",
title: "${LocaleKeys.offerAndPackageDetails.tr(context: context)}",
bottomChild: CustomButton(
text: LocaleKeys.buyNow.tr(context: context),
icon: AppAssets.shoppingCart,
@ -179,9 +177,37 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
);
}
// Only show offer if it matches the requested offerId (prevents showing old cached data)
// Show skeleton loader while offer data is being loaded
if (offer == null || offer.id != widget.offerId) {
return SizedBox.shrink(); // Return empty widget while loading
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Skeleton loader for image
_buildImageLoadingShimmer(),
SizedBox(height: 24.h),
// Skeleton loader for title
_buildTextShimmer(width: 250.w, height: 24.h),
SizedBox(height: 16.h),
// Skeleton loader for price
_buildTextShimmer(width: 150.w, height: 18.h),
SizedBox(height: 16.h),
// Skeleton loaders for chips
Row(
children: [
_buildTextShimmer(width: 120.w, height: 32.h),
SizedBox(width: 12.w),
_buildTextShimmer(width: 80.w, height: 32.h),
SizedBox(width: 12.w),
_buildTextShimmer(width: 100.w, height: 32.h),
],
),
SizedBox(height: 24.h),
// Skeleton loader for description
_buildTextShimmer(width: double.infinity, height: 100.h),
],
).paddingSymmetrical(24.h, 0.h),
);
}
final status = _getOfferStatus(offer.validTo);
@ -199,7 +225,10 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
height: 250.h,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
if (loadingProgress == null) {
return child;
}
// Show shimmer while image is loading
return _buildImageLoadingShimmer();
},
errorBuilder: (context, error, stackTrace) {
@ -225,7 +254,10 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
height: 250.h,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
if (loadingProgress == null) {
return child;
}
// Show shimmer while image is loading
return _buildImageLoadingShimmer();
},
errorBuilder: (context, error, stackTrace) {
@ -255,10 +287,7 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
textDirection: appState.isArabic() ? ui.TextDirection.rtl : ui.TextDirection.ltr,
children: [
"${LocaleKeys.price.tr(context: context)}: ".toText18(isBold: true, weight: FontWeight.w700),
Utils.getPaymentAmountWithSymbol(
((offer.listPrice).toStringAsFixed(2)).toText14(isBold: true, color: AppColors.greyTextColor),
AppColors.greyTextColor,
13,
Utils.getPaymentAmountWithSymbol(((offer.listPrice).toStringAsFixed(2)).toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13,
isSaudiCurrency: offer.currency == "SAR")
],
),
@ -273,7 +302,22 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
children: [
// Valid till chip
AppCustomChipWidget(
labelText: "${LocaleKeys.validTill.tr(context: context)} ${_formatDateString(offer.validTo)}",
// labelText: "${LocaleKeys.validTill.tr(context: context)} ${DateUtil.formatDateToDate(offer.validTo, appState.getLanguageCode() == "ar" ? true : false)}",
richText: Row(
mainAxisSize: MainAxisSize.min,
children: [
"${LocaleKeys.validTill.tr(context: context)}: ".toText10(color: AppColors.textColor, isBold: true, weight: FontWeight.w700),
Directionality(
textDirection: ui.TextDirection.ltr,
child: viewModel.formatExpiryDate(offer.validTo).toText10(
isEnglishOnly: true,
isBold: true,
weight: FontWeight.w700,
color: AppColors.textColor,
)),
],
),
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.calendar,
@ -319,29 +363,12 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
),
SizedBox(height: 24.h),
],
// Description Heading
LocaleKeys.description.tr(context: context).toText18(isBold: true, weight: FontWeight.w700),
SizedBox(height: 12.h),
// Description Content
HtmlWidget(
(appState.getLanguageID() == 1 ? offer.descriptionAr : offer.description),
),
//.toText16(
// color: AppColors.greyTextColor,
// height: 1.5,
// )
HtmlWidget((appState.getLanguageID() == 1 ? offer.descriptionAr : offer.description)),
SizedBox(height: 24.h),
// Offer Lines Section
if (offer.offerLines.isNotEmpty) ...[
LocaleKeys.includedServices.tr(context: context).toText18(
isBold: true,
weight: FontWeight.w700,
),
LocaleKeys.includedServices.tr(context: context).toText18(isBold: true, weight: FontWeight.w700),
SizedBox(height: 12.h),
...offer.offerLines.map((line) {
return Container(
@ -355,23 +382,14 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(appState.getLanguageID() == 1 ? line.nameAr : line.name).toText16(
isBold: true,
weight: FontWeight.w600,
),
(appState.getLanguageID() == 1 ? line.nameAr : line.name).toText16(isBold: true, weight: FontWeight.w600),
SizedBox(height: 4.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
textDirection: appState.isArabic() ? ui.TextDirection.rtl : ui.TextDirection.ltr,
children: [
"${LocaleKeys.quantity.tr(context: context)}: ${line.quantity}".toText14(
color: AppColors.greyTextColor,
),
if (line.discountValue > 0)
"${line.discountValue}% ${LocaleKeys.off.tr(context: context)}".toText14(
color: AppColors.primaryRedColor,
isBold: true,
),
"${LocaleKeys.quantity.tr(context: context)}: ${line.quantity}".toText14(color: AppColors.greyTextColor),
if (line.discountValue > 0) "${line.discountValue}% ${LocaleKeys.off.tr(context: context)}".toText14(color: AppColors.primaryRedColor, isBold: true),
],
),
],
@ -404,4 +422,20 @@ class _OffersAndDiscountsDetailedPageState extends State<OffersAndDiscountsDetai
),
);
}
// Helper method to build shimmer loading placeholder for text elements
Widget _buildTextShimmer({required double width, required double height}) {
return Shimmer.fromColors(
baseColor: AppColors.shimmerBaseColor,
highlightColor: AppColors.shimmerHighlightColor,
child: Container(
width: width,
height: height,
decoration: BoxDecoration(
color: AppColors.whiteColor,
borderRadius: BorderRadius.circular(8.h),
),
),
);
}
}

@ -1,11 +1,12 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -51,7 +52,7 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
appState = getIt.get<AppState>();
return CollapsingListView(
title: LocaleKeys.order.tr(context: context),
title: LocaleKeys.myOrders.tr(context: context),
isLeading: true,
child: SingleChildScrollView(
child: Consumer<OffersAndDiscountsViewModel>(builder: (context, offersAndDiscountVM, child) {
@ -84,7 +85,7 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: _buildHistoryCard(history),
child: _buildHistoryCard(history, offersAndDiscountVM),
),
),
);
@ -122,7 +123,7 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
);
}
Widget _buildHistoryCard(Datum history) {
Widget _buildHistoryCard(Datum history, OffersAndDiscountsViewModel viewModel) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
@ -151,27 +152,53 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
runSpacing: 8.h,
children: [
AppCustomChipWidget(
labelText: "${LocaleKeys.orderLabel.tr(context: context)}: ${history.voucherCode}",
labelText: "${LocaleKeys.voucherNo.tr(context: context)}: ${history.voucherCode}",
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.my_sick_leave_icon,
labelPadding: EdgeInsetsDirectional.only(start: 0.w, end: 8.w),
),
AppCustomChipWidget(
labelText: "${LocaleKeys.purchased.tr(context: context)}: ${_formatDateChipFromDateTime(history.purchaseDate!)}",
richText: Row(
mainAxisSize: MainAxisSize.min,
children: [
"${LocaleKeys.purchased.tr(context: context)}: ".toText10(color: AppColors.textColor, isBold: true, weight: FontWeight.w700),
Directionality(
textDirection: ui.TextDirection.ltr,
child: viewModel.formatExpiryDate(history.purchaseDate!).toText10(
isEnglishOnly: true,
isBold: true,
weight: FontWeight.w700,
color: AppColors.textColor,
)),
],
),
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.calendar,
labelPadding: EdgeInsetsDirectional.only(start: 0.w, end: 8.w),
isEnglishOnly: true,
),
history.offer!.validTo != null
? AppCustomChipWidget(
labelText: "${LocaleKeys.validUntil.tr(context: context)}: ${_formatDateChipFromDateTime(history.offer!.validTo!)}",
richText: Row(
mainAxisSize: MainAxisSize.min,
children: [
"${LocaleKeys.validUntil.tr(context: context)}: ".toText10(color: AppColors.textColor, isBold: true, weight: FontWeight.w700),
Directionality(
textDirection: ui.TextDirection.ltr,
child: viewModel.formatExpiryDate(history.offer!.validTo!).toText10(
isEnglishOnly: true,
isBold: true,
weight: FontWeight.w700,
color: AppColors.textColor,
)),
],
),
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.calendar,
labelPadding: EdgeInsetsDirectional.only(start: 0.w, end: 8.w),
isEnglishOnly: true,
)
: SizedBox(),
// Hospital chips - show multiple chips when expanded
@ -243,7 +270,7 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
if (history.branches == null || history.branches!.isEmpty) {
return [
AppCustomChipWidget(
labelText: "Hospital: N/A",
labelText: "${LocaleKeys.hospital.tr()}: N/A",
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.hospital,
@ -257,7 +284,7 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
final hospitalName = isArabic ? history.branches!.first.nameAr : history.branches!.first.name;
return [
AppCustomChipWidget(
labelText: "Hospital: $hospitalName",
labelText: "${LocaleKeys.hospital.tr()}: $hospitalName",
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.hospital,
@ -272,7 +299,7 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
List<Widget> chips = history.branches!.map<Widget>((branch) {
final hospitalName = isArabic ? branch.nameAr : branch.name;
return AppCustomChipWidget(
labelText: "Hospital: $hospitalName",
labelText: "${LocaleKeys.hospital.tr()}: $hospitalName",
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.hospital,
@ -283,7 +310,7 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
// Add a "Show less" chip at the end
chips.add(
AppCustomChipWidget(
labelText: "Show less",
labelText: LocaleKeys.showLess.tr(),
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
textColor: AppColors.primaryRedColor,
icon: "",
@ -303,7 +330,7 @@ class _OffersAndDiscountsHistoryPageState extends State<OffersAndDiscountsHistor
final remainingCount = history.branches!.length - 1;
return [
AppCustomChipWidget(
labelText: "Hospital: $firstHospital (+$remainingCount more)",
labelText: "${LocaleKeys.hospital.tr()}: $firstHospital (+$remainingCount more)",
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.hospital,

@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@ -8,8 +7,10 @@ import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/offers_and_discounts/offers_and_discounts_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
@ -50,11 +51,18 @@ class _OffersAndDiscountsPageState extends State<OffersAndDiscountsPage> {
// Authenticate patient once when visiting the offers page
if (!appState.isPomsAuthTokenValid()) {
// Show loader during authentication
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context));
await offersAndDiscountsViewModel.authenticatePatient(
onSuccess: (authToken) {
// Hide loader on success
LoaderBottomSheet.hideLoader();
print("POMS Authentication successful: ${authToken.token}");
},
onError: (error) {
// Hide loader on error
LoaderBottomSheet.hideLoader();
print("POMS Authentication failed: $error");
// Authentication failed, but user can still browse offers
},
@ -156,12 +164,10 @@ class _OffersAndDiscountsPageState extends State<OffersAndDiscountsPage> {
offersAndDiscountsViewModel = Provider.of<OffersAndDiscountsViewModel>(context, listen: false);
return CollapsingListView(
title: "${LocaleKeys.offers.tr(context: context)} & ${LocaleKeys.discount.tr(context: context)}",
title: LocaleKeys.offerAndPackages.tr(context: context),
history: () {
Navigator.of(context).push(
CustomPageRoute(
page: OffersAndDiscountsHistoryPage(),
),
CustomPageRoute(page: OffersAndDiscountsHistoryPage()),
);
},
child: SingleChildScrollView(
@ -305,7 +311,23 @@ class _OffersAndDiscountsPageState extends State<OffersAndDiscountsPage> {
SizedBox(height: 8.h),
// Valid till chip
AppCustomChipWidget(
labelText: "${LocaleKeys.validTill.tr(context: context)} ${Utils.formatDateToDisplay(offersAndDiscountVM.filteredOffers[index].validTo.toString())}",
// labelText: "${LocaleKeys.validTill.tr(context: context)}: ${DateUtil.formatDateToDate(offersAndDiscountVM.filteredOffers[index].validTo!, appState.getLanguageCode() == "ar" ? true : false)}",
richText: Row(
mainAxisSize: MainAxisSize.min,
children: [
"${LocaleKeys.validTill.tr(context: context)}: ".toText10(color: AppColors.textColor, isBold: true, weight: FontWeight.w700),
Directionality(
textDirection: ui.TextDirection.ltr,
child: offersAndDiscountVM.formatExpiryDate(offersAndDiscountVM.filteredOffers[index].validTo!).toText10(
isEnglishOnly: true,
isBold: true,
weight: FontWeight.w700,
color: AppColors.textColor,
)),
],
),
backgroundColor: AppColors.chipBgColor,
textColor: AppColors.textColor,
icon: AppAssets.calendar,

Loading…
Cancel
Save