@o2vend/theme-cli 1.0.37 → 1.0.38
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/lib/lib/dev-server.js +309 -40
- package/lib/lib/liquid-engine.js +3 -1
- package/lib/lib/mock-data.js +36 -124
- package/lib/lib/widget-service.js +12 -4
- package/package.json +1 -1
- package/test-theme/assets/async-sections.js +32 -24
- package/test-theme/assets/cart-drawer.js +20 -22
- package/test-theme/assets/cart-manager.js +1 -15
- package/test-theme/assets/checkout-price-handler.js +12 -11
- package/test-theme/assets/checkout.css +1415 -0
- package/test-theme/assets/checkout.js +3174 -0
- package/test-theme/assets/components.css +178 -29
- package/test-theme/assets/delivery-zone.js +1 -1
- package/test-theme/assets/product-detail.css +1050 -0
- package/test-theme/assets/product-detail.js +2940 -0
- package/test-theme/assets/theme.css +95 -120
- package/test-theme/assets/theme.js +781 -186
- package/test-theme/layout/theme.liquid +91 -17
- package/test-theme/sections/content.liquid +64 -57
- package/test-theme/sections/footer-fallback.liquid +57 -7
- package/test-theme/sections/footer.liquid +63 -12
- package/test-theme/sections/header-fallback.liquid +41 -41
- package/test-theme/sections/header.liquid +41 -51
- package/test-theme/sections/hero-fallback.liquid +1 -1
- package/test-theme/sections/hero.liquid +159 -136
- package/test-theme/snippets/account-sidebar.liquid +121 -29
- package/test-theme/snippets/add-to-cart-modal.liquid +258 -206
- package/test-theme/snippets/breadcrumbs.liquid +98 -11
- package/test-theme/snippets/cart-drawer.liquid +93 -0
- package/test-theme/snippets/delivery-zone-city-selector.liquid +101 -15
- package/test-theme/snippets/delivery-zone-modal.liquid +529 -84
- package/test-theme/snippets/delivery-zone-search.liquid +104 -18
- package/test-theme/snippets/login-modal.liquid +269 -82
- package/test-theme/snippets/mega-menu.liquid +130 -43
- package/test-theme/snippets/news-thumbnail.liquid +120 -28
- package/test-theme/snippets/pagination.liquid +1 -1
- package/test-theme/snippets/price.liquid +100 -9
- package/test-theme/snippets/product-card-related.liquid +22 -4
- package/test-theme/snippets/product-card-simple.liquid +521 -25
- package/test-theme/snippets/product-card.liquid +145 -232
- package/test-theme/snippets/rating.liquid +100 -9
- package/test-theme/snippets/skeleton-collection-grid.liquid +94 -8
- package/test-theme/snippets/skeleton-product-card.liquid +102 -16
- package/test-theme/snippets/skeleton-product-grid.liquid +87 -1
- package/test-theme/snippets/social-sharing.liquid +133 -32
- package/test-theme/templates/account/dashboard.liquid +30 -0
- package/test-theme/templates/account/loyalty-redemption.liquid +29 -28
- package/test-theme/templates/account/loyalty.liquid +45 -43
- package/test-theme/templates/account/order-detail.liquid +15 -8
- package/test-theme/templates/account/orders.liquid +189 -35
- package/test-theme/templates/account/profile.liquid +509 -114
- package/test-theme/templates/account/register.liquid +18 -8
- package/test-theme/templates/account/return-orders.liquid +31 -30
- package/test-theme/templates/account/store-credit.liquid +27 -26
- package/test-theme/templates/account/subscriptions.liquid +22 -5
- package/test-theme/templates/account/wishlist.liquid +88 -19
- package/test-theme/templates/address-book.liquid +166 -69
- package/test-theme/templates/categories.liquid +90 -30
- package/test-theme/templates/checkout.liquid +137 -3834
- package/test-theme/templates/error.liquid +23 -21
- package/test-theme/templates/index.liquid +29 -0
- package/test-theme/templates/login.liquid +33 -6
- package/test-theme/templates/order-confirmation.liquid +67 -9
- package/test-theme/templates/page.liquid +418 -206
- package/test-theme/templates/product-detail.liquid +124 -3878
- package/test-theme/templates/products.liquid +155 -30
- package/test-theme/templates/search.liquid +739 -225
- package/test-theme/widgets/brand-carousel.liquid +102 -82
- package/test-theme/widgets/brand.liquid +78 -50
- package/test-theme/widgets/carousel.liquid +253 -121
- package/test-theme/widgets/category-list-carousel.liquid +32 -8
- package/test-theme/widgets/category-list.liquid +21 -6
- package/test-theme/widgets/category.liquid +104 -37
- package/test-theme/widgets/discount-time.liquid +326 -119
- package/test-theme/widgets/footer-menu.liquid +115 -23
- package/test-theme/widgets/footer.liquid +118 -5
- package/test-theme/widgets/gallery.liquid +29 -5
- package/test-theme/widgets/header-menu.liquid +25 -13
- package/test-theme/widgets/header.liquid +64 -26
- package/test-theme/widgets/html.liquid +29 -6
- package/test-theme/widgets/news.liquid +6 -0
- package/test-theme/widgets/product-canvas.liquid +20 -12
- package/test-theme/widgets/product-carousel.liquid +118 -56
- package/test-theme/widgets/shared/product-grid.liquid +12 -0
- package/test-theme/widgets/single-product.liquid +688 -250
- package/test-theme/widgets/spacebar-carousel.liquid +39 -10
- package/test-theme/widgets/spacebar.liquid +77 -6
- package/test-theme/widgets/splash.liquid +40 -30
- package/test-theme/widgets/testimonial-carousel.liquid +111 -67
package/lib/lib/mock-data.js
CHANGED
|
@@ -1671,7 +1671,7 @@ function generateMockWidgets() {
|
|
|
1671
1671
|
})
|
|
1672
1672
|
},
|
|
1673
1673
|
|
|
1674
|
-
// DiscountTime Widget
|
|
1674
|
+
// DiscountTime Widget (template expects data.couponValid and data.coupon at top level)
|
|
1675
1675
|
{
|
|
1676
1676
|
id: 'widget-discount-time-1',
|
|
1677
1677
|
type: 'DiscountTime',
|
|
@@ -1693,18 +1693,16 @@ function generateMockWidgets() {
|
|
|
1693
1693
|
cta_text: '#ffffff'
|
|
1694
1694
|
},
|
|
1695
1695
|
data: {
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
imageUrl: 'https://picsum.photos/seed/discount-banner/1200/400'
|
|
1707
|
-
}
|
|
1696
|
+
couponValid: true,
|
|
1697
|
+
coupon: {
|
|
1698
|
+
discountAmount: 25,
|
|
1699
|
+
couponCode: 'SAVE25',
|
|
1700
|
+
description: 'Get 25% off on all products! Limited time only.',
|
|
1701
|
+
endOn: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1702
|
+
startOn: new Date().toISOString(),
|
|
1703
|
+
ruleToApply: 'by_percent',
|
|
1704
|
+
fileName: '',
|
|
1705
|
+
imageUrl: 'https://picsum.photos/seed/discount-banner/1200/400'
|
|
1708
1706
|
}
|
|
1709
1707
|
}
|
|
1710
1708
|
},
|
|
@@ -1767,58 +1765,6 @@ function generateMockWidgets() {
|
|
|
1767
1765
|
}
|
|
1768
1766
|
},
|
|
1769
1767
|
|
|
1770
|
-
// Splash Widget
|
|
1771
|
-
{
|
|
1772
|
-
id: 'widget-splash-1',
|
|
1773
|
-
type: 'Splash',
|
|
1774
|
-
section: 'content',
|
|
1775
|
-
sectionName: 'content',
|
|
1776
|
-
pageId: 'home',
|
|
1777
|
-
status: 'active',
|
|
1778
|
-
Position: 17,
|
|
1779
|
-
Title: 'Welcome Offer',
|
|
1780
|
-
settings: {
|
|
1781
|
-
backgroundColor: '#ffffff',
|
|
1782
|
-
textColor: '#111111',
|
|
1783
|
-
seconds: 3,
|
|
1784
|
-
showOnce: true,
|
|
1785
|
-
overlayColor: 'rgba(0,0,0,0.6)',
|
|
1786
|
-
eventType: 'Onload'
|
|
1787
|
-
},
|
|
1788
|
-
data: {
|
|
1789
|
-
content: {
|
|
1790
|
-
ImageUrl: 'https://picsum.photos/seed/splash-promo/600/400',
|
|
1791
|
-
imageUrl: 'https://picsum.photos/seed/splash-promo/600/400',
|
|
1792
|
-
Title: 'Welcome to Our Store!',
|
|
1793
|
-
title: 'Welcome to Our Store!',
|
|
1794
|
-
Description: 'Sign up and get 15% off your first order. Use code WELCOME15 at checkout.',
|
|
1795
|
-
description: 'Sign up and get 15% off your first order. Use code WELCOME15 at checkout.',
|
|
1796
|
-
LinkText: 'Shop Now',
|
|
1797
|
-
linkText: 'Shop Now',
|
|
1798
|
-
ButtonText: 'Shop Now',
|
|
1799
|
-
buttonText: 'Shop Now',
|
|
1800
|
-
TargetUrl: '/products',
|
|
1801
|
-
targetUrl: '/products',
|
|
1802
|
-
Link: '/products',
|
|
1803
|
-
link: '/products',
|
|
1804
|
-
BackgroundColor: '#ffffff',
|
|
1805
|
-
backgroundColor: '#ffffff',
|
|
1806
|
-
TextColor: '#111111',
|
|
1807
|
-
textColor: '#111111',
|
|
1808
|
-
Seconds: 3,
|
|
1809
|
-
seconds: 3,
|
|
1810
|
-
DelaySeconds: 3,
|
|
1811
|
-
delaySeconds: 3,
|
|
1812
|
-
ShowOnce: true,
|
|
1813
|
-
showOnce: true,
|
|
1814
|
-
OverlayColor: 'rgba(0,0,0,0.6)',
|
|
1815
|
-
overlayColor: 'rgba(0,0,0,0.6)',
|
|
1816
|
-
EventType: 'Onload',
|
|
1817
|
-
eventType: 'Onload'
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
},
|
|
1821
|
-
|
|
1822
1768
|
// SpaceBarCarousel Widget
|
|
1823
1769
|
{
|
|
1824
1770
|
id: 'widget-spacebar-carousel-1',
|
|
@@ -1889,76 +1835,42 @@ function generateMockWidgets() {
|
|
|
1889
1835
|
}
|
|
1890
1836
|
}
|
|
1891
1837
|
},
|
|
1892
|
-
|
|
1893
|
-
// ==================== FOOTER SECTION ====================
|
|
1894
1838
|
{
|
|
1895
|
-
id: 'widget-
|
|
1896
|
-
type: '
|
|
1897
|
-
section: '
|
|
1898
|
-
sectionName: '
|
|
1899
|
-
pageId: '
|
|
1839
|
+
id: 'widget-news-1',
|
|
1840
|
+
type: 'News',
|
|
1841
|
+
section: 'content',
|
|
1842
|
+
sectionName: 'content',
|
|
1843
|
+
pageId: 'home',
|
|
1900
1844
|
status: 'active',
|
|
1901
|
-
Position:
|
|
1845
|
+
Position: 19,
|
|
1846
|
+
Title: 'Latest News',
|
|
1902
1847
|
settings: {
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1848
|
+
title: 'Latest News',
|
|
1849
|
+
showWidgetTitle: 'Yes',
|
|
1850
|
+
widgetTitleAlignment: 'center',
|
|
1851
|
+
showGridView: true,
|
|
1852
|
+
backgroundColor: '#ffffff',
|
|
1853
|
+
textColor: '#111111'
|
|
1906
1854
|
},
|
|
1907
1855
|
data: {
|
|
1908
1856
|
content: {
|
|
1909
|
-
|
|
1910
|
-
{
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
{ Text: 'Our Story', text: 'Our Story', Url: '/pages/about', url: '/pages/about' },
|
|
1915
|
-
{ Text: 'Careers', text: 'Careers', Url: '/pages/careers', url: '/pages/careers' },
|
|
1916
|
-
{ Text: 'Press', text: 'Press', Url: '/pages/press', url: '/pages/press' }
|
|
1917
|
-
],
|
|
1918
|
-
links: [
|
|
1919
|
-
{ text: 'Our Story', url: '/pages/about' },
|
|
1920
|
-
{ text: 'Careers', url: '/pages/careers' },
|
|
1921
|
-
{ text: 'Press', url: '/pages/press' }
|
|
1922
|
-
]
|
|
1923
|
-
},
|
|
1924
|
-
{
|
|
1925
|
-
Title: 'Customer Service',
|
|
1926
|
-
title: 'Customer Service',
|
|
1927
|
-
Links: [
|
|
1928
|
-
{ Text: 'Contact Us', text: 'Contact Us', Url: '/pages/contact', url: '/pages/contact' },
|
|
1929
|
-
{ Text: 'FAQs', text: 'FAQs', Url: '/pages/faq', url: '/pages/faq' },
|
|
1930
|
-
{ Text: 'Returns', text: 'Returns', Url: '/pages/returns', url: '/pages/returns' }
|
|
1931
|
-
],
|
|
1932
|
-
links: [
|
|
1933
|
-
{ text: 'Contact Us', url: '/pages/contact' },
|
|
1934
|
-
{ text: 'FAQs', url: '/pages/faq' },
|
|
1935
|
-
{ text: 'Returns', url: '/pages/returns' }
|
|
1936
|
-
]
|
|
1937
|
-
},
|
|
1938
|
-
{
|
|
1939
|
-
Title: 'Legal',
|
|
1940
|
-
title: 'Legal',
|
|
1941
|
-
Links: [
|
|
1942
|
-
{ Text: 'Privacy Policy', text: 'Privacy Policy', Url: '/pages/privacy', url: '/pages/privacy' },
|
|
1943
|
-
{ Text: 'Terms of Service', text: 'Terms of Service', Url: '/pages/terms', url: '/pages/terms' },
|
|
1944
|
-
{ Text: 'Shipping Policy', text: 'Shipping Policy', Url: '/pages/shipping', url: '/pages/shipping' }
|
|
1945
|
-
],
|
|
1946
|
-
links: [
|
|
1947
|
-
{ text: 'Privacy Policy', url: '/pages/privacy' },
|
|
1948
|
-
{ text: 'Terms of Service', url: '/pages/terms' },
|
|
1949
|
-
{ text: 'Shipping Policy', url: '/pages/shipping' }
|
|
1950
|
-
]
|
|
1951
|
-
}
|
|
1857
|
+
items: [
|
|
1858
|
+
{ id: 1, title: 'Welcome to Our Store', slug: 'welcome', excerpt: 'Learn about our new collection and special offers.', thumbnailImage: { url: 'https://picsum.photos/seed/n1/400/220' }, publishedAt: new Date().toISOString() },
|
|
1859
|
+
{ id: 2, title: 'Summer Sale 2025', slug: 'summer-sale', excerpt: 'Up to 50% off on selected items.', thumbnailImage: { url: 'https://picsum.photos/seed/n2/400/220' }, publishedAt: new Date().toISOString() },
|
|
1860
|
+
{ id: 3, title: 'New Arrivals', slug: 'new-arrivals', excerpt: 'Check out the latest products in our catalog.', thumbnailImage: { url: 'https://picsum.photos/seed/n3/400/220' }, publishedAt: new Date().toISOString() },
|
|
1861
|
+
{ id: 4, title: 'Tips & Guides', slug: 'tips-guides', excerpt: 'How to get the most from your purchase.', thumbnailImage: { url: 'https://picsum.photos/seed/n4/400/220' }, publishedAt: new Date().toISOString() }
|
|
1952
1862
|
],
|
|
1953
|
-
|
|
1954
|
-
{
|
|
1955
|
-
{
|
|
1956
|
-
{
|
|
1957
|
-
{
|
|
1863
|
+
Items: [
|
|
1864
|
+
{ id: 1, Title: 'Welcome to Our Store', slug: 'welcome', Excerpt: 'Learn about our new collection and special offers.', ThumbnailImage: { url: 'https://picsum.photos/seed/n1/400/220' }, PublishedAt: new Date().toISOString() },
|
|
1865
|
+
{ id: 2, Title: 'Summer Sale 2025', slug: 'summer-sale', Excerpt: 'Up to 50% off on selected items.', ThumbnailImage: { url: 'https://picsum.photos/seed/n2/400/220' }, PublishedAt: new Date().toISOString() },
|
|
1866
|
+
{ id: 3, Title: 'New Arrivals', slug: 'new-arrivals', Excerpt: 'Check out the latest products in our catalog.', ThumbnailImage: { url: 'https://picsum.photos/seed/n3/400/220' }, PublishedAt: new Date().toISOString() },
|
|
1867
|
+
{ id: 4, Title: 'Tips & Guides', slug: 'tips-guides', Excerpt: 'How to get the most from your purchase.', ThumbnailImage: { url: 'https://picsum.photos/seed/n4/400/220' }, PublishedAt: new Date().toISOString() }
|
|
1958
1868
|
]
|
|
1959
1869
|
}
|
|
1960
1870
|
}
|
|
1961
1871
|
},
|
|
1872
|
+
|
|
1873
|
+
// ==================== FOOTER SECTION ====================
|
|
1962
1874
|
{
|
|
1963
1875
|
id: 'widget-footer-menu-1',
|
|
1964
1876
|
type: 'FooterMenu',
|
|
@@ -147,9 +147,17 @@ class WidgetService {
|
|
|
147
147
|
widget.settings.subtitle = widget.subtitle;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
// Store content
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
// Store content: only overwrite when we have parsed content from widget.content; otherwise preserve existing data.content (e.g. mock data with Items)
|
|
151
|
+
const hasParsedContent = parsedContent && typeof parsedContent === 'object' && (Array.isArray(parsedContent) ? parsedContent.length > 0 : Object.keys(parsedContent).length > 0);
|
|
152
|
+
if (hasParsedContent) {
|
|
153
|
+
widget.data.content = parsedContent;
|
|
154
|
+
widget.content = parsedContent;
|
|
155
|
+
} else if (widget.data && typeof widget.data.content === 'undefined') {
|
|
156
|
+
widget.data.content = {};
|
|
157
|
+
}
|
|
158
|
+
if (widget.data.content && !widget.content) {
|
|
159
|
+
widget.content = widget.data.content; // Legacy support
|
|
160
|
+
}
|
|
153
161
|
|
|
154
162
|
// Handle HtmlContent
|
|
155
163
|
if (widget.htmlContent !== undefined && widget.htmlContent !== null && widget.htmlContent !== '') {
|
|
@@ -332,7 +340,7 @@ class WidgetService {
|
|
|
332
340
|
* @returns {Promise<Object>} Widgets organized by section { header:[], hero:[], content:[], footer:[] }
|
|
333
341
|
*/
|
|
334
342
|
async fetchPageWidgets(pageId) {
|
|
335
|
-
const validPageIds = ['home', 'product', 'category', 'products', 'categories', 'brand', 'checkout'];
|
|
343
|
+
const validPageIds = ['home', 'product', 'category', 'products', 'categories', 'brand', 'cart', 'checkout'];
|
|
336
344
|
if (!pageId || !validPageIds.includes(pageId)) {
|
|
337
345
|
console.warn(`[WidgetService] Invalid pageId: ${pageId}, returning empty widgets`);
|
|
338
346
|
return { header: [], hero: [], content: [], footer: [] };
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Loads non-critical page sections asynchronously to improve initial page load time
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
(
|
|
6
|
+
(() => {
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
const AsyncSectionLoader = {
|
|
@@ -18,17 +18,13 @@
|
|
|
18
18
|
*/
|
|
19
19
|
init(sectionsConfig) {
|
|
20
20
|
this.config = sectionsConfig || {};
|
|
21
|
-
console.log('[AsyncSectionLoader] Initializing with config:', this.config);
|
|
22
21
|
|
|
23
22
|
// Find all async sections in the page
|
|
24
23
|
const asyncSections = document.querySelectorAll('[data-async-section]');
|
|
25
24
|
|
|
26
25
|
if (asyncSections.length === 0) {
|
|
27
|
-
console.log('[AsyncSectionLoader] No async sections found');
|
|
28
26
|
return;
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
console.log(`[AsyncSectionLoader] Found ${asyncSections.length} async sections`);
|
|
32
28
|
|
|
33
29
|
// Load each section
|
|
34
30
|
asyncSections.forEach(section => {
|
|
@@ -45,12 +41,9 @@
|
|
|
45
41
|
const sectionConfig = this.config[sectionName];
|
|
46
42
|
|
|
47
43
|
if (!sectionConfig) {
|
|
48
|
-
console.warn(`[AsyncSectionLoader] No config found for section: ${sectionName}`);
|
|
49
44
|
return;
|
|
50
45
|
}
|
|
51
46
|
|
|
52
|
-
console.log(`[AsyncSectionLoader] Loading section: ${sectionName}`);
|
|
53
|
-
|
|
54
47
|
try {
|
|
55
48
|
// Get data from API
|
|
56
49
|
const data = await this.fetchSectionData(
|
|
@@ -84,12 +77,10 @@
|
|
|
84
77
|
|
|
85
78
|
// Check cache first
|
|
86
79
|
if (this.cache.has(url)) {
|
|
87
|
-
console.log(`[AsyncSectionLoader] Using cached data for: ${url}`);
|
|
88
80
|
return this.cache.get(url);
|
|
89
81
|
}
|
|
90
82
|
|
|
91
83
|
try {
|
|
92
|
-
console.log(`[AsyncSectionLoader] Fetching: ${url} (attempt ${attempt})`);
|
|
93
84
|
|
|
94
85
|
const response = await fetch(url, {
|
|
95
86
|
method: 'GET',
|
|
@@ -113,7 +104,6 @@
|
|
|
113
104
|
} catch (error) {
|
|
114
105
|
// Retry logic
|
|
115
106
|
if (attempt < this.retryAttempts) {
|
|
116
|
-
console.warn(`[AsyncSectionLoader] Retry ${attempt}/${this.retryAttempts} for ${url}`);
|
|
117
107
|
await this.delay(this.retryDelay * attempt);
|
|
118
108
|
return this.fetchSectionData(endpoint, params, attempt + 1);
|
|
119
109
|
}
|
|
@@ -129,8 +119,6 @@
|
|
|
129
119
|
* @param {string} sectionName - Section name
|
|
130
120
|
*/
|
|
131
121
|
renderSection(sectionElement, data, sectionName) {
|
|
132
|
-
console.log(`[AsyncSectionLoader] Rendering section: ${sectionName}`, data);
|
|
133
|
-
|
|
134
122
|
// Different rendering strategies based on section type
|
|
135
123
|
switch (sectionName) {
|
|
136
124
|
case 'products':
|
|
@@ -145,7 +133,6 @@
|
|
|
145
133
|
this.renderRelatedProducts(sectionElement, data);
|
|
146
134
|
break;
|
|
147
135
|
default:
|
|
148
|
-
console.warn(`[AsyncSectionLoader] Unknown section type: ${sectionName}`);
|
|
149
136
|
this.renderGenericSection(sectionElement, data);
|
|
150
137
|
}
|
|
151
138
|
|
|
@@ -241,17 +228,42 @@
|
|
|
241
228
|
renderProductCard(product, variant = 'default') {
|
|
242
229
|
const imageUrl = product.images?.[0] || product.thumbnailImage || product.image || '/assets/default/placeholder-product.png';
|
|
243
230
|
const price = this.formatPrice(product.prices?.priceString || product.prices?.price || product.price);
|
|
231
|
+
const priceString = product.prices?.priceString || price;
|
|
232
|
+
const showCallForPricing = (
|
|
233
|
+
product.showCallForPricing === true || product.showCallForPricing === 'true' || product.showCallForPricing === 1 || product.showCallForPricing === '1' ||
|
|
234
|
+
product.ShowCallForPricing === true || product.ShowCallForPricing === 'true' || product.ShowCallForPricing === 1 || product.ShowCallForPricing === '1' ||
|
|
235
|
+
product.isCallForPricing === true || product.isCallForPricing === 'true' || product.isCallForPricing === 1 || product.isCallForPricing === '1' ||
|
|
236
|
+
product.IsCallForPricing === true || product.IsCallForPricing === 'true' || product.IsCallForPricing === 1 || product.IsCallForPricing === '1'
|
|
237
|
+
);
|
|
244
238
|
const comparePrice = product.prices?.mrp && product.prices.mrp > product.prices.price
|
|
245
239
|
? this.formatPrice(product.prices.mrpString || product.prices.mrp)
|
|
246
240
|
: null;
|
|
247
241
|
const slug = product.slug || product.handle || product.id;
|
|
242
|
+
const productUrl = product.url || product.productUrl || slug;
|
|
248
243
|
const available = product.stockQuantity > 0 || product.inStock || product.available !== false;
|
|
249
|
-
const
|
|
244
|
+
const productTypeRaw = product.productType ?? product.type ?? 0;
|
|
245
|
+
const productType = Number.isNaN(Number(productTypeRaw)) ? 0 : Number(productTypeRaw);
|
|
246
|
+
const variants = Array.isArray(product.variations)
|
|
247
|
+
? product.variations
|
|
248
|
+
: (Array.isArray(product.variants) ? product.variants : []);
|
|
249
|
+
const variantsCountRaw = product.variantsCount ?? product.variationCount ?? variants.length ?? 0;
|
|
250
|
+
const variantsCount = Number.isNaN(Number(variantsCountRaw)) ? 0 : Number(variantsCountRaw);
|
|
251
|
+
const baseProductId = product.baseProductId || product.productId || product.id;
|
|
252
|
+
const showSaleBadge = !showCallForPricing && product.prices?.mrp && product.prices.mrp > product.prices.price;
|
|
250
253
|
|
|
251
254
|
return `
|
|
252
255
|
<div class="product-card"
|
|
253
|
-
data-
|
|
256
|
+
data-product-id="${product.productId || product.id}"
|
|
257
|
+
data-base-product-id="${baseProductId}"
|
|
258
|
+
data-price="${product.prices?.price ?? product.price ?? 0}"
|
|
259
|
+
data-price-string="${this.escapeHtml(priceString)}"
|
|
254
260
|
data-name="${this.escapeHtml((product.name || product.title || '').toLowerCase())}"
|
|
261
|
+
data-title="${this.escapeHtml(product.name || product.title || '')}"
|
|
262
|
+
data-product-url="${this.escapeHtml(String(productUrl))}"
|
|
263
|
+
data-image="${this.escapeHtml(String(imageUrl))}"
|
|
264
|
+
data-show-call-for-pricing="${showCallForPricing ? 'true' : 'false'}"
|
|
265
|
+
data-product-type="${productType}"
|
|
266
|
+
data-variants-count="${variantsCount}"
|
|
255
267
|
data-availability="${available ? 'in-stock' : 'out-of-stock'}"
|
|
256
268
|
data-brand="${this.escapeHtml((product.brandName || product.vendor || '').toLowerCase())}">
|
|
257
269
|
|
|
@@ -292,8 +304,8 @@
|
|
|
292
304
|
</h3>
|
|
293
305
|
|
|
294
306
|
<div class="product-price">
|
|
295
|
-
<span class="product-price-current">${price}</span>
|
|
296
|
-
${comparePrice ? `<span class="product-price-original">${comparePrice}</span>` : ''}
|
|
307
|
+
<span class="product-price-current">${showCallForPricing ? 'Call for pricing' : price}</span>
|
|
308
|
+
${(!showCallForPricing && comparePrice) ? `<span class="product-price-original">${comparePrice}</span>` : ''}
|
|
297
309
|
</div>
|
|
298
310
|
|
|
299
311
|
${product.shortDescription ? `<p class="product-description">${this.truncate(product.shortDescription, 80)}</p>` : ''}
|
|
@@ -321,12 +333,12 @@
|
|
|
321
333
|
<div class="collection-image-container">
|
|
322
334
|
<img src="${imageUrl}" alt="${this.escapeHtml(collection.title || collection.name)}" loading="lazy">
|
|
323
335
|
<div class="collection-overlay">
|
|
324
|
-
<a href="/
|
|
336
|
+
<a href="/categories/${slug}" class="collection-link">View Collection</a>
|
|
325
337
|
</div>
|
|
326
338
|
</div>
|
|
327
339
|
<div class="collection-content">
|
|
328
340
|
<h3 class="collection-title">
|
|
329
|
-
<a href="/
|
|
341
|
+
<a href="/categories/${slug}">${this.escapeHtml(collection.title || collection.name)}</a>
|
|
330
342
|
</h3>
|
|
331
343
|
${collection.description ? `<p class="collection-description">${this.truncate(collection.description, 120)}</p>` : ''}
|
|
332
344
|
<div class="collection-meta">
|
|
@@ -341,7 +353,6 @@
|
|
|
341
353
|
* Render generic section (fallback)
|
|
342
354
|
*/
|
|
343
355
|
renderGenericSection(sectionElement, data) {
|
|
344
|
-
console.log('[AsyncSectionLoader] Using generic renderer for:', data);
|
|
345
356
|
sectionElement.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
|
|
346
357
|
},
|
|
347
358
|
|
|
@@ -394,7 +405,6 @@
|
|
|
394
405
|
bubbles: true
|
|
395
406
|
});
|
|
396
407
|
sectionElement.dispatchEvent(event);
|
|
397
|
-
console.log(`[AsyncSectionLoader] Triggered event for: ${sectionName}`);
|
|
398
408
|
},
|
|
399
409
|
|
|
400
410
|
/**
|
|
@@ -440,7 +450,5 @@
|
|
|
440
450
|
// Make available globally
|
|
441
451
|
window.AsyncSectionLoader = AsyncSectionLoader;
|
|
442
452
|
|
|
443
|
-
console.log('[AsyncSectionLoader] Module loaded');
|
|
444
|
-
|
|
445
453
|
})();
|
|
446
454
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
;(
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function qs(sel, ctx){ return (ctx||document).querySelector(sel) }
|
|
5
|
-
function qsa(sel, ctx){ return Array.from((ctx||document).querySelectorAll(sel)) }
|
|
1
|
+
;(() => {
|
|
2
|
+
const qs = (sel, ctx) => (ctx||document).querySelector(sel);
|
|
3
|
+
const qsa = (sel, ctx) => Array.from((ctx||document).querySelectorAll(sel));
|
|
6
4
|
|
|
7
5
|
const bodyEl = () => document.body;
|
|
8
6
|
const shopCurrency = () => (bodyEl() && bodyEl().dataset.shopCurrency) || window.__SHOP_CURRENCY__ || 'USD';
|
|
@@ -10,7 +8,7 @@
|
|
|
10
8
|
const shopLocale = () => (bodyEl() && bodyEl().dataset.shopLocale) || window.__SHOP_LOCALE__ || 'en-US';
|
|
11
9
|
|
|
12
10
|
// Format money helper
|
|
13
|
-
|
|
11
|
+
const formatMoney = (amount, currency = shopCurrency()) => {
|
|
14
12
|
const locale = shopLocale();
|
|
15
13
|
const value = typeof amount === 'number' ? amount : Number(amount) || 0;
|
|
16
14
|
const currencySymbol = shopCurrencySymbol();
|
|
@@ -23,7 +21,7 @@
|
|
|
23
21
|
currency
|
|
24
22
|
}).format(value);
|
|
25
23
|
} catch (error) {
|
|
26
|
-
|
|
24
|
+
// Silently handle formatting errors
|
|
27
25
|
}
|
|
28
26
|
}
|
|
29
27
|
|
|
@@ -36,7 +34,7 @@
|
|
|
36
34
|
return currencySymbol ? `${currencySymbol}${formattedNumber}` : formattedNumber;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
const resetCheckoutButton = () => {
|
|
40
38
|
const checkoutBtn = qs('#cart-drawer-checkout-btn')
|
|
41
39
|
if (checkoutBtn) {
|
|
42
40
|
checkoutBtn.classList.remove('loading')
|
|
@@ -59,7 +57,7 @@
|
|
|
59
57
|
loadCartData()
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
const closeDrawer = () => {
|
|
63
61
|
const drawer = qs('#cart-drawer')
|
|
64
62
|
if(!drawer) return
|
|
65
63
|
drawer.classList.remove('active')
|
|
@@ -139,7 +137,7 @@
|
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
139
|
|
|
142
|
-
async
|
|
140
|
+
const loadCartData = async () => {
|
|
143
141
|
const loading = qs('[data-cart-loading]')
|
|
144
142
|
const empty = qs('[data-cart-empty]')
|
|
145
143
|
const itemsList = qs('[data-cart-items-list]')
|
|
@@ -206,7 +204,7 @@
|
|
|
206
204
|
try {
|
|
207
205
|
variantImage = localStorage.getItem(imageKey);
|
|
208
206
|
} catch (e) {
|
|
209
|
-
|
|
207
|
+
// Silently handle localStorage read failures
|
|
210
208
|
}
|
|
211
209
|
|
|
212
210
|
// Use variant image if available, otherwise use API image
|
|
@@ -240,7 +238,7 @@
|
|
|
240
238
|
}).join('')
|
|
241
239
|
}
|
|
242
240
|
|
|
243
|
-
|
|
241
|
+
const updateCartFooter = (cart) => {
|
|
244
242
|
const total = qs('[data-cart-total]')
|
|
245
243
|
if (total) {
|
|
246
244
|
total.textContent = formatMoney(cart.total)
|
|
@@ -253,7 +251,7 @@
|
|
|
253
251
|
await syncQuantity(input)
|
|
254
252
|
}
|
|
255
253
|
|
|
256
|
-
async
|
|
254
|
+
const syncQuantity = async (input) => {
|
|
257
255
|
const productId = input.dataset.productId
|
|
258
256
|
const variantId = input.dataset.variantId
|
|
259
257
|
const quantity = parseInt(input.value, 10)
|
|
@@ -295,7 +293,7 @@
|
|
|
295
293
|
// Flag to prevent multiple simultaneous API calls
|
|
296
294
|
// Use CartManager instead of making direct API calls
|
|
297
295
|
// This prevents duplicate API calls since CartManager handles caching and deduplication
|
|
298
|
-
|
|
296
|
+
const initCartQuantity = () => {
|
|
299
297
|
// Wait for CartManager to be available
|
|
300
298
|
const checkCartManager = () => {
|
|
301
299
|
if (window.CartManager && typeof window.CartManager.getCartCount === 'function') {
|
|
@@ -342,15 +340,15 @@
|
|
|
342
340
|
runCartQuantityInit()
|
|
343
341
|
}
|
|
344
342
|
|
|
345
|
-
document.addEventListener('DOMContentLoaded',
|
|
343
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
346
344
|
const toggle = qsa('[data-cart-toggle]')
|
|
347
|
-
toggle.forEach(t => t.addEventListener('click',
|
|
345
|
+
toggle.forEach(t => t.addEventListener('click', (e) => {
|
|
348
346
|
e.preventDefault()
|
|
349
347
|
e.stopPropagation()
|
|
350
348
|
openDrawer()
|
|
351
349
|
}))
|
|
352
350
|
|
|
353
|
-
document.body.addEventListener('click',
|
|
351
|
+
document.body.addEventListener('click', (e) => {
|
|
354
352
|
const closeBtn = e.target.closest('[data-cart-close]')
|
|
355
353
|
const overlay = e.target.closest('[data-cart-overlay]')
|
|
356
354
|
if (closeBtn || overlay) {
|
|
@@ -359,7 +357,7 @@
|
|
|
359
357
|
}
|
|
360
358
|
})
|
|
361
359
|
|
|
362
|
-
document.body.addEventListener('click',
|
|
360
|
+
document.body.addEventListener('click', (e) => {
|
|
363
361
|
const dec = e.target.closest('.qty-btn[data-action="decrease"]')
|
|
364
362
|
const inc = e.target.closest('.qty-btn[data-action="increase"]')
|
|
365
363
|
const removeBtn = e.target.closest('[data-remove-item]')
|
|
@@ -377,7 +375,7 @@
|
|
|
377
375
|
}
|
|
378
376
|
})
|
|
379
377
|
|
|
380
|
-
document.body.addEventListener('change',
|
|
378
|
+
document.body.addEventListener('change', (e) => {
|
|
381
379
|
const input = e.target.closest('.qty-input')
|
|
382
380
|
if (input) syncQuantity(input)
|
|
383
381
|
})
|
|
@@ -390,7 +388,7 @@
|
|
|
390
388
|
checkoutBtn.dataset.originalText = checkoutBtn.textContent.trim() || 'Check out'
|
|
391
389
|
}
|
|
392
390
|
|
|
393
|
-
checkoutBtn.addEventListener('click',
|
|
391
|
+
checkoutBtn.addEventListener('click', (e) => {
|
|
394
392
|
// Prevent multiple clicks
|
|
395
393
|
if (checkoutBtn.classList.contains('loading') || checkoutBtn.disabled) {
|
|
396
394
|
e.preventDefault()
|
|
@@ -428,7 +426,7 @@
|
|
|
428
426
|
// Discount toggle
|
|
429
427
|
const discountToggle = qs('[data-discount-toggle]')
|
|
430
428
|
if (discountToggle) {
|
|
431
|
-
discountToggle.addEventListener('click',
|
|
429
|
+
discountToggle.addEventListener('click', () => {
|
|
432
430
|
const content = qs('[data-discount-content]')
|
|
433
431
|
const isExpanded = discountToggle.getAttribute('aria-expanded') === 'true'
|
|
434
432
|
discountToggle.setAttribute('aria-expanded', !isExpanded)
|
|
@@ -439,7 +437,7 @@
|
|
|
439
437
|
}
|
|
440
438
|
|
|
441
439
|
// Close on Escape key
|
|
442
|
-
document.addEventListener('keydown',
|
|
440
|
+
document.addEventListener('keydown', (e) => {
|
|
443
441
|
if (e.key === 'Escape') {
|
|
444
442
|
const drawer = qs('#cart-drawer')
|
|
445
443
|
if (drawer && drawer.classList.contains('active')) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Ensures cart badge updates across all components
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
(
|
|
7
|
+
(() => {
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
const CartManager = {
|
|
@@ -55,9 +55,6 @@
|
|
|
55
55
|
cartToggles.forEach(toggle => {
|
|
56
56
|
toggle.setAttribute('aria-label', `Shopping cart with ${numericCount} item${numericCount !== 1 ? 's' : ''}`);
|
|
57
57
|
});
|
|
58
|
-
|
|
59
|
-
// Debug logging (can be removed in production)
|
|
60
|
-
console.log('[CartManager] Updated cart badge:', numericCount, 'badges:', countElements.length);
|
|
61
58
|
},
|
|
62
59
|
|
|
63
60
|
/**
|
|
@@ -72,14 +69,12 @@
|
|
|
72
69
|
if (!forceRefresh && this._cartQuantityCache !== null && this._cacheTimestamp) {
|
|
73
70
|
const cacheAge = now - this._cacheTimestamp;
|
|
74
71
|
if (cacheAge < this._cacheTTL) {
|
|
75
|
-
console.log('[CartManager] Returning cached cart count:', this._cartQuantityCache);
|
|
76
72
|
return this._cartQuantityCache;
|
|
77
73
|
}
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
// If already loading, return the existing promise to prevent duplicate calls
|
|
81
77
|
if (this._cartQuantityLoading && this._cartQuantityPromise) {
|
|
82
|
-
console.log('[CartManager] Cart quantity already loading, returning existing promise');
|
|
83
78
|
return this._cartQuantityPromise;
|
|
84
79
|
}
|
|
85
80
|
|
|
@@ -105,7 +100,6 @@
|
|
|
105
100
|
// Update cache
|
|
106
101
|
this._cartQuantityCache = count;
|
|
107
102
|
this._cacheTimestamp = Date.now();
|
|
108
|
-
console.log('[CartManager] Fetched and cached cart count:', count);
|
|
109
103
|
return count;
|
|
110
104
|
}
|
|
111
105
|
|
|
@@ -139,8 +133,6 @@
|
|
|
139
133
|
0;
|
|
140
134
|
}
|
|
141
135
|
|
|
142
|
-
console.log('[CartManager] Dispatching cart:updated event with count:', count, 'cartData:', cartData);
|
|
143
|
-
|
|
144
136
|
const event = new CustomEvent('cart:updated', {
|
|
145
137
|
detail: {
|
|
146
138
|
count: count,
|
|
@@ -166,29 +158,24 @@
|
|
|
166
158
|
invalidateCache() {
|
|
167
159
|
this._cartQuantityCache = null;
|
|
168
160
|
this._cacheTimestamp = null;
|
|
169
|
-
console.log('[CartManager] Cart quantity cache invalidated');
|
|
170
161
|
},
|
|
171
162
|
|
|
172
163
|
/**
|
|
173
164
|
* Initialize cart manager and set up event listeners
|
|
174
165
|
*/
|
|
175
166
|
init() {
|
|
176
|
-
console.log('[CartManager] Initializing...');
|
|
177
|
-
|
|
178
167
|
// Listen for cart:updated events from external sources
|
|
179
168
|
// Note: dispatchCartUpdated() already calls updateCartBadge() directly,
|
|
180
169
|
// so this listener handles events from other components (like cart-drawer fallback)
|
|
181
170
|
// It's safe to call updateCartBadge() multiple times as it's idempotent
|
|
182
171
|
document.addEventListener('cart:updated', (event) => {
|
|
183
172
|
const count = event.detail.count || 0;
|
|
184
|
-
console.log('[CartManager] Received cart:updated event with count:', count);
|
|
185
173
|
// updateCartBadge is idempotent, so calling it multiple times is safe
|
|
186
174
|
this.updateCartBadge(count);
|
|
187
175
|
});
|
|
188
176
|
|
|
189
177
|
// Load initial cart count on page load
|
|
190
178
|
const initCart = () => {
|
|
191
|
-
console.log('[CartManager] DOM ready, loading initial cart count...');
|
|
192
179
|
this.loadInitialCartCount();
|
|
193
180
|
};
|
|
194
181
|
|
|
@@ -207,7 +194,6 @@
|
|
|
207
194
|
// Small delay to ensure DOM is ready
|
|
208
195
|
setTimeout(async () => {
|
|
209
196
|
const count = await this.getCartCount();
|
|
210
|
-
console.log('[CartManager] Loaded initial cart count:', count);
|
|
211
197
|
this.updateCartBadge(count);
|
|
212
198
|
}, 100);
|
|
213
199
|
}
|