@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
@@ -11,6 +11,11 @@
11
11
 
12
12
  {% assign image_loading = loading | default: "lazy" %}
13
13
  {% assign variants = product.variations | default: empty %}
14
+ {% assign product_call_for_pricing_raw = product.showCallForPricing | default: product.ShowCallForPricing | default: product.isCallForPricing | default: product.IsCallForPricing | default: false %}
15
+ {% assign product_show_call_for_pricing = false %}
16
+ {% if product_call_for_pricing_raw == true or product_call_for_pricing_raw == 'true' or product_call_for_pricing_raw == 1 or product_call_for_pricing_raw == '1' %}
17
+ {% assign product_show_call_for_pricing = true %}
18
+ {% endif %}
14
19
 
15
20
  {% comment %}Calculate default variant for add-to-cart{% endcomment %}
16
21
  {% assign defaultVariantForCart = null %}
@@ -29,6 +34,22 @@
29
34
  {% endif %}
30
35
  {% endif %}
31
36
 
37
+ {% assign variant_call_for_pricing_raw = defaultVariantForCart.showCallForPricing | default: defaultVariantForCart.ShowCallForPricing | default: defaultVariantForCart.isCallForPricing | default: defaultVariantForCart.IsCallForPricing | default: false %}
38
+ {% assign variant_show_call_for_pricing = false %}
39
+ {% if variant_call_for_pricing_raw == true or variant_call_for_pricing_raw == 'true' or variant_call_for_pricing_raw == 1 or variant_call_for_pricing_raw == '1' %}
40
+ {% assign variant_show_call_for_pricing = true %}
41
+ {% endif %}
42
+ {% assign card_show_call_for_pricing = false %}
43
+ {% if product_show_call_for_pricing or variant_show_call_for_pricing %}
44
+ {% assign card_show_call_for_pricing = true %}
45
+ {% endif %}
46
+
47
+ {% comment %}Product URL (must be set before data attributes){% endcomment %}
48
+ {% assign product_url = product.url | default: product.Url | default: product.link | default: product.Link %}
49
+ {% if product_url == blank %}
50
+ {% assign product_url = product.slug | default: product.id | default: '#' %}
51
+ {% endif %}
52
+
32
53
  {% comment %}Image fallback logic{% endcomment %}
33
54
  {% assign product_image = nil %}
34
55
  {% assign product_image_alt = product.name | default: product.slug | default: product.id %}
@@ -61,25 +82,27 @@
61
82
  {% endif %}
62
83
 
63
84
  <div class="product-card"
64
- data-product-id="{{ product.productId }}"
85
+ data-product-id="{{ product.productId | default: product.id }}"
65
86
  data-price="{{ product.prices.price }}"
87
+ data-price-string="{{ product.prices.price | money_with_settings: shop.settings }}"
66
88
  data-name="{{ product.name | default: product.slug | default: product.id | downcase }}"
89
+ data-title="{{ product.name | default: product.slug | default: product.id }}"
90
+ data-product-url="{{ product_url }}"
91
+ data-base-product-id="{{ product.productId | default: product.id }}"
92
+ {% if product_image %}data-image="{{ product_image }}"{% endif %}
93
+ data-show-call-for-pricing="{% if card_show_call_for_pricing %}true{% else %}false{% endif %}"
67
94
  data-product-type="{{ product.productType | default: product.type | default: 0 }}"
68
95
  data-variants-count="{% if variants and variants.size > 0 %}{{ variants.size }}{% else %}0{% endif %}"
69
- data-availability="{% if product.stockQuantity > 0 %}in-stock{% else %}out-of-stock{% endif %}">
96
+ data-availability="{% if product.inStock or product.available %}in-stock{% else %}out-of-stock{% endif %}">
70
97
 
71
98
  <div class="product-card__image-wrapper">
72
99
  {% hook 'product_card_image_before' %}
73
- {% assign product_url = product.url | default: product.Url | default: product.link | default: product.Link %}
74
- {% if product_url == blank %}
75
- {% assign product_url = product.slug | default: product.id | default: '#' %}
76
- {% endif %}
77
- <a href="{{ product_url }}" class="product-card__link">
100
+ <a href="/{{ product_url }}" class="product-card__link">
78
101
  {% if product_image %}
