storefront-layouts 1.0.9 → 1.0.13
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/dist/components/shipping/ProductDeliveryInfoSection.d.ts.map +1 -1
- package/dist/components/shipping/ProductDeliveryInfoSection.js +10 -3
- package/dist/components/ui/address-selector.d.ts.map +1 -1
- package/dist/components/ui/address-selector.js +4 -3
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/checkout-button.d.ts.map +1 -1
- package/dist/components/ui/checkout-button.js +2 -1
- package/dist/components/ui/product-card.d.ts.map +1 -1
- package/dist/components/ui/product-card.js +15 -4
- package/dist/components/ui/route-skeleton-loader.d.ts.map +1 -1
- package/dist/components/ui/route-skeleton-loader.js +6 -2
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet.js +2 -2
- package/dist/components/ui/skeletons/checkout-skeleton.js +1 -1
- package/dist/editor/blocks/defaults/clothing-minimal-home.js +1 -1
- package/dist/editor/blocks/migrate-clothing-minimal.js +1 -1
- package/dist/hooks/use-pdp-variation.d.ts +57 -0
- package/dist/hooks/use-pdp-variation.d.ts.map +1 -0
- package/dist/hooks/use-pdp-variation.js +69 -0
- package/dist/hooks/use-product-by-slug.d.ts.map +1 -1
- package/dist/hooks/use-product-by-slug.js +0 -5
- package/dist/hooks/use-product-variation-selection.js +1 -1
- package/dist/hooks/use-shipping-quote.d.ts +1 -0
- package/dist/hooks/use-shipping-quote.d.ts.map +1 -1
- package/dist/hooks/use-shipping-quote.js +3 -0
- package/dist/layouts/booking/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/layouts/booking/pages/ProductDetailPage.js +130 -223
- package/dist/layouts/booking-agenda/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/layouts/booking-agenda/pages/ProductDetailPage.js +130 -223
- package/dist/layouts/clothing/components/ClothingHomePage.d.ts.map +1 -1
- package/dist/layouts/clothing/components/ClothingHomePage.js +40 -2
- package/dist/layouts/clothing/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/layouts/clothing/pages/ProductDetailPage.js +127 -209
- package/dist/layouts/clothing/pages/ProductsPage.d.ts.map +1 -1
- package/dist/layouts/clothing/pages/ProductsPage.js +2 -5
- package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.d.ts.map +1 -1
- package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.js +5 -3
- package/dist/layouts/clothing-minimal/components/clothing-minimal-block-render.d.ts.map +1 -1
- package/dist/layouts/clothing-minimal/components/clothing-minimal-block-render.js +13 -4
- package/dist/layouts/clothing-minimal/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/layouts/clothing-minimal/pages/ProductDetailPage.js +130 -223
- package/dist/layouts/clothing-minimal/pages/ProductsPage.d.ts.map +1 -1
- package/dist/layouts/clothing-minimal/pages/ProductsPage.js +2 -5
- package/dist/layouts/electronics/components/ElectronicsGridProductCard.js +1 -1
- package/dist/layouts/electronics/components/ElectronicsStoreHeader.d.ts.map +1 -1
- package/dist/layouts/electronics/components/ElectronicsStoreHeader.js +10 -9
- package/dist/layouts/electronics/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/layouts/electronics/pages/ProductDetailPage.js +130 -223
- package/dist/layouts/electronics/pages/ProductsPage.d.ts.map +1 -1
- package/dist/layouts/electronics/pages/ProductsPage.js +2 -5
- package/dist/layouts/electronics-grid/components/ElectronicsGridProductCard.js +1 -1
- package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.d.ts.map +1 -1
- package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.js +4 -5
- package/dist/layouts/electronics-grid/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/layouts/electronics-grid/pages/ProductDetailPage.js +130 -223
- package/dist/layouts/electronics-grid/pages/ProductsPage.d.ts.map +1 -1
- package/dist/layouts/electronics-grid/pages/ProductsPage.js +2 -5
- package/dist/layouts/food/pages/MenuPage.js +1 -1
- package/dist/layouts/food/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/layouts/food/pages/ProductDetailPage.js +37 -140
- package/dist/layouts/food/pages/ProductsPage.d.ts.map +1 -1
- package/dist/layouts/food/pages/ProductsPage.js +2 -5
- package/dist/layouts/food-modern/pages/MenuPage.js +1 -1
- package/dist/layouts/food-modern/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/layouts/food-modern/pages/ProductDetailPage.js +130 -223
- package/dist/layouts/food-modern/pages/ProductsPage.d.ts.map +1 -1
- package/dist/layouts/food-modern/pages/ProductsPage.js +2 -5
- package/dist/layouts/motivational-speaker/pages/ProductsPage.d.ts.map +1 -1
- package/dist/layouts/motivational-speaker/pages/ProductsPage.js +2 -5
- package/dist/layouts/shared/components/PdpColorSwatch.d.ts +11 -0
- package/dist/layouts/shared/components/PdpColorSwatch.d.ts.map +1 -0
- package/dist/layouts/shared/components/PdpColorSwatch.js +29 -0
- package/dist/layouts/shared/components/PdpVariationSelectors.d.ts +13 -0
- package/dist/layouts/shared/components/PdpVariationSelectors.d.ts.map +1 -0
- package/dist/layouts/shared/components/PdpVariationSelectors.js +65 -0
- package/dist/layouts/shared/components/StoreCartSheet.d.ts +8 -0
- package/dist/layouts/shared/components/StoreCartSheet.d.ts.map +1 -0
- package/dist/layouts/shared/components/StoreCartSheet.js +16 -0
- package/dist/layouts/shared/components/StoreHeader.d.ts.map +1 -1
- package/dist/layouts/shared/components/StoreHeader.js +13 -12
- package/dist/layouts/shared/components/headers/ClothingMinimalHeader.d.ts.map +1 -1
- package/dist/layouts/shared/components/headers/ClothingMinimalHeader.js +5 -7
- package/dist/layouts/shared/pages/CartPage.d.ts.map +1 -1
- package/dist/layouts/shared/pages/CartPage.js +6 -5
- package/dist/layouts/shared/pages/CheckoutPage.d.ts.map +1 -1
- package/dist/layouts/shared/pages/CheckoutPage.js +209 -107
- package/dist/layouts/shared/product-category-filter-chip.d.ts +2 -0
- package/dist/layouts/shared/product-category-filter-chip.d.ts.map +1 -0
- package/dist/layouts/shared/product-category-filter-chip.js +6 -0
- package/dist/lib/pdp-cart-actions.d.ts +5 -0
- package/dist/lib/pdp-cart-actions.d.ts.map +1 -0
- package/dist/lib/pdp-cart-actions.js +19 -0
- package/dist/lib/product-variation-utils.d.ts +17 -0
- package/dist/lib/product-variation-utils.d.ts.map +1 -1
- package/dist/lib/product-variation-utils.js +193 -14
- package/dist/lib/services/checkout.service.d.ts +11 -0
- package/dist/lib/services/checkout.service.d.ts.map +1 -1
- package/dist/lib/services/shipping.service.d.ts +3 -0
- package/dist/lib/services/shipping.service.d.ts.map +1 -1
- package/dist/lib/store-config-utils.d.ts.map +1 -1
- package/dist/lib/store-config-utils.js +104 -11
- package/dist/lib/store-context.d.ts.map +1 -1
- package/dist/lib/store-context.js +23 -8
- package/dist/lib/utils/asset-helpers.d.ts +2 -0
- package/dist/lib/utils/asset-helpers.d.ts.map +1 -1
- package/dist/lib/utils/asset-helpers.js +24 -0
- package/dist/styles/index.css +143 -45
- package/package.json +8 -2
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CartItem } from './store-context.js';
|
|
2
|
+
import type { StoreProduct } from './store-types.js';
|
|
3
|
+
import { type StoreProductVariant } from './product-variation-utils.js';
|
|
4
|
+
export declare function buildPdpBuyNowItem(product: StoreProduct, quantity: number, selectedVariation?: StoreProductVariant | null, displayPrice?: number, selectedAttributes?: Record<string, string>): CartItem;
|
|
5
|
+
//# sourceMappingURL=pdp-cart-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdp-cart-actions.d.ts","sourceRoot":"","sources":["../../src/lib/pdp-cart-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,EAChB,iBAAiB,CAAC,EAAE,mBAAmB,GAAG,IAAI,EAC9C,YAAY,CAAC,EAAE,MAAM,EACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1C,QAAQ,CAkBV"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { assertVariationSelected, buildCartLineProduct, formatVariationLabel, } from './product-variation-utils.js';
|
|
2
|
+
export function buildPdpBuyNowItem(product, quantity, selectedVariation, displayPrice, selectedAttributes) {
|
|
3
|
+
assertVariationSelected(product, selectedVariation, selectedAttributes);
|
|
4
|
+
const lineProduct = buildCartLineProduct(product, selectedVariation);
|
|
5
|
+
const variantId = selectedVariation?.id;
|
|
6
|
+
const price = displayPrice ?? lineProduct.price;
|
|
7
|
+
const variantName = formatVariationLabel(selectedVariation);
|
|
8
|
+
const id = variantId ? `${product.id}-${variantId}` : product.id;
|
|
9
|
+
return {
|
|
10
|
+
id,
|
|
11
|
+
productId: product.id,
|
|
12
|
+
quantity,
|
|
13
|
+
variantId,
|
|
14
|
+
variantName,
|
|
15
|
+
price,
|
|
16
|
+
product: { ...lineProduct, price },
|
|
17
|
+
currency: lineProduct.currency,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
import type { StoreProduct } from './store-types';
|
|
2
2
|
export type StoreProductVariant = NonNullable<StoreProduct['variants']>[number];
|
|
3
|
+
/** Collapse display labels (e.g. from legacy mapping) onto attribute keys used in admin. */
|
|
4
|
+
export declare function canonicalVariationOptionGroupKey(rawKey: string): string;
|
|
5
|
+
/** Matches storefront-admin ProductForm variation fields (e.g. Label "Name/Color" → `attributes.color`). */
|
|
6
|
+
export declare const VARIATION_ATTRIBUTE_LABELS: Record<string, string>;
|
|
7
|
+
export declare function getVariationAttributeLabel(attributeKey: string): string;
|
|
8
|
+
export declare function getSortedVariationOptionKeys(optionGroups: Record<string, string[]>): string[];
|
|
3
9
|
export declare function getVariationOptionGroups(variants: StoreProductVariant[] | undefined): Record<string, string[]>;
|
|
4
10
|
export declare function getDefaultSelectedAttributes(optionGroups: Record<string, string[]>): Record<string, string>;
|
|
5
11
|
export declare function findMatchingVariation(variants: StoreProductVariant[] | undefined, selectedAttributes: Record<string, string>): StoreProductVariant | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Prefer an exact combo; otherwise keep the new value and fill from a
|
|
14
|
+
* compatible variation that includes it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function reconcileSelectedAttributes(variants: StoreProductVariant[] | undefined, current: Record<string, string>, key: string, value: string): Record<string, string>;
|
|
17
|
+
/** True when a variation has this option and still matches the other selected attributes. */
|
|
18
|
+
export declare function isVariationOptionAvailable(variants: StoreProductVariant[] | undefined, selected: Record<string, string>, key: string, value: string): boolean;
|
|
6
19
|
type VariantStockFields = Pick<StoreProduct, 'trackInventory' | 'availableStock' | 'inStock' | 'lowStockThreshold' | 'name'>;
|
|
7
20
|
export declare function resolveVariantStockProduct(product: VariantStockFields, variant?: StoreProductVariant): VariantStockFields;
|
|
8
21
|
export declare function resolveVariantPrice(product: Pick<StoreProduct, 'price'>, variant?: StoreProductVariant): number;
|
|
9
22
|
export declare function resolveVariantSKU(product: Pick<StoreProduct, 'sku' | 'slug'>, variant?: StoreProductVariant): string;
|
|
23
|
+
export declare function resolveProductGalleryImageUrls(product: Pick<StoreProduct, 'images'>, variant?: StoreProductVariant | null): string[];
|
|
24
|
+
export declare function formatVariationLabel(variant?: StoreProductVariant | null): string | undefined;
|
|
25
|
+
export declare function getVariationOptionImage(variants: StoreProductVariant[] | undefined, attributeKey: string, value: string, product?: Pick<StoreProduct, 'images'> | null): string | undefined;
|
|
26
|
+
export declare function assertVariationSelected(product: StoreProduct, variant?: StoreProductVariant | null, _selectedAttributes?: Record<string, string>): void;
|
|
10
27
|
export declare function resolveVariantImageUrls(variant?: StoreProductVariant | null): string[];
|
|
11
28
|
export declare function buildCartLineProduct(product: StoreProduct, variant?: StoreProductVariant | null): StoreProduct;
|
|
12
29
|
export declare function getCartLineImage(item: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-variation-utils.d.ts","sourceRoot":"","sources":["../../src/lib/product-variation-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"product-variation-utils.d.ts","sourceRoot":"","sources":["../../src/lib/product-variation-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,41 @@
|
|
|
1
|
+
import { extractImageUrls } from './store-config-utils';
|
|
1
2
|
const ATTRIBUTE_ORDER = ['color', 'size', 'material', 'style'];
|
|
3
|
+
/** Collapse display labels (e.g. from legacy mapping) onto attribute keys used in admin. */
|
|
4
|
+
export function canonicalVariationOptionGroupKey(rawKey) {
|
|
5
|
+
const key = rawKey.toLowerCase().trim();
|
|
6
|
+
if (key === 'name / color' || key === 'name/color' || key === 'name / colour') {
|
|
7
|
+
return 'color';
|
|
8
|
+
}
|
|
9
|
+
return key;
|
|
10
|
+
}
|
|
11
|
+
/** Matches storefront-admin ProductForm variation fields (e.g. Label "Name/Color" → `attributes.color`). */
|
|
12
|
+
export const VARIATION_ATTRIBUTE_LABELS = {
|
|
13
|
+
color: 'Name / Color',
|
|
14
|
+
size: 'Size',
|
|
15
|
+
material: 'Material',
|
|
16
|
+
style: 'Style',
|
|
17
|
+
option: 'Option',
|
|
18
|
+
};
|
|
19
|
+
export function getVariationAttributeLabel(attributeKey) {
|
|
20
|
+
const key = attributeKey.toLowerCase();
|
|
21
|
+
return (VARIATION_ATTRIBUTE_LABELS[key] ??
|
|
22
|
+
key.charAt(0).toUpperCase() + key.slice(1));
|
|
23
|
+
}
|
|
24
|
+
export function getSortedVariationOptionKeys(optionGroups) {
|
|
25
|
+
const keys = Object.keys(optionGroups).filter((key) => (optionGroups[key]?.length ?? 0) > 0);
|
|
26
|
+
return keys.sort((a, b) => {
|
|
27
|
+
const order = ATTRIBUTE_ORDER;
|
|
28
|
+
const indexA = order.indexOf(a);
|
|
29
|
+
const indexB = order.indexOf(b);
|
|
30
|
+
if (indexA === -1 && indexB === -1)
|
|
31
|
+
return a.localeCompare(b);
|
|
32
|
+
if (indexA === -1)
|
|
33
|
+
return 1;
|
|
34
|
+
if (indexB === -1)
|
|
35
|
+
return -1;
|
|
36
|
+
return indexA - indexB;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
2
39
|
export function getVariationOptionGroups(variants) {
|
|
3
40
|
if (!variants?.length)
|
|
4
41
|
return {};
|
|
@@ -6,19 +43,20 @@ export function getVariationOptionGroups(variants) {
|
|
|
6
43
|
for (const variant of variants) {
|
|
7
44
|
if (variant.isActive === false)
|
|
8
45
|
continue;
|
|
9
|
-
|
|
10
|
-
|
|
46
|
+
const attributes = variant.attributes || {};
|
|
47
|
+
const hasStructuredAttributes = Object.keys(attributes).length > 0;
|
|
48
|
+
if (variant.name && variant.value && !hasStructuredAttributes) {
|
|
49
|
+
const key = canonicalVariationOptionGroupKey(variant.name);
|
|
11
50
|
if (!groups[key])
|
|
12
51
|
groups[key] = [];
|
|
13
52
|
if (!groups[key].includes(variant.value)) {
|
|
14
53
|
groups[key].push(variant.value);
|
|
15
54
|
}
|
|
16
55
|
}
|
|
17
|
-
const attributes = variant.attributes || {};
|
|
18
56
|
for (const [rawKey, rawValue] of Object.entries(attributes)) {
|
|
19
57
|
if (typeof rawValue !== 'string' || rawValue.trim() === '')
|
|
20
58
|
continue;
|
|
21
|
-
const key = rawKey
|
|
59
|
+
const key = canonicalVariationOptionGroupKey(rawKey);
|
|
22
60
|
if (!groups[key])
|
|
23
61
|
groups[key] = [];
|
|
24
62
|
if (!groups[key].includes(rawValue)) {
|
|
@@ -56,6 +94,48 @@ export function getDefaultSelectedAttributes(optionGroups) {
|
|
|
56
94
|
}
|
|
57
95
|
return defaults;
|
|
58
96
|
}
|
|
97
|
+
function getVariantAttributeValue(variant, attributeKey) {
|
|
98
|
+
const canonicalKey = canonicalVariationOptionGroupKey(attributeKey);
|
|
99
|
+
const attrs = variant.attributes || {};
|
|
100
|
+
const direct = attrs[canonicalKey];
|
|
101
|
+
if (typeof direct === 'string' && direct.trim() !== '')
|
|
102
|
+
return direct;
|
|
103
|
+
for (const [rawKey, rawValue] of Object.entries(attrs)) {
|
|
104
|
+
if (canonicalVariationOptionGroupKey(rawKey) === canonicalKey &&
|
|
105
|
+
typeof rawValue === 'string' &&
|
|
106
|
+
rawValue.trim() !== '') {
|
|
107
|
+
return rawValue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (variant.name &&
|
|
111
|
+
canonicalVariationOptionGroupKey(variant.name) === canonicalKey &&
|
|
112
|
+
typeof variant.value === 'string' &&
|
|
113
|
+
variant.value.trim() !== '') {
|
|
114
|
+
return variant.value;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
function attributesFromVariant(variant) {
|
|
119
|
+
const result = {};
|
|
120
|
+
const attrs = variant.attributes || {};
|
|
121
|
+
for (const [rawKey, rawValue] of Object.entries(attrs)) {
|
|
122
|
+
if (typeof rawValue !== 'string' || rawValue.trim() === '')
|
|
123
|
+
continue;
|
|
124
|
+
result[canonicalVariationOptionGroupKey(rawKey)] = rawValue;
|
|
125
|
+
}
|
|
126
|
+
if (Object.keys(result).length === 0 &&
|
|
127
|
+
variant.name &&
|
|
128
|
+
typeof variant.value === 'string' &&
|
|
129
|
+
variant.value.trim() !== '') {
|
|
130
|
+
result[canonicalVariationOptionGroupKey(variant.name)] = variant.value;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
function variantMatchesSelected(variant, selectedEntries) {
|
|
135
|
+
return selectedEntries.every(([key, value]) => {
|
|
136
|
+
return getVariantAttributeValue(variant, key) === value;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
59
139
|
export function findMatchingVariation(variants, selectedAttributes) {
|
|
60
140
|
if (!variants?.length)
|
|
61
141
|
return undefined;
|
|
@@ -63,21 +143,67 @@ export function findMatchingVariation(variants, selectedAttributes) {
|
|
|
63
143
|
if (activeVariants.length === 0)
|
|
64
144
|
return undefined;
|
|
65
145
|
const selectedEntries = Object.entries(selectedAttributes).filter(([, value]) => typeof value === 'string' && value.trim() !== '');
|
|
146
|
+
// No shopper selection → no match (keep base product price / stock until they click).
|
|
66
147
|
if (selectedEntries.length === 0) {
|
|
67
|
-
return
|
|
148
|
+
return undefined;
|
|
68
149
|
}
|
|
69
|
-
return activeVariants.find((variant) =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
150
|
+
return activeVariants.find((variant) => variantMatchesSelected(variant, selectedEntries));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Prefer an exact combo; otherwise keep the new value and fill from a
|
|
154
|
+
* compatible variation that includes it.
|
|
155
|
+
*/
|
|
156
|
+
export function reconcileSelectedAttributes(variants, current, key, value) {
|
|
157
|
+
const canonicalKey = canonicalVariationOptionGroupKey(key);
|
|
158
|
+
if (!value) {
|
|
159
|
+
return {};
|
|
160
|
+
}
|
|
161
|
+
const next = { ...current, [canonicalKey]: value };
|
|
162
|
+
const exact = findMatchingVariation(variants, next);
|
|
163
|
+
if (exact) {
|
|
164
|
+
return attributesFromVariant(exact);
|
|
165
|
+
}
|
|
166
|
+
const activeVariants = (variants || []).filter((variant) => variant.isActive !== false);
|
|
167
|
+
const candidates = activeVariants.filter((variant) => getVariantAttributeValue(variant, canonicalKey) === value);
|
|
168
|
+
if (candidates.length === 0) {
|
|
169
|
+
return { [canonicalKey]: value };
|
|
170
|
+
}
|
|
171
|
+
let best = candidates[0];
|
|
172
|
+
let bestScore = -1;
|
|
173
|
+
for (const candidate of candidates) {
|
|
174
|
+
let score = 0;
|
|
175
|
+
for (const [selectedKey, selectedValue] of Object.entries(current)) {
|
|
176
|
+
if (selectedKey === canonicalKey)
|
|
177
|
+
continue;
|
|
178
|
+
if (getVariantAttributeValue(candidate, selectedKey) === selectedValue) {
|
|
179
|
+
score += 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (score > bestScore) {
|
|
183
|
+
bestScore = score;
|
|
184
|
+
best = candidate;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return attributesFromVariant(best);
|
|
188
|
+
}
|
|
189
|
+
/** True when a variation has this option and still matches the other selected attributes. */
|
|
190
|
+
export function isVariationOptionAvailable(variants, selected, key, value) {
|
|
191
|
+
if (!variants?.length)
|
|
192
|
+
return true;
|
|
193
|
+
if (!value)
|
|
194
|
+
return true;
|
|
195
|
+
const canonicalKey = canonicalVariationOptionGroupKey(key);
|
|
196
|
+
const activeVariants = variants.filter((variant) => variant.isActive !== false);
|
|
197
|
+
return activeVariants.some((variant) => {
|
|
198
|
+
if (getVariantAttributeValue(variant, canonicalKey) !== value)
|
|
199
|
+
return false;
|
|
200
|
+
return Object.entries(selected).every(([selectedKey, selectedValue]) => {
|
|
201
|
+
if (!selectedValue)
|
|
75
202
|
return true;
|
|
76
|
-
if (
|
|
77
|
-
variant.value === value) {
|
|
203
|
+
if (canonicalVariationOptionGroupKey(selectedKey) === canonicalKey) {
|
|
78
204
|
return true;
|
|
79
205
|
}
|
|
80
|
-
return
|
|
206
|
+
return getVariantAttributeValue(variant, selectedKey) === selectedValue;
|
|
81
207
|
});
|
|
82
208
|
});
|
|
83
209
|
}
|
|
@@ -102,6 +228,59 @@ export function resolveVariantSKU(product, variant) {
|
|
|
102
228
|
product.slug?.toUpperCase().replace(/-/g, '').substring(0, 12).padEnd(8, 'X') ||
|
|
103
229
|
'N/A');
|
|
104
230
|
}
|
|
231
|
+
export function resolveProductGalleryImageUrls(product, variant) {
|
|
232
|
+
const base = extractImageUrls(product.images).filter((url) => url.trim().length > 0);
|
|
233
|
+
const variantUrls = resolveVariantImageUrls(variant);
|
|
234
|
+
if (variantUrls.length > 0) {
|
|
235
|
+
const merged = [...variantUrls];
|
|
236
|
+
for (const url of base) {
|
|
237
|
+
if (!merged.includes(url))
|
|
238
|
+
merged.push(url);
|
|
239
|
+
}
|
|
240
|
+
return merged;
|
|
241
|
+
}
|
|
242
|
+
return base;
|
|
243
|
+
}
|
|
244
|
+
export function formatVariationLabel(variant) {
|
|
245
|
+
if (!variant)
|
|
246
|
+
return undefined;
|
|
247
|
+
const fromAttrs = Object.values(variant.attributes || {})
|
|
248
|
+
.filter((value) => typeof value === 'string' && value.trim() !== '')
|
|
249
|
+
.join(' / ');
|
|
250
|
+
if (fromAttrs)
|
|
251
|
+
return fromAttrs;
|
|
252
|
+
if (variant.value)
|
|
253
|
+
return variant.value;
|
|
254
|
+
if (variant.name)
|
|
255
|
+
return variant.name;
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
export function getVariationOptionImage(variants, attributeKey, value, product) {
|
|
259
|
+
const productImage = product
|
|
260
|
+
? extractImageUrls(product.images).find((url) => url.trim().length > 0)
|
|
261
|
+
: undefined;
|
|
262
|
+
if (!variants?.length)
|
|
263
|
+
return productImage;
|
|
264
|
+
const match = variants.find((variant) => {
|
|
265
|
+
if (variant.isActive === false)
|
|
266
|
+
return false;
|
|
267
|
+
const attrs = variant.attributes || {};
|
|
268
|
+
const attrValue = attrs[attributeKey];
|
|
269
|
+
if (attrValue === value)
|
|
270
|
+
return true;
|
|
271
|
+
return variant.name?.toLowerCase() === attributeKey.toLowerCase() && variant.value === value;
|
|
272
|
+
});
|
|
273
|
+
return resolveVariantImageUrls(match)[0] || productImage;
|
|
274
|
+
}
|
|
275
|
+
export function assertVariationSelected(product, variant, _selectedAttributes) {
|
|
276
|
+
const groups = getVariationOptionGroups(product.variants);
|
|
277
|
+
const hasOptions = Object.values(groups).some((values) => values.length > 0);
|
|
278
|
+
if (!hasOptions)
|
|
279
|
+
return;
|
|
280
|
+
if (variant?.id)
|
|
281
|
+
return;
|
|
282
|
+
throw new Error('Please select a variation');
|
|
283
|
+
}
|
|
105
284
|
export function resolveVariantImageUrls(variant) {
|
|
106
285
|
if (!variant?.images)
|
|
107
286
|
return [];
|
|
@@ -5,6 +5,7 @@ export interface CheckoutItem {
|
|
|
5
5
|
storeId?: string;
|
|
6
6
|
quantity: number;
|
|
7
7
|
price: number;
|
|
8
|
+
currency?: string;
|
|
8
9
|
variationId?: string;
|
|
9
10
|
variantId?: string;
|
|
10
11
|
options?: {
|
|
@@ -60,6 +61,10 @@ export interface MarketplaceCheckoutRequest {
|
|
|
60
61
|
customerId?: string;
|
|
61
62
|
isPickup?: boolean;
|
|
62
63
|
pickupLocationId?: string;
|
|
64
|
+
/** Currency used for browsing/display. */
|
|
65
|
+
displayCurrency?: string;
|
|
66
|
+
/** Currency to send to the payment provider. */
|
|
67
|
+
chargeCurrency?: string;
|
|
63
68
|
}
|
|
64
69
|
export interface CheckoutRequest {
|
|
65
70
|
storeId: string;
|
|
@@ -100,6 +105,12 @@ export interface CheckoutRequest {
|
|
|
100
105
|
deliveryProviderKey?: string;
|
|
101
106
|
deliveryMethodId?: string;
|
|
102
107
|
deliveryQuoteId?: string;
|
|
108
|
+
deliveryPrice?: number;
|
|
109
|
+
deliveryCurrency?: string;
|
|
110
|
+
/** Currency used for browsing/display. */
|
|
111
|
+
displayCurrency?: string;
|
|
112
|
+
/** Currency to send to the payment provider. */
|
|
113
|
+
chargeCurrency?: string;
|
|
103
114
|
}
|
|
104
115
|
export interface CheckoutResponse {
|
|
105
116
|
orderId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/checkout.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE3E,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QAER,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,KAAK,CAAC,YAAY,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,aAAa,CAAC,EAAE,KAAK,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC,CAAC;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,eAAe,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,cAAc,CAAC,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"checkout.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/checkout.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,12 +9,15 @@ export interface ShippingMethod {
|
|
|
9
9
|
providerKey?: string;
|
|
10
10
|
deliveryMethodId?: string;
|
|
11
11
|
deliveryQuoteId?: string;
|
|
12
|
+
quoteCurrency?: string;
|
|
13
|
+
quoteCost?: number;
|
|
12
14
|
}
|
|
13
15
|
export interface ShippingCalculation {
|
|
14
16
|
methods: ShippingMethod[];
|
|
15
17
|
selectedMethod?: ShippingMethod;
|
|
16
18
|
freeShippingEligible: boolean;
|
|
17
19
|
freeShippingThreshold?: number;
|
|
20
|
+
freeShippingEnabled?: boolean;
|
|
18
21
|
}
|
|
19
22
|
export interface DeliveryStatus {
|
|
20
23
|
enabled: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shipping.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/shipping.service.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"shipping.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/shipping.service.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,eAAe;IAC1B;;OAEG;4BAC2B;QAC5B,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,GAAG,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE;YACb,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,KAAK,CAAC,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAQhC;;;OAGG;+BAC8B,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAYjE;;OAEG;uBACsB;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,GAAG,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC;CAOvG,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-config-utils.d.ts","sourceRoot":"","sources":["../../src/lib/store-config-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAmB,aAAa,EAAE,YAAY,EAAE,YAAY,EAAqB,MAAM,eAAe,CAAC;AAYhI;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,EAAE,CAgGrD;AAgVD;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,QAAQ,GAAG,aAAa,CAarF;
|
|
1
|
+
{"version":3,"file":"store-config-utils.d.ts","sourceRoot":"","sources":["../../src/lib/store-config-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAmB,aAAa,EAAE,YAAY,EAAE,YAAY,EAAqB,MAAM,eAAe,CAAC;AAYhI;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,EAAE,CAgGrD;AAgVD;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,QAAQ,GAAG,aAAa,CAarF;AA6GD;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,OAAO,GAAG,YAAY,CAgIhF;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,OAAO,GAAG,YAAY,CA6DhF;AA00BD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyHvF"}
|
|
@@ -424,6 +424,109 @@ export function transformCategoryToStoreCategory(apiCategory) {
|
|
|
424
424
|
parentId: apiCategory.parentId,
|
|
425
425
|
};
|
|
426
426
|
}
|
|
427
|
+
const LEGACY_VARIANT_KEYS = ['color', 'size', 'material', 'style', 'option'];
|
|
428
|
+
const LEGACY_VARIANT_LABELS = {
|
|
429
|
+
color: 'Name / Color',
|
|
430
|
+
size: 'Size',
|
|
431
|
+
material: 'Material',
|
|
432
|
+
style: 'Style',
|
|
433
|
+
option: 'Option',
|
|
434
|
+
};
|
|
435
|
+
function normalizeVariationAttributes(variation) {
|
|
436
|
+
const attributes = {};
|
|
437
|
+
const raw = variation.attributes || {};
|
|
438
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
439
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
440
|
+
attributes[key.toLowerCase()] = value.trim();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (Object.keys(attributes).length === 0) {
|
|
444
|
+
const sku = typeof variation.sku === 'string' ? variation.sku.trim() : '';
|
|
445
|
+
if (sku) {
|
|
446
|
+
attributes.option = sku;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return attributes;
|
|
450
|
+
}
|
|
451
|
+
function legacyVariantNameValue(attributes) {
|
|
452
|
+
for (const key of LEGACY_VARIANT_KEYS) {
|
|
453
|
+
const value = attributes[key];
|
|
454
|
+
if (value) {
|
|
455
|
+
return { name: LEGACY_VARIANT_LABELS[key] ?? key, value };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return { name: '', value: '' };
|
|
459
|
+
}
|
|
460
|
+
function mapApiVariationsToStoreVariants(apiProduct) {
|
|
461
|
+
const variations = apiProduct.variations;
|
|
462
|
+
if (!Array.isArray(variations) || variations.length === 0) {
|
|
463
|
+
return undefined;
|
|
464
|
+
}
|
|
465
|
+
return variations
|
|
466
|
+
.filter((variation) => variation.isActive !== false)
|
|
467
|
+
.map((variation) => {
|
|
468
|
+
const attributes = normalizeVariationAttributes(variation);
|
|
469
|
+
const optionValue = attributes.option;
|
|
470
|
+
const legacy = legacyVariantNameValue(attributes);
|
|
471
|
+
const hasStructuredAttributes = Object.keys(attributes).length > 0;
|
|
472
|
+
const mappedName = typeof variation.name === 'string' && variation.name.trim()
|
|
473
|
+
? variation.name.trim()
|
|
474
|
+
: !hasStructuredAttributes && legacy.name
|
|
475
|
+
? legacy.name
|
|
476
|
+
: !hasStructuredAttributes && optionValue
|
|
477
|
+
? 'Option'
|
|
478
|
+
: '';
|
|
479
|
+
return {
|
|
480
|
+
id: variation.id,
|
|
481
|
+
attributes,
|
|
482
|
+
name: mappedName,
|
|
483
|
+
value: variation.value || legacy.value || optionValue || '',
|
|
484
|
+
price: variation.price
|
|
485
|
+
? typeof variation.price === 'string'
|
|
486
|
+
? parseFloat(variation.price)
|
|
487
|
+
: Number(variation.price)
|
|
488
|
+
: undefined,
|
|
489
|
+
compareAtPrice: variation.compareAtPrice
|
|
490
|
+
? typeof variation.compareAtPrice === 'string'
|
|
491
|
+
? parseFloat(variation.compareAtPrice)
|
|
492
|
+
: Number(variation.compareAtPrice)
|
|
493
|
+
: undefined,
|
|
494
|
+
sku: variation.sku,
|
|
495
|
+
stock: variation.stock != null
|
|
496
|
+
? typeof variation.stock === 'string'
|
|
497
|
+
? parseFloat(variation.stock)
|
|
498
|
+
: Number(variation.stock)
|
|
499
|
+
: undefined,
|
|
500
|
+
inStock: variation.inStock !== false,
|
|
501
|
+
isActive: variation.isActive !== false,
|
|
502
|
+
images: (() => {
|
|
503
|
+
const raw = variation.images;
|
|
504
|
+
let parsed = raw;
|
|
505
|
+
if (typeof raw === 'string') {
|
|
506
|
+
try {
|
|
507
|
+
parsed = JSON.parse(raw);
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return raw.trim() ? [raw.trim()] : undefined;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (!Array.isArray(parsed) || parsed.length === 0)
|
|
514
|
+
return undefined;
|
|
515
|
+
const urls = parsed
|
|
516
|
+
.map((img) => {
|
|
517
|
+
if (typeof img === 'string')
|
|
518
|
+
return img.trim();
|
|
519
|
+
if (img && typeof img === 'object' && typeof img.url === 'string') {
|
|
520
|
+
return img.url.trim();
|
|
521
|
+
}
|
|
522
|
+
return '';
|
|
523
|
+
})
|
|
524
|
+
.filter(Boolean);
|
|
525
|
+
return urls.length > 0 ? urls : undefined;
|
|
526
|
+
})(),
|
|
527
|
+
};
|
|
528
|
+
});
|
|
529
|
+
}
|
|
427
530
|
/**
|
|
428
531
|
* Transforms API Product to StoreProduct format
|
|
429
532
|
* Calculates inStock based on trackInventory and inventory data
|
|
@@ -547,17 +650,7 @@ export function transformProductToStoreProduct(apiProduct) {
|
|
|
547
650
|
reviewCount,
|
|
548
651
|
sku: generateSKU(apiProduct),
|
|
549
652
|
currency: apiProduct.currency || 'USD',
|
|
550
|
-
variants: apiProduct
|
|
551
|
-
id: variation.id || variation.name || '',
|
|
552
|
-
name: variation.name || '',
|
|
553
|
-
value: variation.value || '',
|
|
554
|
-
price: variation.price
|
|
555
|
-
? (typeof variation.price === 'string'
|
|
556
|
-
? parseFloat(variation.price)
|
|
557
|
-
: Number(variation.price))
|
|
558
|
-
: undefined,
|
|
559
|
-
sku: variation.sku,
|
|
560
|
-
})),
|
|
653
|
+
variants: mapApiVariationsToStoreVariants(apiProduct),
|
|
561
654
|
specifications: apiProduct.brand ? { Brand: apiProduct.brand, brand: apiProduct.brand } : undefined,
|
|
562
655
|
isActive: apiProduct.isActive,
|
|
563
656
|
status: apiProduct.status,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-context.d.ts","sourceRoot":"","sources":["../../src/lib/store-context.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store-context.d.ts","sourceRoot":"","sources":["../../src/lib/store-context.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAmB/D,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,gBAAgB;IACxB,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,SAAS,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3F,cAAc,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,wBAAwB,CAAC,EAAE,CAAC,UAAU,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxH,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,aAAa,EAAE,OAAO,CAAC;IAGvB,UAAU,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/C,eAAe,EAAE,MAAM,IAAI,CAAC;IAG5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,eAAO,MAAM,YAAY,2CAsBvB,CAAC;AAEH,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,SAAS,EACT,aAAa,EACd,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC,+BAgdA;AAED,eAAO,MAAM,QAAQ,wBAAiC,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { createContext, useContext, useEffect, useState, useMemo, useCallback } from 'react';
|
|
4
|
+
import { usePathname } from 'next/navigation';
|
|
4
5
|
import { getStoreConfig } from './store-config';
|
|
5
6
|
import { shouldUseAPI, getBaseStoreSlug } from './utils/demo-detection';
|
|
6
7
|
import { storeService } from './services/store.service';
|
|
@@ -12,6 +13,7 @@ import { serviceService } from './services/service.service';
|
|
|
12
13
|
import { transformCategoryToStoreCategory, transformProductToStoreProduct, transformServiceToStoreService } from './store-config-utils';
|
|
13
14
|
import { useLoading } from './loading-context';
|
|
14
15
|
import { normalizePrice } from './utils';
|
|
16
|
+
import { assertVariationSelected, buildCartLineProduct, formatVariationLabel, } from './product-variation-utils';
|
|
15
17
|
import { useAuth } from './auth-context';
|
|
16
18
|
import { analyticsService } from './services/analytics.service';
|
|
17
19
|
export const StoreContext = createContext({
|
|
@@ -68,6 +70,10 @@ export function StoreProvider({ children, storeSlug, initialConfig }) {
|
|
|
68
70
|
const [cart, setCart] = useState([]);
|
|
69
71
|
const [isCartOpen, setIsCartOpen] = useState(false);
|
|
70
72
|
const [isCartLoading, setIsCartLoading] = useState(false);
|
|
73
|
+
const pathname = usePathname();
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
setIsCartOpen(false);
|
|
76
|
+
}, [pathname]);
|
|
71
77
|
// Buy Now State (temporary single-item for direct checkout)
|
|
72
78
|
// Initialize from sessionStorage to persist across navigation
|
|
73
79
|
const [buyNowItem, setBuyNowItemState] = useState(() => {
|
|
@@ -288,21 +294,28 @@ export function StoreProvider({ children, storeSlug, initialConfig }) {
|
|
|
288
294
|
localStorage.setItem(`wishlist_${storeSlug}`, JSON.stringify(wishlist));
|
|
289
295
|
}, [cart, wishlist, storeSlug, store]);
|
|
290
296
|
const addToCart = useCallback(async (product, quantity = 1, variantId) => {
|
|
291
|
-
|
|
292
|
-
|
|
297
|
+
const variant = variantId
|
|
298
|
+
? product.variants?.find((entry) => entry.id === variantId)
|
|
299
|
+
: undefined;
|
|
300
|
+
if (variantId && !variant) {
|
|
301
|
+
throw new Error('Please select a variation');
|
|
302
|
+
}
|
|
303
|
+
assertVariationSelected(product, variant);
|
|
304
|
+
const lineProduct = buildCartLineProduct(product, variant);
|
|
305
|
+
if (!lineProduct.inStock) {
|
|
293
306
|
console.warn('[StoreContext] Attempted to add out-of-stock product to cart:', product.id);
|
|
294
|
-
// Note: Error message should be shown by the calling component
|
|
295
307
|
throw new Error('This product is currently out of stock');
|
|
296
308
|
}
|
|
297
309
|
setIsCartLoading(true);
|
|
298
310
|
try {
|
|
299
|
-
|
|
311
|
+
const variantName = formatVariationLabel(variant);
|
|
312
|
+
const linePrice = lineProduct.price;
|
|
300
313
|
setCart(prev => {
|
|
301
314
|
const cartItemId = variantId ? `${product.id}-${variantId}` : product.id;
|
|
302
315
|
const existingItem = prev.find(item => item.id === cartItemId);
|
|
303
316
|
if (existingItem) {
|
|
304
317
|
return prev.map(item => item.id === cartItemId
|
|
305
|
-
? { ...item, quantity: item.quantity + quantity }
|
|
318
|
+
? { ...item, quantity: item.quantity + quantity, price: linePrice, product: lineProduct }
|
|
306
319
|
: item);
|
|
307
320
|
}
|
|
308
321
|
return [...prev, {
|
|
@@ -310,8 +323,10 @@ export function StoreProvider({ children, storeSlug, initialConfig }) {
|
|
|
310
323
|
productId: product.id,
|
|
311
324
|
quantity,
|
|
312
325
|
variantId,
|
|
313
|
-
|
|
314
|
-
|
|
326
|
+
variantName,
|
|
327
|
+
price: linePrice,
|
|
328
|
+
product: lineProduct,
|
|
329
|
+
currency: lineProduct.currency,
|
|
315
330
|
}];
|
|
316
331
|
});
|
|
317
332
|
// Track add to cart event
|
|
@@ -325,7 +340,7 @@ export function StoreProvider({ children, storeSlug, initialConfig }) {
|
|
|
325
340
|
productName: product.name,
|
|
326
341
|
categoryId: product.categoryId,
|
|
327
342
|
quantity,
|
|
328
|
-
price:
|
|
343
|
+
price: linePrice,
|
|
329
344
|
variantId,
|
|
330
345
|
},
|
|
331
346
|
}).catch(err => console.warn('Failed to track add to cart:', err));
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { StoreConfig } from '../store-types.js';
|
|
2
2
|
import { shouldUseAPI } from './demo-detection';
|
|
3
3
|
export { shouldUseAPI };
|
|
4
|
+
/** Hero badge for clothing-minimal: no template placeholder on live stores. */
|
|
5
|
+
export declare function resolveClothingMinimalHeroBadge(storeConfig: StoreConfig | null | undefined): unknown | null;
|
|
4
6
|
/**
|
|
5
7
|
* Normalizes store image URLs to absolute URLs
|
|
6
8
|
* Handles both absolute URLs (R2/Cloudflare/etc.) and relative paths
|