@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.
Files changed (89) hide show
  1. package/lib/lib/dev-server.js +309 -40
  2. package/lib/lib/liquid-engine.js +3 -1
  3. package/lib/lib/mock-data.js +36 -124
  4. package/lib/lib/widget-service.js +12 -4
  5. package/package.json +1 -1
  6. package/test-theme/assets/async-sections.js +32 -24
  7. package/test-theme/assets/cart-drawer.js +20 -22
  8. package/test-theme/assets/cart-manager.js +1 -15
  9. package/test-theme/assets/checkout-price-handler.js +12 -11
  10. package/test-theme/assets/checkout.css +1415 -0
  11. package/test-theme/assets/checkout.js +3174 -0
  12. package/test-theme/assets/components.css +178 -29
  13. package/test-theme/assets/delivery-zone.js +1 -1
  14. package/test-theme/assets/product-detail.css +1050 -0
  15. package/test-theme/assets/product-detail.js +2940 -0
  16. package/test-theme/assets/theme.css +95 -120
  17. package/test-theme/assets/theme.js +781 -186
  18. package/test-theme/layout/theme.liquid +91 -17
  19. package/test-theme/sections/content.liquid +64 -57
  20. package/test-theme/sections/footer-fallback.liquid +57 -7
  21. package/test-theme/sections/footer.liquid +63 -12
  22. package/test-theme/sections/header-fallback.liquid +41 -41
  23. package/test-theme/sections/header.liquid +41 -51
  24. package/test-theme/sections/hero-fallback.liquid +1 -1
  25. package/test-theme/sections/hero.liquid +159 -136
  26. package/test-theme/snippets/account-sidebar.liquid +121 -29
  27. package/test-theme/snippets/add-to-cart-modal.liquid +258 -206
  28. package/test-theme/snippets/breadcrumbs.liquid +98 -11
  29. package/test-theme/snippets/cart-drawer.liquid +93 -0
  30. package/test-theme/snippets/delivery-zone-city-selector.liquid +101 -15
  31. package/test-theme/snippets/delivery-zone-modal.liquid +529 -84
  32. package/test-theme/snippets/delivery-zone-search.liquid +104 -18
  33. package/test-theme/snippets/login-modal.liquid +269 -82
  34. package/test-theme/snippets/mega-menu.liquid +130 -43
  35. package/test-theme/snippets/news-thumbnail.liquid +120 -28
  36. package/test-theme/snippets/pagination.liquid +1 -1
  37. package/test-theme/snippets/price.liquid +100 -9
  38. package/test-theme/snippets/product-card-related.liquid +22 -4
  39. package/test-theme/snippets/product-card-simple.liquid +521 -25
  40. package/test-theme/snippets/product-card.liquid +145 -232
  41. package/test-theme/snippets/rating.liquid +100 -9
  42. package/test-theme/snippets/skeleton-collection-grid.liquid +94 -8
  43. package/test-theme/snippets/skeleton-product-card.liquid +102 -16
  44. package/test-theme/snippets/skeleton-product-grid.liquid +87 -1
  45. package/test-theme/snippets/social-sharing.liquid +133 -32
  46. package/test-theme/templates/account/dashboard.liquid +30 -0
  47. package/test-theme/templates/account/loyalty-redemption.liquid +29 -28
  48. package/test-theme/templates/account/loyalty.liquid +45 -43
  49. package/test-theme/templates/account/order-detail.liquid +15 -8
  50. package/test-theme/templates/account/orders.liquid +189 -35
  51. package/test-theme/templates/account/profile.liquid +509 -114
  52. package/test-theme/templates/account/register.liquid +18 -8
  53. package/test-theme/templates/account/return-orders.liquid +31 -30
  54. package/test-theme/templates/account/store-credit.liquid +27 -26
  55. package/test-theme/templates/account/subscriptions.liquid +22 -5
  56. package/test-theme/templates/account/wishlist.liquid +88 -19
  57. package/test-theme/templates/address-book.liquid +166 -69
  58. package/test-theme/templates/categories.liquid +90 -30
  59. package/test-theme/templates/checkout.liquid +137 -3834
  60. package/test-theme/templates/error.liquid +23 -21
  61. package/test-theme/templates/index.liquid +29 -0
  62. package/test-theme/templates/login.liquid +33 -6
  63. package/test-theme/templates/order-confirmation.liquid +67 -9
  64. package/test-theme/templates/page.liquid +418 -206
  65. package/test-theme/templates/product-detail.liquid +124 -3878
  66. package/test-theme/templates/products.liquid +155 -30
  67. package/test-theme/templates/search.liquid +739 -225
  68. package/test-theme/widgets/brand-carousel.liquid +102 -82
  69. package/test-theme/widgets/brand.liquid +78 -50
  70. package/test-theme/widgets/carousel.liquid +253 -121
  71. package/test-theme/widgets/category-list-carousel.liquid +32 -8
  72. package/test-theme/widgets/category-list.liquid +21 -6
  73. package/test-theme/widgets/category.liquid +104 -37
  74. package/test-theme/widgets/discount-time.liquid +326 -119
  75. package/test-theme/widgets/footer-menu.liquid +115 -23
  76. package/test-theme/widgets/footer.liquid +118 -5
  77. package/test-theme/widgets/gallery.liquid +29 -5
  78. package/test-theme/widgets/header-menu.liquid +25 -13
  79. package/test-theme/widgets/header.liquid +64 -26
  80. package/test-theme/widgets/html.liquid +29 -6
  81. package/test-theme/widgets/news.liquid +6 -0
  82. package/test-theme/widgets/product-canvas.liquid +20 -12
  83. package/test-theme/widgets/product-carousel.liquid +118 -56
  84. package/test-theme/widgets/shared/product-grid.liquid +12 -0
  85. package/test-theme/widgets/single-product.liquid +688 -250
  86. package/test-theme/widgets/spacebar-carousel.liquid +39 -10
  87. package/test-theme/widgets/spacebar.liquid +77 -6
  88. package/test-theme/widgets/splash.liquid +40 -30
  89. package/test-theme/widgets/testimonial-carousel.liquid +111 -67
