Merge branch 'refs/heads/master' into offers_discounts
# Conflicts: # lib/core/api_consts.dart # lib/presentation/home/landing_page.dart # lib/presentation/my_family/widget/family_cards.dartpull/319/head
commit
2b53cce0aa
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@ -0,0 +1,5 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 10.75C0 4.81294 4.81294 0 10.75 0C15.5683 0 19.597 3.1682 20.9655 7.52526C21.037 7.75307 20.9958 8.00133 20.8545 8.19382C20.7133 8.3863 20.4887 8.5 20.25 8.5H17.75C17.3357 8.5 17 8.16421 17 7.75C17 7.33579 17.3357 7 17.75 7H19.1673C17.7335 3.75655 14.5197 1.5 10.75 1.5C5.64137 1.5 1.5 5.64137 1.5 10.75C1.5 15.8586 5.64133 20 10.75 20C11.1642 20 11.5 20.3358 11.5 20.75C11.5 21.1642 11.1642 21.5 10.75 21.5C4.8129 21.5 0 16.6871 0 10.75Z" fill="white"/>
|
||||
<path d="M10.75 6C11.1642 6 11.5 6.33579 11.5 6.75V10.4393L13.2803 12.2197C13.5732 12.5126 13.5732 12.9874 13.2803 13.2803C12.9874 13.5732 12.5125 13.5732 12.2196 13.2803L10.2196 11.2803C10.079 11.1397 9.99996 10.9489 9.99996 10.75V6.75C9.99996 6.33579 10.3357 6 10.75 6Z" fill="white"/>
|
||||
<path d="M15.2803 14.2197C14.9874 13.9268 14.5126 13.9268 14.2197 14.2197C13.9268 14.5126 13.9268 14.9874 14.2197 15.2803L16.6893 17.75L14.2197 20.2197C13.9268 20.5126 13.9268 20.9874 14.2197 21.2803C14.5126 21.5732 14.9874 21.5732 15.2803 21.2803L17.75 18.8107L20.2197 21.2803C20.5126 21.5732 20.9874 21.5732 21.2803 21.2803C21.5732 20.9874 21.5732 20.5126 21.2803 20.2197L18.8107 17.75L21.2803 15.2803C21.5732 14.9874 21.5732 14.5126 21.2803 14.2197C20.9874 13.9268 20.5126 13.9268 20.2197 14.2197L17.75 16.6893L15.2803 14.2197Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 28 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.93712 2.25C5.05309 2.24995 4.28757 2.2499 3.67381 2.33562C3.01162 2.4281 2.38072 2.63711 1.87303 3.16449C1.37047 3.68654 1.17623 4.32619 1.08959 4.99561C1.00807 5.62545 1.0081 6.41382 1.00815 7.33738L1.00815 7.40177C1.00815 7.65552 1.00591 7.89931 1.00378 8.13142C0.998266 8.73244 0.993469 9.25515 1.0301 9.66981C1.08709 10.315 1.25466 10.9156 1.76284 11.4435C2.14586 11.8414 2.77891 11.8535 3.17679 11.4704C3.57468 11.0874 3.58672 10.4544 3.2037 10.0565C3.12657 9.97636 3.05658 9.88147 3.02234 9.49383C2.99474 9.18135 2.99819 8.80852 3.00318 8.27087C3.0055 8.02045 3.00815 7.73426 3.00815 7.40177C3.00815 6.39595 3.01012 5.73847 3.07304 5.25232C3.13273 4.79115 3.23113 4.63751 3.31389 4.55154C3.39151 4.47092 3.52427 4.37592 3.95045 4.31639C4.40942 4.25229 5.0335 4.25001 6.00488 4.25001L17.9951 4.25001C18.9664 4.25001 19.5905 4.25229 20.0495 4.3164C20.4757 4.37592 20.6084 4.47092 20.686 4.55155C20.7688 4.63751 20.8672 4.79115 20.9269 5.25232C20.9898 5.73847 20.9918 6.39595 20.9918 7.40177C20.9918 7.73233 20.9944 8.01713 20.9967 8.26653C21.0016 8.80612 21.005 9.18046 20.9773 9.4935C20.9429 9.88104 20.8729 9.97616 20.7956 10.0565C20.4125 10.4544 20.4246 11.0874 20.8225 11.4704C21.2203 11.8535 21.8534 11.8414 22.2364 11.4435C22.7444 10.9158 22.9123 10.3154 22.9695 9.67014C23.0063 9.25455 23.0016 8.73073 22.9961 8.12825C22.994 7.89711 22.9918 7.65439 22.9918 7.40177L22.9918 7.33739C22.9918 6.41387 22.9919 5.62543 22.9103 4.99561C22.8237 4.32619 22.6295 3.68654 22.1269 3.16449C21.6192 2.63711 20.9883 2.4281 20.3261 2.33562C19.7124 2.2499 18.9468 2.24995 18.0628 2.25L5.93712 2.25Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1978 6.25C14.9586 6.24998 15.598 6.24996 16.1143 6.31403C16.6617 6.38197 17.1602 6.5306 17.5912 6.89146C18.0222 7.25232 18.2562 7.71697 18.4193 8.24389C18.5731 8.74084 18.6855 9.3703 18.8192 10.1193L19.4895 13.8726C19.7725 15.4578 19.9993 16.7275 20.0348 17.7329C20.0715 18.7716 19.9118 19.6676 19.2925 20.4073C18.6731 21.1471 17.8191 21.4618 16.7902 21.6082C15.7942 21.75 14.5044 21.75 12.8942 21.75H11.1065C9.49624 21.75 8.20645 21.75 7.21049 21.6082C6.18153 21.4618 5.32758 21.1471 4.70822 20.4073C4.08886 19.6676 3.92921 18.7716 3.96588 17.7329C4.00137 16.7275 4.22812 15.4578 4.51121 13.8727L5.18145 10.1193C5.31517 9.37034 5.42756 8.74084 5.58139 8.24389C5.74449 7.71697 5.97845 7.25232 6.40946 6.89146C6.84047 6.5306 7.339 6.38197 7.8864 6.31403C8.40264 6.24996 9.04206 6.24998 9.80289 6.25H14.1978ZM12 9.75C12.4142 9.75 12.75 10.0858 12.75 10.5V15.6893L13.9697 14.4697C14.2626 14.1768 14.7374 14.1768 15.0303 14.4697C15.3232 14.7626 15.3232 15.2374 15.0303 15.5303L12.5303 18.0303C12.2374 18.3232 11.7626 18.3232 11.4697 18.0303L8.96967 15.5303C8.67678 15.2374 8.67678 14.7626 8.96967 14.4697C9.26256 14.1768 9.73744 14.1768 10.0303 14.4697L11.25 15.6893V10.5C11.25 10.0858 11.5858 9.75 12 9.75Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@ -0,0 +1,12 @@
|
||||
/// Default configuration values for PayTabs Payment SDK
|
||||
class PaymentSdkDefaultConfig {
|
||||
static const String profileID = "119730";
|
||||
static const String serverKey = "SWJ9LD22MT-JLJZLBKTLK-WGWTLWGTWR";
|
||||
static const String clientKey = "C7K2DQ-PDDN6B-69B2K7-P9GB9T";
|
||||
static const String defaultCurrency = "SAR";
|
||||
static const double defaultAmount = 10.0;
|
||||
static const String defaultMerchantCountryCode = "SA";
|
||||
static const String defaultMerchantName = "Sulaiman Al Habib Medical Group";
|
||||
static const String defaultMerchantAppleBundleID = "merchant.com.hmgwebservices";
|
||||
static const String defaultMerchantAppleBundleIDUAT = "merchant.com.hmgwebservices.uat";
|
||||
}
|
||||
@ -0,0 +1,318 @@
|
||||
class LakumInquiryInformationResponseModel {
|
||||
int? accountNumber;
|
||||
String? accountStatus;
|
||||
String? barCode;
|
||||
num? consumedPoints;
|
||||
dynamic consumedPointsAmount;
|
||||
dynamic consumedPointsAmountPerYear;
|
||||
|
||||
// List<Null>? consumedPointsDetails;
|
||||
String? createdDate;
|
||||
num? expiredPoints;
|
||||
String? expiryDate;
|
||||
num? gainedPoints;
|
||||
num? gainedPointsAmount;
|
||||
List<GainedPointsAmountPerYear>? gainedPointsAmountPerYear;
|
||||
|
||||
// List<Null>? gainedPointsDetails;
|
||||
String? lakumMessageStatus;
|
||||
String? memberName;
|
||||
String? memberUniversalId;
|
||||
String? mobileNumber;
|
||||
num? pointsBalance;
|
||||
num? pointsBalanceAmount;
|
||||
num? pointsWillBeExpired;
|
||||
String? prefLang;
|
||||
num? statusCode;
|
||||
num? transferPoints;
|
||||
dynamic transferPointsAmountPerYear;
|
||||
|
||||
// List<Null>? transferPointsDetails;
|
||||
num? waitingPoints;
|
||||
num? loyalityAmount;
|
||||
num? loyalityPoints;
|
||||
num? purchaseRate;
|
||||
|
||||
LakumInquiryInformationResponseModel(
|
||||
{this.accountNumber,
|
||||
this.accountStatus,
|
||||
this.barCode,
|
||||
this.consumedPoints,
|
||||
this.consumedPointsAmount,
|
||||
this.consumedPointsAmountPerYear,
|
||||
// this.consumedPointsDetails,
|
||||
this.createdDate,
|
||||
this.expiredPoints,
|
||||
this.expiryDate,
|
||||
this.gainedPoints,
|
||||
this.gainedPointsAmount,
|
||||
this.gainedPointsAmountPerYear,
|
||||
// this.gainedPointsDetails,
|
||||
this.lakumMessageStatus,
|
||||
this.memberName,
|
||||
this.memberUniversalId,
|
||||
this.mobileNumber,
|
||||
this.pointsBalance,
|
||||
this.pointsBalanceAmount,
|
||||
this.pointsWillBeExpired,
|
||||
this.prefLang,
|
||||
this.statusCode,
|
||||
this.transferPoints,
|
||||
this.transferPointsAmountPerYear,
|
||||
// this.transferPointsDetails,
|
||||
this.waitingPoints,
|
||||
this.loyalityAmount,
|
||||
this.loyalityPoints,
|
||||
this.purchaseRate});
|
||||
|
||||
LakumInquiryInformationResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
accountNumber = json['AccountNumber'];
|
||||
accountStatus = json['AccountStatus'];
|
||||
barCode = json['BarCode'];
|
||||
consumedPoints = json['ConsumedPoints'];
|
||||
consumedPointsAmount = json['ConsumedPointsAmount'];
|
||||
consumedPointsAmountPerYear = json['ConsumedPointsAmountPerYear'];
|
||||
// if (json['ConsumedPointsDetails'] != null) {
|
||||
// consumedPointsDetails = <Null>[];
|
||||
// json['ConsumedPointsDetails'].forEach((v) {
|
||||
// consumedPointsDetails!.add(new Null.fromJson(v));
|
||||
// });
|
||||
// }
|
||||
createdDate = json['CreatedDate'];
|
||||
expiredPoints = json['ExpiredPoints'];
|
||||
expiryDate = json['ExpiryDate'];
|
||||
gainedPoints = json['GainedPoints'];
|
||||
gainedPointsAmount = json['GainedPointsAmount'];
|
||||
if (json['GainedPointsAmountPerYear'] != null) {
|
||||
gainedPointsAmountPerYear = <GainedPointsAmountPerYear>[];
|
||||
json['GainedPointsAmountPerYear'].forEach((v) {
|
||||
gainedPointsAmountPerYear!.add(new GainedPointsAmountPerYear.fromJson(v));
|
||||
});
|
||||
}
|
||||
// if (json['GainedPointsDetails'] != null) {
|
||||
// gainedPointsDetails = <Null>[];
|
||||
// json['GainedPointsDetails'].forEach((v) {
|
||||
// gainedPointsDetails!.add(new Null.fromJson(v));
|
||||
// });
|
||||
// }
|
||||
lakumMessageStatus = json['LakumMessageStatus'];
|
||||
memberName = json['MemberName'];
|
||||
memberUniversalId = json['MemberUniversalId'];
|
||||
mobileNumber = json['MobileNumber'];
|
||||
pointsBalance = json['PointsBalance'];
|
||||
pointsBalanceAmount = json['PointsBalanceAmount'];
|
||||
pointsWillBeExpired = json['PointsWillBeExpired'];
|
||||
prefLang = json['PrefLang'];
|
||||
statusCode = json['StatusCode'];
|
||||
transferPoints = json['TransferPoints'];
|
||||
transferPointsAmountPerYear = json['TransferPointsAmountPerYear'];
|
||||
// if (json['TransferPointsDetails'] != null) {
|
||||
// transferPointsDetails = <Null>[];
|
||||
// json['TransferPointsDetails'].forEach((v) {
|
||||
// transferPointsDetails!.add(new Null.fromJson(v));
|
||||
// });
|
||||
// }
|
||||
waitingPoints = json['WaitingPoints'];
|
||||
loyalityAmount = json['loyalityAmount'];
|
||||
loyalityPoints = json['loyalityPoints'];
|
||||
purchaseRate = json['purchaseRate'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['AccountNumber'] = this.accountNumber;
|
||||
data['AccountStatus'] = this.accountStatus;
|
||||
data['BarCode'] = this.barCode;
|
||||
data['ConsumedPoints'] = this.consumedPoints;
|
||||
data['ConsumedPointsAmount'] = this.consumedPointsAmount;
|
||||
data['ConsumedPointsAmountPerYear'] = this.consumedPointsAmountPerYear;
|
||||
// if (this.consumedPointsDetails != null) {
|
||||
// data['ConsumedPointsDetails'] = this.consumedPointsDetails!.map((v) => v.toJson()).toList();
|
||||
// }
|
||||
data['CreatedDate'] = this.createdDate;
|
||||
data['ExpiredPoints'] = this.expiredPoints;
|
||||
data['ExpiryDate'] = this.expiryDate;
|
||||
data['GainedPoints'] = this.gainedPoints;
|
||||
data['GainedPointsAmount'] = this.gainedPointsAmount;
|
||||
if (this.gainedPointsAmountPerYear != null) {
|
||||
data['GainedPointsAmountPerYear'] = this.gainedPointsAmountPerYear!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
// if (this.gainedPointsDetails != null) {
|
||||
// data['GainedPointsDetails'] = this.gainedPointsDetails!.map((v) => v.toJson()).toList();
|
||||
// }
|
||||
data['LakumMessageStatus'] = this.lakumMessageStatus;
|
||||
data['MemberName'] = this.memberName;
|
||||
data['MemberUniversalId'] = this.memberUniversalId;
|
||||
data['MobileNumber'] = this.mobileNumber;
|
||||
data['PointsBalance'] = this.pointsBalance;
|
||||
data['PointsBalanceAmount'] = this.pointsBalanceAmount;
|
||||
data['PointsWillBeExpired'] = this.pointsWillBeExpired;
|
||||
data['PrefLang'] = this.prefLang;
|
||||
data['StatusCode'] = this.statusCode;
|
||||
data['TransferPoints'] = this.transferPoints;
|
||||
data['TransferPointsAmountPerYear'] = this.transferPointsAmountPerYear;
|
||||
// if (this.transferPointsDetails != null) {
|
||||
// data['TransferPointsDetails'] = this.transferPointsDetails!.map((v) => v.toJson()).toList();
|
||||
// }
|
||||
data['WaitingPoints'] = this.waitingPoints;
|
||||
data['loyalityAmount'] = this.loyalityAmount;
|
||||
data['loyalityPoints'] = this.loyalityPoints;
|
||||
data['purchaseRate'] = this.purchaseRate;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class GainedPointsAmountPerYear {
|
||||
num? amountPerYear;
|
||||
List<PointsAmountPerMonth>? pointsAmountPerMonth;
|
||||
num? pointsPerYear;
|
||||
int? year;
|
||||
|
||||
GainedPointsAmountPerYear({this.amountPerYear, this.pointsAmountPerMonth, this.pointsPerYear, this.year});
|
||||
|
||||
GainedPointsAmountPerYear.fromJson(Map<String, dynamic> json) {
|
||||
amountPerYear = json['AmountPerYear'];
|
||||
if (json['PointsAmountPerMonth'] != null) {
|
||||
pointsAmountPerMonth = <PointsAmountPerMonth>[];
|
||||
json['PointsAmountPerMonth'].forEach((v) {
|
||||
pointsAmountPerMonth!.add(new PointsAmountPerMonth.fromJson(v));
|
||||
});
|
||||
}
|
||||
pointsPerYear = json['PointsPerYear'];
|
||||
year = json['Year'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['AmountPerYear'] = this.amountPerYear;
|
||||
if (this.pointsAmountPerMonth != null) {
|
||||
data['PointsAmountPerMonth'] = this.pointsAmountPerMonth!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
data['PointsPerYear'] = this.pointsPerYear;
|
||||
data['Year'] = this.year;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PointsAmountPerMonth {
|
||||
num? amountPerMonth;
|
||||
String? month;
|
||||
num? monthNumber;
|
||||
List<PointsAmountPerday>? pointsAmountPerday;
|
||||
num? pointsPerMonth;
|
||||
|
||||
PointsAmountPerMonth({this.amountPerMonth, this.month, this.monthNumber, this.pointsAmountPerday, this.pointsPerMonth});
|
||||
|
||||
PointsAmountPerMonth.fromJson(Map<String, dynamic> json) {
|
||||
amountPerMonth = json['AmountPerMonth'];
|
||||
month = json['Month'];
|
||||
monthNumber = json['MonthNumber'];
|
||||
if (json['PointsAmountPerday'] != null) {
|
||||
pointsAmountPerday = <PointsAmountPerday>[];
|
||||
json['PointsAmountPerday'].forEach((v) {
|
||||
pointsAmountPerday!.add(new PointsAmountPerday.fromJson(v));
|
||||
});
|
||||
}
|
||||
pointsPerMonth = json['PointsPerMonth'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['AmountPerMonth'] = this.amountPerMonth;
|
||||
data['Month'] = this.month;
|
||||
data['MonthNumber'] = this.monthNumber;
|
||||
if (this.pointsAmountPerday != null) {
|
||||
data['PointsAmountPerday'] = this.pointsAmountPerday!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
data['PointsPerMonth'] = this.pointsPerMonth;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PointsAmountPerday {
|
||||
num? amountPerDay;
|
||||
String? day;
|
||||
List<PointsDetails>? pointsDetails;
|
||||
num? pointsPerDay;
|
||||
String? transationDate;
|
||||
|
||||
PointsAmountPerday({this.amountPerDay, this.day, this.pointsDetails, this.pointsPerDay, this.transationDate});
|
||||
|
||||
PointsAmountPerday.fromJson(Map<String, dynamic> json) {
|
||||
amountPerDay = json['AmountPerDay'];
|
||||
day = json['Day'];
|
||||
if (json['PointsDetails'] != null) {
|
||||
pointsDetails = <PointsDetails>[];
|
||||
json['PointsDetails'].forEach((v) {
|
||||
pointsDetails!.add(new PointsDetails.fromJson(v));
|
||||
});
|
||||
}
|
||||
pointsPerDay = json['PointsPerDay'];
|
||||
transationDate = json['TransationDate'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['AmountPerDay'] = this.amountPerDay;
|
||||
data['Day'] = this.day;
|
||||
if (this.pointsDetails != null) {
|
||||
data['PointsDetails'] = this.pointsDetails!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
data['PointsPerDay'] = this.pointsPerDay;
|
||||
data['TransationDate'] = this.transationDate;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PointsDetails {
|
||||
int? accNumber;
|
||||
String? accountStatus;
|
||||
num? amount;
|
||||
int? lineItemNo;
|
||||
String? operationType;
|
||||
num? points;
|
||||
num? purchasePoints;
|
||||
int? subTransactionType;
|
||||
String? subTransactionTypeDescription;
|
||||
String? transactionDate;
|
||||
|
||||
PointsDetails(
|
||||
{this.accNumber,
|
||||
this.accountStatus,
|
||||
this.amount,
|
||||
this.lineItemNo,
|
||||
this.operationType,
|
||||
this.points,
|
||||
this.purchasePoints,
|
||||
this.subTransactionType,
|
||||
this.subTransactionTypeDescription,
|
||||
this.transactionDate});
|
||||
|
||||
PointsDetails.fromJson(Map<String, dynamic> json) {
|
||||
accNumber = json['AccNumber'];
|
||||
accountStatus = json['AccountStatus'];
|
||||
amount = json['Amount'];
|
||||
lineItemNo = json['LineItemNo'];
|
||||
operationType = json['OperationType'];
|
||||
points = json['Points'];
|
||||
purchasePoints = json['PurchasePoints'];
|
||||
subTransactionType = json['SubTransactionType'];
|
||||
subTransactionTypeDescription = json['SubTransactionTypeDescription'];
|
||||
transactionDate = json['TransactionDate'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['AccNumber'] = this.accNumber;
|
||||
data['AccountStatus'] = this.accountStatus;
|
||||
data['Amount'] = this.amount;
|
||||
data['LineItemNo'] = this.lineItemNo;
|
||||
data['OperationType'] = this.operationType;
|
||||
data['Points'] = this.points;
|
||||
data['PurchasePoints'] = this.purchasePoints;
|
||||
data['SubTransactionType'] = this.subTransactionType;
|
||||
data['SubTransactionTypeDescription'] = this.subTransactionTypeDescription;
|
||||
data['TransactionDate'] = this.transactionDate;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
class GetPharmacyInvoicesResponseModel {
|
||||
String? setupId;
|
||||
int? projectID;
|
||||
int? pharmacyProjectID;
|
||||
int? patientID;
|
||||
int? appointmentNo;
|
||||
String? appointmentDate;
|
||||
dynamic appointmentDateN;
|
||||
int? clinicID;
|
||||
int? doctorID;
|
||||
int? invoiceNo;
|
||||
int? status;
|
||||
String? arrivedOn;
|
||||
String? doctorName;
|
||||
String? doctorNameN;
|
||||
String? displayInvoiceNo;
|
||||
String? clinicName;
|
||||
num? decimalDoctorRate;
|
||||
String? doctorImageURL;
|
||||
num? doctorRate;
|
||||
num? patientNumber;
|
||||
String? projectName;
|
||||
dynamic locationID;
|
||||
|
||||
GetPharmacyInvoicesResponseModel(
|
||||
{this.setupId,
|
||||
this.projectID,
|
||||
this.pharmacyProjectID,
|
||||
this.patientID,
|
||||
this.appointmentNo,
|
||||
this.appointmentDate,
|
||||
this.appointmentDateN,
|
||||
this.clinicID,
|
||||
this.doctorID,
|
||||
this.invoiceNo,
|
||||
this.status,
|
||||
this.arrivedOn,
|
||||
this.doctorName,
|
||||
this.doctorNameN,
|
||||
this.displayInvoiceNo,
|
||||
this.clinicName,
|
||||
this.decimalDoctorRate,
|
||||
this.doctorImageURL,
|
||||
this.doctorRate,
|
||||
this.patientNumber,
|
||||
this.projectName, this.locationID});
|
||||
|
||||
GetPharmacyInvoicesResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
setupId = json['SetupId'];
|
||||
projectID = json['ProjectID'];
|
||||
pharmacyProjectID = json['PharmacyProjectID'];
|
||||
patientID = json['PatientID'];
|
||||
appointmentNo = json['AppointmentNo'];
|
||||
appointmentDate = json['AppointmentDate'];
|
||||
appointmentDateN = json['AppointmentDateN'];
|
||||
clinicID = json['ClinicID'];
|
||||
doctorID = json['DoctorID'];
|
||||
invoiceNo = json['InvoiceNo'];
|
||||
status = json['Status'];
|
||||
arrivedOn = json['ArrivedOn'];
|
||||
doctorName = json['DoctorName'];
|
||||
doctorNameN = json['DoctorNameN'];
|
||||
displayInvoiceNo = json['DisplayInvoiceNo'];
|
||||
clinicName = json['ClinicName'];
|
||||
decimalDoctorRate = json['DecimalDoctorRate'];
|
||||
doctorImageURL = json['DoctorImageURL'];
|
||||
doctorRate = json['DoctorRate'];
|
||||
patientNumber = json['PatientNumber'];
|
||||
projectName = json['ProjectName'];
|
||||
locationID = json['LocationId'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['SetupId'] = this.setupId;
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['PharmacyProjectID'] = this.pharmacyProjectID;
|
||||
data['PatientID'] = this.patientID;
|
||||
data['AppointmentNo'] = this.appointmentNo;
|
||||
data['AppointmentDate'] = this.appointmentDate;
|
||||
data['AppointmentDateN'] = this.appointmentDateN;
|
||||
data['ClinicID'] = this.clinicID;
|
||||
data['DoctorID'] = this.doctorID;
|
||||
data['InvoiceNo'] = this.invoiceNo;
|
||||
data['Status'] = this.status;
|
||||
data['ArrivedOn'] = this.arrivedOn;
|
||||
data['DoctorName'] = this.doctorName;
|
||||
data['DoctorNameN'] = this.doctorNameN;
|
||||
data['DisplayInvoiceNo'] = this.displayInvoiceNo;
|
||||
data['ClinicName'] = this.clinicName;
|
||||
data['DecimalDoctorRate'] = this.decimalDoctorRate;
|
||||
data['DoctorImageURL'] = this.doctorImageURL;
|
||||
data['DoctorRate'] = this.doctorRate;
|
||||
data['PatientNumber'] = this.patientNumber;
|
||||
data['ProjectName'] = this.projectName;
|
||||
data['LocationId'] = this.locationID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
class PaytabsTransactionResponseModel {
|
||||
int? tranTotal;
|
||||
String? transactionReference;
|
||||
String? cartCurrency;
|
||||
String? cartDescription;
|
||||
String? cartID;
|
||||
String? tranCurrency;
|
||||
String? payResponseReturn;
|
||||
bool? isPending;
|
||||
bool? isOnHold;
|
||||
String? token;
|
||||
String? transactionType;
|
||||
bool? isAuthorized;
|
||||
String? trace;
|
||||
dynamic cartAmount;
|
||||
int? merchantId;
|
||||
int? profileId;
|
||||
bool? isProcessed;
|
||||
int? serviceId;
|
||||
PaymentInfo? paymentInfo;
|
||||
bool? isSuccess;
|
||||
|
||||
PaytabsTransactionResponseModel(
|
||||
{this.tranTotal,
|
||||
this.transactionReference,
|
||||
this.cartCurrency,
|
||||
this.cartDescription,
|
||||
this.cartID,
|
||||
this.tranCurrency,
|
||||
this.payResponseReturn,
|
||||
this.isPending,
|
||||
this.isOnHold,
|
||||
this.token,
|
||||
this.transactionType,
|
||||
this.isAuthorized,
|
||||
this.trace,
|
||||
this.cartAmount,
|
||||
this.merchantId,
|
||||
this.profileId,
|
||||
this.isProcessed,
|
||||
this.serviceId,
|
||||
this.paymentInfo,
|
||||
this.isSuccess});
|
||||
|
||||
PaytabsTransactionResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
tranTotal = json['tran_total'];
|
||||
transactionReference = json['transactionReference'];
|
||||
cartCurrency = json['cartCurrency'];
|
||||
cartDescription = json['cartDescription'];
|
||||
cartID = json['cartID'];
|
||||
tranCurrency = json['tran_currency'];
|
||||
payResponseReturn = json['payResponseReturn'];
|
||||
isPending = json['isPending'];
|
||||
isOnHold = json['isOnHold'];
|
||||
token = json['token'];
|
||||
transactionType = json['transactionType'];
|
||||
isAuthorized = json['isAuthorized'];
|
||||
trace = json['trace'];
|
||||
cartAmount = json['cartAmount'];
|
||||
merchantId = json['merchantId'];
|
||||
profileId = json['profileId'];
|
||||
isProcessed = json['isProcessed'];
|
||||
serviceId = json['serviceId'];
|
||||
paymentInfo = json['paymentInfo'] != null
|
||||
? PaymentInfo.fromJson(Map<String, dynamic>.from(json['paymentInfo'] as Map))
|
||||
: null;
|
||||
isSuccess = json['isSuccess'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['tran_total'] = this.tranTotal;
|
||||
data['transactionReference'] = this.transactionReference;
|
||||
data['cartCurrency'] = this.cartCurrency;
|
||||
data['cartDescription'] = this.cartDescription;
|
||||
data['cartID'] = this.cartID;
|
||||
data['tran_currency'] = this.tranCurrency;
|
||||
data['payResponseReturn'] = this.payResponseReturn;
|
||||
data['isPending'] = this.isPending;
|
||||
data['isOnHold'] = this.isOnHold;
|
||||
data['token'] = this.token;
|
||||
data['transactionType'] = this.transactionType;
|
||||
data['isAuthorized'] = this.isAuthorized;
|
||||
data['trace'] = this.trace;
|
||||
data['cartAmount'] = this.cartAmount;
|
||||
data['merchantId'] = this.merchantId;
|
||||
data['profileId'] = this.profileId;
|
||||
data['isProcessed'] = this.isProcessed;
|
||||
data['serviceId'] = this.serviceId;
|
||||
if (this.paymentInfo != null) {
|
||||
data['paymentInfo'] = this.paymentInfo!.toJson();
|
||||
}
|
||||
data['isSuccess'] = this.isSuccess;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PaymentInfo {
|
||||
String? cardScheme;
|
||||
String? cardType;
|
||||
int? expiryMonth;
|
||||
int? expiryYear;
|
||||
String? paymentDescription;
|
||||
String? paymentMethod;
|
||||
|
||||
PaymentInfo(
|
||||
{this.cardScheme,
|
||||
this.cardType,
|
||||
this.expiryMonth,
|
||||
this.expiryYear,
|
||||
this.paymentDescription,
|
||||
this.paymentMethod});
|
||||
|
||||
PaymentInfo.fromJson(Map<String, dynamic> json) {
|
||||
cardScheme = json['cardScheme'];
|
||||
cardType = json['cardType'];
|
||||
expiryMonth = json['expiryMonth'];
|
||||
expiryYear = json['expiryYear'];
|
||||
paymentDescription = json['paymentDescription'];
|
||||
paymentMethod = json['payment_method'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['cardScheme'] = this.cardScheme;
|
||||
data['cardType'] = this.cardType;
|
||||
data['expiryMonth'] = this.expiryMonth;
|
||||
data['expiryYear'] = this.expiryYear;
|
||||
data['paymentDescription'] = this.paymentDescription;
|
||||
data['payment_method'] = this.paymentMethod;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
|
||||
|
||||
abstract class PaytabsService {
|
||||
Future<Either<Failure, dynamic>> getAskDoctorAppointmentsList();
|
||||
|
||||
Future<Either<Failure, dynamic>> getDoctorResponse();
|
||||
}
|
||||
|
||||
class PaytabsServiceImp implements PaytabsService {
|
||||
@override
|
||||
Future<Either<Failure, dynamic>> getAskDoctorAppointmentsList() {
|
||||
// TODO: implement getAskDoctorAppointmentsList
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, dynamic>> getDoctorResponse() {
|
||||
// TODO: implement getDoctorResponse
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_paytabs_bridge/BaseBillingShippingInfo.dart';
|
||||
import 'package:flutter_paytabs_bridge/IOSThemeConfiguration.dart';
|
||||
import 'package:flutter_paytabs_bridge/PaymentSdkConfigurationDetails.dart';
|
||||
import 'package:flutter_paytabs_bridge/PaymentSdkLocale.dart';
|
||||
import 'package:flutter_paytabs_bridge/PaymentSdkTokeniseType.dart';
|
||||
import 'package:flutter_paytabs_bridge/PaymentSdkTransactionType.dart';
|
||||
import 'package:flutter_paytabs_bridge/flutter_paytabs_bridge.dart';
|
||||
import 'package:hmg_patient_app_new/core/api_consts.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/core/paytabs_config.dart';
|
||||
import 'package:hmg_patient_app_new/features/paytabs/models/paytabs_transaction_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
class PayTabsViewModel extends ChangeNotifier {
|
||||
late PaymentSdkConfigurationDetails paymentConfiguration;
|
||||
final AppState appState;
|
||||
late PaytabsTransactionResponseModel paytabsTransactionResponseModel;
|
||||
|
||||
PayTabsViewModel({
|
||||
required this.appState,
|
||||
});
|
||||
|
||||
setPaymentConfiguration(String paymentDescription, num amount) {
|
||||
String cartID = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
paymentConfiguration = PaymentSdkConfigurationDetails(
|
||||
profileId: PaymentSdkDefaultConfig.profileID,
|
||||
serverKey: PaymentSdkDefaultConfig.serverKey,
|
||||
clientKey: PaymentSdkDefaultConfig.clientKey,
|
||||
transactionType: PaymentSdkTransactionType.SALE,
|
||||
cartId: cartID.substring((cartID.length - 5), cartID.length),
|
||||
cartDescription: paymentDescription,
|
||||
merchantName: PaymentSdkDefaultConfig.defaultMerchantName,
|
||||
locale: appState.isArabic() ? PaymentSdkLocale.AR : PaymentSdkLocale.EN,
|
||||
merchantApplePayIndentifier:
|
||||
ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? PaymentSdkDefaultConfig.defaultMerchantAppleBundleIDUAT : PaymentSdkDefaultConfig.defaultMerchantAppleBundleID,
|
||||
// merchantApplePayIndentifier: PaymentSdkDefaultConfig.defaultMerchantAppleBundleIDUAT,
|
||||
screentTitle: paymentDescription,
|
||||
amount: amount.toDouble(),
|
||||
showBillingInfo: false,
|
||||
forceShippingInfo: false,
|
||||
currencyCode: PaymentSdkDefaultConfig.defaultCurrency,
|
||||
merchantCountryCode: PaymentSdkDefaultConfig.defaultMerchantCountryCode,
|
||||
billingDetails: BillingDetails(
|
||||
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" ?? "HMG Patient", "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com",
|
||||
appState.getAuthenticatedUser()!.mobileNumber ?? "0000000000", "Riyadh", "SA", "Riyadh", "Riyadh", "12626"),
|
||||
shippingDetails: ShippingDetails("${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" ?? "HMG Patient", "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com",
|
||||
appState.getAuthenticatedUser()!.mobileNumber ?? "0000000000", "Riyadh", "SA", "Riyadh", "Riyadh", "12626"),
|
||||
alternativePaymentMethods: [],
|
||||
linkBillingNameWithCardHolderName: true,
|
||||
simplifyApplePayValidation: true);
|
||||
|
||||
paymentConfiguration.iOSThemeConfigurations = IOSThemeConfigurations(
|
||||
// logoImage: "assets/images/png/hmg_logo.png",
|
||||
backgroundColor: "ffffff",
|
||||
backgroundColorDark: "ffffff",
|
||||
secondaryColor: "191919",
|
||||
secondaryColorDark: "191919",
|
||||
primaryColor: "ffffff",
|
||||
primaryColorDark: "ffffff",
|
||||
// primaryFont: "Poppins",
|
||||
titleFontColor: "2E3039",
|
||||
titleFontColorDark: "2E3039",
|
||||
// titleFont: "Poppins",
|
||||
primaryFontColor: "2E3039",
|
||||
primaryFontColorDark: "2E3039",
|
||||
inputFieldBackgroundColor: "ffffff",
|
||||
inputFieldBackgroundColorDark: "ffffff",
|
||||
placeholderColor: "2E3039",
|
||||
placeholderColorDark: "2E3039",
|
||||
buttonColor: "ED1C2B",
|
||||
buttonColorDark: "ED1C2B",
|
||||
// buttonFont: "Poppins",
|
||||
);
|
||||
paymentConfiguration.tokeniseType = PaymentSdkTokeniseType.MERCHANT_MANDATORY;
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
startApplePayPayment({Function(PaytabsTransactionResponseModel)? onSuccess, Function(String)? onError}) {
|
||||
FlutterPaytabsBridge.startApplePayPayment(paymentConfiguration, (event) {
|
||||
final transactionDetails = event["data"] as Map<Object?, Object?>?;
|
||||
if (event["status"] == "success") {
|
||||
// Cast Map<Object?, Object?> to Map<String, dynamic>
|
||||
final data = Map<String, dynamic>.from(event["data"] as Map);
|
||||
paytabsTransactionResponseModel = PaytabsTransactionResponseModel.fromJson(data);
|
||||
if (paytabsTransactionResponseModel.isSuccess!) {
|
||||
onSuccess!(paytabsTransactionResponseModel);
|
||||
} else {
|
||||
// Transaction was processed but failed
|
||||
final reason = transactionDetails?["payResponseReturn"] ?? transactionDetails?["responseMessage"] ?? transactionDetails?["message"] ?? "Unknown error";
|
||||
final responseCode = transactionDetails?["responseCode"] ?? "";
|
||||
final errorMessage = responseCode.toString().isNotEmpty ? "Transaction failed (Code: $responseCode): $reason" : "Transaction failed: $reason";
|
||||
onError!(errorMessage);
|
||||
}
|
||||
} else if (event["status"] == "error") {
|
||||
final errorMessage = event["message"] ?? "An error occurred";
|
||||
debugPrint("Error occurred in transaction: $errorMessage");
|
||||
debugPrint("Full error event: $event");
|
||||
onError!(errorMessage);
|
||||
} else if (event["status"] == "event") {
|
||||
final eventMessage = event["message"] ?? "Event occurred";
|
||||
debugPrint("Event occurred: $eventMessage");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startCardPayment({Function(PaytabsTransactionResponseModel)? onSuccess, Function(String)? onError}) {
|
||||
FlutterPaytabsBridge.startCardPayment(paymentConfiguration, (event) {
|
||||
final transactionDetails = event["data"] as Map<Object?, Object?>?;
|
||||
if (event["status"] == "success") {
|
||||
// Cast Map<Object?, Object?> to Map<String, dynamic>
|
||||
final data = Map<String, dynamic>.from(event["data"] as Map);
|
||||
paytabsTransactionResponseModel = PaytabsTransactionResponseModel.fromJson(data);
|
||||
if (paytabsTransactionResponseModel.isSuccess!) {
|
||||
onSuccess!(paytabsTransactionResponseModel);
|
||||
} else {
|
||||
// Transaction was processed but failed
|
||||
final reason = transactionDetails?["payResponseReturn"] ?? transactionDetails?["responseMessage"] ?? transactionDetails?["message"] ?? "Unknown error";
|
||||
final responseCode = transactionDetails?["responseCode"] ?? "";
|
||||
final errorMessage = responseCode.toString().isNotEmpty ? "Transaction failed (Code: $responseCode): $reason" : "Transaction failed: $reason";
|
||||
onError!(errorMessage);
|
||||
}
|
||||
} else if (event["status"] == "error") {
|
||||
final errorMessage = event["message"] ?? "An error occurred";
|
||||
debugPrint("Error occurred in transaction: $errorMessage");
|
||||
debugPrint("Full error event: $event");
|
||||
onError!(errorMessage);
|
||||
} else if (event["status"] == "event") {
|
||||
final eventMessage = event["message"] ?? "Event occurred";
|
||||
debugPrint("Event occurred: $eventMessage");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,438 @@
|
||||
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 Utils.getNoDataWidget(context);
|
||||
// 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: 110.h,
|
||||
height: 100.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,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: _buildStatChip(
|
||||
LocaleKeys.expired.tr(), accountInfo?.expiredPoints?.toString() ?? "0", Icons.calendar_month,
|
||||
isSvg: true
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatChip(String label, String value, IconData icon, {bool isSvg = false}) {
|
||||
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: [
|
||||
isSvg ? Utils.buildSvgWithAssets(icon: AppAssets.expired_icon, width: 15.w, height: 15.h, applyThemeColor: false) : 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;
|
||||
}
|
||||
@ -0,0 +1,186 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.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/my_invoices/models/get_pharmacy_invoices_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/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
class PharmacyInvoiceCard extends StatelessWidget {
|
||||
final GetPharmacyInvoicesResponseModel pharmacyInvoice;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const PharmacyInvoiceCard({
|
||||
super.key,
|
||||
required this.pharmacyInvoice,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isArabic = getIt<AppState>().isArabic();
|
||||
|
||||
return Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 20.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(14.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Type chips
|
||||
// Wrap(
|
||||
// alignment: WrapAlignment.start,
|
||||
// direction: Axis.horizontal,
|
||||
// spacing: 6.w,
|
||||
// runSpacing: 6.h,
|
||||
// children: [
|
||||
// AppCustomChipWidget(
|
||||
// icon: AppAssets.pharmacy_icon,
|
||||
// iconColor: AppColors.primaryRedColor,
|
||||
// labelText: LocaleKeys.pharmacy.tr(context: context),
|
||||
// textColor: AppColors.primaryRedColor,
|
||||
// ),
|
||||
// if (pharmacyInvoice.totalAmount != null)
|
||||
// AppCustomChipWidget(
|
||||
// labelText: '${pharmacyInvoice.totalAmount!.toStringAsFixed(2)} ${LocaleKeys.sar.tr(context: context)}',
|
||||
// backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
|
||||
// textColor: AppColors.primaryRedColor,
|
||||
// isEnglishOnly: true,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Main content row
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Image.network(
|
||||
pharmacyInvoice.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png',
|
||||
width: 63.h,
|
||||
height: 63.h,
|
||||
fit: BoxFit.cover,
|
||||
).circle(100.r),
|
||||
Transform.translate(
|
||||
offset: Offset(0.0, -20.h),
|
||||
child: Container(
|
||||
width: 40.w,
|
||||
height: 40.h,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: AppColors.scaffoldBgColor,
|
||||
width: 1.5.w,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, applyThemeColor: false),
|
||||
SizedBox(height: 2.h),
|
||||
"${pharmacyInvoice.decimalDoctorRate ?? '0.0'}".toText11(isBold: true, color: AppColors.textColor),
|
||||
],
|
||||
),
|
||||
).circle(100),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
(getIt<AppState>().isArabic()
|
||||
? (pharmacyInvoice.doctorNameN ?? pharmacyInvoice.doctorName ?? LocaleKeys.doctor.tr(context: context))
|
||||
: (pharmacyInvoice.doctorName ?? pharmacyInvoice.doctorNameN ?? LocaleKeys.doctor.tr(context: context))
|
||||
).toText16(isBold: true),
|
||||
SizedBox(height: 8.h),
|
||||
Wrap(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 6.w,
|
||||
runSpacing: 6.h,
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
labelText: "${LocaleKeys.invoiceNo.tr(context: context)}: ${pharmacyInvoice.invoiceNo ?? '-'}",
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
isEnglishOnly: true,
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText: ((pharmacyInvoice.clinicName ?? LocaleKeys.clinic.tr(context: context)).length > 15
|
||||
? '${(pharmacyInvoice.clinicName ?? LocaleKeys.clinic.tr(context: context)).substring(0, 12)}...'
|
||||
: (pharmacyInvoice.clinicName ?? LocaleKeys.clinic.tr(context: context))),
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText: pharmacyInvoice.projectName ?? LocaleKeys.hospital.tr(context: context),
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
if (pharmacyInvoice.appointmentDate != null)
|
||||
Directionality(
|
||||
textDirection: ui.TextDirection.ltr,
|
||||
child: AppCustomChipWidget(
|
||||
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w),
|
||||
icon: AppAssets.doctor_calendar_icon,
|
||||
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(pharmacyInvoice.appointmentDate), false),
|
||||
isEnglishOnly: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Action button
|
||||
CustomButton(
|
||||
text: Utils.isVidaPlusProject(pharmacyInvoice.projectID ?? 0)
|
||||
? LocaleKeys.sendEmail.tr(context: context)
|
||||
: LocaleKeys.downloadInvoice.tr(context: context),
|
||||
onPressed: onTap,
|
||||
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
|
||||
borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01),
|
||||
textColor: AppColors.primaryRedColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
borderRadius: 12.r,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||
height: 40.h,
|
||||
iconSize: 14.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
).paddingOnly(bottom: 16.h);
|
||||
}
|
||||
|
||||
String _formatDate(String? dateString) {
|
||||
if (dateString == null || dateString.isEmpty) return '-';
|
||||
try {
|
||||
final date = DateUtil.convertStringToDate(dateString);
|
||||
return DateUtil.formatDateToDate(date, false);
|
||||
} catch (e) {
|
||||
return dateString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue