@numueg/theme-sdk 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/dist/{chunk-7RZISLP2.cjs → chunk-H7RGDWSU.cjs} +3 -3
- package/dist/{chunk-7RZISLP2.cjs.map → chunk-H7RGDWSU.cjs.map} +1 -1
- package/dist/{chunk-ZECKSIDL.mjs → chunk-QPXMBK5U.mjs} +3 -3
- package/dist/{chunk-ZECKSIDL.mjs.map → chunk-QPXMBK5U.mjs.map} +1 -1
- package/dist/{entities-1XRRUTlO.d.mts → entities-DRKn04q0.d.mts} +32 -0
- package/dist/{entities-1XRRUTlO.d.ts → entities-DRKn04q0.d.ts} +32 -0
- package/dist/index.cjs +161 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +215 -5
- package/dist/index.d.ts +215 -5
- package/dist/index.mjs +146 -29
- package/dist/index.mjs.map +1 -1
- package/dist/{mount-DDFMacmj.d.mts → mount-BTaDtz8k.d.mts} +1 -1
- package/dist/{mount-qofjiBxw.d.ts → mount-CLQniVfc.d.ts} +1 -1
- package/dist/types.d.mts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/v2-compat.d.mts +1 -1
- package/dist/v2-compat.d.ts +1 -1
- package/dist/validation.cjs +11 -11
- package/dist/validation.mjs +1 -1
- package/dist/verify.cjs +3 -3
- package/dist/verify.d.mts +2 -2
- package/dist/verify.d.ts +2 -2
- package/dist/verify.mjs +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunkTBSNHHFH_cjs = require('./chunk-TBSNHHFH.cjs');
|
|
4
|
-
var
|
|
4
|
+
var chunkH7RGDWSU_cjs = require('./chunk-H7RGDWSU.cjs');
|
|
5
5
|
var chunkV3JDQXD3_cjs = require('./chunk-V3JDQXD3.cjs');
|
|
6
6
|
var react = require('react');
|
|
7
7
|
var client = require('react-dom/client');
|
|
@@ -1023,6 +1023,133 @@ function useRelatedProducts(productId, options = {}) {
|
|
|
1023
1023
|
});
|
|
1024
1024
|
return { items: data ?? EMPTY4, loading: isLoading, error: error ?? null };
|
|
1025
1025
|
}
|
|
1026
|
+
function useActivePromotions(page = "/", locale = "ar") {
|
|
1027
|
+
const key = `numu:promotions:${page}:${locale}`;
|
|
1028
|
+
const fetcher = react.useCallback(
|
|
1029
|
+
async (signal) => {
|
|
1030
|
+
const qs = new URLSearchParams({ page, locale });
|
|
1031
|
+
const res = await fetch(`/api/storefront/promotions?${qs.toString()}`, {
|
|
1032
|
+
credentials: "include",
|
|
1033
|
+
cache: "no-store",
|
|
1034
|
+
signal
|
|
1035
|
+
});
|
|
1036
|
+
if (!res.ok) return null;
|
|
1037
|
+
const json = await res.json();
|
|
1038
|
+
if (!json) return null;
|
|
1039
|
+
return (json.data ?? json) || null;
|
|
1040
|
+
},
|
|
1041
|
+
[page, locale]
|
|
1042
|
+
);
|
|
1043
|
+
const { data } = useCachedResource(
|
|
1044
|
+
key,
|
|
1045
|
+
fetcher,
|
|
1046
|
+
{ initialData: null }
|
|
1047
|
+
);
|
|
1048
|
+
return data ?? null;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/utils/money.ts
|
|
1052
|
+
function resolveLocale(locale) {
|
|
1053
|
+
return locale === "ar" ? "ar-EG" : "en-EG";
|
|
1054
|
+
}
|
|
1055
|
+
function formatMoney(cents, options = {}) {
|
|
1056
|
+
const { currency, locale, fractionDigits = 0 } = options;
|
|
1057
|
+
const safe = Number.isFinite(cents) ? cents : 0;
|
|
1058
|
+
return new Intl.NumberFormat(resolveLocale(locale), {
|
|
1059
|
+
style: "currency",
|
|
1060
|
+
currency: currency || "EGP",
|
|
1061
|
+
maximumFractionDigits: fractionDigits
|
|
1062
|
+
}).format(safe / 100);
|
|
1063
|
+
}
|
|
1064
|
+
function formatMoneyMajor(amount, options = {}) {
|
|
1065
|
+
return formatMoney(
|
|
1066
|
+
(Number.isFinite(amount) ? amount : 0) * 100,
|
|
1067
|
+
options
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
function centsToMajor(cents) {
|
|
1071
|
+
return (Number.isFinite(cents) ? cents : 0) / 100;
|
|
1072
|
+
}
|
|
1073
|
+
function majorToCents(amount) {
|
|
1074
|
+
return Math.round((Number.isFinite(amount) ? amount : 0) * 100);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// src/lib/promotions.ts
|
|
1078
|
+
function multibuyOffers(promotions) {
|
|
1079
|
+
if (!promotions) return [];
|
|
1080
|
+
const list = Array.isArray(promotions) ? promotions : promotions.auto_discounts ?? [];
|
|
1081
|
+
const offers = [];
|
|
1082
|
+
for (const promo of list) {
|
|
1083
|
+
const rule = promo?.discount_rule;
|
|
1084
|
+
if (!rule || rule.kind !== "multibuy") continue;
|
|
1085
|
+
const quantity = rule.multibuy_quantity;
|
|
1086
|
+
const groupPriceCents = rule.multibuy_price_cents;
|
|
1087
|
+
if (typeof quantity !== "number" || quantity < 2) continue;
|
|
1088
|
+
if (typeof groupPriceCents !== "number" || groupPriceCents <= 0) continue;
|
|
1089
|
+
const eligibleProductIds = promo.eligible_product_ids ?? [];
|
|
1090
|
+
const eligibleCategoryIds = promo.eligible_category_ids ?? [];
|
|
1091
|
+
offers.push({
|
|
1092
|
+
promotionId: promo.promotion_id,
|
|
1093
|
+
quantity,
|
|
1094
|
+
groupPriceCents,
|
|
1095
|
+
groupPriceMajor: centsToMajor(groupPriceCents),
|
|
1096
|
+
headline: promo.translated_content?.headline,
|
|
1097
|
+
eligibleProductIds,
|
|
1098
|
+
eligibleCategoryIds,
|
|
1099
|
+
// No scoping rows at all ⇒ every product qualifies. Note an older
|
|
1100
|
+
// backend that doesn't send these fields also lands here, which is the
|
|
1101
|
+
// right default: counting everything is what themes did before.
|
|
1102
|
+
isStoreWide: eligibleProductIds.length === 0 && eligibleCategoryIds.length === 0,
|
|
1103
|
+
raw: promo
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
return offers;
|
|
1107
|
+
}
|
|
1108
|
+
function offerIncludesProduct(offer, product) {
|
|
1109
|
+
if (!offer) return false;
|
|
1110
|
+
if (offer.isStoreWide) return true;
|
|
1111
|
+
const id = product?.product_id ?? product?.id;
|
|
1112
|
+
if (id && offer.eligibleProductIds.includes(id)) return true;
|
|
1113
|
+
const category = product?.category_id;
|
|
1114
|
+
return Boolean(category && offer.eligibleCategoryIds.includes(category));
|
|
1115
|
+
}
|
|
1116
|
+
function eligibleUnitsInCart(offer, cart) {
|
|
1117
|
+
if (!offer) return 0;
|
|
1118
|
+
return (cart?.items ?? []).reduce((sum, item) => {
|
|
1119
|
+
if (!item) return sum;
|
|
1120
|
+
if (!offerIncludesProduct(offer, item)) return sum;
|
|
1121
|
+
return sum + (item.quantity || 0);
|
|
1122
|
+
}, 0);
|
|
1123
|
+
}
|
|
1124
|
+
function offerProgress(offer, cart, eligibleUnits) {
|
|
1125
|
+
const empty = {
|
|
1126
|
+
unitsInCart: 0,
|
|
1127
|
+
unitsNeeded: 0,
|
|
1128
|
+
groupsUnlocked: 0,
|
|
1129
|
+
savingMajor: 0
|
|
1130
|
+
};
|
|
1131
|
+
if (!offer) return empty;
|
|
1132
|
+
const unitsInCart = typeof eligibleUnits === "number" ? Math.max(0, eligibleUnits) : eligibleUnitsInCart(offer, cart);
|
|
1133
|
+
const groupsUnlocked = Math.floor(unitsInCart / offer.quantity);
|
|
1134
|
+
const remainder = unitsInCart % offer.quantity;
|
|
1135
|
+
const unitsNeeded = remainder === 0 ? 0 : offer.quantity - remainder;
|
|
1136
|
+
const applied = (cart?.applied_promotions ?? []).find(
|
|
1137
|
+
(p) => p && p.id === offer.promotionId
|
|
1138
|
+
);
|
|
1139
|
+
return {
|
|
1140
|
+
unitsInCart,
|
|
1141
|
+
unitsNeeded,
|
|
1142
|
+
groupsUnlocked,
|
|
1143
|
+
savingMajor: applied ? applied.amount || 0 : 0
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
function offerBeatsRegularPrice(offer, unitPriceMajor) {
|
|
1147
|
+
if (!offer || typeof unitPriceMajor !== "number" || unitPriceMajor <= 0) {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
const unitCents = Math.round(unitPriceMajor * 100);
|
|
1151
|
+
return unitCents * offer.quantity > offer.groupPriceCents;
|
|
1152
|
+
}
|
|
1026
1153
|
function useProductSizeChart(productOverride) {
|
|
1027
1154
|
const ctxProduct = useProductOptional();
|
|
1028
1155
|
const product = productOverride ?? ctxProduct;
|
|
@@ -1496,11 +1623,27 @@ var EMPTY_CART = {
|
|
|
1496
1623
|
};
|
|
1497
1624
|
function normalizeCartFromServer(cart) {
|
|
1498
1625
|
const toMajor = (n) => typeof n === "number" ? n / 100 : 0;
|
|
1626
|
+
const { automatic_discount_cents: wireAutomaticCents, ...rest } = cart;
|
|
1499
1627
|
return {
|
|
1500
|
-
...
|
|
1628
|
+
...rest,
|
|
1501
1629
|
subtotal: toMajor(cart.subtotal),
|
|
1502
1630
|
total: toMajor(cart.total),
|
|
1503
1631
|
...cart.discount_amount != null ? { discount_amount: toMajor(cart.discount_amount) } : {},
|
|
1632
|
+
// Offers-v2 automatic promotions. These MUST be converted here too —
|
|
1633
|
+
// they arrive in cents next to already-major totals, so forwarding them
|
|
1634
|
+
// raw makes every theme render a saving 100x too large. This function is
|
|
1635
|
+
// the one and only cents→major boundary for the cart.
|
|
1636
|
+
//
|
|
1637
|
+
// The backend field is `automatic_discount_cents`; themes get
|
|
1638
|
+
// `automatic_discount` (major), because a `_cents` name holding pounds
|
|
1639
|
+
// invites exactly the double-divide this conversion exists to prevent.
|
|
1640
|
+
...wireAutomaticCents != null ? { automatic_discount: toMajor(wireAutomaticCents) } : {},
|
|
1641
|
+
...Array.isArray(cart.applied_promotions) ? {
|
|
1642
|
+
applied_promotions: cart.applied_promotions.map((p) => ({
|
|
1643
|
+
...p,
|
|
1644
|
+
amount: toMajor(p.amount)
|
|
1645
|
+
}))
|
|
1646
|
+
} : {},
|
|
1504
1647
|
items: Array.isArray(cart.items) ? cart.items.map((it) => {
|
|
1505
1648
|
const raw = it;
|
|
1506
1649
|
return {
|
|
@@ -3851,32 +3994,6 @@ function assetUrl(name) {
|
|
|
3851
3994
|
return `${cleanBase}${filename}`;
|
|
3852
3995
|
}
|
|
3853
3996
|
|
|
3854
|
-
// src/utils/money.ts
|
|
3855
|
-
function resolveLocale(locale) {
|
|
3856
|
-
return locale === "ar" ? "ar-EG" : "en-EG";
|
|
3857
|
-
}
|
|
3858
|
-
function formatMoney(cents, options = {}) {
|
|
3859
|
-
const { currency, locale, fractionDigits = 0 } = options;
|
|
3860
|
-
const safe = Number.isFinite(cents) ? cents : 0;
|
|
3861
|
-
return new Intl.NumberFormat(resolveLocale(locale), {
|
|
3862
|
-
style: "currency",
|
|
3863
|
-
currency: currency || "EGP",
|
|
3864
|
-
maximumFractionDigits: fractionDigits
|
|
3865
|
-
}).format(safe / 100);
|
|
3866
|
-
}
|
|
3867
|
-
function formatMoneyMajor(amount, options = {}) {
|
|
3868
|
-
return formatMoney(
|
|
3869
|
-
(Number.isFinite(amount) ? amount : 0) * 100,
|
|
3870
|
-
options
|
|
3871
|
-
);
|
|
3872
|
-
}
|
|
3873
|
-
function centsToMajor(cents) {
|
|
3874
|
-
return (Number.isFinite(cents) ? cents : 0) / 100;
|
|
3875
|
-
}
|
|
3876
|
-
function majorToCents(amount) {
|
|
3877
|
-
return Math.round((Number.isFinite(amount) ? amount : 0) * 100);
|
|
3878
|
-
}
|
|
3879
|
-
|
|
3880
3997
|
// src/utils/templates.ts
|
|
3881
3998
|
function resolveSections(group) {
|
|
3882
3999
|
if (!group) return [];
|
|
@@ -3971,43 +4088,43 @@ Object.defineProperty(exports, "resolveThemeSettings", {
|
|
|
3971
4088
|
});
|
|
3972
4089
|
Object.defineProperty(exports, "KNOWN_SETTING_TYPES", {
|
|
3973
4090
|
enumerable: true,
|
|
3974
|
-
get: function () { return
|
|
4091
|
+
get: function () { return chunkH7RGDWSU_cjs.KNOWN_SETTING_TYPES; }
|
|
3975
4092
|
});
|
|
3976
4093
|
Object.defineProperty(exports, "KNOWN_TEMPLATES", {
|
|
3977
4094
|
enumerable: true,
|
|
3978
|
-
get: function () { return
|
|
4095
|
+
get: function () { return chunkH7RGDWSU_cjs.KNOWN_TEMPLATES; }
|
|
3979
4096
|
});
|
|
3980
4097
|
Object.defineProperty(exports, "REQUIRED_TEMPLATES", {
|
|
3981
4098
|
enumerable: true,
|
|
3982
|
-
get: function () { return
|
|
4099
|
+
get: function () { return chunkH7RGDWSU_cjs.REQUIRED_TEMPLATES; }
|
|
3983
4100
|
});
|
|
3984
4101
|
Object.defineProperty(exports, "SDK_VERSION", {
|
|
3985
4102
|
enumerable: true,
|
|
3986
|
-
get: function () { return
|
|
4103
|
+
get: function () { return chunkH7RGDWSU_cjs.SDK_VERSION; }
|
|
3987
4104
|
});
|
|
3988
4105
|
Object.defineProperty(exports, "THEME_CONTRACT_VERSION", {
|
|
3989
4106
|
enumerable: true,
|
|
3990
|
-
get: function () { return
|
|
4107
|
+
get: function () { return chunkH7RGDWSU_cjs.THEME_CONTRACT_VERSION; }
|
|
3991
4108
|
});
|
|
3992
4109
|
Object.defineProperty(exports, "mergeResults", {
|
|
3993
4110
|
enumerable: true,
|
|
3994
|
-
get: function () { return
|
|
4111
|
+
get: function () { return chunkH7RGDWSU_cjs.mergeResults; }
|
|
3995
4112
|
});
|
|
3996
4113
|
Object.defineProperty(exports, "validateBuiltManifest", {
|
|
3997
4114
|
enumerable: true,
|
|
3998
|
-
get: function () { return
|
|
4115
|
+
get: function () { return chunkH7RGDWSU_cjs.validateBuiltManifest; }
|
|
3999
4116
|
});
|
|
4000
4117
|
Object.defineProperty(exports, "validateManifest", {
|
|
4001
4118
|
enumerable: true,
|
|
4002
|
-
get: function () { return
|
|
4119
|
+
get: function () { return chunkH7RGDWSU_cjs.validateManifest; }
|
|
4003
4120
|
});
|
|
4004
4121
|
Object.defineProperty(exports, "validateSectionSchema", {
|
|
4005
4122
|
enumerable: true,
|
|
4006
|
-
get: function () { return
|
|
4123
|
+
get: function () { return chunkH7RGDWSU_cjs.validateSectionSchema; }
|
|
4007
4124
|
});
|
|
4008
4125
|
Object.defineProperty(exports, "validateSettingsAgainstSchema", {
|
|
4009
4126
|
enumerable: true,
|
|
4010
|
-
get: function () { return
|
|
4127
|
+
get: function () { return chunkH7RGDWSU_cjs.validateSettingsAgainstSchema; }
|
|
4011
4128
|
});
|
|
4012
4129
|
Object.defineProperty(exports, "CartContext", {
|
|
4013
4130
|
enumerable: true,
|
|
@@ -4142,6 +4259,7 @@ exports.defineBlock = defineBlock;
|
|
|
4142
4259
|
exports.defineSection = defineSection;
|
|
4143
4260
|
exports.defineThemeEntry = defineThemeEntry;
|
|
4144
4261
|
exports.dynamicSource = dynamicSource;
|
|
4262
|
+
exports.eligibleUnitsInCart = eligibleUnitsInCart;
|
|
4145
4263
|
exports.findVariantByOptions = findVariantByOptions;
|
|
4146
4264
|
exports.flattenMessages = flattenMessages;
|
|
4147
4265
|
exports.focalSrc = focalSrc;
|
|
@@ -4157,6 +4275,10 @@ exports.logoImgStyle = logoImgStyle;
|
|
|
4157
4275
|
exports.logoStyleTokens = logoStyleTokens;
|
|
4158
4276
|
exports.majorToCents = majorToCents;
|
|
4159
4277
|
exports.mountTheme = mountTheme;
|
|
4278
|
+
exports.multibuyOffers = multibuyOffers;
|
|
4279
|
+
exports.offerBeatsRegularPrice = offerBeatsRegularPrice;
|
|
4280
|
+
exports.offerIncludesProduct = offerIncludesProduct;
|
|
4281
|
+
exports.offerProgress = offerProgress;
|
|
4160
4282
|
exports.pickTranslations = pickTranslations;
|
|
4161
4283
|
exports.productHref = productHref;
|
|
4162
4284
|
exports.publishVariantSelection = publishVariantSelection;
|
|
@@ -4173,6 +4295,7 @@ exports.resolveSourcePath = resolveSourcePath;
|
|
|
4173
4295
|
exports.sanitizeHtml = sanitizeHtml;
|
|
4174
4296
|
exports.selectChromeSections = selectChromeSections;
|
|
4175
4297
|
exports.selectTemplateSections = selectTemplateSections;
|
|
4298
|
+
exports.useActivePromotions = useActivePromotions;
|
|
4176
4299
|
exports.useAnalytics = useAnalytics;
|
|
4177
4300
|
exports.useApp = useApp;
|
|
4178
4301
|
exports.useArticle = useArticle;
|