Compare commits
2 Commits
369a566006
...
d4d245d299
| Author | SHA1 | Date |
|---|---|---|
|
|
d4d245d299 | 1 month ago |
|
|
b02831834f | 2 months ago |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 45 KiB |
@ -0,0 +1,12 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.9384 2C5.28428 1.99994 4.69656 1.99989 4.22085 2.06384C3.70149 2.13367 3.16868 2.2958 2.73224 2.73224C2.2958 3.16868 2.13367 3.70149 2.06384 4.22085C1.99989 4.69656 1.99994 5.28428 2 5.9384V6.06161C1.99994 6.71574 1.99989 7.30346 2.06384 7.77916C2.13367 8.29852 2.2958 8.83134 2.73224 9.26777C3.16868 9.70421 3.70149 9.86635 4.22085 9.93617C4.69656 10.0001 5.28427 10.0001 5.9384 10H6.06162C6.71574 10.0001 7.30346 10.0001 7.77916 9.93617C8.29852 9.86635 8.83134 9.70421 9.26777 9.26777C9.70421 8.83134 9.86635 8.29852 9.93617 7.77916C10.0001 7.30346 10.0001 6.71574 10 6.06162V5.9384C10.0001 5.28427 10.0001 4.69656 9.93617 4.22085C9.86635 3.70149 9.70421 3.16868 9.26777 2.73224C8.83134 2.2958 8.29852 2.13367 7.77916 2.06384C7.30346 1.99989 6.71574 1.99994 6.06161 2H5.9384Z" fill="white"/>
|
||||
<path d="M5.9384 14C5.28428 13.9999 4.69655 13.9999 4.22085 14.0638C3.70149 14.1337 3.16868 14.2958 2.73224 14.7322C2.2958 15.1687 2.13367 15.7015 2.06384 16.2209C1.99989 16.6966 1.99994 17.2843 2 17.9384V18.0616C1.99994 18.7157 1.99989 19.3035 2.06384 19.7792C2.13367 20.2985 2.2958 20.8313 2.73224 21.2678C3.16868 21.7042 3.70149 21.8663 4.22085 21.9362C4.69656 22.0001 5.28427 22.0001 5.9384 22H6.06162C6.71574 22.0001 7.30346 22.0001 7.77916 21.9362C8.29852 21.8663 8.83134 21.7042 9.26777 21.2678C9.70421 20.8313 9.86635 20.2985 9.93617 19.7792C10.0001 19.3035 10.0001 18.7157 10 18.0616V17.9384C10.0001 17.2843 10.0001 16.6966 9.93617 16.2209C9.86635 15.7015 9.70421 15.1687 9.26777 14.7322C8.83134 14.2958 8.29852 14.1337 7.77916 14.0638C7.30346 13.9999 6.71575 13.9999 6.06163 14H5.9384Z" fill="white"/>
|
||||
<path d="M2 12C2 11.4477 2.44772 11 3 11L9 11C9.55228 11 10 11.4477 10 12C10 12.5523 9.55228 13 9 13L3 13C2.44772 13 2 12.5523 2 12Z" fill="white"/>
|
||||
<path d="M13 3C13 2.44772 12.5523 2 12 2C11.4477 2 11 2.44772 11 3L11 8C11 8.55228 11.4477 9 12 9C12.5523 9 13 8.55228 13 8L13 3Z" fill="white"/>
|
||||
<path d="M17.9384 2H18.0616C18.7157 1.99994 19.3035 1.99989 19.7792 2.06384C20.2985 2.13367 20.8313 2.2958 21.2678 2.73224C21.7042 3.16868 21.8663 3.70149 21.9362 4.22085C22.0001 4.69656 22.0001 5.28427 22 5.9384V6.06162C22.0001 6.71574 22.0001 7.30346 21.9362 7.77916C21.8663 8.29852 21.7042 8.83134 21.2678 9.26777C20.8313 9.70421 20.2985 9.86635 19.7792 9.93617C19.3035 10.0001 18.7157 10.0001 18.0616 10H17.9384C17.2843 10.0001 16.6966 10.0001 16.2209 9.93617C15.7015 9.86635 15.1687 9.70421 14.7322 9.26777C14.2958 8.83134 14.1337 8.29852 14.0638 7.77916C13.9999 7.30346 13.9999 6.71575 14 6.06163V5.9384C13.9999 5.28428 13.9999 4.69655 14.0638 4.22085C14.1337 3.70149 14.2958 3.16868 14.7322 2.73224C15.1687 2.2958 15.7015 2.13367 16.2209 2.06384C16.6966 1.99989 17.2843 1.99994 17.9384 2Z" fill="white"/>
|
||||
<path d="M14.9384 11C14.2843 10.9999 13.6966 10.9999 13.2209 11.0638C12.7015 11.1337 12.1687 11.2958 11.7322 11.7322C11.2958 12.1687 11.1337 12.7015 11.0638 13.2209C10.9999 13.6966 10.9999 14.2843 11 14.9384L11 15C11 15.5523 11.4477 16 12 16C12.5523 16 13 15.5523 13 15C13 14.2646 13.0021 13.8137 13.046 13.4873C13.0846 13.2005 13.1394 13.1526 13.1459 13.1469L13.1469 13.1459C13.1526 13.1394 13.2005 13.0846 13.4873 13.046C13.8137 13.0021 14.2646 13 15 13L21 13C21.5523 13 22 12.5523 22 12C22 11.4477 21.5523 11 21 11L14.9384 11Z" fill="white"/>
|
||||
<path d="M18 16C18.7354 16 19.1862 16.0021 19.5125 16.0461C19.799 16.0846 19.8468 16.1395 19.8526 16.146L19.8536 16.1471C19.8604 16.1531 19.9154 16.2016 19.954 16.4891C19.9979 16.8162 20 17.268 20 18.0043C20 18.7407 19.9979 19.1925 19.954 19.5196C19.9154 19.8071 19.8604 19.8556 19.8536 19.8616L19.8522 19.863C19.8442 19.8712 19.8181 19.8979 19.7034 19.9272C19.5612 19.9636 19.3416 19.9892 18.9698 20.0005C18.4178 20.0171 17.9838 20.4781 18.0005 21.0302C18.0171 21.5822 18.4781 22.0162 19.0302 21.9996C19.7921 21.9766 20.64 21.9045 21.2683 21.2753C21.7045 20.8385 21.8664 20.3054 21.9362 19.7857C22.0001 19.3095 22.0001 18.7211 22 18.066V17.9428C22.0001 17.2876 22.0001 16.6992 21.9362 16.223C21.8664 15.7033 21.7045 15.1702 21.2683 14.7334C20.8319 14.2964 20.299 14.1339 19.7794 14.0639C19.3036 13.9999 18.7157 13.9999 18.0617 14L18 14C17.4477 14 17 14.4477 17 15C17 15.5523 17.4477 16 18 16Z" fill="white"/>
|
||||
<path d="M16 15C16 14.4477 15.5523 14 15 14C14.4477 14 14 14.4477 14 15V16.5C14 17.394 14.2542 18.2178 15.0565 18.6656C15.3946 18.8543 15.7549 18.928 16.0621 18.9627C16.3698 18.9975 16.6995 19 17 19C17 19.5523 17.4477 20 18 20C18.5523 20 19 19.5523 19 19C19 17.8954 18.1046 17 17 17C16.6924 17 16.468 16.9958 16.2866 16.9753C16.152 16.9601 16.0822 16.9397 16.051 16.9279C16.0333 16.8814 16 16.7576 16 16.5V15Z" fill="white"/>
|
||||
<path d="M13 17.7692C13 17.217 12.5523 16.7692 12 16.7692C11.4477 16.7692 11 17.217 11 17.7692L11 20.5385C11 21.0908 11.4477 21.5385 12 21.5385C12.5523 21.5385 13 21.0908 13 20.5385L13 17.7692Z" fill="white"/>
|
||||
<path d="M15 20C14.4477 20 14 20.4477 14 21C14 21.5523 14.4477 22 15 22H16C16.5523 22 17 21.5523 17 21C17 20.4477 16.5523 20 16 20H15Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@ -0,0 +1,30 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class OfferPurchaseRequestModel {
|
||||
String identificationType;
|
||||
String idValue;
|
||||
String mobileNumber;
|
||||
|
||||
OfferPurchaseRequestModel({
|
||||
required this.identificationType,
|
||||
required this.idValue,
|
||||
required this.mobileNumber,
|
||||
});
|
||||
|
||||
factory OfferPurchaseRequestModel.fromRawJson(String str) => OfferPurchaseRequestModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory OfferPurchaseRequestModel.fromJson(Map<String, dynamic> json) => OfferPurchaseRequestModel(
|
||||
identificationType: json["identification_type"],
|
||||
idValue: json["id_value"],
|
||||
mobileNumber: json["mobile_number"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"identification_type": identificationType,
|
||||
"id_value": idValue,
|
||||
"mobile_number": mobileNumber,
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class OfferPurchaseResponseModel {
|
||||
int orderId;
|
||||
String orderRef;
|
||||
String amount;
|
||||
String currency;
|
||||
bool paymentRequired;
|
||||
dynamic paymentUrl;
|
||||
|
||||
OfferPurchaseResponseModel({
|
||||
required this.orderId,
|
||||
required this.orderRef,
|
||||
required this.amount,
|
||||
required this.currency,
|
||||
required this.paymentRequired,
|
||||
required this.paymentUrl,
|
||||
});
|
||||
|
||||
factory OfferPurchaseResponseModel.fromRawJson(String str) => OfferPurchaseResponseModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory OfferPurchaseResponseModel.fromJson(Map<String, dynamic> json) => OfferPurchaseResponseModel(
|
||||
orderId: json["order_id"],
|
||||
orderRef: json["order_ref"],
|
||||
amount: json["amount"],
|
||||
currency: json["currency"],
|
||||
paymentRequired: json["payment_required"],
|
||||
paymentUrl: json["payment_url"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"order_id": orderId,
|
||||
"order_ref": orderRef,
|
||||
"amount": amount,
|
||||
"currency": currency,
|
||||
"payment_required": paymentRequired,
|
||||
"payment_url": paymentUrl,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,229 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class OfferPurchaseHistoryResponseModel {
|
||||
List<Datum> data;
|
||||
Meta meta;
|
||||
|
||||
OfferPurchaseHistoryResponseModel({
|
||||
required this.data,
|
||||
required this.meta,
|
||||
});
|
||||
|
||||
factory OfferPurchaseHistoryResponseModel.fromRawJson(String str) => OfferPurchaseHistoryResponseModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory OfferPurchaseHistoryResponseModel.fromJson(Map<String, dynamic> json) => OfferPurchaseHistoryResponseModel(
|
||||
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
|
||||
meta: Meta.fromJson(json["meta"]),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"data": List<dynamic>.from(data.map((x) => x.toJson())),
|
||||
"meta": meta.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
class Datum {
|
||||
String voucherCode;
|
||||
String state;
|
||||
DateTime purchaseDate;
|
||||
dynamic redemptionDate;
|
||||
String qrCode;
|
||||
String qrCodeUrl;
|
||||
String pdfUrl;
|
||||
DateTime validTo;
|
||||
UsageValidity usageValidity;
|
||||
Offer offer;
|
||||
String branchApplicability;
|
||||
List<Branch> branches;
|
||||
|
||||
Datum({
|
||||
required this.voucherCode,
|
||||
required this.state,
|
||||
required this.purchaseDate,
|
||||
required this.redemptionDate,
|
||||
required this.qrCode,
|
||||
required this.qrCodeUrl,
|
||||
required this.pdfUrl,
|
||||
required this.validTo,
|
||||
required this.usageValidity,
|
||||
required this.offer,
|
||||
required this.branchApplicability,
|
||||
required this.branches,
|
||||
});
|
||||
|
||||
factory Datum.fromRawJson(String str) => Datum.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
|
||||
voucherCode: json["voucher_code"],
|
||||
state: json["state"],
|
||||
purchaseDate: DateTime.parse(json["purchase_date"]),
|
||||
redemptionDate: json["redemption_date"],
|
||||
qrCode: json["qr_code"],
|
||||
qrCodeUrl: json["qr_code_url"],
|
||||
pdfUrl: json["pdf_url"],
|
||||
validTo: DateTime.parse(json["valid_to"]),
|
||||
usageValidity: UsageValidity.fromJson(json["usage_validity"]),
|
||||
offer: Offer.fromJson(json["offer"]),
|
||||
branchApplicability: json["branch_applicability"],
|
||||
branches: List<Branch>.from(json["branches"].map((x) => Branch.fromJson(x))),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"voucher_code": voucherCode,
|
||||
"state": state,
|
||||
"purchase_date": purchaseDate.toIso8601String(),
|
||||
"redemption_date": redemptionDate,
|
||||
"qr_code": qrCode,
|
||||
"qr_code_url": qrCodeUrl,
|
||||
"pdf_url": pdfUrl,
|
||||
"valid_to": "${validTo.year.toString().padLeft(4, '0')}-${validTo.month.toString().padLeft(2, '0')}-${validTo.day.toString().padLeft(2, '0')}",
|
||||
"usage_validity": usageValidity.toJson(),
|
||||
"offer": offer.toJson(),
|
||||
"branch_applicability": branchApplicability,
|
||||
"branches": List<dynamic>.from(branches.map((x) => x.toJson())),
|
||||
};
|
||||
}
|
||||
|
||||
class Branch {
|
||||
String code;
|
||||
String name;
|
||||
String nameAr;
|
||||
|
||||
Branch({
|
||||
required this.code,
|
||||
required this.name,
|
||||
required this.nameAr,
|
||||
});
|
||||
|
||||
factory Branch.fromRawJson(String str) => Branch.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Branch.fromJson(Map<String, dynamic> json) => Branch(
|
||||
code: json["code"],
|
||||
name: json["name"],
|
||||
nameAr: json["name_ar"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"code": code,
|
||||
"name": name,
|
||||
"name_ar": nameAr,
|
||||
};
|
||||
}
|
||||
|
||||
class Offer {
|
||||
int id;
|
||||
String name;
|
||||
String nameAr;
|
||||
String branchApplicability;
|
||||
DateTime validTo;
|
||||
UsageValidity usageValidity;
|
||||
|
||||
Offer({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.nameAr,
|
||||
required this.branchApplicability,
|
||||
required this.validTo,
|
||||
required this.usageValidity,
|
||||
});
|
||||
|
||||
factory Offer.fromRawJson(String str) => Offer.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Offer.fromJson(Map<String, dynamic> json) => Offer(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
nameAr: json["name_ar"],
|
||||
branchApplicability: json["branch_applicability"],
|
||||
validTo: DateTime.parse(json["valid_to"]),
|
||||
usageValidity: UsageValidity.fromJson(json["usage_validity"]),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"name": name,
|
||||
"name_ar": nameAr,
|
||||
"branch_applicability": branchApplicability,
|
||||
"valid_to": "${validTo.year.toString().padLeft(4, '0')}-${validTo.month.toString().padLeft(2, '0')}-${validTo.day.toString().padLeft(2, '0')}",
|
||||
"usage_validity": usageValidity.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
class UsageValidity {
|
||||
String type;
|
||||
dynamic days;
|
||||
String rule;
|
||||
String ruleAr;
|
||||
DateTime redeemBy;
|
||||
String redeemByDisplay;
|
||||
|
||||
UsageValidity({
|
||||
required this.type,
|
||||
required this.days,
|
||||
required this.rule,
|
||||
required this.ruleAr,
|
||||
required this.redeemBy,
|
||||
required this.redeemByDisplay,
|
||||
});
|
||||
|
||||
factory UsageValidity.fromRawJson(String str) => UsageValidity.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory UsageValidity.fromJson(Map<String, dynamic> json) => UsageValidity(
|
||||
type: json["type"],
|
||||
days: json["days"],
|
||||
rule: json["rule"],
|
||||
ruleAr: json["rule_ar"],
|
||||
redeemBy: DateTime.parse(json["redeem_by"]),
|
||||
redeemByDisplay: json["redeem_by_display"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"type": type,
|
||||
"days": days,
|
||||
"rule": rule,
|
||||
"rule_ar": ruleAr,
|
||||
"redeem_by": "${redeemBy.year.toString().padLeft(4, '0')}-${redeemBy.month.toString().padLeft(2, '0')}-${redeemBy.day.toString().padLeft(2, '0')}",
|
||||
"redeem_by_display": redeemByDisplay,
|
||||
};
|
||||
}
|
||||
|
||||
class Meta {
|
||||
int total;
|
||||
int page;
|
||||
int perPage;
|
||||
int pages;
|
||||
|
||||
Meta({
|
||||
required this.total,
|
||||
required this.page,
|
||||
required this.perPage,
|
||||
required this.pages,
|
||||
});
|
||||
|
||||
factory Meta.fromRawJson(String str) => Meta.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Meta.fromJson(Map<String, dynamic> json) => Meta(
|
||||
total: json["total"],
|
||||
page: json["page"],
|
||||
perPage: json["per_page"],
|
||||
pages: json["pages"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"total": total,
|
||||
"page": page,
|
||||
"per_page": perPage,
|
||||
"pages": pages,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,229 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class OfferPurchaseHistoryResponseModel {
|
||||
List<Datum>? data;
|
||||
Meta? meta;
|
||||
|
||||
OfferPurchaseHistoryResponseModel({
|
||||
this.data,
|
||||
this.meta,
|
||||
});
|
||||
|
||||
factory OfferPurchaseHistoryResponseModel.fromRawJson(String str) => OfferPurchaseHistoryResponseModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory OfferPurchaseHistoryResponseModel.fromJson(Map<String, dynamic> json) => OfferPurchaseHistoryResponseModel(
|
||||
data: json["data"] == null ? [] : List<Datum>.from(json["data"]!.map((x) => Datum.fromJson(x))),
|
||||
meta: json["meta"] == null ? null : Meta.fromJson(json["meta"]),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"data": data == null ? [] : List<dynamic>.from(data!.map((x) => x.toJson())),
|
||||
"meta": meta?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
class Datum {
|
||||
String? voucherCode;
|
||||
String? state;
|
||||
DateTime? purchaseDate;
|
||||
dynamic redemptionDate;
|
||||
String? qrCode;
|
||||
String? qrCodeUrl;
|
||||
String? pdfUrl;
|
||||
dynamic validTo;
|
||||
UsageValidity? usageValidity;
|
||||
Offer? offer;
|
||||
String? branchApplicability;
|
||||
List<Branch>? branches;
|
||||
|
||||
Datum({
|
||||
this.voucherCode,
|
||||
this.state,
|
||||
this.purchaseDate,
|
||||
this.redemptionDate,
|
||||
this.qrCode,
|
||||
this.qrCodeUrl,
|
||||
this.pdfUrl,
|
||||
this.validTo,
|
||||
this.usageValidity,
|
||||
this.offer,
|
||||
this.branchApplicability,
|
||||
this.branches,
|
||||
});
|
||||
|
||||
factory Datum.fromRawJson(String str) => Datum.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
|
||||
voucherCode: json["voucher_code"],
|
||||
state: json["state"],
|
||||
purchaseDate: json["purchase_date"] == null ? null : DateTime.parse(json["purchase_date"]),
|
||||
redemptionDate: json["redemption_date"],
|
||||
qrCode: json["qr_code"],
|
||||
qrCodeUrl: json["qr_code_url"],
|
||||
pdfUrl: json["pdf_url"],
|
||||
validTo: json["valid_to"],
|
||||
usageValidity: json["usage_validity"] == null ? null : UsageValidity.fromJson(json["usage_validity"]),
|
||||
offer: json["offer"] == null ? null : Offer.fromJson(json["offer"]),
|
||||
branchApplicability: json["branch_applicability"],
|
||||
branches: json["branches"] == null ? [] : List<Branch>.from(json["branches"]!.map((x) => Branch.fromJson(x))),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"voucher_code": voucherCode,
|
||||
"state": state,
|
||||
"purchase_date": purchaseDate?.toIso8601String(),
|
||||
"redemption_date": redemptionDate,
|
||||
"qr_code": qrCode,
|
||||
"qr_code_url": qrCodeUrl,
|
||||
"pdf_url": pdfUrl,
|
||||
"valid_to": validTo,
|
||||
"usage_validity": usageValidity?.toJson(),
|
||||
"offer": offer?.toJson(),
|
||||
"branch_applicability": branchApplicability,
|
||||
"branches": branches == null ? [] : List<dynamic>.from(branches!.map((x) => x.toJson())),
|
||||
};
|
||||
}
|
||||
|
||||
class Branch {
|
||||
String? code;
|
||||
String? name;
|
||||
String? nameAr;
|
||||
|
||||
Branch({
|
||||
this.code,
|
||||
this.name,
|
||||
this.nameAr,
|
||||
});
|
||||
|
||||
factory Branch.fromRawJson(String str) => Branch.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Branch.fromJson(Map<String, dynamic> json) => Branch(
|
||||
code: json["code"],
|
||||
name: json["name"],
|
||||
nameAr: json["name_ar"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"code": code,
|
||||
"name": name,
|
||||
"name_ar": nameAr,
|
||||
};
|
||||
}
|
||||
|
||||
class Offer {
|
||||
int? id;
|
||||
String? name;
|
||||
String? nameAr;
|
||||
String? branchApplicability;
|
||||
DateTime? validTo;
|
||||
UsageValidity? usageValidity;
|
||||
|
||||
Offer({
|
||||
this.id,
|
||||
this.name,
|
||||
this.nameAr,
|
||||
this.branchApplicability,
|
||||
this.validTo,
|
||||
this.usageValidity,
|
||||
});
|
||||
|
||||
factory Offer.fromRawJson(String str) => Offer.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Offer.fromJson(Map<String, dynamic> json) => Offer(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
nameAr: json["name_ar"],
|
||||
branchApplicability: json["branch_applicability"],
|
||||
validTo: json["valid_to"] == null ? null : DateTime.parse(json["valid_to"]),
|
||||
usageValidity: json["usage_validity"] == null ? null : UsageValidity.fromJson(json["usage_validity"]),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"name": name,
|
||||
"name_ar": nameAr,
|
||||
"branch_applicability": branchApplicability,
|
||||
"valid_to": "${validTo!.year.toString().padLeft(4, '0')}-${validTo!.month.toString().padLeft(2, '0')}-${validTo!.day.toString().padLeft(2, '0')}",
|
||||
"usage_validity": usageValidity?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
class UsageValidity {
|
||||
String? type;
|
||||
dynamic days;
|
||||
String? rule;
|
||||
String? ruleAr;
|
||||
dynamic redeemBy;
|
||||
dynamic redeemByDisplay;
|
||||
|
||||
UsageValidity({
|
||||
this.type,
|
||||
this.days,
|
||||
this.rule,
|
||||
this.ruleAr,
|
||||
this.redeemBy,
|
||||
this.redeemByDisplay,
|
||||
});
|
||||
|
||||
factory UsageValidity.fromRawJson(String str) => UsageValidity.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory UsageValidity.fromJson(Map<String, dynamic> json) => UsageValidity(
|
||||
type: json["type"],
|
||||
days: json["days"],
|
||||
rule: json["rule"],
|
||||
ruleAr: json["rule_ar"],
|
||||
redeemBy: json["redeem_by"],
|
||||
redeemByDisplay: json["redeem_by_display"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"type": type,
|
||||
"days": days,
|
||||
"rule": rule,
|
||||
"rule_ar": ruleAr,
|
||||
"redeem_by": redeemBy,
|
||||
"redeem_by_display": redeemByDisplay,
|
||||
};
|
||||
}
|
||||
|
||||
class Meta {
|
||||
int? total;
|
||||
int? page;
|
||||
int? perPage;
|
||||
int? pages;
|
||||
|
||||
Meta({
|
||||
this.total,
|
||||
this.page,
|
||||
this.perPage,
|
||||
this.pages,
|
||||
});
|
||||
|
||||
factory Meta.fromRawJson(String str) => Meta.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Meta.fromJson(Map<String, dynamic> json) => Meta(
|
||||
total: json["total"],
|
||||
page: json["page"],
|
||||
perPage: json["per_page"],
|
||||
pages: json["pages"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"total": total,
|
||||
"page": page,
|
||||
"per_page": perPage,
|
||||
"pages": pages,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class PaymentResultRequestModel {
|
||||
String status;
|
||||
String paymentRef;
|
||||
String amount;
|
||||
String currency;
|
||||
|
||||
PaymentResultRequestModel({
|
||||
required this.status,
|
||||
required this.paymentRef,
|
||||
required this.amount,
|
||||
required this.currency,
|
||||
});
|
||||
|
||||
factory PaymentResultRequestModel.fromRawJson(String str) => PaymentResultRequestModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PaymentResultRequestModel.fromJson(Map<String, dynamic> json) => PaymentResultRequestModel(
|
||||
status: json["status"],
|
||||
paymentRef: json["payment_ref"],
|
||||
amount: json["amount"],
|
||||
currency: json["currency"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"status": status,
|
||||
"payment_ref": paymentRef,
|
||||
"amount": amount,
|
||||
"currency": currency,
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class PaymentResultResponseModel {
|
||||
String status;
|
||||
|
||||
PaymentResultResponseModel({
|
||||
required this.status,
|
||||
});
|
||||
|
||||
factory PaymentResultResponseModel.fromRawJson(String str) => PaymentResultResponseModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PaymentResultResponseModel.fromJson(Map<String, dynamic> json) => PaymentResultResponseModel(
|
||||
status: json["status"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"status": status,
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,92 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class PomsAuthTokenModel {
|
||||
String token;
|
||||
DateTime expiresAt;
|
||||
Partner partner;
|
||||
PortalAccount portalAccount;
|
||||
|
||||
PomsAuthTokenModel({
|
||||
required this.token,
|
||||
required this.expiresAt,
|
||||
required this.partner,
|
||||
required this.portalAccount,
|
||||
});
|
||||
|
||||
factory PomsAuthTokenModel.fromRawJson(String str) => PomsAuthTokenModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PomsAuthTokenModel.fromJson(Map<String, dynamic> json) => PomsAuthTokenModel(
|
||||
token: json["token"],
|
||||
expiresAt: DateTime.parse(json["expires_at"]),
|
||||
partner: Partner.fromJson(json["partner"]),
|
||||
portalAccount: PortalAccount.fromJson(json["portal_account"]),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"token": token,
|
||||
"expires_at": expiresAt.toIso8601String(),
|
||||
"partner": partner.toJson(),
|
||||
"portal_account": portalAccount.toJson(),
|
||||
};
|
||||
|
||||
// Helper method to check if token is expired
|
||||
bool isExpired() {
|
||||
return DateTime.now().isAfter(expiresAt);
|
||||
}
|
||||
|
||||
// Helper method to check if token is valid
|
||||
bool isValid() {
|
||||
return !isExpired() && token.isNotEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
class Partner {
|
||||
int id;
|
||||
String name;
|
||||
|
||||
Partner({
|
||||
required this.id,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
factory Partner.fromRawJson(String str) => Partner.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory Partner.fromJson(Map<String, dynamic> json) => Partner(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"name": name,
|
||||
};
|
||||
}
|
||||
|
||||
class PortalAccount {
|
||||
bool created;
|
||||
String login;
|
||||
|
||||
PortalAccount({
|
||||
required this.created,
|
||||
required this.login,
|
||||
});
|
||||
|
||||
factory PortalAccount.fromRawJson(String str) => PortalAccount.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PortalAccount.fromJson(Map<String, dynamic> json) => PortalAccount(
|
||||
created: json["created"],
|
||||
login: json["login"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"created": created,
|
||||
"login": login,
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,324 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.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/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/models/offers_purchases_history_response_model.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';
|
||||
import 'package:hmg_patient_app_new/presentation/offers_and_discounts/offers_and_discounts_history_page.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:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
class OffersAndDiscountsSuccessQrPage extends StatefulWidget {
|
||||
final Datum purchaseData;
|
||||
final bool isFromPaymentScreen;
|
||||
|
||||
const OffersAndDiscountsSuccessQrPage({super.key, required this.purchaseData, required this.isFromPaymentScreen});
|
||||
|
||||
@override
|
||||
State<OffersAndDiscountsSuccessQrPage> createState() => _OffersAndDiscountsSuccessQrPageState();
|
||||
}
|
||||
|
||||
class _OffersAndDiscountsSuccessQrPageState extends State<OffersAndDiscountsSuccessQrPage> {
|
||||
late AppState appState;
|
||||
late OffersAndDiscountsViewModel offersAndDiscountsViewModel;
|
||||
String? qrCodeFilePath; // Store the QR code file path for sharing
|
||||
final GlobalKey _screenshotKey = GlobalKey();
|
||||
|
||||
Color _getStatusColor(String status) {
|
||||
switch (status) {
|
||||
case 'purchased':
|
||||
return AppColors.successColor;
|
||||
|
||||
case 'redeemed':
|
||||
return AppColors.greyTextColor;
|
||||
|
||||
case 'partially_redeemed':
|
||||
return AppColors.alertColor;
|
||||
|
||||
case 'expired':
|
||||
return AppColors.errorColor;
|
||||
|
||||
case 'active':
|
||||
case 'valid':
|
||||
return AppColors.successColor;
|
||||
|
||||
default:
|
||||
return AppColors.alertColor;
|
||||
}
|
||||
}
|
||||
|
||||
String _getStatusText(String status) {
|
||||
switch (status) {
|
||||
case 'purchased':
|
||||
return LocaleKeys.purchased.tr(context: context);
|
||||
|
||||
case 'redeemed':
|
||||
return LocaleKeys.redeemed.tr(context: context);
|
||||
|
||||
case 'partially_redeemed':
|
||||
return LocaleKeys.partiallyRedeemed.tr(context: context);
|
||||
|
||||
case 'expired':
|
||||
return LocaleKeys.expired.tr(context: context);
|
||||
|
||||
case 'active':
|
||||
case 'valid':
|
||||
return LocaleKeys.active.tr(context: context);
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle QR code action by delegating to view model
|
||||
Future<void> _handleQRCodeAction({required bool isShareAction}) async {
|
||||
try {
|
||||
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
|
||||
context: context,
|
||||
offerName: widget.purchaseData.offer!.name,
|
||||
offerNameAr: widget.purchaseData.offer!.nameAr,
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (result['success']) {
|
||||
if (isShareAction) {
|
||||
// Share action: Saved to permanent storage, now share the file
|
||||
final xFile = XFile(result['filePath']);
|
||||
final offerName = result['offerName'];
|
||||
// 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),
|
||||
);
|
||||
} else {
|
||||
// Save action: Show success and optionally navigate
|
||||
if (result['isFromPayment']) {
|
||||
// From payment screen: Show success bottom sheet and navigate to history
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Utils.getSuccessWidget(loadingText: LocaleKeys.qrCodeSavedSuccessfully.tr(context: context)),
|
||||
isCloseButtonVisible: true,
|
||||
isDismissible: true,
|
||||
isFullScreen: false,
|
||||
isAutoDismiss: true,
|
||||
callBackFunc: () {
|
||||
// Navigate after bottom sheet closes
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).push(CustomPageRoute(page: OffersAndDiscountsHistoryPage()));
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// Not from payment screen: Show success bottom sheet and stay on page
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Utils.getSuccessWidget(loadingText: LocaleKeys.qrCodeSavedSuccessfully.tr(context: context)),
|
||||
isCloseButtonVisible: true,
|
||||
isDismissible: true,
|
||||
isFullScreen: false,
|
||||
isAutoDismiss: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Utils.showToast(LocaleKeys.failedToSaveQRCode.tr(context: context));
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error in _handleQRCodeAction: $e');
|
||||
Utils.showToast(LocaleKeys.failedToSaveQRCode.tr(context: context));
|
||||
}
|
||||
}
|
||||
|
||||
@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(
|
||||
title: LocaleKeys.orderDetails.tr(context: context),
|
||||
trailing: Utils.buildSvgWithAssets(icon: AppAssets.share, iconColor: AppColors.blackColor).onPress(() {
|
||||
_handleQRCodeAction(isShareAction: true); // Share action - saves and shares
|
||||
}),
|
||||
bottomChild: CustomButton(
|
||||
text: LocaleKeys.saveQRCode.tr(context: context),
|
||||
onPressed: () {
|
||||
_handleQRCodeAction(isShareAction: false); // Save action - saves and shows success
|
||||
},
|
||||
backgroundColor: AppColors.successColor,
|
||||
borderColor: AppColors.successColor,
|
||||
textColor: AppColors.whiteColor,
|
||||
icon: AppAssets.qrCodeIcon,
|
||||
fontSize: 16.f,
|
||||
isBold: true,
|
||||
borderRadius: 12.r,
|
||||
height: 56.h,
|
||||
).paddingSymmetrical(24.h, 24.h),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
//Save Qr Screenshot Area Start
|
||||
RepaintBoundary(
|
||||
key: _screenshotKey,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: false),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 24.h),
|
||||
Utils.buildSvgWithAssets(icon: AppAssets.hmgLogoSvg, width: 48.w, height: 48.h),
|
||||
SizedBox(height: 6.h),
|
||||
_buildQRCode().paddingSymmetrical(0.h, 0.h),
|
||||
AppCustomChipWidget(
|
||||
labelText: "${LocaleKeys.expiresOn.tr(context: context)}: ${Utils.formatDateToDisplay(widget.purchaseData.offer!.validTo.toString())}",
|
||||
backgroundColor: AppColors.lightRedButtonColor,
|
||||
textColor: AppColors.primaryRedColor,
|
||||
icon: "",
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w),
|
||||
isEnglishOnly: true,
|
||||
),
|
||||
SizedBox(
|
||||
height: 6.h,
|
||||
),
|
||||
if (viewModel.isSaveQrCodePressed) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
textDirection: appState.isArabic() ? ui.TextDirection.rtl : ui.TextDirection.ltr,
|
||||
children: [
|
||||
("${LocaleKeys.voucherNo.tr(context: context)}: ").toText14(isBold: true, weight: FontWeight.w700),
|
||||
"${widget.purchaseData.voucherCode}".toText14(isBold: true, weight: FontWeight.w700),
|
||||
],
|
||||
),
|
||||
],
|
||||
SizedBox(height: 24.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
).paddingSymmetrical(24.h, 24.h),
|
||||
|
||||
// Save Qr Screenshot Area End
|
||||
// Order Details Card
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: false),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
textDirection: appState.isArabic() ? ui.TextDirection.rtl : ui.TextDirection.ltr,
|
||||
children: [
|
||||
Row(
|
||||
textDirection: appState.isArabic() ? ui.TextDirection.rtl : ui.TextDirection.ltr,
|
||||
children: [
|
||||
("${LocaleKeys.voucherNo.tr(context: context)}: ").toText14(isBold: true, weight: FontWeight.w700),
|
||||
"${widget.purchaseData.voucherCode}".toText14(isBold: true, weight: FontWeight.w700),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.h, vertical: 6.h),
|
||||
decoration: BoxDecoration(
|
||||
color: _getStatusColor(widget.purchaseData.state!).withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(12.h),
|
||||
),
|
||||
child: _getStatusText(widget.purchaseData.state!).toText10(
|
||||
isBold: true,
|
||||
color: _getStatusColor(widget.purchaseData.state!),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
SizedBox(height: 8.h),
|
||||
LocaleKeys.saveTheQRCode.tr(context: context).toText21(isBold: true, weight: FontWeight.w700),
|
||||
LocaleKeys.pleaseShowQRCodeInHospital.tr(context: context).toText12(isBold: true, fontWeight: FontWeight.w700),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 24.h),
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQRCode() {
|
||||
if (widget.purchaseData.qrCode == null || widget.purchaseData.qrCode!.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.qr_code_2, size: 48.h, color: AppColors.greyTextColor),
|
||||
SizedBox(height: 8.h),
|
||||
LocaleKeys.qrCodeNotAvailable.tr(context: context).toText12(color: AppColors.greyTextColor),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return FutureBuilder<String>(
|
||||
future: Utils.createFileFromString(widget.purchaseData.qrCode!, "png"),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(color: AppColors.primaryRedColor),
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 48.h, color: AppColors.errorColor),
|
||||
SizedBox(height: 8.h),
|
||||
LocaleKeys.errorLoadingQRCode.tr(context: context).toText12(color: AppColors.errorColor),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.hasData && snapshot.data != null) {
|
||||
qrCodeFilePath = snapshot.data;
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
child: Image.file(File(snapshot.data!), width: MediaQuery.of(context).size.width * 0.6.w, height: MediaQuery.of(context).size.width * 0.6.h, fit: BoxFit.cover),
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue