Lakum integration completed

haroon_dev
haroon amjad 2 days ago
parent 40a0ecdda4
commit 9d04133b8d

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@ -420,6 +420,7 @@ class AppAssets {
static const String maleIcon = '$pngBasePath/male_icon.png'; static const String maleIcon = '$pngBasePath/male_icon.png';
static const String femaleIcon = '$pngBasePath/female_icon.png'; static const String femaleIcon = '$pngBasePath/female_icon.png';
static const String lakumLogo = '$pngBasePath/lakum_logo.png'; static const String lakumLogo = '$pngBasePath/lakum_logo.png';
static const String lakumLogoWhite = '$pngBasePath/lakum_logo_white.png';
static const String fullBodyFrontMale = '$pngBasePath/full_body_front_male.png'; static const String fullBodyFrontMale = '$pngBasePath/full_body_front_male.png';
static const String fullBodyBackMale = '$pngBasePath/full_body_back_male.png'; static const String fullBodyBackMale = '$pngBasePath/full_body_back_male.png';

@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/models/lakum_inquiry_information_response_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/models/lakum_inquiry_information_response_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/models/patient_advance_balance_response_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/models/patient_advance_balance_response_model.dart';
@ -49,6 +50,52 @@ class HabibWalletViewModel extends ChangeNotifier {
bool isLakumAccountInfoLoading = false; bool isLakumAccountInfoLoading = false;
late LakumInquiryInformationResponseModel lakumAccountInfo; late LakumInquiryInformationResponseModel lakumAccountInfo;
// Get all flattened transactions sorted by date descending
List<PointsDetails> getAllLakumTransactions() {
if (lakumAccountInfo.gainedPointsAmountPerYear == null) {
return [];
}
List<PointsDetails> allTransactions = [];
// Flatten the nested structure
for (var yearData in lakumAccountInfo.gainedPointsAmountPerYear!) {
if (yearData.pointsAmountPerMonth != null) {
for (var monthData in yearData.pointsAmountPerMonth!) {
if (monthData.pointsAmountPerday != null) {
for (var dayData in monthData.pointsAmountPerday!) {
if (dayData.pointsDetails != null) {
allTransactions.addAll(dayData.pointsDetails!);
}
}
}
}
}
}
// Sort by transaction date DESCENDING (latest/most recent transactions first, older transactions last)
allTransactions.sort((a, b) {
// Null dates go to the end
if (a.transactionDate == null && b.transactionDate == null) return 0;
if (a.transactionDate == null) return 1; // a goes after b
if (b.transactionDate == null) return -1; // a goes before b
try {
DateTime dateA = DateUtil.convertStringToDate(a.transactionDate!);
DateTime dateB = DateUtil.convertStringToDate(b.transactionDate!);
// Compare dateB to dateA (not dateA to dateB) for DESCENDING order
// This puts newer dates (larger values) at the top of the list
return dateB.compareTo(dateA);
} catch (e) {
// If date parsing fails, maintain current order
return 0;
}
});
return allTransactions;
}
// Clear amount error // Clear amount error
void clearAmountError() { void clearAmountError() {
_amountError = null; _amountError = null;

@ -347,8 +347,11 @@ class PrescriptionsViewModel extends ChangeNotifier {
result.fold( result.fold(
(failure) async { (failure) async {
isPrescriptionsDeliveryOrdersLoading = false; isPrescriptionsDeliveryOrdersLoading = false;
prescriptionsOrderList.clear();
notifyListeners(); notifyListeners();
onError!(failure.message); if(onError != null) {
onError(failure.message);
}
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {

@ -1840,5 +1840,6 @@ abstract class LocaleKeys {
static const submitEReferral = 'submitEReferral'; static const submitEReferral = 'submitEReferral';
static const redeem = 'redeem'; static const redeem = 'redeem';
static const points = 'points'; static const points = 'points';
static const transactions = 'transactions';
} }

@ -33,6 +33,7 @@ class _HabibWalletState extends State<HabibWalletPage> {
scheduleMicrotask(() async { scheduleMicrotask(() async {
habibWalletVM.initHabibWalletProvider(); habibWalletVM.initHabibWalletProvider();
habibWalletVM.getPatientBalanceAmount(); habibWalletVM.getPatientBalanceAmount();
habibWalletVM.getLakumAccountInformation();
}); });
super.initState(); super.initState();
@ -123,6 +124,7 @@ class _HabibWalletState extends State<HabibWalletPage> {
.then((val) { .then((val) {
habibWalletVM.initHabibWalletProvider(); habibWalletVM.initHabibWalletProvider();
habibWalletVM.getPatientBalanceAmount(); habibWalletVM.getPatientBalanceAmount();
habibWalletVM.getLakumAccountInformation();
}); });
}, },
), ),

@ -0,0 +1,430 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.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';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/models/lakum_inquiry_information_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
class LakumWalletDetails extends StatelessWidget {
const LakumWalletDetails({super.key});
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return CollapsingListView(
title: LocaleKeys.lakumPoints.tr(context: context),
child: Consumer<HabibWalletViewModel>(
builder: (context, viewModel, child) {
// Check if loading
if (viewModel.isLakumAccountInfoLoading) {
return Center(
child: Padding(
padding: EdgeInsets.all(32.w),
child: CircularProgressIndicator(
color: AppColors.primaryRedColor,
),
),
);
}
// Check if account number is not available
if (viewModel.yahalaAccountNumber == "0" || viewModel.yahalaAccountNumber.isEmpty) {
return Center(
child: Padding(
padding: EdgeInsets.all(32.w),
child: LocaleKeys.lakumMsg.tr().toText14(
color: AppColors.greyTextColor,
),
),
);
}
// Get all transactions
List<PointsDetails> transactions = viewModel.getAllLakumTransactions();
// Display main UI
return SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Main Lakum Card with gradient and user info
_buildLakumMainCard(viewModel.lakumAccountInfo, appState),
SizedBox(height: 24.h),
// Redeem Button
_buildRedeemButton(context),
SizedBox(height: 24.h),
// Transaction List Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.transactions.tr(context: context).toText16(isBold: true),
if (transactions.isNotEmpty) "${transactions.length} Records".toText12(color: AppColors.greyTextColor),
],
),
SizedBox(height: 12.h),
// Transaction List Container
if (transactions.isEmpty) _buildEmptyTransactions() else _buildTransactionsList(transactions),
SizedBox(height: 24.h),
],
),
),
);
},
),
);
}
Widget _buildLakumMainCard(LakumInquiryInformationResponseModel accountInfo, AppState appState) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.habibPharmacyColor,
AppColors.habibPharmacyColor.withAlpha(220),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(24.r),
boxShadow: [
BoxShadow(
color: AppColors.habibPharmacyColor.withAlpha(76),
blurRadius: 16,
offset: Offset(0, 8),
),
],
),
child: Padding(
padding: EdgeInsets.all(20.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Top Row: User Info and Grid Icon
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${appState.getAuthenticatedUser()?.firstName ?? ''} ${appState.getAuthenticatedUser()?.lastName ?? ''}".toText18(
isBold: true,
color: Colors.white,
),
SizedBox(height: 4.h),
(accountInfo.accountNumber ?? 0).toString().toText14(
color: Colors.white.withAlpha(230),
),
],
),
),
Utils.buildImgWithAssets(
icon: AppAssets.lakumLogoWhite,
width: 80.h,
height: 80.h,
),
],
),
// SizedBox(height: 24.h),
LocaleKeys.lakumPoints.tr().toText18(color: Colors.white.withAlpha(230), isBold: true),
SizedBox(height: 8.h),
// Points Balance
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
(accountInfo.pointsBalance?.toString() ?? "0").toText44(
isBold: true,
color: Colors.white,
),
SizedBox(width: 8.w),
LocaleKeys.points
.tr()
.toText18(
color: Colors.white.withAlpha(230),
isBold: true,
)
.paddingOnly(bottom: 8.h),
],
),
SizedBox(height: 4.h),
// Balance Amount in SAR
"${accountInfo.pointsBalanceAmount?.toStringAsFixed(2) ?? "0.00"} SAR".toText18(
color: Colors.white.withAlpha(204),
),
SizedBox(height: 20.h),
// Stats Row
Row(
children: [
Expanded(
child: _buildStatChip(
LocaleKeys.gained.tr(),
accountInfo.gainedPoints?.toString() ?? "0",
Icons.arrow_circle_up,
),
),
SizedBox(width: 8.w),
Expanded(
child: _buildStatChip(
LocaleKeys.consumed.tr(),
accountInfo.consumedPoints?.toString() ?? "0",
Icons.arrow_circle_down,
),
),
],
),
],
),
),
);
}
Widget _buildStatChip(String label, String value, IconData icon) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.h),
decoration: BoxDecoration(
color: Colors.white.withAlpha(51),
borderRadius: BorderRadius.circular(12.r),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: Colors.white, size: 16.f),
SizedBox(width: 6.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
value.toText14(color: Colors.white, isBold: true),
label.toText10(color: Colors.white.withAlpha(204)),
],
),
],
),
);
}
Widget _buildRedeemButton(BuildContext context) {
return CustomButton(
height: 46.h,
icon: AppAssets.redeem_icon,
iconColor: AppColors.whiteColor,
iconSize: 24.h,
backgroundColor: AppColors.habibPharmacyColor,
textColor: AppColors.whiteColor,
text: LocaleKeys.redeem.tr(context: context),
borderWidth: 0.w,
fontWeight: FontWeight.w600,
borderColor: AppColors.habibPharmacyColor.withAlpha(15),
padding: EdgeInsets.fromLTRB(4, 0, 12, 0),
fontSize: 14.f,
onPressed: () {
Uri uri = Uri.parse(PHARMACY_REDIRECT_URL);
launchUrl(uri, mode: LaunchMode.externalApplication);
},
);
}
Widget _buildEmptyTransactions() {
return Container(
padding: EdgeInsets.all(32.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.r,
hasShadow: false,
),
child: Center(
child: Column(
children: [
Icon(Icons.receipt_long_outlined, size: 48.f, color: AppColors.greyTextColor),
SizedBox(height: 12.h),
"No transactions found".toText14(
color: AppColors.greyTextColor,
isCenter: true,
),
],
),
),
);
}
Widget _buildTransactionsList(List<PointsDetails> transactions) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.r,
hasShadow: false,
),
child: ListView.separated(
padding: EdgeInsets.all(16.w),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: transactions.length,
separatorBuilder: (context, index) => Divider(
color: AppColors.dividerColor,
height: 24.h,
),
itemBuilder: (context, index) {
return _buildTransactionItem(transactions[index]);
},
),
);
}
Widget _buildTransactionItem(PointsDetails transaction) {
// Determine if points were gained or used
bool isGained = transaction.operationType?.toLowerCase() == 'gain' || transaction.operationType?.toLowerCase() == 'credit' || (transaction.points ?? 0) > 0;
Color transactionColor = isGained ? AppColors.habibPharmacyColor : AppColors.primaryRedColor;
String pointsText = "${isGained ? '+' : '-'}${transaction.points?.abs() ?? 0}";
// Format date
String formattedDate = "";
if (transaction.transactionDate != null) {
try {
formattedDate = DateUtil.formatDateToDate(
DateUtil.convertStringToDate(transaction.transactionDate!),
false,
);
} catch (e) {
formattedDate = transaction.transactionDate ?? "";
}
}
return Row(
children: [
// Transaction Icon
Container(
width: 44.w,
height: 44.h,
decoration: BoxDecoration(
color: transactionColor.withAlpha(25),
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
isGained ? Icons.add_rounded : Icons.remove_rounded,
color: transactionColor,
size: 24.f,
),
),
SizedBox(width: 12.w),
// Transaction Details
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(transaction.subTransactionTypeDescription ?? transaction.operationType ?? "Transaction").toText14(
isBold: true,
maxlines: 2,
),
SizedBox(height: 2.h),
formattedDate.toText12(color: AppColors.greyTextColor),
],
),
),
// Points Value
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
pointsText.toText18(
isBold: true,
color: transactionColor,
),
if (transaction.amount != null && transaction.amount! > 0)
"${transaction.amount?.toStringAsFixed(2) ?? "0.00"} SAR".toText11(
color: AppColors.greyTextColor,
),
],
),
],
);
}
}
// Custom painter for wavy background pattern
class _WavyBackgroundPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// Base color from AppColors
final baseColor = AppColors.habibPharmacyColor;
final paint = Paint()
..color = Colors.white.withAlpha(25)
..style = PaintingStyle.fill;
// Draw multiple wave patterns
final path1 = Path();
path1.moveTo(0, size.height * 0.3);
path1.quadraticBezierTo(
size.width * 0.25,
size.height * 0.2,
size.width * 0.5,
size.height * 0.3,
);
path1.quadraticBezierTo(
size.width * 0.75,
size.height * 0.4,
size.width,
size.height * 0.3,
);
path1.lineTo(size.width, 0);
path1.lineTo(0, 0);
path1.close();
canvas.drawPath(path1, paint);
// Second wave with darker shade
paint.color = Colors.black.withAlpha(15);
final path2 = Path();
path2.moveTo(0, size.height * 0.6);
path2.quadraticBezierTo(
size.width * 0.3,
size.height * 0.5,
size.width * 0.6,
size.height * 0.6,
);
path2.quadraticBezierTo(
size.width * 0.8,
size.height * 0.7,
size.width,
size.height * 0.6,
);
path2.lineTo(size.width, size.height);
path2.lineTo(0, size.height);
path2.close();
canvas.drawPath(path2, paint);
// Decorative circles with white overlay
paint.color = Colors.white.withAlpha(20);
canvas.drawCircle(Offset(size.width * 0.85, size.height * 0.15), 30, paint);
paint.color = Colors.white.withAlpha(10);
canvas.drawCircle(Offset(size.width * 0.15, size.height * 0.75), 40, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

@ -12,6 +12,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/home/lakum_wallet_details.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
@ -66,7 +67,7 @@ class LakumWalletCard extends StatelessWidget {
// mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [ // children: [
// LocaleKeys.habibWallet.tr(context: context).toText16(isBold: true, letterSpacing: -0.2), // LocaleKeys.habibWallet.tr(context: context).toText16(isBold: true, letterSpacing: -0.2),
"Lakum Wallet".toText16(isBold: true, letterSpacing: -0.2), LocaleKeys.lakumPoints.tr(context: context).toText16(isBold: true, letterSpacing: -0.2),
// Container( // Container(
// height: 40.h, // height: 40.h,
// width: 40.h, // width: 40.h,
@ -121,14 +122,14 @@ class LakumWalletCard extends StatelessWidget {
child: CustomButton( child: CustomButton(
height: 40.h, height: 40.h,
icon: AppAssets.redeem_icon, icon: AppAssets.redeem_icon,
iconColor: AppColors.successColor, iconColor: AppColors.habibPharmacyColor,
iconSize: 24.h, iconSize: 24.h,
backgroundColor: AppColors.successColor.withAlpha(15), backgroundColor: AppColors.habibPharmacyColor.withAlpha(15),
textColor: AppColors.successColor, textColor: AppColors.habibPharmacyColor,
text: LocaleKeys.redeem.tr(context: context), text: LocaleKeys.redeem.tr(context: context),
borderWidth: 0.w, borderWidth: 0.w,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
borderColor: AppColors.successColor.withAlpha(15), borderColor: AppColors.habibPharmacyColor.withAlpha(15),
padding: EdgeInsets.fromLTRB(4, 0, 12, 0), padding: EdgeInsets.fromLTRB(4, 0, 12, 0),
fontSize: 14.f, fontSize: 14.f,
onPressed: () { onPressed: () {
@ -164,7 +165,7 @@ class LakumWalletCard extends StatelessWidget {
).onPress(() { ).onPress(() {
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: HabibWalletPage(), page: LakumWalletDetails(),
), ),
); );
}), }),

@ -40,6 +40,7 @@ class AppColors {
static Color get lightGreenColor => isDarkMode ? dark.lightGreenColor : const Color(0xFF0ccedde); static Color get lightGreenColor => isDarkMode ? dark.lightGreenColor : const Color(0xFF0ccedde);
static Color get ratingColorYellow => isDarkMode ? dark.ratingColorYellow : const Color(0xFFFFAF15); static Color get ratingColorYellow => isDarkMode ? dark.ratingColorYellow : const Color(0xFFFFAF15);
static Color get spacerLineColor => isDarkMode ? dark.spacerLineColor : const Color(0x2E30391A); static Color get spacerLineColor => isDarkMode ? dark.spacerLineColor : const Color(0x2E30391A);
static Color get habibPharmacyColor => const Color(0xFF2d9137);
// Doctor Rating Colors // Doctor Rating Colors
static const Color ratingStarIconColor = Color(0xFFFFA726); // Orange-amber for star icons static const Color ratingStarIconColor = Color(0xFFFFA726); // Orange-amber for star icons

Loading…
Cancel
Save