@@ -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
- content: {
1697
- couponValid: true,
1698
- coupon: {
1699
- discountAmount: 25,
1700
- couponCode: 'SAVE25',
1701
- description: 'Get 25% off on all products! Limited time only.',
1702
- endOn: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
1703
- startOn: new Date().toISOString(),
1704
- ruleToApply: 'by_percent',
1705
- fileName: '',
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-footer-1',
1896
- type: 'Footer',
1897
- section: 'footer',
1898
- sectionName: 'footer',
1899
- pageId: 'all',
1839
+ id: 'widget-news-1',
1840
+ type: 'News',
1841
+ section: 'content',
1842
+ sectionName: 'content',
1843
+ pageId: 'home',
1900
1844
  status: 'active',
1901
- Position: 1,
1845
+ Position: 19,
1846
+ Title: 'Latest News',
1902
1847
  settings: {
1903
- showWidgetTitle: 'No',
1904
- showNewsletter: true,
1905
- showSocial: true
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
- columns: [
1910
- {
1911
- Title: 'About Us',
1912
- title: 'About Us',
1913
- Links: [
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
- socialLinks: [
1954
- { Platform: 'facebook', platform: 'facebook', Url: 'https://facebook.com', url: 'https://facebook.com' },
1955
- { Platform: 'twitter', platform: 'twitter', Url: 'https://twitter.com', url: 'https://twitter.com' },
1956
- { Platform: 'instagram', platform: 'instagram', Url: 'https://instagram.com', url: 'https://instagram.com' },
1957
- { Platform: 'youtube', platform: 'youtube', Url: 'https://youtube.com', url: 'https://youtube.com' }
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
- widget.data.content = parsedContent;
152
- widget.content = parsedContent; // Legacy support
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o2vend/theme-cli",
3
- "version": "1.0.37",
3
+ "version": "1.0.38",
4
4
  "description": "O2VEND Theme Development CLI - Standalone tool for local theme development",
5
5
  "bin": {
6
6
  "o2vend": "./bin/o2vend"
@@ -3,7 +3,7 @@
3
3
  * Loads non-critical page sections asynchronously to improve initial page load time
4
4
  */
5
5
 
6
- (function() {
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 showSaleBadge = product.prices?.mrp && product.prices.mrp > product.prices.price;
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-price="${product.prices?.price || product.price}"
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="/collections/${slug}" class="collection-link">View Collection</a>
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="/collections/${slug}">${this.escapeHtml(collection.title || collection.name)}</a>
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
- ;(function(){
2
- console.log('[Cart Drawer] Script loaded')
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
- function formatMoney(amount, currency = shopCurrency()) {
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
- console.warn('Failed to format money with locale/currency', locale, currency, error);
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
- function resetCheckoutButton() {
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
- function closeDrawer(){
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 function loadCartData(){
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
- console.warn('Failed to read variant image from localStorage:', e);
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
- function updateCartFooter(cart) {
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 function syncQuantity(input){
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
- function initCartQuantity() {
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', function(){
343
+ document.addEventListener('DOMContentLoaded', () => {
346
344
  const toggle = qsa('[data-cart-toggle]')
347
- toggle.forEach(t => t.addEventListener('click', function(e){
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', function(e){
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', function(e){
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', function(e){
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', function(e) {
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', function() {
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', function(e) {
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
- (function() {
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
  }