79
102
  <img src="{{ product_image }}"
80
103
  alt="{{ product_image_alt }}"
81
104
  class="product-card__image"
82
- loading="{{ image_loading }}">
105
+ loading="{{ image_loading }}" {% if image_fetchpriority %}fetchpriority="{{ image_fetchpriority }}"{% endif %}>
83
106
  {% else %}
84
107
  <div class="product-card__placeholder">
85
108
  <svg class="product-card__placeholder-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
@@ -109,7 +132,7 @@
109
132
  <span class="product-card__badge product-card__badge--new">New</span>
110
133
  {% endif %}
111
134
 
112
- {% if product.prices.mrp and product.prices.mrp > product.prices.price %}
135
+ {% if product.prices.mrp and product.prices.mrp > product.prices.price and card_show_call_for_pricing == false %}
113
136
  {% assign discount = product.prices.mrp | minus: product.prices.price | times: 100 | divided_by: product.prices.mrp | round %}
114
137
  <span class="product-card__badge product-card__badge--sale">-{{ discount }}%</span>
115
138
  {% endif %}
@@ -117,12 +140,28 @@
117
140
  <span class="product-card__badge product-card__badge--sold-out">Sold Out</span>
118
141
  {% endunless %}
119
142
  </div>
143
+
144
+ <!-- Product Actions (Wishlist, Quick View) -->
145
+ <div class="product-card__actions">
146
+ <button class="product-action-btn wishlist-btn" data-product-id="{{ product.productId | default: product.id }}" aria-label="Add to wishlist" aria-pressed="false" title="Add to wishlist" tabindex="0">
147
+ <!-- Empty heart by default; theme.js will toggle filled state -->
148
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
149
+ <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
150
+ </svg>
151
+ </button>
152
+ <button class="product-action-btn quick-view-btn" data-product-id="{{ product.productId | default: product.id }}" aria-label="Quick view" title="Quick view" tabindex="0">
153
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
154
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
155
+ <circle cx="12" cy="12" r="3"></circle>
156
+ </svg>
157
+ </button>
158
+ </div>
120
159
  </div>
121
160
 
122
161
  <div class="product-card__content">
123
162
  {% hook 'product_card_title_before' %}
124
163
  <h3 class="product-card__title">
125
- <a href="{{ product_url }}" class="product-card__title-link">
164
+ <a href="/{{ product_url }}" class="product-card__title-link">
126
165
  {{ product.name | default: product.slug | default: product.id }}
127
166
  </a>
128
167
  </h3>
@@ -130,7 +169,9 @@
130
169
 
131
170
  {% hook 'product_card_price_before' %}
132
171
  <div class="product-card__price" data-product-card-price="{{ product.productId }}">
133
- {% if variants and variants.size > 0 %}
172
+ {% if card_show_call_for_pricing %}
173
+ <span class="product-card__price-current">Call for pricing</span>
174
+ {% elsif variants and variants.size > 0 %}
134
175
  {% assign defaultVariant = null %}
135
176
  {% for variant in variants %}
136
177
  {% assign isAvailable = variant.inStock | default: variant.available | default: true %}
@@ -159,7 +200,7 @@
159
200
  <!-- Add to Cart Button -->
160
201
  <button class="product-card__cart-btn add-to-cart-btn"
161
202
  data-product-id="{{ defaultVariantProductId }}"
162
- data-base-product-id="{{ product.productId }}"
203
+ data-base-product-id="{{ product.productId | default: product.id }}"
163
204
  data-product-type="{{ product.productType | default: product.type | default: 0 }}"
164
205
  {% if variants and variants.size > 0 %}
165
206
  {% assign defaultAvailable = defaultVariantForCart.inStock | default: defaultVariantForCart.available | default: true %}
@@ -187,19 +228,19 @@
187
228
  <style>
188
229
  /* Product Card Variables */
189
230
  :root {
190
- --card-bg: #ffffff;
191
- --card-border: #e5e7eb;
192
- --card-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
193
- --card-radius: 8px;
194
- --card-padding: 16px;
195
- --badge-radius: 4px;
196
- --btn-radius: 6px;
197
- --color-primary: #111827;
198
- --color-secondary: #6b7280;
199
- --color-sale: #ef4444;
200
- --color-new: #10b981;
201
- --color-text: #111827;
202
- --color-text-muted: #6b7280;
231
+ --card-bg: {{ settings.color_background | default: '#ffffff' }};
232
+ --card-border: {{ settings.color_border | default: '#e5e7eb' }};
233
+ --card-shadow: 0 1px 2px rgba(0, 0, 0, calc({{ settings.shadow_opacity | default: 0.1 }} * 0.5));
234
+ --card-radius: {{ settings.border_radius_medium | default: 8 }}px;
235
+ --card-padding: {{ settings.spacing_element | default: 16 }}px;
236
+ --badge-radius: {{ settings.border_radius_small | default: 4 }}px;
237
+ --btn-radius: {{ settings.border_radius_small | default: 6 }}px;
238
+ --color-primary: {{ settings.color_primary | default: '#111827' }};
239
+ --color-secondary: {{ settings.color_secondary | default: '#6b7280' }};
240
+ --color-sale: {{ settings.color_error | default: '#ef4444' }};
241
+ --color-new: {{ settings.color_success | default: '#10b981' }};
242
+ --color-text: {{ settings.color_text | default: '#111827' }};
243
+ --color-text-muted: {{ settings.color_text_muted | default: '#6b7280' }};
203
244
  }
204
245
 
205
246
  /* Card Container */
@@ -273,6 +314,78 @@
273
314
  letter-spacing: 0.3px;
274
315
  }
275
316
 
317
+ /* Product action buttons (wishlist, quick view) */
318
+ /* Hidden by default: shown only when hovering/focusing the specific product card */
319
+ .product-card__actions {
320
+ position: absolute;
321
+ top: 10px;
322
+ right: 10px;
323
+ display: flex;
324
+ gap: 8px;
325
+ z-index: 3;
326
+ opacity: 1;
327
+ visibility: visible;
328
+ transform: translateY(0);
329
+ transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
330
+ pointer-events: auto;
331
+ }
332
+
333
+ /* Keep actions visible on all cards so quick view is always available */
334
+ .product-card:hover .product-card__actions,
335
+ .product-card:focus-within .product-card__actions {
336
+ opacity: 1;
337
+ visibility: visible;
338
+ transform: translateY(0);
339
+ pointer-events: auto;
340
+ }
341
+
342
+ .product-action-btn {
343
+ border: none;
344
+ background: rgba(255,255,255,0.92);
345
+ width: 36px;
346
+ height: 36px;
347
+ border-radius: 50%;
348
+ display: inline-flex;
349
+ align-items: center;
350
+ justify-content: center;
351
+ cursor: pointer;
352
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
353
+ transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease, opacity 0.12s ease;
354
+ }
355
+
356
+ .product-action-btn:hover {
357
+ transform: translateY(-2px);
358
+ box-shadow: 0 6px 20px rgba(0,0,0,0.12);
359
+ }
360
+
361
+ /* Ensure svg sizing still applies */
362
+ .product-action-btn svg {
363
+ width: 18px;
364
+ height: 18px;
365
+ color: #111827;
366
+ }
367
+
368
+ /* On touch devices (no hover), keep actions visible for accessibility */
369
+ @media (hover: none) and (pointer: coarse) {
370
+ .product-card__actions {
371
+ opacity: 1;
372
+ visibility: visible;
373
+ transform: translateY(0);
374
+ pointer-events: auto;
375
+ }
376
+ }
377
+
378
+ /* Wishlist active state */
379
+ .wishlist-btn.in-wishlist {
380
+ background: rgba(239, 68, 68, 0.95);
381
+ }
382
+
383
+ .wishlist-btn.in-wishlist svg {
384
+ fill: #ffffff;
385
+ stroke: #ffffff;
386
+ color: #ffffff;
387
+ }
388
+
276
389
  .product-card__badge--new {
277
390
  background: var(--color-new);
278
391
  color: white;
@@ -347,7 +460,7 @@
347
460
  font-weight: 500;
348
461
  cursor: pointer;
349
462
  margin-top: auto;
350
- border-radius: var(--border-radius-medium);
463
+ border-radius: var(--card-radius);
351
464
  border: none;
352
465
  }
353
466
 
@@ -391,10 +504,10 @@
391
504
  /* Mobile */
392
505
  @media screen and (max-width: 768px) {
393
506
  :root {
394
- --card-radius: 6px;
395
- --card-padding: 10px;
396
- --badge-radius: 3px;
397
- --btn-radius: 5px;
507
+ --card-radius: {{ settings.border_radius_small | default: 6 }}px;
508
+ --card-padding: {{ settings.spacing_small | default: 10 }}px;
509
+ --badge-radius: {{ settings.border_radius_small | default: 3 }}px;
510
+ --btn-radius: {{ settings.border_radius_small | default: 5 }}px;
398
511
  }
399
512
 
400
513
  .product-card {
@@ -477,7 +590,7 @@
477
590
  </style>
478
591
 
479
592
  {% if variants and variants.size > 0 %}
480
- <script type="application/json" class="product-card-variant-data" data-product-id="{{ product.productId }}">
593
+ <script type="application/json" class="product-card-variant-data" data-product-id="{{ product.productId | default: product.id }}">
481
594
  {
482
595
  "variants": [
483
596
  {% for variant in variants %}
@@ -490,208 +603,8 @@
490
603
  }{% unless forloop.last %},{% endunless %}
491
604
  {% endfor %}
492
605
  ],
493
- "baseProductId": {{ product.productId }}
606
+ "baseProductId": {{ product.productId | default: product.id }}
494
607
  }
495
608
  </script>
496
609
  {% endif %}
497
610
 
498
- <script>
499
- document.addEventListener('DOMContentLoaded', function() {
500
- const productCards = document.querySelectorAll('.product-card');
501
-
502
- productCards.forEach(card => {
503
- initializeProductCard(card);
504
- });
505
-
506
- function initializeProductCard(card) {
507
- const cartBtn = card.querySelector('.product-card__cart-btn');
508
- if (cartBtn) {
509
- const productType = parseInt(card.dataset.productType || cartBtn.dataset.productType || '0', 10);
510
- const variantsCount = parseInt(card.dataset.variantsCount || '0', 10);
511
-
512
- if (productType === 0 && variantsCount === 0) {
513
- cartBtn.addEventListener('click', function(e) {
514
- e.preventDefault();
515
- e.stopPropagation();
516
- handleAddToCart(this, card);
517
- }, true);
518
- }
519
- }
520
- }
521
-
522
- function handleAddToCart(button, card) {
523
- if (button.disabled) return;
524
-
525
- const productId = button.getAttribute('data-product-id');
526
- const originalText = button.querySelector('.product-card__cart-text')?.textContent || 'Add to Cart';
527
-
528
- if (button.querySelector('.product-card__cart-text')) {
529
- button.querySelector('.product-card__cart-text').textContent = 'Adding...';
530
- }
531
- button.disabled = true;
532
-
533
- addToCartAPI(productId)
534
- .then(response => {
535
- if (button.querySelector('.product-card__cart-text')) {
536
- button.querySelector('.product-card__cart-text').textContent = '✓ Added';
537
- }
538
-
539
- updateHeaderCart();
540
-
541
- // Unified bottom-center toast confirmation
542
- if (window.Theme && typeof window.Theme.showNotification === 'function') {
543
- window.Theme.showNotification('Product added to cart!', 'success', 3000);
544
- }
545
-
546
- setTimeout(() => {
547
- if (button.querySelector('.product-card__cart-text')) {
548
- button.querySelector('.product-card__cart-text').textContent = originalText;
549
- }
550
- button.disabled = false;
551
- }, 2000);
552
- })
553
- .catch(error => {
554
- console.error('Add to cart error:', error);
555
-
556
- // Check if error message indicates authentication is required
557
- // This handles cases where the error object might have requiresAuth info
558
- if (error.message && error.message.includes('Authentication required')) {
559
- // Try to open login modal
560
- if (window.Theme && window.Theme.openLoginModal) {
561
- window.Theme.openLoginModal();
562
- } else if (window.CartManager && window.CartManager.openLoginModal) {
563
- window.CartManager.openLoginModal();
564
- } else {
565
- const loginTrigger = document.querySelector('[data-login-modal-trigger]');
566
- if (loginTrigger) {
567
- loginTrigger.click();
568
- }
569
- }
570
- } else {
571
- // Show error message
572
- if (button.querySelector('.product-card__cart-text')) {
573
- button.querySelector('.product-card__cart-text').textContent = 'Error';
574
- }
575
- // Unified error toast
576
- if (window.Theme && typeof window.Theme.showNotification === 'function') {
577
- window.Theme.showNotification('Failed to add product to cart. Please try again.', 'error', 3000);
578
- }
579
- }
580
-
581
- setTimeout(() => {
582
- if (button.querySelector('.product-card__cart-text')) {
583
- button.querySelector('.product-card__cart-text').textContent = originalText;
584
- }
585
- button.disabled = false;
586
- }, 2000);
587
- });
588
- }
589
-
590
- function addToCartAPI(productId) {
591
- return fetch('/webstoreapi/carts/add', {
592
- method: 'POST',
593
- headers: {
594
- 'Content-Type': 'application/json',
595
- 'Accept': 'application/json'
596
- },
597
- body: JSON.stringify({
598
- productId: productId,
599
- quantity: 1
600
- })
601
- })
602
- .then(response => response.json())
603
- .then(data => {
604
- if (data.success) {
605
- return data;
606
- } else {
607
- // Check if authentication is required
608
- if (data.requiresAuth) {
609
- // Try to open login modal from Theme instance
610
- if (window.Theme && window.Theme.openLoginModal) {
611
- window.Theme.openLoginModal();
612
- } else if (window.CartManager && window.CartManager.openLoginModal) {
613
- window.CartManager.openLoginModal();
614
- } else {
615
- // Fallback: trigger login modal via data attribute
616
- const loginTrigger = document.querySelector('[data-login-modal-trigger]');
617
- if (loginTrigger) {
618
- loginTrigger.click();
619
- }
620
- }
621
- throw new Error('Authentication required');
622
- }
623
- throw new Error(data.error || data.message || 'Failed to add to cart');
624
- }
625
- });
626
- }
627
-
628
- function updateHeaderCart() {
629
- if (window.CartManager && typeof window.CartManager.getCartCount === 'function') {
630
- window.CartManager.getCartCount()
631
- .then(count => {
632
- updateCartBadge(count);
633
- })
634
- .catch(error => {
635
- fetchCartFromAPI();
636
- });
637
- } else {
638
- fetchCartFromAPI();
639
- }
640
-
641
- function fetchCartFromAPI() {
642
- fetch('/webstoreapi/cart', {
643
- method: 'GET',
644
- headers: {
645
- 'Accept': 'application/json',
646
- 'X-Requested-With': 'XMLHttpRequest'
647
- }
648
- })
649
- .then(response => response.json())
650
- .then(data => {
651
- if (data.success && data.cart) {
652
- const itemCount = data.cart.itemCount || data.cart.cartQuantity || (data.cart.items ? data.cart.items.length : 0) || 0;
653
- updateCartBadge(itemCount);
654
- } else {
655
- updateCartBadge(0);
656
- }
657
- })
658
- .catch(error => {
659
- console.error('Error updating header cart:', error);
660
- updateCartBadge(0);
661
- });
662
- }
663
-
664
- function updateCartBadge(count) {
665
- const itemCount = parseInt(count, 10) || 0;
666
-
667
- const cartCountElements = document.querySelectorAll('.site-header__cart-count, [data-cart-count]');
668
- cartCountElements.forEach(el => {
669
- el.textContent = itemCount;
670
- el.setAttribute('data-cart-count', itemCount.toString());
671
- el.style.display = itemCount > 0 ? 'flex' : 'none';
672
- });
673
-
674
- if (window.CartManager && typeof window.CartManager.updateCartBadge === 'function') {
675
- window.CartManager.updateCartBadge(itemCount);
676
- }
677
-
678
- const event = new CustomEvent('cart:updated', {
679
- detail: { count: itemCount }
680
- });
681
- document.dispatchEvent(event);
682
- }
683
- }
684
-
685
- function initCartUpdate() {
686
- setTimeout(() => {
687
- updateHeaderCart();
688
- }, 100);
689
- }
690
-
691
- if (document.readyState === 'loading') {
692
- document.addEventListener('DOMContentLoaded', initCartUpdate);
693
- } else {
694
- initCartUpdate();
695
- }
696
- });
697
- </script>
@@ -48,38 +48,129 @@
48
48
  </div>
49
49
 
50
50
  <style>
51
+ :root {
52
+ --color-primary: {{ settings.color_primary | default: '#000000' }};
53
+ --color-primary-hover: {{ settings.color_primary_dark | default: '#333333' }};
54
+ --color-primary-light: {{ settings.color_primary_light | default: '#666666' }};
55
+ --color-success: {{ settings.color_success | default: '#22c55e' }};
56
+ --color-success-light: {{ settings.color_success | default: '#22c55e' }};
57
+ --color-success-dark: {{ settings.color_success | default: '#22c55e' }};
58
+ --color-danger: {{ settings.color_error | default: '#ef4444' }};
59
+ --color-danger-light: {{ settings.color_error | default: '#ef4444' }};
60
+ --color-danger-dark: {{ settings.color_error | default: '#ef4444' }};
61
+ --color-text: {{ settings.color_text | default: '#000000' }};
62
+ --color-text-light: {{ settings.color_text_light | default: '#999999' }};
63
+ --color-text-muted: {{ settings.color_text_muted | default: '#666666' }};
64
+ --color-background: {{ settings.color_background | default: '#ffffff' }};
65
+ --color-card-bg: {{ settings.color_surface | default: '#f5f5f5' }};
66
+ --color-border: {{ settings.color_border | default: '#cccccc' }};
67
+ --color-border-light: {{ settings.color_surface | default: '#f5f5f5' }};
68
+ --color-white: #ffffff;
69
+ --color-black: {{ settings.color_primary_dark | default: '#333333' }};
70
+ --color-gray-25: {{ settings.color_background | default: '#ffffff' }};
71
+ --color-gray-50: {{ settings.color_surface | default: '#f5f5f5' }};
72
+ --color-gray-100: {{ settings.color_surface | default: '#f5f5f5' }};
73
+ --color-gray-200: {{ settings.color_border | default: '#cccccc' }};
74
+ --color-gray-300: {{ settings.color_border | default: '#cccccc' }};
75
+ --color-gray-400: {{ settings.color_text_muted | default: '#666666' }};
76
+ --color-gray-500: {{ settings.color_text_muted | default: '#666666' }};
77
+ --color-gray-600: {{ settings.color_text_muted | default: '#666666' }};
78
+ --color-gray-700: {{ settings.color_text | default: '#000000' }};
79
+ --color-gray-800: {{ settings.color_text | default: '#000000' }};
80
+ --color-gray-900: {{ settings.color_text | default: '#000000' }};
81
+ /* Shadow variables - mapped from settings */
82
+ --shadow-opacity: {{ settings.shadow_opacity | default: 0.1 }};
83
+ --shadow-blur: {{ settings.shadow_blur | default: 8 }}px;
84
+ --shadow-spread: {{ settings.shadow_spread | default: 0 }}px;
85
+
86
+ /* Border radius - mapped from settings */
87
+ --radius-sm: {{ settings.border_radius_small | default: 6 }}px;
88
+ --radius-md: {{ settings.border_radius_medium | default: 10 }}px;
89
+ --radius-lg: {{ settings.border_radius_large | default: 16 }}px;
90
+ --radius-xl: 24px;
91
+ --radius-2xl: 32px;
92
+ --radius-full: 9999px;
93
+
94
+ /* Spacing - mapped from settings */
95
+ --spacing-xs: {{ settings.spacing_xsmall | default: 4 }}px;
96
+ --spacing-sm: {{ settings.spacing_small | default: 8 }}px;
97
+ --spacing-md: {{ settings.spacing_element | default: 16 }}px;
98
+ --spacing-lg: {{ settings.spacing_component | default: 24 }}px;
99
+ --spacing-xl: {{ settings.spacing_large | default: 32 }}px;
100
+
101
+ /* Typography - mapped from settings */
102
+ --font-primary: {{ settings.font_primary | default: '' }}, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
103
+ --font-heading: {{ settings.font_heading | default: '' }}, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
104
+ --font-display: {{ settings.font_display | default: '' }}, 'Georgia', 'Times New Roman', serif;
105
+ --font-body: {{ settings.font_body | default: '' }}, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
106
+ --font-weight-bold: {{ settings.font_weight_bold | default: 700 }};
107
+ --font-weight-medium: {{ settings.font_weight_medium | default: 500 }};
108
+ --line-height-heading: {{ settings.line_height_heading | default: 1.2 }};
109
+
110
+ /* Text sizes - based on font_size_base from settings */
111
+ --text-xs: 12px;
112
+ --text-sm: {{ settings.font_size_base | default: 14 }}px;
113
+ --text-base: {{ settings.font_size_base | default: 14 }}px;
114
+ --text-lg: 16px;
115
+ --text-xl: 18px;
116
+ --text-2xl: 21px;
117
+
118
+ /* Transitions - mapped from settings */
119
+ --transition-fast: 133ms cubic-bezier(0, 0, 0.2, 1);
120
+ --transition: {{ settings.button_transition_speed | default: 200 }}ms cubic-bezier(0, 0, 0.2, 1);
121
+ --transition-slow: 300ms cubic-bezier(0, 0, 0.2, 1);
122
+ --duration-150: 150ms;
123
+ --duration-300: 300ms;
124
+ --ease-out: cubic-bezier(0, 0, 0.2, 1);
125
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
126
+ --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
127
+
128
+ /* Z-index values */
129
+ --z-10: 10;
130
+ --z-modal: 1050;
131
+ --z-dropdown: 1000;
132
+
133
+ /* Line heights - mapped from settings */
134
+ --leading-none: 1;
135
+ --leading-normal: {{ settings.line_height_base | default: 1.6 }};
136
+ --leading-snug: 1.375;
137
+
138
+ /* Letter spacing - mapped from settings */
139
+ --tracking-wider: {{ settings.letter_spacing_uppercase | default: 0.05 }}em;
140
+ }
141
+
51
142
  .rating {
52
143
  display: flex;
53
144
  align-items: center;
54
- gap: 0.5rem;
145
+ gap: var(--space-2);
55
146
  }
56
147
 
57
148
  .rating-stars {
58
149
  display: inline-flex;
59
- gap: 0.125rem;
60
- font-size: 1rem;
61
- line-height: 1;
150
+ gap: var(--space-0-5);
151
+ font-size: var(--text-base);
152
+ line-height: var(--leading-none);
62
153
  }
63
154
 
64
155
  .rating-star {
65
156
  color: #fbbf24;
66
- font-size: 1rem;
157
+ font-size: var(--text-base);
67
158
  }
68
159
 
69
160
  .rating-star-empty {
70
- color: #e5e7eb;
161
+ color: var(--color-gray-200);
71
162
  }
72
163
 
73
164
  .rating-star-half {
74
- background: linear-gradient(90deg, #fbbf24 50%, #e5e7eb 50%);
165
+ background: linear-gradient(90deg, #fbbf24 50%, var(--color-gray-200) 50%);
75
166
  -webkit-background-clip: text;
76
167
  -webkit-text-fill-color: transparent;
77
168
  background-clip: text;
78
169
  }
79
170
 
80
171
  .rating-count {
81
- font-size: 0.875rem;
82
- color: {{ settings.color_text_light }};
172
+ font-size: var(--text-sm);
173
+ color: var(--color-text-light);
83
174
  }
84
175
  </style>
85
176